自定义 ELF 文件中未定义引用,但符号在文件符号 table 中定义
Undefined reference in a custom ELF file, but the symbol is defined in the files symbol table
我一直在尝试了解 x86-64 机器码和 ELF 文件。为此,我编写了一些代码来生成一个 ELF 文件,其中包含一些机器代码。我使用了一些我使用 nasm
assembled 的机器代码(它只是打印一条消息并调用 exit
系统调用,接下来是我自己学习 assemble 机器代码)和编写了一个 C 程序,将正确的 ELF header/Section headers/Symbol table 等手动写入文件。
现在我正在尝试 link 我的文件(其中只有一个函数)针对另一个 elf 文件,该文件是我通过 gcc
从 C 代码 (test.c
) 生成的:
// does not work with or without "extern"
extern void hello();
void _start()
{
hello();
// exit system call
asm(
"movl ,%eax;"
"xorl %ebx,%ebx;"
"syscall");
}
readelf -a
在我的 ELF 文件上的输出是 (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.
我用
编译了test.c
gcc -c -nostdlib -fno-asynchronous-unwind-tables test.c -o test.o
然后 link 与 ld test.o hello.o
,不幸的是产生
ld: test.o: in function `_start':
test.c:(.text+0xa): undefined reference to `hello'
即使 hello
函数在 hello.o
中定义(请注意第 1 节中名为 hello
的符号 table 中的条目,.text
部分,并且似乎有正确的 size/type/value/bind).
如果我用编译 test.c
的方式编译一个只有 void hello(){}
的文件,那两个目标文件显然可以被 link 编辑。此外,如果我生成自己的 ELF 文件 hello.o
作为 executable,将 hello
函数重命名为 _start
它执行得很好。一段时间以来,我一直在用头撞墙,有两件事我想知道:显然我想知道我的 ELF 文件问题。但我也想知道将来如何调试此类问题。我尝试使用调试符号从源代码(克隆 GNU binutils 存储库)构建 ld
,但我没有深入调试 ld
本身。
编辑:我在这里上传了我的精灵文件:
https://drive.google.com/file/d/1cRNr0VPAjkEbueuWFYwLYbpijVnLySqq/view?usp=sharing
这很难调试。
这是您上传到 Google 驱动器的文件的 readelf -WSs hello.o
的输出(它与您问题中的信息不匹配):
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
问题出在 .symtab
部分的 .sh_info
值 (14) 上。
根据 documentation,SYMTAB
部分的 .sh_info
应该包含“大于最后一个局部符号的符号 table 索引(绑定 STB_LOCAL)."
因此值 14 告诉链接器此文件中的 所有 符号都是本地的,因此不可能用于解析对它们的任何外部引用。
您需要将所有 LOCAL
个符号移动到 GLOBAL
个符号之前(此处,msg
和 len
需要在 hello
之前移动),所以符号 table 看起来像这样:
...
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
然后将 .symtab
部分的 .sh_info
设置为 12。
But also I would like to know how I can debug such issues in the future.
如您所见,调试 binutils ld
非常困难,部分原因是它使用了 libbfd
,它充满了宏并且本身很难调试。
我通过从源代码构建 Gold 来调试它,幸运的是产生了完全相同的失败。
我一直在尝试了解 x86-64 机器码和 ELF 文件。为此,我编写了一些代码来生成一个 ELF 文件,其中包含一些机器代码。我使用了一些我使用 nasm
assembled 的机器代码(它只是打印一条消息并调用 exit
系统调用,接下来是我自己学习 assemble 机器代码)和编写了一个 C 程序,将正确的 ELF header/Section headers/Symbol table 等手动写入文件。
现在我正在尝试 link 我的文件(其中只有一个函数)针对另一个 elf 文件,该文件是我通过 gcc
从 C 代码 (test.c
) 生成的:
// does not work with or without "extern"
extern void hello();
void _start()
{
hello();
// exit system call
asm(
"movl ,%eax;"
"xorl %ebx,%ebx;"
"syscall");
}
readelf -a
在我的 ELF 文件上的输出是 (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.
我用
编译了test.c
gcc -c -nostdlib -fno-asynchronous-unwind-tables test.c -o test.o
然后 link 与 ld test.o hello.o
,不幸的是产生
ld: test.o: in function `_start':
test.c:(.text+0xa): undefined reference to `hello'
即使 hello
函数在 hello.o
中定义(请注意第 1 节中名为 hello
的符号 table 中的条目,.text
部分,并且似乎有正确的 size/type/value/bind).
如果我用编译 test.c
的方式编译一个只有 void hello(){}
的文件,那两个目标文件显然可以被 link 编辑。此外,如果我生成自己的 ELF 文件 hello.o
作为 executable,将 hello
函数重命名为 _start
它执行得很好。一段时间以来,我一直在用头撞墙,有两件事我想知道:显然我想知道我的 ELF 文件问题。但我也想知道将来如何调试此类问题。我尝试使用调试符号从源代码(克隆 GNU binutils 存储库)构建 ld
,但我没有深入调试 ld
本身。
编辑:我在这里上传了我的精灵文件: https://drive.google.com/file/d/1cRNr0VPAjkEbueuWFYwLYbpijVnLySqq/view?usp=sharing
这很难调试。
这是您上传到 Google 驱动器的文件的 readelf -WSs hello.o
的输出(它与您问题中的信息不匹配):
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
问题出在 .symtab
部分的 .sh_info
值 (14) 上。
根据 documentation,SYMTAB
部分的 .sh_info
应该包含“大于最后一个局部符号的符号 table 索引(绑定 STB_LOCAL)."
因此值 14 告诉链接器此文件中的 所有 符号都是本地的,因此不可能用于解析对它们的任何外部引用。
您需要将所有 LOCAL
个符号移动到 GLOBAL
个符号之前(此处,msg
和 len
需要在 hello
之前移动),所以符号 table 看起来像这样:
...
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
然后将 .symtab
部分的 .sh_info
设置为 12。
But also I would like to know how I can debug such issues in the future.
如您所见,调试 binutils ld
非常困难,部分原因是它使用了 libbfd
,它充满了宏并且本身很难调试。
我通过从源代码构建 Gold 来调试它,幸运的是产生了完全相同的失败。