Solaris: Analizando ficheros Core Dump

Kernel, OpenSolaris, mdb Dejar un comentario

Vamos a ver de una forma sencilla y rápida como podemos analizar el fichero core generado por una aplicación en nuestro sistema. No vamos a utilizar ningún tipo de herramienta, sino únicamente las que disponemos en un sistema normalmente, esto es así por que quiero enseñar lo sencillo que es intentar descubrir la causa de un core simplemente con mdb.


Lo primero que tenemos que hacer, antes de enfrentarnos a un core desconocido es analizar el core que genere una aplicación que nosotros conozcamos, nada mejor para esto, que crear nuestra propia aplicación, la cual generará un fichero core de una forma controlada.

Creamos el siguiente fichero gen_core.c:

 

#include < stdio.h >
void funcion(int *a);

main()
{
int *p;
printf(" main(): Entrada n");
funcion(p);
printf(" main(): Salida n");
return 0;
}

void funcion(int *a)
{
printf("funcion(): Entrada0 n");
a=NULL;
printf("funcion(): Entrada1 n");
*a=1;
printf("funcion(): Salida n");
}

El programa tiene una función la cual es la encargada de generar el coredump, la razón de que el core lo genere una función es poder identificar en la pila los distintos anidamientos en las llamadas.

Compilamos nuestro programa C.

 

(root@huelva)# gcc gen_core.c -o gen_core
(root@huelva)#

Una vez terminada la compilación podemos ejecutar el binario para generar el fichero core que necesitamos, pero antes de hacer esto debemos comprobar cual es el límite del tamaño de los fichero core, para ello utilizamos el comando ulimit.

(root@huelva)# ulimit -a
core file size        (blocks, -c) 0
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
open files                    (-n) 256
pipe size          (512 bytes, -p) 10
stack size            (kbytes, -s) 8480
cpu time             (seconds, -t) unlimited
max user processes            (-u) 7957
virtual memory        (kbytes, -v) unlimited

Podemos ver en la salida del comando que la línea core file size está a 0, esto signifca que no se generará ficheros core, para cambiar este valor ejecutamos el siguiente comando.

(root@huelva)# ulimit -c unlimited

Ahora podemos ejecutar nuestro binario para que se genere un fichero core.

(root@huelva)# ./gen_core
main(): Entrada
funcion(): Entrada0
funcion(): Entrada1
Segmentation Fault
(root@huelva)#
(root@huelva)# ls -lrt
total 4598
-rw-------   1 root     root         287 Apr  3 13:37 gen_core.c
-rwx------   1 root     root        7044 Apr  3 13:37 gen_core
-rw-------   1 root     root     2324149 Apr  3 13:50 core

Podemos ver en la salida que se ha producido un Segmentation Fault y que se ha generado un fichero core. Para analizar el fichero core vamos a utilizar la herramienta mdb, como primer parámetro utilizaremos el fichero binario que hemos compilado y como segundo parámetro el fichero core que se ha generado.

(root@huelva)# mdb ./gen_core ./core
Loading modules: [ libc.so.1 ld.so.1 ]
>
> ::status
debugging core file of gen_core (32-bit) from huelva
file: gen_core
initial argv: ./gen_core
threading model: native threads
status: process terminated by SIGSEGV (Segmentation Fault)
>

Con el comando de mdb ::status podemos ver el estatus del fichero core.

El primer paso es analizar el contenido de la pila justo cuando se produjo el core, para ello utilizaremos el comando de mdb ::stack.

>::stack
funcion+0x30(0, 80508b9, 8060bb0, 8060bc0)
main+0x37(1, 8047958, 8047960)
_start+0x80(1, 8047a80, 0, 8047a8b, 8047ae2, 8047b10)
>

La pila se lee de abajo hacia arriba, con lo cual, lo último que se ejecutó antes del core está en la dirección de memoria funcion y el desplazamiento 0×30. MDB dispone de un comando que nos permite desensamblar las intrucciones, vamos a ejecutar el siguiente comando en mdb para ver el contenido de la función funcion().

>> funcion::dis
funcion:                        pushl  %ebp
funcion+1:                      movl   %esp,%ebp
funcion+3:                      subl   $0x8,%esp
funcion+6:                      subl   $0xc,%esp
funcion+9:                      pushl  $0x8050a1f
funcion+0xe:                    call   -0x260   < plt ="libc.so.1`printf" >
funcion+0x13:                   addl   $0x10,%esp
funcion+0x16:                   movl   $0x0,0x8(%ebp)
funcion+0x1d:                   subl   $0xc,%esp
funcion+0x20:                   pushl  $0x8050a35
funcion+0x25:                   call   -0x277   < plt ="libc.so.1`printf" >
funcion+0x2a:                   addl   $0x10,%esp
funcion+0x2d:                   movl   0x8(%ebp),%eax
funcion+0x30:                   movl   $0x1,(%eax)
funcion+0x36:                   subl   $0xc,%esp
funcion+0x39:                   pushl  $0x8050a4b
funcion+0x3e:                   call   -0x290   < plt ="libc.so.1`printf" >
funcion+0x43:                   addl   $0x10,%esp
funcion+0x46:                   leave
funcion+0x47:                   ret

Podemos comparar la salida desemsamblada de la función y con el código fuente de nuestro programa en C, podemos ver las tres llamadas a printf, sabemos que el core se debe generar entre la segunda y la tercera llamada a printf. Antes, cuando ejecutamos el comando ::stack apareció la dirección de la llamada a funcion() con un desplazamiento, podemos decirle a mdb que nos desensamble esta parte y nos daría algo parecido a:

> ::stack
funcion+0x30(0, 80508b9, 8060bb0, 8060bc0)
main+0x37(1, 8047958, 8047960)
_start+0x80(1, 8047a80, 0, 8047a8b, 8047ae2, 8047b10)
> funcion+0x30::dis
funcion:                        pushl  %ebp
funcion+1:                      movl   %esp,%ebp
funcion+3:                      subl   $0x8,%esp
funcion+6:                      subl   $0xc,%esp
funcion+9:                      pushl  $0x8050a1f
funcion+0xe:                    call   -0x260   < plt ="libc.so.1`printf" >
funcion+0x13:                   addl   $0x10,%esp
funcion+0x16:                   movl   $0x0,0x8(%ebp)
funcion+0x1d:                   subl   $0xc,%esp
funcion+0x20:                   pushl  $0x8050a35
funcion+0x25:                   call   -0x277   < plt ="libc.so.1`printf" >
funcion+0x2a:                   addl   $0x10,%esp
funcion+0x2d:                   movl   0x8(%ebp),%eax
funcion+0x30:                   movl   $0x1,(%eax)
funcion+0x36:                   subl   $0xc,%esp
funcion+0x39:                   pushl  $0x8050a4b
funcion+0x3e:                   call   -0x290   < plt ="libc.so.1`printf" >
funcion+0x43:                   addl   $0x10,%esp
funcion+0x46:                   leave
funcion+0x47:                   ret

Donde podemos ver en negrita cual es la línea que ha generado el core y que coincide con la asignación que haciamos en el programa en C, asignando al puntero *a el valor 1.

 

# Representación desensamblada
funcion+0x30:                   movl   $0x1,(%eax)

# Parte del código de la función funcion().
*a=1;

De esta forma sencilla podemos intentar identificar, analizando el fichero core y el binario de la aplicación, donde se ha producido el problema que ha generado el fichero core.

Technorati Tag(s) :

Los comentarios están cerrados.

My name is null, /dev/null
Algunos derechos reservados. Licencia Creative Commons