Example 1: Debugging the unexpected death

The program sym.c examines a compiled-in table and reports whether a given symbol is found. It has a bug that causes it to die unexpectedly. This simple program is for example purposes. You might be able to discover the bug by simple examination. The program is found in the examples directory.

You can use this program by first compiling it with the command gcc -g -o sym sym.c. In the same directory, run it with:

$ ./sym
symbol 'add' exists
symbol 'div' exists
Memory fault
$

You can debug the program by starting gdb(1) with the sym argument. The --quiet option suppresses the copyright notice:

$ gdb --quiet sym
(gdb)

Now you can run sym with the run command from the gdb prompt:

(gdb) run
Starting program: /dev/fs/C/SFU/usr/examples/gdb/sym
symbol 'add' exists
symbol 'div' exists

Program received signal SIGSEGV, Segmentation fault.
strcmp () at strcmp.asm:135
strcmp.asm:135: No such file or directory.
(gdb)

The program has died. The message "No such file or directory" is not helpful, because it only indicates that the source for the strcmp() function is not available. It is not available because the Interix subsystem does not ship with debugging libraries. If you had the source to strcmp(), you could give gdb its location using the directory command.

Each time a program calls a function, information about the program and the call is saved as a block of data known as a stack frame. The information includes the name of the function, its arguments, and its local variables. The frame is stored in a stack called the call stack. You can check the call stack to determine where the process was when it died by using the backtrace command. The backtrace command can also be entered as bt or where:

(gdb) backtrace
#0 strcmp () at strcmp.asm:135
#1 0x4014a9 in find_sym (sym=0x40d041 "call") at sym.c:26
#2 0x4014d4 in test_sym (sym=0x40d041 "call") at sym.c:38
#3 0x401534 in main (argc=1, argv=0x20570) at sym.c:49
#4 0x4013f7 in _mainCRTStartup ()
#5 0x401061 in __PosixProcessStartup ()
#6 0x4d0058b2 in ?? ()
(gdb)

When sym died, it was in strcmp(). The strcmp() routine was called by find_sym() with the argument "call," which was called by test_sym(), also with the argument "call." The function test_sym() was, in turn, called by main(), and so on. Although a stack trace can be quite helpful, not all instances of sudden death leave a usable stack trace.

You should also examine the source code with the list command. The following example demonstrates a common problem that can occur when using list:

(gdb) list
strcmp.asm:130: No such file or directory.

In this example, gdb gives you an error message because it is trying to get line 130 of strcmp.asm, and it does not have access to that source file. You can remedy this by specifying the source file sym.c instead, using the format filename:linenumber. This method works for listing any file in the source search path:

(gdb) list sym.c:26
21	char *
22	find_sym(char *sym) {
23	 char **sp = syms;
24
25	 for (sp = syms; ENDTABLE != *sp; sp++) {
26		if (0 == strcmp(sym, *sp))
27			 return sym;
28	 }
29		return 0;
30}
(gdb)

By default, the list command shows ten lines of code. The specified line number, 26, is in the middle of the listing. The list command displays five lines before and four lines after the specified line number.

In this example, it seems likely that the problem is not in strcmp(). It more likely in the comparison of sym and *sp. You can print the value of the sym variable by using the print command:

(gdb) print sym
No symbol "sym" in the current context.

The current context, or stack frame, is in strcmp.asm, and there is no debugging information. You can, however, go to the previous stack frame with the up command:

(gdb) up
#1 0x402aa5 in find_sym (sym=0x410299 "call") at sym.c:26
26			if (0 == strcmp(sym, *sp))

The down command would go back to stack frame #0. Now print sym and *sp:

(gdb) print sym
$7 = 0x410299 "call"
(gdb) print *sp
$8 = 0x0

The argument to print is a C-language expression when debugging C source. In this example, *sp has a value of 0, which is not what you would expect. It should have a value of an element of syms.

Using list, you can look at the declaration of the syms array.

(gdb) list 12
7	 /*
8		* Define the static symbol table.
9		*/
10	 char *syms[] = {
11		"print",
12		"add",
13		"sub",
14		"mul",
15		"div"
16	 };
(gdb)

From this listing, it is clear that the problem is in the static symbol table. It should be terminated with an ENDTABLE element. Inserting ENDTABLE into the static symbol table corrects the problem:

	 "div",
		 ENDTABLE
};