Reputation: 45
I have been trying to learn about x86-64 machine code and ELF files. For that purpose i wrote some code to generate an ELF file with some machine code in it. I use a some machine code that i assembled using nasm
(it just prints a message and calls the exit
syscall, learning to assemble machine code myself comes next) and wrote a C program to write the correct ELF header/Section headers/Symbol table etc. manually into a file.
Now I am trying to link my file (with a single function in it) against another elf file, which I generate via gcc
from C code (test.c
):
// does not work with or without "extern"
extern void hello();
void _start()
{
hello();
// exit system call
asm(
"movl $60,%eax;"
"xorl %ebx,%ebx;"
"syscall");
}
The output of readelf -a
on my ELF file is (hello.o
):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 64 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 9
Section header string table index: 8
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000280
0000000000000044 0000000000000000 AX 0 0 16
[ 2] .rela.text RELA 0000000000000000 000002c8
0000000000000030 0000000000000018 I 6 1 8
[ 3] .data PROGBITS 0000000000000000 00000300
0000000000000005 0000000000000000 WA 0 0 16
[ 4] .bss NOBITS 0000000000000000 00000310
0000000000000080 0000000000000000 A 0 0 16
[ 5] .rodata PROGBITS 0000000000000000 00000310
000000000000000d 0000000000000000 A 0 0 16
[ 6] .symtab SYMTAB 0000000000000000 00000320
0000000000000150 0000000000000018 7 14 8
[ 7] .strtab STRTAB 0000000000000000 00000470
0000000000000028 0000000000000000 0 0 1
[ 8] .shstrtab STRTAB 0000000000000000 00000498
000000000000003f 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
There is no dynamic section in this file.
Relocation section '.rela.text' at offset 0x2c8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
00000000001a 000500000001 R_X86_64_64 0000000000000000 .rodata + 0
000000000024 00050000000a R_X86_64_32 0000000000000000 .rodata + d
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
11: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
12: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
No version information found in this file.
I have compiled test.c
with
gcc -c -nostdlib -fno-asynchronous-unwind-tables test.c -o test.o
to then link with ld test.o hello.o
, which unfortunately yields
ld: test.o: in function `_start':
test.c:(.text+0xa): undefined reference to `hello'
even though the hello
function is defined in hello.o
(note the entry in the symbol table named hello
which is in section 1, the .text
section, and seems to have the correct size/type/value/bind).
If I compile a file with just void hello(){}
in it the same way I compiled test.c
, those two object files can obviously be linked. Also, if I generate my own ELF file hello.o
as an executable, renaming the hello
function to _start
it executes just fine. I have been banging my head against the Wall for a while now, and there is two things I would like to know: Obviously I would like to know my issue with the ELF file. But also I would like to know how I can debug such issues in the future. I have tried to build ld
from source (cloning the GNU binutils repo) with debugging symbols, but I did not get very far debugging ld
itself.
Edit: I have uploaded my elf file here: https://drive.google.com/file/d/1cRNr0VPAjkEbueuWFYwLYbpijVnLySqq/view?usp=sharing
Upvotes: 2
Views: 2189
Reputation: 213385
This was quite hard to debug.
Here is the output from readelf -WSs hello.o
for the file you uploaded to Google drive (it doesn't match the info in your question):
There are 9 section headers, starting at offset 0x40:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000280 000044 00 AX 0 0 16
[ 2] .rela.text RELA 0000000000000000 0002c8 000030 18 I 6 1 8
[ 3] .data PROGBITS 0000000000000000 000300 000005 00 WA 0 0 16
[ 4] .bss NOBITS 0000000000000000 000310 000080 00 A 0 0 16
[ 5] .rodata PROGBITS 0000000000000000 000310 00000d 00 A 0 0 16
[ 6] .symtab SYMTAB 0000000000000000 000320 000150 18 7 14 8
[ 7] .strtab STRTAB 0000000000000000 000470 000028 00 0 0 1
[ 8] .shstrtab STRTAB 0000000000000000 000498 00003f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
11: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
12: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
The issue is with the .sh_info
value (14) of the .symtab
section.
According to documentation, .sh_info
for SYMTAB
section is supposed to contain "one greater than the symbol table index of the last local symbol (binding STB_LOCAL)."
So the value 14 tells the linker that all symbols in this file are local, and therefore can't possibly be used to resolve any external references to them.
You need to move all LOCAL
symbols before GLOBAL
ones (here, msg
and len
would need to move before hello
), so that the symbol table looks like this:
...
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
11: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
12: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
and then set .sh_info
for the .symtab
section to 12.
But also I would like to know how I can debug such issues in the future.
As you've discovered, debugging binutils ld
is very hard, partially because it uses libbfd
, which is choke-full of macros and is itself very hard to debug.
I debugged this by building Gold from source, which fortunately produced the exact same failure.
Upvotes: 4