开发者

mips _Unwind_Backtrace on SIGSEGV

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 Fau开发者_如何学Clt
        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();
        }
    }
}


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜