Investigating The Stack With the Debugger
This post was an early draft of material for my book The New Programmer’s Survival Manual, now published by Pragmatic Programmers.
This section assumes I already covered some basics of GDB earlier in the book, which I plan to do.
Once you have some concept of registers and the stack, it’s time to play. Take our original example and save this as stack.c:
int main()
{
int i = 0x11223344;
char s[4];
s[0] = 'f';
s[1] = 'o';
s[2] = 'o';
s[3] = '\\0';
return 0;
}
Compile this with GCC:
gcc -m32 -S stack.c
The -m32 option tells it to generate 32-bit code (you can investigate 64 bit on your own) and the -S option tells GCC to compile to assembly rather than machine code. This generates stack.s which you can look at in a text editor.
Now assemble the code:
gcc -m32 -gstabs -o stack stack.s
Again we need to specify 32 bit, and the -gstabs option tells GCC to include debugging info for GDB. This generates stack, which you can now run in GDB:
gdb stack
Try list main (or simply l main) first, and you’ll see the beginning of main():
(gdb) l main
1 .text
2 .globl _main
3 _main:
4 pushl %ebp
5 movl %esp, %ebp
6 subl $24, %esp
[...]
Looks familiar, right? Set a breakpoint at main (break main or b main) then run the program (run or r). Now you can single-step (step or s) and follow along with the program putting stuff on the stack. Once you’ve stepped past the subl ..., %esp instruction, investigate the stack and base pointers:
(gdb) p $esp
$1 = (void *) 0xbffff7a0
(gdb) p $ebp
$2 = (void *) 0xbffff7b8
Note that registers are displayed with a percent sign in front (like %esp) but GDB makes you put a dollar sign in front (like $esp).
On my computer, GCC makes a stack frame of 24 bytes (subl $24, %esp), and I can verify this with GDB:
(gdb) p $ebp - $esp
$3 = 24
Now let’s investigate some items on the stack. Keep stepping until you see the MOV instruction:
7 movl $287454020, -12(%ebp)
Step one more time to execute the instruction. Since GCC decided to move 0x11223344 to the base pointer minus 12, I can look at that using the x command:
(gdb) x/w $ebp - 12
0xbffff7ac: 0x11223344
Remember, x is used to eXamine the contents of memory. Where p $ebp just prints the value of EBP, x $ebp prints the memory that EBP points to. In this case, I specify x/w to explicitly tell GDB that I want to examine a “word” of memory. I say “word” in quotes because “word” means different things depending on who you ask; in the Intel 8086 universe it means 2 bytes. In GDB universe it means 4 bytes.
Go ahead and step through the program and investigate the other stuff on the stack. We’ll get to a beefier example in just a moment. Before executing the LEAVE instruction, however, examine that integer one more time using ‘x/4b’ just for grins:
(gdb) x/4b $ebp - 12
0xbffff7ac: 0x44 0x33 0x22 0x11
Notice the four bytes appear to be backwards? Mull over that one for a while. We’ll revisit it later.