Работая над компилятором, нужна помощь в понимании и работе с 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
, чтобы размотать стек и восстановить состояние процессора сразу после вызова set_try
, но до условна. Хотя в настоящее время это всего лишь небольшая программа C, я намерен использовать общую структуру этих функций в компиляторе, который будет генерировать необходимые вызовы функций (аналогично тому, как выполняются исключения в C ++ с использованием g ++), поэтому У меня есть ярлыки + goto как способ быстро скопировать c сборку, которую я буду генерировать. Я пытался использовать реализацию setjmp в libunwind, но она недостаточно хорошо подходит для моего варианта использования.
Проблема, с которой я столкнулся, связана с тем, где unw_resume
возобновляется после разматывания стека вызовов. printf("This should never run\n")
, кажется, работает каждый раз, несмотря ни на что. Насколько я понимаю, он должен восстановить состояние стека и процессора до того, что было сохранено при вызове unw_getcontext
, и я думаю, что сохраняемое состояние является правильным, поскольку значение регистра IP (или регистра P C) , поскольку это x86_64) точно так же в курсоре, когда я вызываю set_try
и throw_exception
. Я даже несколько раз заходил в gdb, чтобы посмотреть на регистр P C сразу после вызова set_try и перед условным, и он соответствовал выводу на печать каждый раз.
Мои вопросы:
- Не понимаю ли я
unw_resume
? - Нужно ли изменять регистр P C (UNW_REG_IP)?
- Есть ли где-то (кроме nongnu.org docs) еще можно обратиться за помощью по libunwind?
Заранее спасибо!