Table of Contents
GDB – The GNU Debugger
The GNU Debugger (GDB) is a utility which works with an application and allows a user to analyze what a piece of software is doing whilst it executes. Specifically GDB allows a user to:
- start a program and specify command line options for that program on a local machine, or to start it on a remote machine and connect to that machine via a second.
- stop the execution of a program when any one or more of a number of criteria are satisfied.
- examine the status of a stopped program in terms of memory/register content.
- to change the content of both memory and registers of a stopped program.
In order to demonstrate some of the features gdb’s features we will be working with the following example code.
#include<stdio.h> int call_02(int var) { int *foo = NULL; *foo = var * 2; return *foo; } int call_01(int var) { int temp; temp = var + 1; try { call_02(temp); } catch(…) { temp = temp + 3; } return temp; } int main(void) { int var1 = 0; int var2 = 2; var2 = call_01(var1); printf("%d\n",var2); return 0; }
which can be compiled using the command
$ g++ -g main.c
Starting to Debug
Starting the debug process going is as simple as entering the command followed by the location of the executable you want to debug. If you need to run with arguments then the command is slightly more involved as shown below.
$ gdb --args <executable> <arg1> <arg2> <arg3> ...
An alternative to providing the arguments from the command line is to start gdb without arguments and supply then once gdb is running.
$ gdb <executable> (gdb) r <arg1> <arg2> <arg3> ...
Executing Code
There are a number of ways in which you may want to execute code. At the most basic level you may want to execute a single machine instruction “stepi”. At a source code level you may want to step through a single line of source using the “step” command (abbreviated to s). In both cases a numeric argument can be appended which will be the number of times to step. To step through code but not enter any functions there is the “next” command (abbreviated n) which again can take a numeric argument. The command “nexti” provides the same functionality at the machine code level. If the user wants to exit the current function then there is the “finish” command (abbreviated as “fin”), whilst to exit a loop there is the “until” command (abbreviated as “u”) which executes until the source after the current line is reached or the specified location if an appropriate argument is given.
To execute code from the current position until completion or the next breakpoint there is the command “continue” (abbreviated c), and to restart execution from the beginning the user enters “run” (abbreviated r).
Halting Execution
Execution of the code can be halted at specific locations in the code dependent on a given criteria (breakpoints) or when a specified location is read or altered (watchpoints). It is also possible to halt an event such as the throwing of an exception (catchpoint).
Breakpoints
Breakpoints are locations within the code at which the execution may be halted and control returned to gdb according to the specified criteria. When a breakpoint is hit gdb will return the location of that breakpoint together with the number of times it has been hit. It is possible to list the current breakpoints (active and inactive) using the command
(gdb) i b
Adding Breakpoints
Breakpoints are specified busing the ‘b’ command followed by a location and an optional condition. The location can be specified as either a function, an offset from the current position, a line number in either the current or another specified file or an address. If the break command is used without and arguments then a breakpoint is added at the next instruction to be executed in the current stack frame. Commands are generally of the form shown below
(gdb) b <location> if <condition>
where location is of the form
(gdb) b <function> (gdb) b <file:function> (gdb) b <line number> (gdb) b <file:line number> (gdb) b <+offset> (gdb) b <-offset> (gdb) b *<address> (gdb) b
Conditions are boolean expressions in the appropriate programming language e.g.
(gdb) b <function> if foofoo == -1 (gdb) b <file:line number> if *pntr != 0
Conditions can be modified at any time using the condition command followed by the breakpoint number and the new condition.
Deleting/ Disabling Breakpoints
Breakpoints can be deleted, disabled, or re-enabled on a singlar or multiple basis. Where a specific breakpoint is to be manipulated it must be identified by its number as indicated when listing breakpoints. Breakpoints 1, 3, and 4 could be deleted, disabled, and re-enabled as shown below.
(gdb) delete 1 3 4 (gdb) disable 1 3 4 (gdb) enable 1 3 4
If the numbers are omitted following any of these commands then all breakpoints will be deleted, disabled, or re-enabled.
Watchpoint
Watchpoints are triggered when memory at a given address is manipulated in some way. A break on read/write access to a memory location can be requested using the command below
(gdb) watch *<address>
Alternatively, it is also possible to request a break when a given value is written into a location.
(gdb) watch *<address> == <value>
Catchpoint
Setting catchpoints causes the debugger to stop execution as a result of a program event. Typically events are either throw or catch although other events are available under HP-UX. In the example below we have set a catchpoint on the throw event. This is triggered in call_02 when there is an attempt to use a NULL pointer. The output shows that the event was triggered by line 7 of the code.
(gdb) catch throw Catchpoint 1 (throw) (gdb) r Starting program: a.out Program received signal SIGSEGV, Segmentation fault. 0x0000555555555166 in call_02 (var=1) at main.c:7 7 *foo = var * 2; (gdb) bt 0 0x0000555555555166 in call_02 (var=1) at main.c:7 1 0x0000555555555192 in call_01 (var=0) at main.c:18 2 0x00005555555551bb in main () at main.c:32
Reading/Writing Data
Working with Variables
The variables local to a region can be viewed using the command info locals as shown below. Alternatively you could also request the display of specific variables and the state the way in which they are to be displayed.
(gdb) b main.c:32 Breakpoint 1 at 0x11b1: file main.c, line 32. (gdb) r Starting program: a.out Breakpoint 1, main () at main.c:32 32 var2 = call_01(var1); (gdb) info locals var1 = 0 var2 = 2
Variables can be modified using gdb. The example below shows how the value of var2 is modified to -1 and displayed using the “print” command (abbreviated to p). The example also shows that it is possible to cast this variable and display it in both decimal and hex formats.
(gdb) set var2=-1 (gdb) p var2 $6 = -1 (gdb) p (unsigned int) var2 $7 = 4294967295 (gdb) p/x (unsigned int) var2 $8 = 0xffffff
Specifier | Description |
---|---|
x | Print the binary representation in hexadecimal |
d | Print the binary representation in decimal |
u | Print the binary representation as an unsigned decimal |
o | Print the binary representation in octal |
t | Print the value in binary |
a | Print an absolute address and as an offset from the nearest preceding symbol |
c | Print the numerical value and the character representation. If the value of the character is greater than the 7-bit ASCII value the character representation is replaced with an octal value. |
f | Print the value in floating point representation |
s | Represent the data as a string |
z | Print in hexadecimal format but with leading zeros |
i | Print the address and assembler for the given location (this format is only applicable for use with the x command). |
Working with Memory
Displaying the contents of memory is achieved with the ‘x’ command. The command can be used to display multiple words of data in a specified format and of a specified size. The command is entered in the format shown below
x/<number of words to display><size of a word><style of display>
The size of a word is requested according to the character in the table below
Modifier | Description |
---|---|
b | byte (8-bit value) |
h | halfward (16-bit value) |
w | word (32-bit value) |
g | giant word (64-bit value) |
Whilst the style of display is specified according to the characters already described in the previous section.
Dumping / Restoring From File
If there is a need to extract the contents of memory to file this can be achieved using the dump/append commands. Commands are entered as show below, where the format specifier is used as described in the following table.
(gdb) dump <format> memory <filename> <start address> <end address> (gdb) restore <filename> <format> <bias> <start> <end>
Format | Description |
---|---|
binary | Data is stored in raw binary format |
ihex | Data is stored in Intel hex format |
srec | Data is stored in motorola s-record format |
tekhex | Data is stored in Tektronix hex format |
verilog | Data is stored in Verilog hex format |
Navigating the Stack
When a breakpoint is hit such as in the catchpoint example earlier you may want to examine local variables from previous stack frames. Using the up and down commands allows you to move between stack frames. Both up and down commands can be followed by a value expressing the number of stack frames to step through.