在“重定位具有无效符号索引”错误期间会发生什么?

这是一个重现问题的测试:

$ echo "void whatever() {}" > prog.c $ gcc prog.c 

这会在GCC 4.8.4上产生以下错误:

  /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11 /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12 /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2 /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2 ... etc ... /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13 /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21 /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2 /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' collect2: error: ld returned 1 exit status 

请注意,在GCC 6.2.0上,与此问题相关的错误消失了,而它只产生:

 /usr/lib/x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' collect2: error: ld returned 1 exit status 

许多用户,Stack Overflow和其他地方已经多次报告过这种情况。

我想了解这个错误,而不是解决它(它已经解决了)。

执行gcc-4.8 prog.c而没有gcc-4.8 prog.cmain()函数时会发生此错误。


我在binutils-source包上对这个错误进行了文本搜索。 一个乏味的谷歌搜索只给了我一个有用的链接,帮助我更好地理解重定位处理的概念 。

错误的数量似乎并不依赖于程序,这表明考虑的重定位不是源于此文件,而是缺少main()函数的直接结果。 我假设其中3个带有错误索引的重定位可能适用于main()argcargv ,但许多仍然存在,这只是一个未经证实的假设。

这完全超出了我的想象,任何有助于我更好地理解它的信息,或者在GCC的后续版本中发生了哪些变化,都将受到热烈欢迎。

C程序function(类Unix)

  • 每个程序都分别编译成精灵格式
  • c程序可以使用外部变量/函数引用,后面链接
  • main不是你原先想到的程序的开始,c lib有一个启动程序( crt1.o ),它有一个_start程序,它将在main之后调用我们的main和do crt1.o工作
  • 总结以上陈述,我们可以知道即使是一个非常简单的OP程序也需要联系起来

ELF格式

ELF有两个标题,如下所示:

  • section header – 用于链接多个elf以制作流程图像
  • 程序头 – 用于加载过程映像

这里我们只关注节头结构:

  mapping // and special cases mapping 

每个程序都是单独编译的,这意味着地址分配是相似的(在Linux的早期版本中,每个编译的程序都以相同的虚拟地址 – 0x08000000 ,并且许多攻击可以利用这个,所以它改为添加一些随机增量到解决问题的地址),因此可能存在一些覆盖区域。 这就是需要重新定位地址的原因。

搬迁

重定位信息(偏移量,值等)存储在.rel.*部分中:

  Relocation section '.rel.text' at offset 0x7a4 contains 2 entries: Offset Info Type Sym.Value Sym. Name 0000000d 00000e02 R_386_PC32 00000000 main 00000015 00000f02 R_386_PC32 00000000 exit Relocation section '.rel.debug_info' at offset 0x7b4 contains 43 entries: Offset Info Type Sym.Value Sym. Name 00000006 00000601 R_386_32 00000000 .debug_abbrev 0000000c 00000901 R_386_32 00000000 .debug_str 

当链接器想要在重定位过程中设置main的地址时,它无法在已编译的elf文件中找到符号,因此它会抱怨并停止链接过程。

这是os实现的简化版本, start.c对应于crt1.o的源代码:

  int entry(char *); // corresponds to main void _start(char *args) { entry(args); exit(); } 

参考

  • ELF
  • 搬迁

不是直接编译代码,而是经历编译的所有阶段以找出错误发生的位置(据我所知,在链接期间会发生这样的错误)。 以下gcc参数将有所帮助:

  • -E仅预处理; 不编译,汇编或链接
  • -S仅编译; 不要组装或链接
  • -c编译和汇编,但不要链接

现在:

 gcc -E prog.c gcc -S prog.c gcc -c prog.c 

使用您提到的程序/代码,所有这些步骤都适用于gcc 4.8.4。 但是,在链接期间,当您使用gcc prog.c进行编译时,编译器无法链接到相应的库,因为它没有被提及。 另外,我们在prog.c文件中没有main函数。 所以,我们需要指明-nostartfiles开关。 因此,您可以将prog.c编译为:

 gcc prog.c -lc -nostartfiles 

这会产生警告:

/ usr / bin / ld:警告:找不到条目符号_start; 默认为00000000004002a3

这是因为序列。 即, init调用_start函数, _start函数调用main函数。 此警告意味着_start函数无法找到main函数,其中init调用无法找到_start 。 请注意,这只是一个警告。 为了避免这种警告,我们需要将命令更改为编译而不发出警告,如下所示。

 gcc prog.c -lc --entry whatever -nostartfiles 

使用此命令,我们指示内核使用gcc编译prog.c方法是将libc.so库与起始点链接为函数,其中此代码不包含main函数。

这是我编译的gcc 4.8.4的上下文。

来到gcc 6.2.0的情况,我认为所有这些链接的东西都由编译器本身来处理。 因此,您可以简单地提及编译命令,如下所示。

 gcc -c prog.c -nostartfiles 

如果它产生任何其他错误或警告,您可以使用上面提到的开关。

另请注意,在init调用_start之前执行crt0crtNN依赖于ELF文件),这会提供有关内存和其他机器相关参数的元数据。 链接错误显示为

/ usr / bin / ld:/usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info):重定位0具有无效的符号索引11

不提供纠正问题的完整信息,因为机器在识别错误​​点方面不如人类聪明。

这将生成完整的可执行文件。 请注意,当我们处理项目中的库/模块时,会使用这样的代码(没有main函数)。

提供的所有数据都是通过逐步分析完成的。 您可以重新创建所有提到的步骤。 希望这清除了你的怀疑。 美好的一天!

我们可以将其分为两部分:

对’main’的未定义引用

 /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' collect2: error: ld returned 1 exit status 

这只是因为C运行时库( crt1.o )正在尝试调用您的(缺少的) main()函数。 这里 / 这里有各种C运行时文件的概述。

重定位X具有无效的符号索引Y.

注意 :这对我来说是一个使命(学习机会) – 我花了几天时间在有限的空闲时间内进行研究和理解。 这是我长期以来一直想知道的事情,但从未考虑过……希望我的理解是正确的(虽然显然不完整 – 如果可以,我会更新它)。

这有点复杂,隐藏起来。

只需阅读消息,我们就可以发现它与调试信息有关(提到了crt1.o 调试文件的.debug_info.debug_line部分)。 注意/usr/lib/debug/ path, crt1.o包含调试信息,另一个crt1.o是一个“ 剥离 ”文件…

格式字符串位于binutils项目中,特别是bfd/elfcode.h 。 BFD是二进制文件描述符 – 在许多系统体系结构中处理目标文件的GNU方法。

BFD是用于二进制文件的中间格式 – GCC在最终写入a.outELF或其他二进制文件之前将使用BFD。

查看手册,我们可以找到一些有趣的知识片段:

[…]对于散列表中的每个条目,a.out链接器保留符号在最终输出文件中具有的索引(使用此索引号,以便在执行可重定位链接时,输出文件中使用的符号索引可以复制重定位时快速填写)。 [资源]

标准记录仅包含地址,符号索引和类型字段。 [资源]

这意味着由于与特定(缺少?)’符号’相关的重定位而发出这些错误。 在此上下文中的“符号”是任何命名的“事物” – 例如:函数和变量。

由于这些“无效符号”似乎是通过简单地声明main()来解决的,我猜这些符号索引中的一些(全部?)是从main() ,它的调试信息和/或关系派生的。

我不能告诉你在提到的符号索引(2,11,12,13,21)应该是什么,但有趣的是我的测试产生了相同的符号索引列表。

单独使用crt1.o运行ld会给我们类似的输出:

 $ ld /usr/lib/x86_64-linux-gnu/crt1.o ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 4 has invalid symbol index 11 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 5 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 6 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 7 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 8 has invalid symbol index 12 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 9 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 10 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 11 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 12 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 13 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 14 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 15 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 16 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 17 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21 ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2 /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x12): undefined reference to `__libc_csu_fini' /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x19): undefined reference to `__libc_csu_init' /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x25): undefined reference to `__libc_start_main'