GDB, short for GNU Debugger, is part of the GNU project. It is a powerful and widely used command-line debugger designed for Unix and Unix-like operating systems, and it is one of the preferred debugging tools for programmers, especially on Linux. GDB is mainly used to debug programs written in many languages, including C, C++, Pascal, FORTRAN, Ada, Objective-C, Free Pascal, and Go.
Once you know GDB well, it becomes a sharp tool that helps you locate problems quickly and drastically reduces time cost. The GDB you install from a package manager or build from source is basically a bare shell; adding a few customizations before using it feels much better. So next, let’s walk through how to customize your own GDB step by step.
Customize the GDB Configuration File
GDB has a configuration file called .gdbinit, which supports GDB commands and user-defined commands. When GDB starts, it looks for a .gdbinit file, first under ~/, then in the directory where GDB was started. The GDB commands in this file are executed automatically at startup.
You can write different GDB commands into the configuration file to customize GDB for your needs.
For example:
1set debuginfod enabled on
2set print pretty on
3set max-value-size unlimited
These three commands enable automatic download of GDB debug symbols, pretty-print structured values, and remove the limit on the amount of data GDB prints.
In addition to native GDB commands, you can also define user commands (macro language). These can combine all other standard GDB commands with flow-control instructions and pass parameters to them, allowing debugger behavior to be customized for a specific application. It is a good idea to put general-purpose user commands in ~/.gdbinit, and project-specific user commands in each project’s own .gdbinit file. Then you only need to start GDB from the corresponding directory to use them.
GDB user commands follow this basic format:
1define <command>
2 <code>
3end
4document <command>
5 <help text>
6end
Start simple: clear the screen
GDB does not have a built-in clear-screen command, but it can call shell functions. The following code steps outside the debugger to use the clear command to clear the xterm console:
1define cls
2shell clear
3end
4document cls
5Clears the screen with a simple command.
6end
The first half of this definition (the code inside define … end) is what gets executed when the command is called. The second half (the code inside document … end) is used by the GDB command interpreter to display the text associated with cls when you type help cls.
If you type help user, you can see a summary of all user commands entered in the .gdbinit file. One important feature provided by .gdbinit user-defined commands should not be ignored when writing your own commands: the document … end clause. As the number of these commands grows, maintaining documentation for how they work becomes critical.
Breakpoint aliases
It is well known that many GDB commands are cumbersome. Although they can be abbreviated, the GDB macro language allows even further simplification. Commands such as info breakpoints can be made as short as bpl. Here is a set of common breakpoint-alias wrappers:
1define bpl
2info breakpoints
3end
4document bpl
5List breakpoints
6end
7
8define bpa
9break * $arg0
10end
11document bpa
12Set a breakpoint on an address
13Usage: bpa addr
14end
15
16define bpc
17clear $arg0
18end
19document bpc
20Clear a breakpoint at a function/address
21Usage: bpc addr
22end
23
24define bpe
25enable $arg0
26end
27document bpe
28Enable breakpoint #
29Usage: bpe num
30end
31
32define bpd
33disable $arg0
34end
35document bpd
36Disable breakpoint #
37Usage: bpd num
38end
39
40define bpt
41tbreak $arg0
42end
43document bpt
44Set a temporary breakpoint on an address
45Usage: bpt addr
46end
47
48define bpm
49awatch $arg0
50end
51document bpm
52Set a read/write breakpoint on an address
53Usage: bpm addr
54end
Make Good Use of GDB Python Scripts
GDB’s user commands are a macro language, and the syntax is rather limited. They can only wrap existing commands, which makes them clumsy for more complex debugging scenarios and special debugging requirements, such as recording instruction streams or automating certain debugging tasks. Fortunately, GDB supports Python scripting, so some debugging workflows can be automated with scripts, freeing your hands as much as possible and letting developers focus on debugging logic (but to use Python normally in GDB, you first need to make sure it is installed).
Here is a simple example:
1(gdb) python # 1. After entering GDB, type python to enter the Python interpreter
2>print(1 + 1) # 2. Print 1 + 1 here
3>end # 3. Type the end command
42 # Execution result
5(gdb)
If that feels cumbersome, you can also put the command on one line:
1(gdb) python print(1+1)
22
3(gdb)
You can go one step further and put Python code in a .py file, then call it directly from GDB:
1(gdb) source script.py
Below is a simple example of a GDB Python script that records the instruction stream:
1import gdb # If you execute Python code directly inside GDB, you do not need to import gdb
2
3# Name of the file to write
4logfile = "gdb_jump_instr_log.txt"
5
6# Clear or create the file
7with open(logfile, 'w') as f:
8 f.write('')
9
10def is_jump_instruction(instruction):
11 # This is only a basic example; a real implementation needs to be extended
12 # based on the target architecture and the specific instruction set.
13 # For example, on x86, call, jmp, j*, and similar instructions are jump instructions.
14 jump_instructions = ["call", "jmp", "je", "jne", "jz", "jnz"] # Example, incomplete list
15 return any(inst.lower() in instruction.lower() for inst in jump_instructions)
16
17while True:
18 # Get the current PC value
19 pc_value = gdb.parse_and_eval("$pc")
20
21 # Get the assembly instruction at the current address
22 disassembly_line = gdb.execute(f"x/i {pc_value}", to_string=True).split(None, 1)[1]
23
24 if is_jump_instruction(disassembly_line):
25 with open(logfile, 'a') as f:
26 f.write('%s\n' % pc_value)
27
28 # Single-step the instruction (this assumes you want to single-step after each check)
29 gdb.execute("si")
30
31 # You can add more debugging information here, such as register state
32
33 # If you want to end the script when a condition is met, you can add the corresponding logic here
Use GDB Plugins
If user commands and Python scripts still feel too cumbersome and you want an out-of-the-box experience, you can look into GDB plugins (which are generally built on user commands and Python scripts). The plugin I use most often is peda. The installation steps are as follows:
1git clone https://github.com/longld/peda.git ~/peda
2echo "source ~/peda/peda.py" >> ~/.gdbinit
3echo "DONE! debug your program with gdb and enjoy"
The interface looks like this and is especially suitable for reverse engineering:
1[----------------------------------registers-----------------------------------]
2EAX: 0xb0c ('\x0c\x0b')
3EBX: 0x1804e990 --> 0x91085c7
4ECX: 0xb0
5EDX: 0x568
6ESI: 0xf7ff24d4 ("DHRYSTONE PROGRAM, 2'ND STRING")
7EDI: 0xf7ff2504 ("M, 1'ST STRING")
8EBP: 0x1a000000 (0x1a000000)
9ESP: 0xffffd6e8 --> 0x0
10EIP: 0x402a2269 (add esi,0x10)
11EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
12[-------------------------------------code-------------------------------------]
13 0x402a225e: add edi,0x10
14 0x402a2261: pxor xmm0,xmm0
15 0x402a2265: pcmpeqb xmm2,xmm1
16=> 0x402a2269: add esi,0x10
17 0x402a226c: pcmpeqb xmm0,xmm1
18 0x402a2270: pmovmskb eax,xmm2
19 0x402a2274: pmovmskb edx,xmm0
20 0x402a2278: xor eax,0xffff
21[------------------------------------stack-------------------------------------]
220000| 0xffffd6e8 --> 0x0
230004| 0xffffd6ec --> 0x0
240008| 0xffffd6f0 --> 0x105a3 (inc esi)
250012| 0xffffd6f4 --> 0x0
260016| 0xffffd6f8 --> 0x1804ea17 --> 0xc7c08941
270020| 0xffffd6fc --> 0x0
280024| 0xffffd700 --> 0xf7ff24f4 ("DHRYSTONE PROGRAM, 1'ST STRING")
290028| 0xffffd704 --> 0xf7ff24d4 ("DHRYSTONE PROGRAM, 2'ND STRING")
30[------------------------------------------------------------------------------]
31Legend: code, data, rodata, value
32Stopped reason: SIGINT
330x402a2269 in ?? ()
34gdb-peda$
This concludes the introduction to several GDB customization techniques. Later, I will combine them with real-world use cases to show how to use these tools more flexibly.