Direcciones explicitas
Objetivo: Entender donde vive cada cosa en memoria.
Código
#include <stdio.h>
int main() {
char a[] = "Hola, memoria";
char *p = "Hola, memoria";
printf("&a = %p\n", &a);
printf("&p = %p\n", &p);
printf("p = %p\n", p);
return 0;
}
Procedimiento
Creación y compilación.
Creamos nuestro archivo con touch, en mi caso lo cree con el nombre ejercicio01.c
touch ejercicio01.c
Luego con nvim abrimos el archivo
nvim ejercicio01.c
Copiamos el código de arriba, presionamos la tecla “esc” luego “ shift + .” en la parte inferior izquierda nos aparecerán dos puntitos que es el modo comando, presionamos la tecla “w” luego “enter” y podremos guardar nuestro archivo.
Para compilar nuestro archivo sin salir de nvim podemos hacerlo de siguiente forma: nos aseguramos de no estar en ningún modo presionando “esc”, luego las teclas “shift + . “, para entrar en modo comando y escribimos la siguiente linea
!gcc -g ejercicio01.c -o ejercicio01
Con esto compilamos el programa e incluimos la información necesaria para poder depurarlo con GDB.
Depurando el código con gdb.
Para comenzar a depurar con gdb debemos hacerlo la siguiente manera
gdb ./ejercicio01
con esto entraamos a la teminal interactiva de gdb deberiamos ver algo asi
Reading symbols from ./ejercicio01...
Ahora debemos poner un breakpoint en el main
break main
Al ejecutar la sentencia nos devolvera el siguiente mensaje indicado que el punto ruptura que fue seleccionado
Breakpoint 1, main () at /home/student/workspace/chapter01/ejercicios/ejercicio01/ejercicio01.c:4
4 char a[] = "Hola, memoria";
Para avanzar debemos continuar con la ejecucion del programa
run
Del cual obtendremos la siguente salida
Starting program: /home/student/workspace/chapter01/ejercicios/ejercicio01/ejercicio01
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
En este punto ya podemos ver como se comportan las variables dentro del stack. Nuestro siguiente ejercicio sera ver que contienen con print
print &a
print &p
print p
La salida que nos devuelve cada uno de los print son las siguientes, tambien iremos revisando que significa cada una.
$1 = (char (*)[14]) 0x7ffc195fbae2}
$2 = (char **) 0x7ffc195fbad8
$3 = 0x7824e6a2b5c0 <dl_main> "Uf\017\357\300H\211\345AWI\211\377AVAUATSH\201\354x\002"
De acuerdo al código fuente expuesto podemos darnos cuenta que tenemos dos tipos de variable.
| Variable | Descripcion |
|---|---|
| char a[] = “Hola, memoria”; | arreglo en el stack |
| char *p = “Hola, memoria”; | puntero en el stack que apunta a .rodata |
Con esto en mente podemos explicar los diferentes comandos print
| Comando | Que imprime |
|---|---|
| print &a | Dirección del arreglo en el stack |
| print &p | Dirección del puntero en el stack |
| print p | Dirección a la que apunta p |
Con respecto a la salida de print p
$3 = 0x7824e6a2b5c0 <dl_main> "Uf\017\357\300H\211\345AWI\211\377AVAUATSH\201\354x\002"
Debemos mencionar que GDB intenta asociar direcciones con símbolos conocidos del binario o librerías cargadas. Cuando la dirección cae dentro de un segmento compartido con otros símbolos, GDB muestra el símbolo más cercano.
Si quieres probar esto en un entorno controlado aqui tienes este lab completo