CMSC313, Computer Organization & Assembly Language Programming, Fall 2012
gdb Help
Introduction
You will use the gdb debugger to debug assembly language
programs. Using a debugger is necessary for assembly because printing
out values from an assembly language program is non-trivial. Hence
adding debugging code to an assembly language program might itself
introduce new bugs, or at least make the bug behave differently.
Before using gdb, you should ask nasm to provide some
debugging information. This is done with the -g switch. For
example,
nasm -f elf -g hello.asm
It is also convenient to have a listing of your program:
nasm -f elf -g hello.asm -l hello.lst
This creates a file hello.lst with line numbers and the machine
code for each line of your assembly language program.
If your program contains portions written in C, those portions should be
compiled using the -g option to retain debugging information in
the a.out file.
The complete documentation for gdb is available on the Linux system using
the info command:
info gdb
We will summarize some of the more useful commands for assembly language
debugging below.
Starting and stopping programs
After you have assembled and linked your program using nasm and
ld, you can invoke the debugger using the UNIX command:
gdb a.out
If gdb complains that "no debugging symbols found", you should
re-assemble your program uisng the -g switch.
You can now run your program inside gdb by
typing "run" at the (gdb) prompt. However, the program will just behave
exactly as it did when it ran under the Linux shell. The point of using a
debugger is to set breakpoints where the execution of the program is
halted. This returns you to the (gdb) prompt and you can use various
gdb
commands to examine the contents of the registers and/or memory. It is
common to have breakpoints set at the beginning of a program, at the end
of a program, at the top of a loop and anywhere you think the bug occurred
in your program.
For example:
break *_start
Stops the execution of the program just before the execution of
the first instruction. Also,
break *_start+5
Will stop the program at the instruction that is 5 bytes after
_start. You can also set a break point at a line number
in the source file:
break 11
The listing file (e.g., hello.lst) has line numbers, or you can
use gdb's "list" command to see your source code:
list _start
list 7
list 7, 20
After execution of your program has been halted by a breakpoint, you can
resume execution in several ways. The "cont" (continue) command resumes
execution after the breakpoint. The execution continues until the next
breakpoint is reached or the program terminates. Alternatively the single
step commands "stepi" or "nexti" command may be used to execute a single
machine instruction after the breakpoint. The difference between "stepi"
and "nexti" arises when the next instruction is a function call. The
"nexti" command will continue execution until the return from the function
call (which might be never). On the other hand, the "stepi" command will
enter the function. The command "where" will show where the execution of a
program has halted.
To help you determine where to set the breakpoints, gdb also has a
disassembler. The command:
disassemble _start
will print out the mnemonics for the machine instructions starting at the
label _start. Since the GNU assembler uses AT&T style syntax,
gdb also
uses AT&T style syntax instead of Intel-style syntax. Most importantly,
in AT&T style syntax the instruction for loading register EAX with the
constant 1 looks like
mov 0x1, %eax
In Intel-style syntax, the order of the source and destination are
reversed. Recent versions of gdb allow you to switch the order of the
source and destination during disassembly with the command:
set disassembly-flavor intel
You can configure gdb to always use Intel-style syntax by including the
command above in a file named .gdb in your home directory.
Examining register and memory contents
Using a combination of breakpoints and single-stepping, you should be able
to trace through the sections of your code that you suspect to be buggy.
This allows you to look at the contents of the registers and memory when
the execution of the program has halted. The command:
info registers
prints out the contents of every register, including all the segment
registers. This is often too much information. The "print" command is much
more useful. For example, the commands:
print/d $ecx
print/x $ecx
print/t $ecx
print out the contents of the ECX register in decimal, hexadecimal, and
binary, respectively.
Memory contents can be examined using the "x" command. The "x" command is
optionally followed by a "/", a count field, a format field, a size
field and finally a memory address. The count field is a number in decimal.
The format field is a single letter with 'd' for decimal, 'x' for
hexadecimal, 't' for binary and 'c' for ASCII. The size field is also a
single letter with 'b' for byte, 'h' for 16-bit word (half word) and 'w'
for a 32-bit word. For example, if your program has a label "msg", the
following commands:
x/12cb &msg
x/12db &msg
x/12xh &msg
x/12xw &msg
will print out respectively the contents of memory starting at msg in the
following manner: the next 12 bytes as ASCII characters, the next 12 bytes
as decimal numbers, the next 12 16-bit words in hex, and the next 12 32-bit
words in hex.
Sometimes, as you are tracing your program, you are really interested in
the contents of a specific register. If you issue the command:
display $eax
then the contents of the EAX register will be printed to the screen every
time the program is halted. This saves a lot of typing. You may issue more
than one display command and you can use format codes like "/x" to output
the values in hex. The gdb command "info display" will list all the active
displays. Use "undisplay" to remove an item on this list.
A very good use of the display command is to have the next instruction
of the program printed whenever the program is halted:
display/i $eip
Command Summary
We've only covered a handful of useful gdb commands in this tutorial to get
you started using gdb. Many of these commands can be shortened (e.g., "si"
is equivalent to "stepi"). The gdb debugger itself has a nice help feature.
Simply type "help" to get a list of commands, and
help [command]
will give you more information on that command. Again, more complete
documentation on gdb is available through the "info gdb" command typed in
the UNIX shell.
Command |
Example |
Description |
run | | start program |
quit | | quit out of gdb |
cont | | continue execution after a break |
break [addr] | break *_start+5 | sets a breakpoint |
delete [n] | delete 4 | removes nth breakpoint |
delete | | removes all breakpoints |
info break | | lists all breakpoints |
list _start | | list a few lines of the source code around _start |
list 7 | | list 10 lines of the source code starting on line 7 |
list 7, 20 | | list lines 7 thru 20 of the source code |
stepi | | execute next instruction |
stepi [n] | stepi 4 | execute next n instructions |
nexti | |
execute next instruction, stepping over function calls |
nexti [n] | nexti 4 |
execute next n instructions, stepping over function calls |
where | | show where execution halted |
disas [addr] | disas _start |
disassemble instructions at given address |
info registers | | dump contents of all registers |
print/d [expr] | print/d $ecx |
print expression in decimal |
print/x [expr] | print/x $ecx |
print expression in hex |
print/t [expr] | print/t $ecx |
print expression in binary |
x/NFU [addr] | x/12xw &msg |
Examine contents of memory in given format |
display [expr] | display $eax |
automatically print the expression each time the program is halted |
info display | |
show list of automatically displays |
undisplay [n] | undisplay 1 |
remove an automatic display |
Last Modified:
22 Jul 2024 11:28:26 EDT
by
Richard Chang
to Fall 2012 CMSC 313 Homepage