Debugging with GDB on Intel x86.
Intel x86 assembly
Lets say we have the following source code:
void func()
{
/* do nothing */
}
int main()
{
func();
return 0;
}
We compile it with GCC (including debugging symbols): gcc -g test.c
We then run GDB: gdb a.out
Typing "disas main" gives us the main function in assembly.
(gdb) disas main
Dump of assembler code for function main:
0x80483a8 <main>: push %ebp
0x80483a9 <main+1>: mov %esp,%ebp
0x80483ab <main+3>: call 0x80483a0 <func>
0x80483b0 <main+8>: xor %eax,%eax
0x80483b2 <main+10>: jmp 0x80483b4 <main+12>
0x80483b4 <main+12>: leave
0x80483b5 <main+13>: ret
End of assembler dump.
As you can see, it calls "func" which is our function "func".
(gdb) disas func
Dump of assembler code for function func:
0x80483a0 <func>: push %ebp
0x80483a1 <func+1>: mov %esp,%ebp
0x80483a3 <func+3>: leave
0x80483a4 <func+4>: ret
End of assembler dump.
This function does nothing, so nothing much is done. However, this
explains some of the opcodes.
push %ebp
mov %esp, %ebp
This will occur at the beginning of every function. Register ebp is
sometimes referred to as the frame pointer. The purpose is to allocate
stack for the function so that the function can allocate variables. You
can see code like this: sub $0x64, %ebp when you allocate some
variables.
func was changed:
void func()
{
char buf[100];
/* do nothing */
}
(gdb) disas func
Dump of assembler code for function func:
0x80483a0 <func>:
push %ebp
0x80483a1 <func+1>: mov
%esp,%ebp
0x80483a3 <func+3>: sub
$0x64,%esp
0x80483a6 <func+6>: leave
0x80483a7 <func+7>: ret
End of assembler dump.
Obviously, LEAVE and RET are opcodes produced at the end of the
function. LEAVE restores the stack so that it will automatically
deallocate stack variables, and RET returns to the previous caller
(resets instruction pointer).
Breakpoints
Say you wanted to stop just when func() is called. You would set a
breakpoint. To do this under GDB:
(gdb) break func
Breakpoint 1 at 0x80483a6: file test.c, line 5.
Then to run the program up to the breakpoint:
(gdb) run
Starting program: /home/test/a.out
Breakpoint 1, func () at test.c:5
5 }
(gdb)
Core dumps
int main()
{
char *string=0x324789234;
printf(string);
return 0;
}
./a.out
Segmentation fault (core dumped)
To see what happened:
$ gdb a.out core
GNU gdb 19991004
Copyright 1998 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"...
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0 _IO_vfprintf (s=0x40108980,
format=0x24789234 <Address 0x24789234 out of
bounds>, ap=0xbffffb94)
at printf-parse.h:95
95 printf-parse.h: No such file or
directory.
(gdb)
This doesn't reveal that much, in fact, it goes too far into detail and
is telling us something happened inside GLIBC.
What we do is a backtrace -- that is tracing all the function calls up
to the library call.
(gdb) bt
#0 _IO_vfprintf (s=0x40108980,
format=0x24789234 <Address 0x24789234 out of
bounds>, ap=0xbffffb94)
at printf-parse.h:95
#1 0x40068070 in printf (format=0x24789234 <Address 0x24789234
out of bounds>)
at printf.c:31
#2 0x80483e6 in main () at test.c:5
Ah ha!
test.c line 5! It is just a printf(string)... which is innocuous by
itself. But the problem is that string was initialised with garbage
values!
EOF
[That's all there is for now.]