Search code examples
linuxgdbx86x86-64ptrace

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


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

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

So, for test program, compiled in -O0 mode by 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 will start a my program and connect with ptrace to test program at very beginning. Then, I will do PTRACE_CONT and will wait for signal. When test program will do a self-kill; the signal will be delivered to my program. At this moment I want to read return addresses, they will be like (because kill function is active at the moment):

 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 can I find return addresses of currently stopped test process with ptrace? There will be a loop over frames? When should I stop such loop?

PS: yes, this is also very like backtrace(3) libc function in idea, but I want to do this externally via ptrace.


Solution

  • The example posted by osgx will work only with code that uses frame pointers. x86_64 code produced by GCC with optimizations doesn't. The kernel vdso code on x86 doesn't use frame pointers on at least some processors. GCC 4.6 (with optimization) doesn't use frame pointers in x86 mode either.

    All of the above combine to make the "stack crawl via frame pointers" exceedingly unreliable.

    You can use libunwind (which supports both local (in-process) and global (out-of-process via ptrace) unwinding).

    Or you'll have to re-implement very large portion of libunwind.

    Example of getting backtrace via ptrace using libunwind.