objdump
+ gdb
ejemplo 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 readelf
y objdump
, podemos volcar la información contenida en el core
archivo 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 core
archivo 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 prstatus
contiene 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.
exe
no es un script de shell (para establecer algunas variables, etc.) como, por ejemplo,firefox
en Linux?