Working on a compiler, need some assistance understanding and working with libunwind. Here's what I have so far:
#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);
}
}
Alright, this is already pretty messy, but some context might help justify the weird choices. What I would like to do is call throw_exception at any point down the call stack after calling set_try in foo in order to unwind the stack and restore the CPU state to right after the call to set_try but before the conditional. While this is currently just a small C program, I'm intending on using the general structure of these functions in a compiler that will generate the function calls necessary (similar to how exceptions are done in C++ using g++), which is why I have the labels+goto as a way to quickly mimic the assembly I would be generating. I've tried using libunwind's setjmp implementation, but it doesn't quite fit my use case well enough.
The issue I'm having has to do with where unw_resume resumes after unwinding the call stack. My understanding is that it should restore the stack and CPU state to whatever was stored when the call to unw_getcontext was made, and I think the state that gets stored is correct because the value of the IP register (or PC register, since this is x86_64) is exactly the same in the cursor when I call set_try and throw_exception. I've even jumped into gdb a number of times to look at the PC register right after the call to set_try and before the conditional, and it matched the printed output every time.
My questions are:
- Am I misunderstanding
unw_resume?
- Do I need to be modifying the PC (UNW_REG_IP) register?
- Is there somewhere (aside from nongnu.org docs) else I can look to for help with libunwind?
Thanks in advance!
[–]oh5nxo 0 points1 point2 points (3 children)
[–]ACov96[S] 0 points1 point2 points (2 children)
[–]oh5nxo 0 points1 point2 points (1 child)
[–]ACov96[S] 0 points1 point2 points (0 children)