I'm debugging using core dumps, and note that gdb needs you to supply the executable as well as the core dump. Why is this? If the core dump contains all the memory that the process uses, isn't the executable contained within the core dump? Perhaps there's no guarantee that the whole exe is loaded into memory (individual executables are not usually that big though) or maybe the core dump doesn't contain all relevant memory after all? Is it for the symbols (perhaps they're not loaded into memory normally)?
5 Answers
The core dump is just the dump of your programs memory footprint, if you know where everything was then you could just use that.
You use the executable because it explains where (in terms of logical addresses) things are located in memory, i.e. the core file.
If you use a command objdump
it will dump the meta data about the executable object you are investigating.
Using an executable object named a.out as an example.
objdump -h a.out
dumps the header information only, you will see sections named eg. .data or .bss or .text (there are many more). These inform the kernel loader where in the object various sections can be found and where in the process address space the section should be loaded, and for some sections (eg .data .text) what should be loaded. (.bss section doesn't contain any data in the file but it refers to the amount of memory to reserve in the process for uninitialised data, it is filled with zeros ).
The layout of the executable object file conforms to a standard, ELF.
objdump -x a.out
- dumps everything
If the executable object still contains its symbol tables (it hasn't been stripped - man strip
and you used -g
to generate debug generation to gcc
assuming a c source compilation), then you can examine the core contents by symbol names, e.g. if you had a variable/buffer named inputLine in your source code, you could use that name in gdb
to look at its content. i.e. gdb
would know the offset from the start of your programs initialised data segment where inputLine starts and the length of that variable.
Further reading Article1, Article 2, and for the nitty gritty Executable and Linking Format (ELF) specification.
Update after @mirabilos comment below.
But if using the symbol table as in
$ gdb --batch -s a.out -c core -q -ex "x buf1"
Produces
0x601060 <buf1>: 0x72617453
and then not using symbol table and examining address directly in,
$ gdb --batch -c core -q -ex "x 0x601060"
Produces
0x601060: 0x72617453
I have examined memory directly without using the symbol table in the 2nd command.
I also see, @user580082 's answer adds further to explanation, and will up-vote.
-
6Never heard of "basic stack section". .bss is (historically) "block started by symbol" and practically, "unitialized data", while .data is "initialized data" and text (not .code) is used to store machine code. There is no stack section in a binary, as stacks are created at run time.– jlliagreAug 25, 2017 at 13:37
-
“if you know where everything was then you could just use that” is not true either because not everything in the program is necessarily included in the footprint. Aug 25, 2017 at 16:36
-
2@jlliagre you are correct, I mistakenly called .text .code (because I was thinking an explanation whilst composing the answer) - updated. I have mistakenly thought of bss incorrectly by name, and have updated my answer, but avoided *Block Started by Symbol since I don't think it really adds to the equation, and have explained it's usaged as un-initialised data, which was our common understanding. Thank you - I appreciated your comment to correct this posting.– X TianAug 27, 2017 at 9:43
The core file is a snapshot of the stack image, memory mappings and registers at the time of process termination. The contents of which can be manipulated as given in core man page. By default private mappings, shared mappings and ELF header information is dumped into core file.
Coming to your question, the reason that gdb requires executable is because it doesn't simulate the execution, by reading and interpreting the binary instructions like valgrind does instead it becomes the parent of the process so as to control the behavior of the process during run time. It uses the core file to determine the memory mappings and processor state of process during crash.
In Linux parent processes can get additional information about their children, in particular the ability to ptrace them which allow the debugger to access process's low level information like read/write its memory, registers, change signal mappings, stop its execution etc.
You will understand the requirement of executable inspite of having core file more once you read how any debugger works.
(in addition of other good answers)
On modern Linux (and many Unix-like) systems, the debugging information (including meta-data about symbol's types, source code location, type of variables, etc etc ....) is in DWARF format and sits inside the ELF executable (or ELF shared libraries) when it is compiled with some -g
option. I recommend compiling programs to be debugged with -g3 -O0
and perhaps -fno-inline
if using a recent GCC; however, with GCC you can even compile with both optimization & debugging info, e.g. with -O2 -g1
, although the debug info might in that case be a bit "fuzzy" (this might slightly help to catch some naughty Heisenbugs).
It is quite sensible to avoid putting that information in core files, because you might have many different core files (imagine a widely used software with many users making bug reports, most of them with a core
dump) for the same executable. Also core(5) files are dumped by the kernel, which should not care about existence of DWARF sections in elf(5) executables (because these sections are not mapped into the virtual address space of the faulting process which dumped core on some signal(7)). There is even the possibility to have the debug information being put in separate files (outside of the executable).
BTW, GDB can be painfully used to debug core dumps for executables without any debug information. But then you practically debug at the machine code level (not at the symbolic level provided by programming languages and their compilers).
There are two reasons to that:
By default, the OS does not waste disk space with data it can get otherwise. So, the
.text
and.rodata
sections are not included in the coredump. gdb is able to find them in the executable. However, this behavior can be changed (on Linux at least). See section "Controlling which mappings are written to the core dump" of core(5). To get full memory images, you can try to do:echo 0xF > /proc/[PID]/coredump_filter
- Then, a dump of the memory does not contains the debug symbols (aka DWARF information). Technically, you can debug your code without that, but it far more convenient to have debug symbols. In common case, these symbols are located with the executable. BTW, it is possible to place debug symbols in a separate location (see debuginfod, debian dbgsym, etc...).
As executables are write-protected during execution, they cannot be changed by the running process. Also when multiple processes execute the same image, the code (and constant) pages have to be loaded mostly once into memory. Even a single process executing an image will not load all the pages into RAM (demand-paging). Despite of all that there are parts of the executable file that don't have to be loaded into RAM at all, e.g. debug information.
For all these reasons it makes sense not to duplicate the executable file in every core dump file; it would be a "common factor" anyway.
So to display the code (e.g. disassemble
) gdb
needs the code pages.
In theory if only the demand-paged code were included in the core dump, you could list the failing part of the program, but also the calling code page could have been "paged out", so you probably could inspect only a part of the failure.
Also gdb
needs the debug symbols (if any) to correlate code addresses with source files, lines and variable names.
Finally when using dynamic libraries, the core dump would have to contain all the libraries as well otherwise.