Lubarsky's Law of Cybernetic Entomology: There's always one more bug.
An inevitable part of software development is debugging. Many languages offer inbuilt debugging tools, such as Python's pdb.py debugger, while other languages rely on external debugging tools.
The GNU debugger, GDB, is designed to work with a variety of languages, but is primarily targeted at C and C++ developers. GDB is a command line interface, but it can also be run over serial links for embedded debugging and there are a number of graphical interfaces to GDB, such as DDD, Code Medic and xxgdb:
![]() |
![]() |
DDD | xxgdb |
Assume we have a program called cipher that accepts a file and performs simple encryption on the file. The problem is that the program dumps core with a segmentation fault, which simple means that the program is attempting to write into memory that it does not own:
[~/work/]> ./cipher CIPHER syntax: cipher file password Segmentation fault (core dumped)
Note that we have not supplied a filename or a password. A core file is generated, which will help us track down the problem...
[~/work/]> ls -l c* -rwxrwxr-x 1 graeme graeme 8469 Mar 2 11:35 cipher -rw-rw-r-- 1 graeme graeme 1108 Mar 2 11:35 cipher.c -rw------- 1 graeme graeme 110592 Mar 2 11:35 core
Assuming that cipher was compiled with the -g flag, we can load the executable into GDB:
[~/work/]> gdb cipher GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb)
The list command will (by default) list the first 10 lines of the source code for the current function, and subsequent calls to list will display the next 10 lines, and so on:
(gdb) list 15 16 int 17 main(int argc, char *argv[]) { 18 19 char fileName[BUFSIZE]; 20 char password[BUFSIZE]; 21 char buf[BUFSIZE]; 22 FILE *f; 23 24 if (argc != 3) { (gdb)
Firstly, let's run the program and ensure that the bug is reproduced:
(gdb) run Starting program: /home/graeme/work/cipher CIPHER syntax: cipher file password Program received signal SIGSEGV, Segmentation fault.
Using the backtrace command, we can see a stack trace, which reveals that the problem is the strcpy() call at line 30:
(gdb) backtrace #0 strcpy (dest=0xbffff830 "", src=0x0) at ../sysdeps/generic/strcpy.c:34 #1 0x80485f6 in main (argc=1, argv=0xbffffc4c) at cipher.c:30 (gdb)
Using the list command again, we have now identified the location of the problem:
(gdb) list cipher.c:30 25 fprintf(stderr, "\nCIPHER syntax: cipher file password\n\n"); 26 /* we should be exiting here */ 27 } 28 29 /* what happens if argv[1] or argv[2] is greater than BUFSIZE? */ 30 strcpy(fileName, argv[1]); 31 strcpy(password, argv[2]); 32 33 if ((f = fopen(fileName, "r")) == NULL) { 34 fprintf(stderr, "Error: could not open %s for reading!\n\n", fileName); (gdb)
Clearly, argv[1] is somehow corrupting the call to strcpy. We can check the value of argv[1] by setting a breakpoint and then printing the value of argv[1]:
(gdb) break 30 Breakpoint 1 at 0x80485e1: file cipher.c, line 30. (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/graeme/work/cipher CIPHER syntax: cipher file password Breakpoint 1, main (argc=1, argv=0xbffffc4c) at cipher.c:30 30 strcpy(fileName, argv[1]); (gdb) print argv[1] $1 = 0x0 (gdb)
Hmmm... seems argv[1] equals 0x0 (or NULL), which is not good!
Checking back, there were no command line arguments, which means that argv[1] is not defined. The routine starting at line 24 checks for this condition, but fails to exit! If we insert the following line at line 26, then the bug is fixed:
exit(0);
GDB has a set of info pages and also has inbuilt help, which can be accessed via the help command:
(gdb) help List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands obscure -- Obscure features running -- Running the program stack -- Examining the stack status -- Status inquiries support -- Support facilities tracepoints -- Tracing of program execution without stopping the program user-defined -- User-defined commands Type "help" followed by a class name for a list of commands in that class. Type "help" followed by command name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb)