How to get "backtrace" (like gdb) using only ptrace (linux, x86 / x86_64)

I want to get backtrace like output like gdb does. But I want to do this via ptrace() directly. My platform is Linux, x86; and later x86_64.

Now I only want to read the return addresses from the stack, without conversion to character names.

So, for a test program compiled in -O0 mode on 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(); } 

I ran my program and connected to ptrace to test the program from the start. Then I will do PTRACE_CONT and wait for the signal. When the test program will commit suicide; the signal will be delivered to my program. At this point, I want to read the return addresses, they will be similar (since the kill function is currently active):

  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 

How to find the return addresses of the currently stopped testing process using ptrace ? Will there be a loop over the frames? When should I stop such a cycle?

PS: yes, it is also very similar to the backtrace(3) libc function in the idea, but I want to do it from the outside via ptrace.

+4
source share
2 answers

The example submitted by osgx will only work with code that uses frame pointers. x86_64 code generated by GCC with optimization is missing. The x86 vdso kernel code does not use pointers to at least some processors. GCC 4.6 (with optimization) does not use frame pointers in x86 mode.

All of the above combine to make the "stack scan through frame pointers" extremely unreliable.

You can use libunwind (which supports both local (in-process) and global (due to the process via ptrace) unwind).

Or you will have to libunwind very large part of libunwind .

An example of getting a return path through ptrace using libunwind .

+6
source

Maybe the source of the pstack(1) utility will help me: (online git from debian ). Unfortunately, this is only x86 32-bit

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 

In addition, a quick test says that it is not very reliable, but sometimes it can print something.

0
source

All Articles