自定义 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) 上。

根据 documentationSYMTAB 部分的 .sh_info 应该包含“大于最后一个局部符号的符号 table 索引(绑定 STB_LOCAL)."

因此值 14 告诉链接器此文件中的 所有 符号都是本地的,因此不可能用于解析对它们的任何外部引用。

您需要将所有 LOCAL 个符号移动到 GLOBAL 个符号之前(此处,msglen 需要在 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 来调试它,幸运的是产生了完全相同的失败。