[英]Using libunwind for implementing exceptions
在编译器上工作,需要一些帮助来理解和使用 libunwind。 这是我到目前为止所拥有的:
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef void *data_t;
typedef struct exception_stack_st *exception_stack_t;
struct exception_stack_st {
unw_cursor_t catch_block;
exception_stack_t prev;
};
/* PROTOTYPES */
void foo(void);
void bar(void);
void set_try(void);
void clear_try(void);
bool check_exception(void);
void throw_exception(data_t);
void print_backtrace();
/* GLOBALS */
exception_stack_t exception_stack = NULL;
data_t curr_exception = NULL;
int main(void) {
foo();
}
void foo(void) {
printf("In foo\n");
set_try();
if(check_exception()) {
printf("EXCEPTION: %s\n", (char*)curr_exception);
goto CATCH;
}
bar();
printf("This should never run\n");
CATCH:
clear_try();
printf("Leaving foo\n");
}
void bar(void) {
printf("In bar\n");
throw_exception("Throwing an exception in bar");
printf("Leaving bar\n");
}
void set_try(void) {
unw_cursor_t cursor;
unw_context_t context;
unw_word_t ip, offp;
char buf[1024];
unw_getcontext(&context);
unw_init_local(&cursor, &context);
unw_step(&cursor);
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_proc_name(&cursor, buf, 1024, &offp);
printf("%s+0x%lx IP %lx\n", buf, offp, ip);
exception_stack_t cb = malloc(sizeof(struct exception_stack_st));
cb->catch_block = cursor;
cb->prev = exception_stack;
exception_stack = cb;
}
void clear_try(void) {
if (exception_stack != NULL)
exception_stack = exception_stack->prev;
curr_exception = NULL;
}
void throw_exception(data_t exception) {
unw_word_t ip, offp;
char buf[1024];
curr_exception = exception;
unw_get_reg(&(exception_stack->catch_block), UNW_REG_IP, &ip);
unw_get_proc_name(&(exception_stack->catch_block), buf, 1024, &offp);
printf("%s+0x%lx IP %lx\n", buf, offp, ip);
unw_resume(&(exception_stack->catch_block));
printf("PANIC: unw_resume returned.\n");
exit(1);
}
bool check_exception(void) {
return curr_exception != NULL;
}
void print_backtrace() {
unw_cursor_t cursor;
unw_context_t context;
char buf[1024];
unw_getcontext(&context);
unw_init_local(&cursor, &context);
printf("BACKTRACE:\n");
while(unw_step(&cursor) > 0) {
unw_get_proc_name(&cursor, buf, 1024, NULL);
printf("%s\n", buf);
}
}
好吧,这已经很混乱了,但一些上下文可能有助于证明这些奇怪的选择是合理的。 我希望做的就是调用throw_exception
在调用后的任意点下降调用堆栈set_try
在foo
,以展开堆栈和调用后恢复CPU状态向右set_try
但在此之前的条件。 虽然这目前只是一个小型 C 程序,但我打算在编译器中使用这些函数的一般结构,该结构将生成必要的函数调用(类似于使用 g++ 在 C++ 中完成异常的方式),这就是为什么我有标签+goto 作为一种快速模仿我将生成的程序集的方式。 我试过使用 libunwind 的 setjmp 实现,但它不太适合我的用例。
我遇到的问题与展开调用堆栈后unw_resume
在哪里恢复有关。 printf("This should never run\\n")
似乎每次都运行,无论如何。 我的理解是它应该将堆栈和 CPU 状态恢复到调用unw_getcontext
时存储的任何状态,我认为存储的状态是正确的,因为 IP 寄存器(或 PC 寄存器,因为这是 x86_64 ) 在我调用set_try
和throw_exception
时在游标中throw_exception
。 我什至多次跳入 gdb 以在调用 set_try 之后和条件之前查看 PC 寄存器,并且每次都匹配打印输出。
我的问题是:
unw_resume
吗?提前致谢!
首先是好消息:修复后,您的程序会产生(我认为是预期的)输出:
./a.out
In foo
foo+0x31 IP 557615f273a1
In bar
foo+0x31 IP 557615f273a1
foo+0x31 IP 557615f273a1
EXCEPTION: Throwing an exception in bar
Leaving foo
现在坏消息是:您的原始程序存在一些单独的问题:
用于初始化游标的上下文(机器状态)必须在使用游标的持续时间内保持有效。 这在unw_init_local 手册页中有明确说明。
违反此规则会导致您的程序在unw_resume
调用期间在我的机器上执行 SIGSEGV。
unw_step
更新cursor ,但不更新上下文,实际上是后者用于恢复unw_resume
机器状态。
这可以在unw_resume 文档中更清楚地说明。
要解决问题 1,只需将unw_context_t
移动到exception_stack_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_try
转换为宏或始终内联的函数,并摆脱unw_step
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.