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