### 5.1 Examining core files ¶

In addition to allowing programs to be run under the debugger, an important benefit of the `-g` option is the ability to examine the cause of a program crash from a “core dump”.

When a program exits abnormally (i.e. crashes) 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. This file is often referred to as a core dump.14 Combined with information from the symbol table produced by `-g`, the core dump 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 an invalid memory access bug, which we will use to produce a core file:

    int foo (int *p);

    int
    main (void)
    {
      int *p = 0;   /* null pointer */
      return foo (p);
    }

    int
    foo (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. 15

In order to be able to find the cause of the crash later, we will need to 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.16 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 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:17

    $ 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 foo (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 debugger `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.

#### Footnotes

##### (14)

The terminology dates back to the time of magnetic core memory.

##### (15)

Historically, a null pointer corresponded to memory location 0, which is typically restricted to the operating system kernel. In practice this is not always how a null pointer works, but the result is usually the same.

##### (16)

Some systems, such as FreeBSD and Solaris, can also be configured to write core files in specific directories, e.g. `/var/coredumps/`, using the `sysctl` or `coreadm` commands.

##### (17)

This example uses the `ulimit` command in the GNU Bash shell. On other systems the usage of the `ulimit` command may vary, or have a different name (the `tcsh` shell uses the `limit` command instead). The size limit for core files can also be set to a specific value in kilobytes.