SIGSEGV 上的 mips _Unwind_Backtrace

发布于 2024-11-14 15:53:59 字数 3227 浏览 4 评论 0原文

在 mips 平台上,我正在尝试进行 Unwind 工作。目前,如果我手动发出 print_trace 堆栈跟踪正确显示如下:

backtrace_helper 0x4b6958

backtrace_helper 0x4b6ab4

backtrace_helper 0x2ac2f628

获得3个堆栈帧。

./v(print_trace+0x38) [0x4b6958]

./v(主+0x90) [0x4b6ab4]

/lib/libc.so.0(__uClibc_main+0x24c) [0x2ac2f628]

但是当发生 SIGSEGV 时,堆栈跟踪不会显示正确的函数调用顺序。

backtrace_helper 0x4b7a74

backtrace_helper 0x2ab9b84c

获得2个堆栈帧。

./v(getLineIDByPhyIdx+0x3d8) [0x4b7a74]

/lib/libpthread.so.0(__new_sem_post+0x2c8) [0x2ab9b84c]

我正在使用 -g -fexceptions -rdynamic 进行编译。我还看到了 如何当我的 gcc C++ 应用程序崩溃时生成堆栈跟踪,其中第二个答案提到了错误的地址,但是当我按照他的方式设置时,它只更改了第二帧,其余的是同样的,代码片段如下:

caller_address = (void *) uc->uc_mcontext.gregs[30]; // Frame pointer (from wikipedia here)  

 fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
  sig_num, strsignal(sig_num), info->si_addr, 
  (void *)caller_address);

 size = backtrace(array, 50);

 /* overwrite sigaction with caller's address */
 array[1] = caller_address;

 messages = backtrace_symbols(array, size);

代码:

int main(int argc, char *argv[]) {
    registerSignalHandler(signalHandler);

    print_trace();

    {
        // Seg Fault
        int *p = NULL;
        *p = 54;
    }
}

void print_trace(void) {
    void *array[10];
    size_t size;
    char **strings;
    size_t i;

    /* Get the address at the time the signal was raised from the EIP (x86) */
    size = backtrace(array, 10);
    strings = backtrace_symbols(array, size);

    printf("Obtained %zd stack frames.\n", size);

    for (i = 0; i < size; i++)
        printf("%s\n", strings[i]);

    free(strings);
}



static _Unwind_Reason_Code
backtrace_helper (struct _Unwind_Context *ctx, void *a)
{
    struct trace_arg *arg = a;

    assert (unwind_getip != NULL);

    /* We are first called with address in the __backtrace function. Skip it. */
    if (arg->cnt != -1) {
        arg->array[arg->cnt] = (void *) unwind_getip (ctx);
        printf("backtrace_helper %p \n", arg->array[arg->cnt]);
    }
    if (++arg->cnt == arg->size)
        return _URC_END_OF_STACK;
    return _URC_NO_REASON;
}

/*
 * Perform stack unwinding by using the _Unwind_Backtrace.
 *
 * User application that wants to use backtrace needs to be
 * compiled with -fexceptions option and -rdynamic to get full
 * symbols printed.
 */
int backtrace (void **array, int size)
{
    struct trace_arg arg = { .array = array, .size = size, .cnt = -1 };

    if (unwind_backtrace == NULL)
        backtrace_init();

    if (size >= 1)
        unwind_backtrace (backtrace_helper, &arg);

    return arg.cnt != -1 ? arg.cnt : 0;
}


void signalHandler( int sig, siginfo_t* siginfo, void* notused)
{
    /* Print out the signal info */
    signalInfo(sig, siginfo);

    switch (sig) {
        case SIGSEGV:
        {
            print_trace();
            abort();
        }
    }
}

On a mips platform, I am trying to get Unwind work. Currently if I issue print_trace manually stack trace is correctly shown as below:

backtrace_helper 0x4b6958

backtrace_helper 0x4b6ab4

backtrace_helper 0x2ac2f628

Obtained 3 stack frames.

./v(print_trace+0x38) [0x4b6958]

./v(main+0x90) [0x4b6ab4]

/lib/libc.so.0(__uClibc_main+0x24c) [0x2ac2f628]

But when a SIGSEGV occurs, stack trace does not show correct function call sequence.

backtrace_helper 0x4b7a74

backtrace_helper 0x2ab9b84c

Obtained 2 stack frames.

./v(getLineIDByPhyIdx+0x3d8) [0x4b7a74]

/lib/libpthread.so.0(__new_sem_post+0x2c8) [0x2ab9b84c]

I am compiling with -g -fexceptions -rdynamic. Also I have seen How to generate a stacktrace when my gcc C++ app crashes in which 2nd answer mentiones about wrong address but when I set as he does but it only changes 2nd frame and rest is the same, code snippet is below:

caller_address = (void *) uc->uc_mcontext.gregs[30]; // Frame pointer (from wikipedia here)  

 fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
  sig_num, strsignal(sig_num), info->si_addr, 
  (void *)caller_address);

 size = backtrace(array, 50);

 /* overwrite sigaction with caller's address */
 array[1] = caller_address;

 messages = backtrace_symbols(array, size);

Code:

int main(int argc, char *argv[]) {
    registerSignalHandler(signalHandler);

    print_trace();

    {
        // Seg Fault
        int *p = NULL;
        *p = 54;
    }
}

void print_trace(void) {
    void *array[10];
    size_t size;
    char **strings;
    size_t i;

    /* Get the address at the time the signal was raised from the EIP (x86) */
    size = backtrace(array, 10);
    strings = backtrace_symbols(array, size);

    printf("Obtained %zd stack frames.\n", size);

    for (i = 0; i < size; i++)
        printf("%s\n", strings[i]);

    free(strings);
}



static _Unwind_Reason_Code
backtrace_helper (struct _Unwind_Context *ctx, void *a)
{
    struct trace_arg *arg = a;

    assert (unwind_getip != NULL);

    /* We are first called with address in the __backtrace function. Skip it. */
    if (arg->cnt != -1) {
        arg->array[arg->cnt] = (void *) unwind_getip (ctx);
        printf("backtrace_helper %p \n", arg->array[arg->cnt]);
    }
    if (++arg->cnt == arg->size)
        return _URC_END_OF_STACK;
    return _URC_NO_REASON;
}

/*
 * Perform stack unwinding by using the _Unwind_Backtrace.
 *
 * User application that wants to use backtrace needs to be
 * compiled with -fexceptions option and -rdynamic to get full
 * symbols printed.
 */
int backtrace (void **array, int size)
{
    struct trace_arg arg = { .array = array, .size = size, .cnt = -1 };

    if (unwind_backtrace == NULL)
        backtrace_init();

    if (size >= 1)
        unwind_backtrace (backtrace_helper, &arg);

    return arg.cnt != -1 ? arg.cnt : 0;
}


void signalHandler( int sig, siginfo_t* siginfo, void* notused)
{
    /* Print out the signal info */
    signalInfo(sig, siginfo);

    switch (sig) {
        case SIGSEGV:
        {
            print_trace();
            abort();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

亢潮 2024-11-21 15:53:59

MIPS 上实际上从未使用过帧指针,并且在不深入符号的情况下获得回溯需要一些启发式方法。

典型的方法是分析当前指令指针之前的代码并尝试找到调整 SP 的函数序言。使用该信息可以找出前一帧的位置等。

请参阅这些幻灯片了解一些血淋淋的细节:
http://elinux.org/images/0/07/Intricacies_of_a_MIPS_Stack_Backtrace_Implementation.pdf

Frame pointer is practically never used on MIPS and obtaining backtrace without digging into symbols requires some heuristics.

Typical approach is to analyze code preceding current instruction pointer and try finding function prologue that adjusts SP. Using that info one can figure out location of preceding frame, etc.

See these slides for some of the gory details:
http://elinux.org/images/0/07/Intricacies_of_a_MIPS_Stack_Backtrace_Implementation.pdf

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文