4

backtrace我想像gdb 一样获得类似的输出。但我想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:是的,这也很像idea中的backtrace(3)libc函数,但我想通过ptrace在外部执行此操作。

4

2 回答 2

7

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

以上所有因素结合起来,使“通过帧指针进行堆栈爬行”变得非常不可靠。

您可以使用libunwind(支持本地(进程内)和全局(通过 ptrace 进程外)展开)。

否则您将不得不重新实现很大一部分libunwind.

ptrace通过using获取回溯的示例libunwind

于 2011-08-31T15:19:47.517 回答
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 

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

于 2011-08-31T14:26:50.170 回答