如何使用ptrace(linux,x86 / x86_64)获得“回溯”(如gdb)

时间:2011-08-31 14:15:33

标签: linux gdb x86 x86-64 ptrace

我希望像gdb那样获得backtrace - 类似的输出。但是我想直接通过ptrace()来做这件事。我的平台是Linux,x86;以及后来的x86_64。

现在我只想从堆栈中读取返回地址,而不转换为符号名称。

因此,对于测试程序,由-O0编译为gcc-4.5模式:

  int g() {
    kill(getpid(),SIGALRM);
  }
  int f() {
    int a;
    int b;
    a = g();
    b = a;
    return a+b;
  }
  int e() {
    int c;
    c = f();
  }
  main() {
    return e();
  }

我将启动一个程序,并在一开始就与ptrace连接以测试程序。然后,我将做PTRACE_CONT并等待信号。当测试程序会做自杀时;信号将传递给我的程序。此时我想读取返回地址,它们就像(因为kill函数此时处于活动状态):

 0x00_some_address_in_g
 0x00_some_address_in_f
 0x00_some_address_in_e
 0x00_some_address_in_main
 0x00_some_address_in__libc_start_main

如何使用ptrace找到当前停止的测试流程的返回地址?框架上会有循环吗?什么时候应该停止这样的循环?

PS:是的,这也很像backtrace(3) libc function的想法,但我想通过ptrace从外部做到这一点。

2 个答案:

答案 0 :(得分:6)

osgx发布的示例仅适用于使用帧指针的代码。 GCC通过优化生成的x86_64代码没有。 vdso上的内核x86代码在至少某些处理器上不使用帧指针。 GCC 4.6(带优化)也不在x86模式下使用帧指针。

所有这些结合起来使“通过帧指针进行堆栈爬行”非常不可靠。

您可以使用libunwind(同时支持local(进程中)和global(通过ptrace进程外)展开。

或者你必须重新实现libunwind的很大一部分。

Example使用ptrace通过libunwind获取回溯。

答案 1 :(得分:0)

可能是pstack(1)实用程序的来源将帮助我:(来自debian的在线git)。不幸的是,这只是x86 32位

http://anonscm.debian.org/gitweb/?p=collab-maint/pstack.git;a=blob;f=pstack.c;h=61beb8d10fa490492ab351115f261614d00adb6d;hb=HEAD#l547

 547 static int crawl(int pid)
 548 {
 549   unsigned long pc, fp, nextfp, nargs, i, arg;
 550   int error_occured = 0;
 551 
 552   errno = 0;
 553   fp = -1;
 554 
 555   pc = ptrace(PTRACE_PEEKUSER, pid, EIP * 4, 0);
 556   if (pc != -1 || !errno)
 557     fp = ptrace(PTRACE_PEEKUSER, pid, EBP * 4, 0);
 558 
 559   if ((pc != -1 && fp != -1) || !errno) {
 560     print_pc(pc);
 561     for ( ; !errno && fp; ) {
 562       nextfp = ptrace(PTRACE_PEEKDATA, pid, fp, 0);
 563       if (nextfp == (unsigned) -1 && errno) break;
 564 
 565       nargs = (nextfp - fp - 8) / 4;
 566       if (nargs > MAXARGS) nargs = MAXARGS;
 567       if (nargs > 0) {
 568         fputs(" (", stdout);
 569         for (i = 1; i <= nargs; i++) {
 570           arg = ptrace(PTRACE_PEEKDATA, pid, fp + 4 * (i + 1), 0);
 571           if (arg == (unsigned) -1 && errno) break;
 572           printf("%lx", arg);
 573           if (i < nargs) fputs(", ", stdout);
 574         }
 575         fputc(')', stdout);
 576         nargs = nextfp - fp - 8 - (4 * nargs);
 577         if (!errno && nargs > 0) printf(" + %lx\n", nargs);
 578         else fputc('\n', stdout);
 579       } else fputc('\n', stdout);
 580 
 581       if (errno || !nextfp) break;
 582       pc = ptrace(PTRACE_PEEKDATA, pid, fp + 4, 0);
 583       if (pc == (unsigned) -1 && errno) break;
 584       fp = nextfp;
 585       print_pc(pc);
 586     }
 587     if (fp) error_occured = 1;
 588   } else error_occured = 1;
 589 
 590   if (error_occured) perror("crawl");
 591   else errno = 0;
 592   return errno;
 593 }
 594 

此外,快速测试表明它不是很可靠,但有时它可以打印一些东西。