簡體   English   中英

使用 libunwind 實現異常

[英]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_tryfoo ,以展開堆棧和調用后恢復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_trythrow_exception時在游標中throw_exception 我什至多次跳入 gdb 以在調用 set_try 之后和條件之前查看 PC 寄存器,並且每次都匹配打印輸出。

我的問題是:

  • 我誤解了unw_resume嗎?
  • 我需要修改 PC (UNW_REG_IP) 寄存器嗎?
  • 有沒有其他地方(除了 nongnu.org 文檔)我可以尋求 libunwind 的幫助?

提前致謝!

首先是好消息:修復后,您的程序會產生(我認為是預期的)輸出:

./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

現在壞消息是:您的原始程序存在一些單獨的問題:

  1. 用於初始化游標的上下文(機器狀態)必須在使用游標的持續時間內保持有效。 這在unw_init_local 手冊頁中有明確說明。

    違反此規則會導致您的程序在unw_resume調用期間在我的機器上執行 SIGSEGV。

  2. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM