¿Cómo analizo el archivo de volcado de núcleo de un programa con GDB cuando tiene parámetros de línea de comandos?


156

Mi programa funciona así:

exe -p param1 -i param2 -o param3

Se estrelló y se genera un archivo de volcado de memoria, core.pid.

Quiero analizar el archivo de volcado de núcleo por

gdb ./exe -p param1 -i param2 -o param3 core.pid

Pero GDB reconoce los parámetros del archivo EXE como entrada de GDB.

¿Cómo analizo un archivo de volcado de núcleo en esta situación?


1
¿Está seguro de que su exeno es un script de shell (para establecer algunas variables, etc.) como, por ejemplo, firefoxen Linux?
Basile Starynkevitch

Respuestas:


182

Puede usar el núcleo con GDB de muchas maneras, pero pasar los parámetros que se pasarán al ejecutable a GDB no es la forma de usar el archivo central. Esta también podría ser la razón por la que obtuvo ese error. Puede usar el archivo principal de las siguientes maneras:

gdb <executable> <core-file>o gdb <executable> -c <core-file>o

gdb <executable>
...
(gdb) core <core-file>

Al usar el archivo central no tiene que pasar argumentos. El escenario de bloqueo se muestra en GDB (verificado con GDB versión 7.1 en Ubuntu).

Por ejemplo:

$ ./crash -p param1 -o param2
Segmentation fault (core dumped)
$ gdb ./crash core
GNU gdb (GDB) 7.1-ubuntu
...
Core was generated by `./crash -p param1 -o param2'. <<<<< See this line shows crash scenario
Program terminated with signal 11, Segmentation fault.
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

Si desea pasar parámetros al ejecutable para su depuración en GDB, use --args.

Por ejemplo:

$ gdb --args ./crash -p param1 -o param2
GNU gdb (GDB) 7.1-ubuntu
...
(gdb) r
Starting program: /home/@@@@/crash -p param1 -o param2

Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

Las páginas de manual serán útiles para ver otras opciones de GDB.


38

Uso simple de GDB, para depurar archivos coredump:

gdb <executable_path> <coredump_file_path>

Un archivo coredump para un "proceso" se crea como un archivo "core.pid".

Después de ingresar al indicador GDB (en la ejecución del comando anterior), escriba:

...
(gdb) where

Esto le proporcionará la información de la pila, donde puede analizar la causa del accidente / falla. Otro comando, para los mismos propósitos es:

...
(gdb) bt full

Esto es lo mismo que arriba. Por convención, enumera toda la información de la pila (que finalmente conduce a la ubicación del bloqueo).


22

Simplemente omita los parámetros. GDB no los necesita:

gdb ./exe core.pid

Pero esto no funciona. La advertencia de salida de gdb: el archivo central puede no coincidir con el archivo ejecutable especificado. Error al leer una imagen de archivo de objeto válida de la memoria.
Treper

66
"el archivo principal puede no coincidir con el ejecutable especificado". ¿Modificó exe después de que produjo el núcleo? ¿Quizás lo reconstruiste con diferentes opciones de línea de comandos? Es muy importante darle a GDB exactamente el mismo binario que produjo el núcleo. Si no lo haces, sacarás basura.
Empleado ruso

2
También asegúrese de que el binario que se pasa a gdb no esté eliminado. Puede ejecutar 'archivo <nombre binario>' que muestra que está despojado o no.
Diwakar Sharma

12

objdump+ gdbejemplo ejecutable mínimo

TL; DR:

Ahora para la configuración de prueba educativa completa:

C Principal

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int myfunc(int i) {
    *(int*)(NULL) = i; /* line 7 */
    return i - 1;
}

int main(int argc, char **argv) {
    /* Setup some memory. */
    char data_ptr[] = "string in data segment";
    char *mmap_ptr;
    char *text_ptr = "string in text segment";
    (void)argv;
    mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
    strcpy(mmap_ptr, data_ptr);
    mmap_ptr[10] = 'm';
    mmap_ptr[11] = 'm';
    mmap_ptr[12] = 'a';
    mmap_ptr[13] = 'p';
    printf("text addr: %p\n", text_ptr);
    printf("data addr: %p\n", data_ptr);
    printf("mmap addr: %p\n", mmap_ptr);

    /* Call a function to prepare a stack trace. */
    return myfunc(argc);
}

Compila y ejecuta para generar el núcleo:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out

Salida:

text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)

GDB nos señala la línea exacta donde ocurrió la falla de segmentación, que es lo que la mayoría de los usuarios desean durante la depuración:

gdb -q -nh main.out core

luego:

Reading symbols from main.out...done.
[New LWP 27479]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
7           *(int*)(NULL) = i;
(gdb) bt
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
#1  0x000000000040072b in main (argc=1, argv=0x7ffec6739328) at main.c:28

lo que nos lleva directamente a la línea buggy 7.

Los argumentos de la CLI se almacenan en el archivo principal y no es necesario volver a pasarlos

Para responder a las preguntas específicas del argumento CLI, vemos que si cambiamos los argumentos cli, por ejemplo, con:

rm -f core
./main.out 1 2

entonces esto se refleja en el bactrace anterior sin ningún cambio en nuestros comandos:

Reading symbols from main.out...done.
[New LWP 21838]
Core was generated by `./main.out 1 2'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
7           *(int*)(NULL) = i; /* line 7 */
(gdb) bt
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
#1  0x0000564583cf2858 in main (argc=3, argv=0x7ffcca4effa8) at main.c:2

Entonces nota cómo ahora argc=3. Por lo tanto, esto debe significar que el archivo central almacena esa información. Supongo que solo lo almacena como argumentos de main, al igual que almacena los argumentos de cualquier otra función.

Esto tiene sentido si considera que el volcado del núcleo debe estar almacenando toda la memoria y el estado del registro del programa, por lo que tiene toda la información necesaria para determinar el valor de los argumentos de la función en la pila actual.

Menos obvio es cómo inspeccionar las variables de entorno: cómo obtener la variable de entorno de un volcado de núcleo Las variables de entorno también están presentes en la memoria, por lo que el objdump contiene esa información, pero no estoy seguro de cómo enumerarlas todas de una vez. , uno por uno de la siguiente manera funcionó en mis pruebas:

p __environ[0]

Análisis de Binutils

Mediante el uso de herramientas binutils como readelfy objdump, podemos volcar la información contenida en el corearchivo como el estado de la memoria.

La mayor parte / todo también debe ser visible a través de GDB, pero esas herramientas binutils ofrecen un enfoque más masivo que es conveniente para ciertos casos de uso, mientras que GDB es más conveniente para una exploración más interactiva.

Primero:

file core

nos dice que el corearchivo es en realidad un archivo ELF :

core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'

Es por eso que podemos inspeccionarlo más directamente con las herramientas habituales de binutils.

Un vistazo rápido al estándar ELF muestra que en realidad hay un tipo ELF dedicado a él:

Elf32_Ehd.e_type == ET_CORE

Se puede encontrar más información sobre el formato en:

man 5 core

Luego:

readelf -Wa core

da algunas pistas sobre la estructura del archivo. La memoria parece estar contenida en los encabezados regulares del programa:

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  NOTE           0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000     0
  LOAD           0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
  LOAD           0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R   0x1000
  LOAD           0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW  0x1000

y hay algunos metadatos más presentes en un área de notas, en particular prstatuscontiene la PC :

Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
  Owner                 Data size       Description
  CORE                 0x00000150       NT_PRSTATUS (prstatus structure)
  CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
  CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
  CORE                 0x00000130       NT_AUXV (auxiliary vector)
  CORE                 0x00000246       NT_FILE (mapped files)
    Page size: 4096
                 Start                 End         Page Offset
    0x0000000000400000  0x0000000000401000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000600000  0x0000000000601000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000601000  0x0000000000602000  0x0000000000000001
        /home/ciro/test/main.out
    0x00007f8d939ee000  0x00007f8d93bae000  0x0000000000000000
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93bae000  0x00007f8d93dae000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93dae000  0x00007f8d93db2000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db2000  0x00007f8d93db4000  0x00000000000001c4
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db8000  0x00007f8d93dde000  0x0000000000000000
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fdd000  0x00007f8d93fde000  0x0000000000000025
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fde000  0x00007f8d93fdf000  0x0000000000000026
        /lib/x86_64-linux-gnu/ld-2.23.so
  CORE                 0x00000200       NT_FPREGSET (floating point registers)
  LINUX                0x00000340       NT_X86_XSTATE (x86 XSAVE extended state)

objdump puede volcar fácilmente toda la memoria con:

objdump -s core

que contiene:

Contents of section load1:

 4007d0 01000200 73747269 6e672069 6e207465  ....string in te
 4007e0 78742073 65676d65 6e740074 65787420  xt segment.text 

Contents of section load15:

 7ffec6739220 73747269 6e672069 6e206461 74612073  string in data s
 7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd  egment....g{.gx.

Contents of section load4:

 1612010 73747269 6e672069 6e206d6d 61702073  string in mmap s
 1612020 65676d65 6e740000 11040000 00000000  egment..........

que coincide exactamente con el valor stdout en nuestra ejecución.

Esto se probó en Ubuntu 16.04 amd64, GCC 6.4.0 y binutils 2.26.1.



9

Un enfoque ligeramente diferente le permitirá omitir GDB por completo. Si todo lo que quiere es una traza inversa, la utilidad específica de Linux 'catchsegv' capturará SIGSEGV y mostrará una traza inversa.


3

No importa si el ejecutable tiene argumentos o no. Para ejecutar GDB en cualquier binario con un archivo core generado, la sintaxis se encuentra a continuación.

Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile

Permítanme tomar el siguiente ejemplo para una mayor comprensión.

bash-4.1$ **gdb l3_entity 6290-corefile**

**Core was generated** by `/dir1/dir2/dir3/l3_entity **Program terminated with signal SIGABRT, Aborted.**
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#10
(gdb)

De la salida anterior, puede adivinar algo sobre el núcleo, ya sea un acceso NULL, SIGABORT, etc.

Estos números del 0 al 10 son los marcos de pila de GDB. Estos marcos de pila no son de su binario. En los cuadros 0-10 anteriores, si sospecha que algo está mal, seleccione ese cuadro

(gdb) frame 8

Ahora para ver más detalles al respecto:

(gdb) list +

Para seguir investigando el problema, puede imprimir los valores variables sospechosos aquí en este momento.

(gdb) print thread_name

0

Simplemente escriba el comando:

$ gdb <Binary> <codeDump>

O

$ gdb <binary>

$ gdb) core <coreDump>

No es necesario proporcionar ningún argumento de línea de comando. El volcado de código generado debido a un ejercicio anterior.


-1

Puede analizar el archivo de volcado de núcleo utilizando el comando "gdb".

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 

1
out.txt es un archivo ejecutable? Eso parece una extensión de archivo engañosa.
Alan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.