Búsqueda personalizada

Anuncios Google

Depurador gdb

Que es un depurador

Un depurador es una aplicación que permite correr otros programas, permitiendo al usuario ejercer cierto control sobre los mismos a medida que los estos se ejecutan, y examinar el estado del sistema (variables, registros, banderas, etc.) en el momento en que se presente algún problema. El propósito final de un depurador consiste en permitir al usuario observar y comprender lo que ocurre "dentro" de un programa mientras el mismo es ejecutado.

En los sistemas operativos UNIX/LINUX, el depurador más comúnmente utilizado es gdb, es decir el depurador de GNU. Éste ofrece una cantidad muy extensa y especializada de opciones. Es muy importante entender el hecho de que un depurador trabaja sobre archivos ejecutables. Esto quiere decir que el mismo funciona de forma independiente al lenguaje en que se escribió el programa original, sea éste lenguaje ensamblador o un lenguaje de medio o alto nivel como C.

Ejecución de programas con el depurador gbb

Para iniciar el depurador, se utiliza el comando gdb. Esto ocasionará que aparezca un nuevo prompt simbolizado por la cadena (gdb), lo que indica que el depurador está listo para recibir una orden (de forma similar al símbolo $ o # en una cónsola de Linux):

linux< gdb

GNU gdb Red Hat Linux (6.3.0.0-1.21rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

(gdb)

Una vez dentro del depurador, existen diferentes comandos para analizar el comportamiento de un programa. Un hecho a resaltar es la gran semejanza que existe entre la cónsola proporcionada por GDB y la consola que comúnmente utilizamos para llevar a cabo todas las tareas en Linux. En particular, es importante mencionar que la utilización del tabulador para completar los comandos automáticamente también funciona dentro de gdb. También es posible utilizar las teclas ↑(Flecha Arriba) y ↓(Flecha Abajo) para navegar por los últimos comandos que se han utilizado.

El comando help permite obtener ayuda sobre cada uno de los comandos. Esta ayuda funciona de una forma jerárquica. Por ejemplo, si se utiliza el comando help sin argumentos, se desplegará una lista con las principales categorías sobre las cuales es posible obtener ayuda:

(gdb) help

List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.

Type "help" followed by command name for full documentation.

Command name abbreviations are allowed if unambiguous.

Sin embargo, es posible proporcionar información más detallada para obtener la ayuda sobre el comando preciso que se necesite. Por ejemplo, utilizando el comando help info, se obtiene una lista de todos los comandos que comienzan con la palabra info, tales como info registers, info variables o info stack. Una vez que se pide ayuda sobre un comando completo, gdb muestra información más detallada acerca del mismo:

(gdb) help info registers

List of integer registers and their contents, for selected stack frame.

Register name as argument means describe only that register.

Dentro del depurador, el primer paso es cargar el programa ejecutable que se quiere depurar. Para ello se utiliza el comando file. Por ejemplo, supongamos que se desea depurar el programa de ejemplo que utilizamos en secciones anteriores:

(gdb) file ejemplo

Reading symbols from /root/ejemplo...(no debugging symbols found)...done.

Using host libthread_db library "/lib/libthread_db.so.1".

El comando "file" no suele utilizarse, ya que (como veremos más adelante) existe una forma más directa para indicar el archivo de entrada. Para ejecutar el programa se utiliza el comando run:

(gdb) run

Starting program: /root/ejemplo

Reading symbols from shared object read from target memory...(no debugging symbols found)...done.

Loaded system supplied DSO at 0x531000

(no debugging symbols found)

(no debugging symbols found)

programa de ejemplo

Program exited normally.

Observe que el depurador intercala mensajes de depuración con la salida del programa (en este caso "programa de ejemplo"). En especial note que el depurador indica que el programa terminó normalmente. En caso de un error que ocasione una terminación prematura, el depurador mostrará la información apropiada. Finalmente, para salir del depurador se utiliza el commando quit.

Ahora bien, supongamos que se tiene un programa llamado suma.s, que toma un arreglo de N posiciones, calcula la suma de los elementos que se encuentran en las posiciones impares (A[1], A[3], etc.), e indica si el resultado fue positivo, negativo o cero:

.section .data
RES0: .asciz "El resultado de la suma es cero (%d)\n"
RESP: .asciz "El resultado de la suma es positivo (%d)\n"
RESN: .asciz "El resultado de la suma es negativo (%d)\n"
A: .long 1,10,-8,7,14,-3,23,-52
N: .long 8
.section .text
.globl _start
_start: xorl %eax, %eax # suma = 0
xorl %ecx, %ecx # i = 0
ciclo: cmpl N, %ecx # Mientras i<N
jge fciclo
movl A(,%ecx,4), %edx # Carga A[i]
addl %edx, %eax # suma += A[i]
addl $1, %ecx # i++
jmp ciclo # Repite el ciclo
fciclo: pushl %eax # Primer argumento del printf
cmpl $0, %eax # Seleccionar el mensaje
je R0
jl RN
RP: pushl $RESP # Segundo argumento del printf
jmp fin
RN: pushl $RESN
jmp fin
R0: pushl $RES0
jmp fin
fin: call printf # Muestra el resultado
addl $8, %esp
movl $1, %eax # Termina el programa
xorl %ebx, %ebx
int $0x80

En teoría, el programa calcula la suma de los elementos en las posiciones impares, esto es:

A[1] + A[3] + A[5] + A[7] = 10 + 7 + (-3) + (-52) = -38

Sin embargo, al ensamblar y ejecutar el programa el resultado fue el siguiente:

linux> gcc suma.s -o suma -nostartfiles

linux>./suma

El resultado de la suma es negativo (-8)

Si se analiza el programa, es muy fácil descubrir los errores que llevaron a este resultado, ya que los mismos son evidentes. Sin embargo, recomendamos no examinar el código fuente, sino proseguir con la lectura. En las siguientes páginas se utilizará el depurador para determinar lo que está ocurriendo, tal y como se haría en un caso real en el que no se tenga acceso al código fuente, o los errores no sean tan evidentes como para detectarse por simple inspección.