C 使用libunwind实现异常

C 使用libunwind实现异常,c,exception,stack-unwinding,libunwind,C,Exception,Stack Unwinding,Libunwind,使用编译器时,需要一些帮助来理解和使用libunwind。以下是我目前掌握的情况: #仅定义UNW\u本地 #包括 #包括 #包括 #包括 #包括 typedef void*数据; typedef结构异常\u堆栈\u st*异常\u堆栈\u t; 结构异常\u堆栈\u st{ 取消光标锁定块; 异常(堆栈)(上一个);; }; /*原型*/ void foo(void); 空栏(void); 无效集合(void); 无效清除(无效); bool检查异常(无效); 无效抛出异常(数据); 无效打印

使用编译器时,需要一些帮助来理解和使用libunwind。以下是我目前掌握的情况:

#仅定义UNW\u本地
#包括
#包括
#包括
#包括
#包括
typedef void*数据;
typedef结构异常\u堆栈\u st*异常\u堆栈\u t;
结构异常\u堆栈\u st{
取消光标锁定块;
异常(堆栈)(上一个);;
};
/*原型*/
void foo(void);
空栏(void);
无效集合(void);
无效清除(无效);
bool检查异常(无效);
无效抛出异常(数据);
无效打印回溯();
/*全球的*/
exception\u stack\u t exception\u stack=NULL;
数据当前异常=空;
内部主(空){
foo();
}
无效foo(无效){
printf(“在foo\n中”);
set_try();
如果(检查异常(){
printf(“异常:%s\n”,(char*)当前异常);
去抓;
}
bar();
printf(“这不应该运行\n”);
捕获:
清除并重试();
printf(“离开foo\n”);
}
空心条(空心){
printf(“条形图中”);
抛出异常(“在条中抛出异常”);
printf(“离开酒吧”);
}
无效集\尝试(无效){
unw_光标\u t光标;
环境背景;
unw_word_t ip,offp;
char-buf[1024];
unw_getcontext(上下文和上下文);
unw_init_local(光标和上下文);
unw_步进(&光标);
unw_get_reg(&cursor,unw_reg_IP,&IP);
unw_get_proc_名称(&光标、buf、1024和offp);
printf(“%s+0x%lx IP%lx\n”,buf,offp,IP);
异常堆栈cb=malloc(sizeof(结构异常堆栈st));
cb->catch_block=光标;
cb->prev=异常\u堆栈;
异常_stack=cb;
}
作废清除(作废){
if(异常_堆栈!=NULL)
异常堆栈=异常堆栈->上一个;
curr_exception=NULL;
}
无效抛出异常(数据异常){
unw_word_t ip,offp;
char-buf[1024];
当前异常=异常;
unw_get_reg(&(异常堆栈->捕获块),unw_reg_IP,&IP);
unw_get_proc_name(&(异常堆栈->捕获块)、buf、1024和offp);
printf(“%s+0x%lx IP%lx\n”,buf,offp,IP);
unw_恢复(&(异常堆栈->捕获块);
printf(“恐慌:返回unw_恢复。\n”);
出口(1);
}
bool检查异常(无效){
返回当前异常!=NULL;
}
无效打印\u回溯(){
unw_光标\u t光标;
环境背景;
char-buf[1024];
unw_getcontext(上下文和上下文);
unw_init_local(光标和上下文);
printf(“回溯:\n”);
while(卸载步骤和光标)>0){
unw_get_proc_name(&cursor,buf,1024,NULL);
printf(“%s\n”,buf);
}
}
好吧,这已经相当混乱了,但是一些上下文可能有助于证明这些奇怪的选择是正确的。我想做的是,在调用
foo
中的
set\u try
之后,在调用堆栈的任何一点调用
throw\u exception
,以便在调用
set\u try
之后,但在条件调用之前,展开堆栈并将CPU状态恢复为。虽然这只是一个小的C程序,但我打算使用这些函数的一般结构,编译器将生成必要的函数调用(类似于C++中如何使用G++来完成异常),这就是为什么我有标签+ GOTO作为快速模仿我将要生成的程序集的原因。我尝试过使用libunwind的setjmp实现,但它不太适合我的用例


我遇到的问题是,在解除调用堆栈后,unw_resume在何处恢复。无论发生什么情况,
printf(“这应该永远不会运行”)
似乎每次都会运行。我的理解是,它应该将堆栈和CPU状态恢复到调用
unw_getcontext
时存储的任何状态,我认为存储的状态是正确的,因为IP寄存器(或PC寄存器,因为这是x86_64)的值当我调用
set\u try
throw\u exception
时,光标中的值完全相同。我甚至多次跳入gdb,在调用set_try之后和条件之前查看PC寄存器,每次它都匹配打印输出

我的问题是:

  • 我是不是误解了你的简历
  • 我是否需要修改PC(UNW_REG_IP)寄存器
  • 是否有其他地方(除了nongnu.org文档)可以帮助我使用libunwind

提前谢谢

首先是好消息:修复后,您的程序将产生(我假设是预期的)输出:

现在坏消息是:您的原始程序有几个单独的问题:

  • 用于初始化游标的上下文(计算机状态)必须在使用游标的持续时间内保持有效。这在本手册中有明确说明

    违反此规则导致您的程序在
    unw\u resume
    调用期间在我的机器上SIGSEGV

  • unw\u步骤
    更新光标,但不更新上下文,后者实际上用于在
    unw\u resume
    中恢复机器状态

    这一点在未来几年可能会更加明确

  • 要解决问题1,只需将
    unw\u context\u t
    移动到
    exception\u stack\u st
    中,如下所示:

    struct exception_stack_st {
      unw_context_t context;
      unw_cursor_t cursor;
      exception_stack_t prev;
    };
    
    并一起初始化它们:

      exception_stack_t cb = malloc(sizeof(*cb));
      unw_getcontext(&cb->context);
      unw_init_local(&cb->cursor, &cb->context);
    
    要解决问题2,需要在要返回的帧中建立机器上下文


    这需要将
    set\u try
    转换为宏或始终内联的函数,并摆脱
    unw\u步骤

    “我要解决的问题与……”——您忘了描述实际问题。你已经描述了(某种程度上)你预期会发生什么,但是实际发生了什么?好的,我已经更新了这个问题。问题是每次我执行程序时都会运行“printf”(“thisline shouldn should run”\n),即使应该跳过它。太棒了!我来试一试,非常感谢!
      exception_stack_t cb = malloc(sizeof(*cb));
      unw_getcontext(&cb->context);
      unw_init_local(&cb->cursor, &cb->context);