Articles > How to examine core files - a tutorial
This article is an excerpt from the book "An Introduction to GCC" (ISBN: 0-9541617-9-3), published by Network Theory Ltd.
For more information, visit the webpage for the book itself.
Normally an executable file does not contain any references to the original program source code, such as variable names or line-numbers--the executable file is simply the sequence of machine code instructions produced by the compiler. This is insufficient for debugging, since there is no easy way to find the cause of an error if the program crashes.
GCC provides the -g debug option to store
additional debugging information in object files and executables.
This debugging information allows errors to be traced back from a
specific machine instruction to the corresponding line in the original
source file, and permits the execution of a program to be traced in a
debugger, such as the GNU Debugger gdb. Using a debugger
also allows the values of variables to be examined while the program
is running.
The debug option works by storing the names of functions and variables (and all the references to them) with the corresponding source code line-numbers in a symbol table in object files and executables.
Examining core files
One helpful application of the -g debugging option is to find
the circumstances of a program crash.
When a program exits abnormally the operating system can write out a
core file, usually named 'core', which contains the in-memory
state of the program at the time it crashed. Combined with information
from the symbol table produced by -g, the core file can be used
to find the line where the program stopped, and the values of its
variables at that point.
This is useful both during the development of software, and after deployment--it allows problems to be investigated when a program has crashed "in the field".
Here is a simple program containing a bug which we will use to cause a crash and produce a core file:
int a (int *p);
int
main (void)
{
int *p = 0; /* null pointer */
return a (p);
}
int
a (int *p)
{
int y = *p;
return y;
}
The program attempts to dereference a null pointer p, which is an
invalid operation. On most systems this will cause a
crash--historically, a null pointer corresponds to memory location 0,
which is usually restricted to the operating system kernel and not
accessible to user programs.
In order to be able to find the cause of the crash later, we can compile
the program with the -g option:
$ gcc -Wall -g null.c
Note that a null pointer will only cause a problem at run-time, so the
option -Wall does not produce any warnings.
Running the executable file on an x86 GNU/Linux system will cause the operating system to terminate the program abnormally:
$ ./a.out Segmentation fault (core dumped)
Whenever the error message 'core dumped' is displayed, the operating system should produce a file called 'core' in the current directory. This core file contains a complete copy of the pages of memory used by the program at the time it was terminated. Incidentally, the term segmentation fault refers to the fact that the program tried to access a restricted memory "segment" outside the area of memory which had been allocated to it.
Some systems are configured not to write core files by default, since
the files can be large and rapidly fill up the available disk space on a
system. In the GNU Bash shell the command ulimit -c
controls the maximum size of core files. If the size limit is set to
zero, no core files are produced. The current size limit can be shown
by typing the following command:
$ ulimit -c 0
If the result is zero, as shown above, then it can be increased with the following command to allow core files of any size to be written:
$ ulimit -c unlimited
Note that this setting only applies to the current shell. To set the limit for future sessions the command should be placed in an appropriate login file, such as '.bash_profile' for the GNU Bash shell.
Core files can be loaded into the GNU Debugger gdb with the
following command:
$ gdb EXECUTABLE-FILE CORE-FILE
Note that both the original executable file and the core file are required for debugging--it is not possible to debug a core file without the corresponding executable. In this example we can load the executable and core file with the command:
$ gdb a.out core
The debugger immediately begins printing diagnostic information, and shows a listing of the line where the program crashed (line 13):
$ gdb a.out core Core was generated by './a.out'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x080483ed in a (p=0x0) at null.c:13 13 int y = *p; (gdb)
The final line (gdb) is the GNU Debugger prompt--it indicates
that further commands can be entered at this point.
To investigate the cause of the crash we display the value of the
pointer p using the print command:
(gdb) print p $1 = (int *) 0x0
This shows that p is a null pointer (0x0) of type
'int *', so we know that dereferencing it with the expression
*p in this line has caused the crash.
Displaying a backtrace
The debugger can also show the function calls and arguments up to the
current point of execution--this is called a stack backtrace
and is displayed with the command backtrace:
(gdb) backtrace #0 0x080483ed in a (p=0x0) at null.c:13 #1 0x080483d9 in main () at null.c:7
In this case the backtrace shows that the crash at line 13 occurred when
the function a() was called with an argument of p=0x0 from
line 7 in main(). It is possible to move to different levels in
the stack trace, and examine their variables, using the debugger
commands up and down.
A complete description of all the commands available in gdb can
be found in the manual "Debugging with GDB: The GNU Source-Level
Debugger".
Bibilographic details:
Title: "An Introduction to GCC"Author: Brian J. Gough
Published by Network Theory Ltd, May 2004
Paperback (6"x9"), 124 pages
Retail Price: $19.95 (£12.95 in UK)
ISBN: 0-9541617-9-3
Webpage: http://www.network-theory.co.uk/gcc/intro/