如何得到一个“回溯”(如gdb)只使用ptrace(Linux,x86 / x86_64)

我想像gdb那样得到一个类似于backtrace的输出。 但我想直接通过ptrace()来做到这一点。 我的平台是Linux,x86; 和后来的x86_64。

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

所以,对于testing程序,由gcc-4.5-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连接,以开始testing程序。 然后,我会做PTRACE_CONT并等待信号。 当testing程序会做自杀时, 信号将被传送到我的程序。 这时我要读取返回地址,他们会像(因为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 

如何find当前停止的testing过程的返回地址与ptrace ? 会有帧循环? 我应该什么时候停止这种循环?

PS:是的,这也是非常像backtrace(3) libc函数的想法,但我想通过ptrace在外部执行此操作。

osgx发布的例子只能用于使用帧指针的代码。 由GCC生成的x86_64代码优化不会。 x86上的内核vdso代码至少在某些处理器上不使用帧指针。 GCC 4.6(有优化)在x86模式下也不使用帧指针。

以上所有的组合使得“通过帧指针爬栈”极不可靠。

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

或者你将不得不重新实现libunwind很大一部分。

使用libunwind通过ptrace获取回溯的示例 。

可能是, 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 

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