Unleashing the Power of GDB (part 2)

The GNU Debugger[1] is my favorite debugging tool and I personally think it’s essential for any *nix developer to know how to use it properly if you’re working with C/C++, D, Go, Obj-C, Fortran, Pascal, Modula-2 or Ada[2].

This is the second part of Unleashing the power of GDB. It is recommended reading the first part before proceeding. First section is about integrating GDB with Emacs, the second is about how to debug errors as they occur with common cases and the last section is about debugging without any debug symbols.

Integration with Emacs

GDB is taken to the next level when used together with my favorite editor Emacs[3] because it adds autocompletion of commands and arguments, and easy stepping in the command history - saves a lot of time. But perhaps even more important: If the program is compiled with debugging symbols it will also load and show the source code. Thus hitting breakpoints and stepping through the program will show the placement in the source code as well.

First it is observed that we are denied access to some memory at address 0x0..0 and that it’s the line *ptr = 1 (prog1.cpp:3) that caused it. So we already know from the error message that ptr must then be a null-pointer but we double-check by inspecting it with p ptr.

Another example might be the following:

./prog2
prog2(12673) malloc: *** error for object 0x7ffd08c03930: pointer being freed was not allocated
***set a breakpoint in malloc_error_break to debug
abort ./prog2

From the error message we now know that some unallocated memory was freed. It also gives a tip on where to start debugging, so let’s do just that:

Which in this case is a division-by-zero error. Pay attention to the warnings your compiler issues to avoid these things. It might have looked like this:

prog3.cpp:3: warning: division by zero in ‘a / 0’

Note that these are just examples of runtime errors. A lot more exist that I have not covered here.

Debugging with no debugging symbols

Sometimes the program you are debugging has no symbol table and you still have to locate the problem. It might be that you are debugging a third party binary so you don’t have access to the source code. This section will be a bit advanced.

Let’s start with the first problematic program from last section: prog1. This is what GDB tells us with no debug symbols:

On the third line the pointer is accessed and 1 is attempted saved at the location. So this information can be given to the third party. However, we actually know the source code so I’ll show the parallels:

This basically does the operation 0xFF / 0. 0xFF is saved to -0x1c(%rbp) and moved to %eax, then 0 is “created” by doing an XOR on %ecx with itself (recall that XOR on two equal operands always yields 0) and storing in %ecx, cltd converts signed long word %eax to a double word in %edx:%eax and finally idiv does the signed division.