extract debug symbol info from elf binary - debugging

Let's have a look to this basic c program:
#include <stdio.h>
int myadd(int a, int b);
int myadd(int a, int b)
{
return a+b;
}
int main(int argc, char *argv[])
{
int res = myadd(argc,3);
printf("%d\n",res);
return 0;
}
What i want is to understand how debug symbol files work.
If i compile this way:
gcc test.c
I can see debug symbols in gdb:
gdb ./a.out
(gdb) disassemble myadd
Dump of assembler code for function myadd:
0x00000000000006b0 <+0>: push %rbp
That's fine !
Now, if i run:
gcc -s test.c
Here what i get in gdb:
(gdb) disassemble myadd
No symbol table is loaded. Use the "file" command.
That's fine too, because i have stripped symbols with -s gcc option.
Now, i want to "split" my elf executable in 2 files:
- A stripped elf executable
- an external debug symbol files.
Here what i read in some tutorials:
gcc test.c
objcopy --only-keep-debug a.out a.dbg
strip ./a.out
But, now, if i want to run gdb, i say to gdb to look inside ./a.dbg for debug symbols
gdb -s ./a.dbg a.out
And gdb cannot resolve myadd function:
(gdb) disassemble myadd
No symbol table is loaded. Use the "file" command.
And this is what i do not understand: Why gdb does not resolv myadd function?
Thanks

If i compile this way: gcc test.c I can see debug symbols in gdb
You do not see debug symbols here, only the symbol table (which is distinct from debug symbols).
To see debug symbols, compile with gcc -g test.c.
gdb -s a.dbg a.out
The problem here is that when GDB sees "unadorned" a.out, it throws away previously specified symbol file (a.dbg) and replaces it with (fully stripped) a.out. You want:
gdb -s a.dbg -e a.out
Update:
What does mean a "stripped" file: Does it mean this is a file without symbol table or without debuging informations?
On ELF platforms, the state of the file with respect to "strip"-ness is not binary: you can remove individual sections of the file, and depending on exactly what you stripped, your debugging experience will be affected to varying degree.
This command: strip -g a.out removes all .debug_* sections, leaving you without instruction address to source file and line mapping, and without stack address to local variables mapping. However, the symbol table remains in the binary, and can be used to provide instruction address to function name mapping.
This command: strip a.out removes all .debug_* sections, as well as .symtab and .strtab (which together form the symbol table). Such binary is often called "fully stripped".
One could also use obcopy to remove individual sections. It is possible to remove source file/line info (.debug_line section) without removing variable info, and vice versa.
I have tried eu-unstrip ./a.out ./a.dbg but ./a.out result file does not contains debug informations.
You may be hitting a bug in eu-unstrip, perhaps this one.

Related

GDB symbols work for "break" and "print", but "list" fails with "No debugging symbols found"

There are tons of questions and answers about GDB and the "No debugging symbols found" warning, but all of those assume that the debugging symbols are not part of the .elf file.
I'm dealing with the following:
I have a .elf file with debugging symbols. I can verify this by doing objdump, and seeing a disassembly with the subroutine labels being present.
When I load the .elf file, it loads file correctly.
When I then do list to list the C code, I get No symbol table is loaded. Use the "file" command.
However, I can still do things like break main or p/x global_cntr!
When I do file progmem.elf, there's no difference in behavior: I get (No debugging symbols found in progmem.elf), but breakpoints etc still work.
GCC and GDB are using the same version of the GCC toolchain
I tried using -gdwarf-3 instead of -ggdb. No difference.
I'm lost...
I'm using a RISC-V toolchain, if that matters.
Here's an excerpt of my Makefile:
TARGET = $(TOOLS_PREFIX)/riscv32-unknown-elf
AS = $(TARGET)-as
ASFLAGS = -march=$(MARCH) -mabi=ilp32
LD = $(TARGET)-gcc
LDFLAGS = -march=$(MARCH) -g -mabi=ilp32 -Wl,-Tsections.lds,-Map,progmem.map -ffreestanding -nostartfiles -Wl,--no-relax
CC = $(TARGET)-gcc
CFLAGS = -march=$(MARCH) -g -ggdb -mno-div -mabi=ilp32 -Wall -Wextra -pedantic -DCPU_FREQ=$(CPU_FREQ_MHZ)000000 $(CC_OPT)
...
progmem.elf: $(OBJ_FILES) top_defines.h sections.lds Makefile
$(LD) $(LDFLAGS) -o $# $(OBJ_FILES) -lm
And here's a log of my GDB session:
/opt/riscv32im/bin//riscv32-unknown-elf-gdb progmem.elf \
-ex "target remote localhost:3333"
...
Remote debugging using localhost:3333
0x0000002e in rdcycle64 ()
(gdb)
(gdb)
(gdb) monitor soft_reset_halt
requesting target halt and executing a soft reset
(gdb) file progmem.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from progmem.elf...
(No debugging symbols found in progmem.elf)
(gdb) br main
Breakpoint 1 at 0x5d6
(gdb) load
Loading section .memory, size 0x5790 lma 0x0
Start address 0x0, load size 22416
Transfer rate: 23 KB/sec, 11208 bytes/write.
(gdb) c
Continuing.
Program stopped.
0x000005d6 in main ()
(gdb) p/x global_cntr
$1 = 0x0
(gdb) l
No symbol table is loaded. Use the "file" command.
(gdb)
I have a .elf file with debugging symbols. I can verify this by doing objdump, and seeing a disassembly with the subroutine labels being present.
Debugging symbols are not the same as symbols. For disassembly, you only need the latter. For source listing you need the former.
I can still do things like break main or p/x global_cntr!
These also require only the symbol table.
You can confirm that you don't have debug symbols using objdump -g progmem.elf or readelf -wi progmem.elf.
Your command lines look like debug symbols should be included, but there is no telling what you do with .debug_* sections in your sections.lds linker script. Probably you discard them, which would explain why they aren't there.
Update:
Do you by any chance has an example sections.lds file that has them included?
ld --verbose should print the default linker script. Here is one example.
My original linker script was the following:
SECTIONS {
.memory : {
. = 0x00000;
start*(.text);
*(.text);
*(*);
end = .;
}
}
I suspect that my issue was caused by the catchall *(*); which moved all sections into the .text section.
I replaced it with the following script:
MEMORY
{
ram (ax) : ORIGIN = 0x00000000, LENGTH = 16K
}
SECTIONS {
}
After this, .debug_* symbols are included.
This script should be refined with more precise placement of various sections, but it's good enough to unblock me.

Dynamic Symbol Table for PE compiled on Linux

I'm trying to get the content of the dynamic symbol table of compiled c file
#include<stdio.h>
int main(){
printf("Hello, World!");
return 0;
}
as portable executable (PE) on Linux with
i686-w64-mingw32-gcc hello_world.c -o hello32
x86_64-w64-mingw32-gcc hello_world.c -o hello64
I'm using objdump:
objdump --dynamic-syms hello32
and get the output:
hello32: file format pei-i386
objdump: hello32: not a dynamic object
DYNAMIC SYMBOL TABLE:
no symbols
I would expect to have functions like printf in the table. It works with gcc and ELF binaries.
Does anyone know how to compile the file correctly to have a dynamic symbol table with content?
The concept of dynamic symbols seems to be a bit lost in PE-targetting binutils. Use objdump -p <file> or objdump -x <file> and look for import tables in the output. I haven't found a better solution with binutils yet. There's llvm-readobj --coff-imports <file> from LLVM. If you want to see where the symbols will come from at runtime, ntldd is a good tool.

are there debugging options for ld

I have written an assembly program that, for testing purposes, just exits. The code is as follows:
section .text
_global start
_start:
mov eax, 1
mov ebx, 0
int 0x80
The program is obviously in 32-bit; however, I am using 1 64-bit processor and operating system, so I compiled it (using nasm) and linked it as follows:
nasm -f elf exit.asm
ld -m elf_i386 -s -o exit exit.o
debugging the program with gdb, I can't list the code since there are no debugging symbols.
(gdb) list
No symbol table is loaded. Use the "file" command.
In using gcc, you can use the options -ggdb to load the symbols while compiling a c file. but since I don't how to use gcc to compile 32-bit assembly for 64-bit machines (I have searched this but can't find a solution,) I am forced to use ld. can I load the debugging symbols using ld? sorry for the long question and the excess information. Thanks in advance.
Debugging information is generated by nasm when you pass -g. Additionally, you also need to specify what type of debugging information you want (typically dwarf), which is done with the -F switch. So to assemble your file, write
nasm -f elf -F dwarf -g file.asm
then link without -s to preserve the symbol table and debugging information:
ld -m elf_i386 -o file file.o
The -s switch tells ld to "strip" the debugging info. Lose that!

How to set the dynamic linker path for a shared library?

I want to compile a shared library with an .interp segment.
#include <stdio.h>
int foo(int argc, char** argv) {
printf("Hello, world!\n");
return 0;
}
I'm using the following commands.
gcc -c -o test.o test.c
ld --dynamic-linker=blah -shared -o test.so test.o
I end up without an INTERP segment, as if I never passed the --dynamic-linker=blah option. Check with readelf -l test.so. When building an executable, the linker processes the option correctly and puts an INTERP segment in the program header. How to do I make it work for shared libraries too?
ld doesn't include a .interp section if -shared is used, as #MichaelDillon already said. You can however provide this section yourself.
const char interp_section[] __attribute__((section(".interp"))) = "/path/to/dynamic/linker";
The line above will save the string "/path/to/dynamic/linker" in the .interp section using GCC attributes.
If you're trying to build a shared object that's also executable by itself, check this question out. It has a more comprehensive description of the process.
The INTERP segment only goes into binaries which need to load the ELF interpreter (ld.so) in the first place. A shared library has no INTERP segment because the ELF interpreter is already loaded before the shared library is loaded.
In most linux systems the ldconfig is run at every system boot and it looks definitions in /etc/ld.so.conf for looking in directories that have shared libraries. In the file /etc/ld.so.cache there are mappings for shared libraries sonames and the library full path. Consider reading this article: http://grahamwideman.wordpress.com/2009/02/09/the-linux-loader-and-how-it-finds-libraries/#comment-164

Details on gdb memory access complaint

I have an object file compiled using as (from assembler code).
If I link it using ld, when I try to stepi (or nexti) gdb complains about memory access at address 0x0. If I link it using gcc, all is fine.
I am guessing the problem is caused by ld, which produces fewer sections when compared to the linking result of gcc.
Is there a way to configure gdb to be more verbose so I can maybe figure out what's wrong with the executable?
(gdb) b main
Breakpoint 1 at 0x100000f8e
(gdb) r
Breakpoint 1, 0x0000000100000f8e in main ()
(gdb) x/10i $pc
0x100000f8e <main>: fbld 0x6c(%rip) # 0x100001000 <data1>
0x100000f94 <main+6>: fimul 0x7a(%rip) # 0x100001014 <data2>
0x100000f9a <main+12>: fbstp 0x60(%rip) # 0x100001000 <data1>
0x100000fa0 <main+18>: mov0x0 $0x2000001,%rax
0x100000fa7 <main+25>: mov $,%rdi
0x100000fae <main+32>: syscall
(gdb) si
Cannot access memory at address 0x0
0x0000000100000f94 in main ()
PS: The executable itself runs as expected in both versions.
Later edit: commands i've used to compile:
as -arch x86_64 src.s -o src.o
ld -e _main -arch x86_64 src.o -o src
gcc -o src src.o
gdb has a "show debug" command, giving various internal debug settings. E.g. "set debug target 1" will turn on tracing for gdb's interaction with the target process. You might want to experiment with every flag they have (there aren't that many).
GCC doesn't actually do the linking, it just calls ld on your behalf. The options it's providing must be different from the ones you are using.
Per this thread:
How to get GCC linker command?
You should be able to see the ld invocation's command line by running gcc -v.
That should tell you how to modify your ld command line so things work for you.

Resources