Normally, if a crash occurs while a program is being run under a debugger, the debugger automatically shows you where in the source code the crash occurred. This works only if the debugged program includes debug information. That debug information is stored in the executable file itself. It is ignored by the operating system, but the debugger can use it to resolve addresses to symbols, i.e. to variable and function names.
In my case, if a crash is caused by the dynamically loaded file, then all I see is the register contents and maybe a plain stack trace. That's because the debugger does not know where to find the debug information. Seeing the addresses is useful as it can be used to look up the problematic instruction with objdump. However, debugging would be a lot easier if the addresses could be automatically resolved to symbols by the debugger.
The GNU Debugger (gdb), my debugger of choice, has a add-symbol-file command that instructs the debugger to load symbol information from a file on disk. This command can be issued before the program to be debugged is started, while it's running or even after it has crashed. There are two parameters required by the command. The first one will tell it where to load the debug information from, i.e. the path to the ELF file on disk. The other one will tell it at what address the ELF .text section of the ELF file has been loaded. So the syntax is:
(gdb) add-symbol-file file.elf <address>For my project, I'm expecting frequent crashes in the dynamically loaded program. So in order to make debugging less time consuming, I created a little .gdbinit file in my home directory that does these things:
- Tell gdb which program is being debugged. This is the program that will load another one at runtime.
- Set a breakpoint in that first stage program. The breakpoint needs to be somewhere where the address of the .text section of the dynamically loaded file can be determined.
- Then start the program.
- When the breakpoint is hit, print the address of the .text section of the dynamically loaded ELF file.
file primary_stage break jump_to_second_stage run p/x entry_pointNow anytime the debugger is started, it will automatically load the primary stage ELF file, set a breakpoint and start the program. As soon as the breakpoint is hit, the address of the .text section will be printed. Since that's the first print instruction in the debug session, it can now be accessed through the use of a placeholder: $1.
(gdb) add-symbol-file second_stage $1