我希望像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从外部做到这一点。
答案 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位
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
此外,快速测试表明它不是很可靠,但有时它可以打印一些东西。