linux gdb x86 x86-64 ptrace

Cómo obtener un "backtrace"(como gdb) usando solo ptrace(linux, x86/x86_64)



x86-64 (2)

Puede ser que la fuente de la pstack(1) me ayude: (git en línea de Debian ). Lamentablemente, esto es solo x86 de 32 bits

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

Además, la prueba rápida dice que no es muy confiable, pero a veces puede imprimir algo.

Quiero obtener una salida de tipo backtrace como lo hace gdb. Pero quiero hacer esto a través de ptrace() directamente. Mi plataforma es Linux, x86; y, más tarde x86_64.

Ahora solo quiero leer las direcciones de devolución de la pila, sin convertirlas en nombres de símbolos.

Entonces, para el programa de prueba, compilado en modo -O0 por 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(); }

Comenzaré mi programa y me conectaré con ptrace para probar el programa al principio. Luego, haré PTRACE_CONT y esperaré la señal. Cuando el programa de prueba hará una auto-matanza; la señal será entregada a mi programa. En este momento quiero leer las direcciones de retorno, serán como (porque la función de kill está activa en este momento):

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

¿Cómo puedo encontrar las direcciones de retorno del proceso de prueba actualmente detenido con ptrace ? Habrá un ciclo sobre los cuadros? ¿Cuándo debería detener ese ciclo?

PD: sí, esto también es muy parecido a backtrace(3) función libc en idea, pero quiero hacerlo externamente a través de ptrace.


El ejemplo publicado por osgx solo funcionará con código que usa punteros de marco. x86_64 código x86_64 producido por GCC con optimizaciones no lo hace. El código kernel vdso en x86 no usa punteros de marco en al menos algunos procesadores. GCC 4.6 (con optimización) tampoco usa punteros de cuadro en el modo x86 .

Todo lo anterior se combina para hacer que el "rastreo de la pila a través de los indicadores de cuadro" sea muy poco confiable.

Puede usar libunwind (que admite el libunwind local (en proceso) y global (fuera de proceso a través de ptrace)).

O tendrá que volver a implementar una gran parte de libunwind .

Ejemplo de obtener retroceso a través de ptrace utilizando libunwind .