在"重定位期间发生的事情具有无效的符号索引"错误?

时间:2014-10-16 15:30:36

标签: c gcc

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

$ 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内没有main()功能的情况下执行prog.c时会发生此错误。


我在binutils-source包上对此错误进行了文本搜索。一个乏味的谷歌搜索只给了我one useful link helping me better understanding the concept of relocation handling

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

这非常高于我的头脑,任何有助于我更好地理解它的信息,或者在后续版本的GCC中发生的变化都会受到热烈欢迎。

3 个答案:

答案 0 :(得分:5)

C程序功能(类Unix)

  • 每个程序都单独编译成精灵格式
  • c程序可以使用外部变量/函数引用,稍后链接
  • main不是您最初想到的程序的开始,c lib有一个启动程序(crt1.o),它有一个_start程序将调用我们的main并在main
  • 之后进行清洁工作
  • 总结以上陈述,我们可以知道,即使是一个非常简单的OP程序也需要链接

ELF格式

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

  • 节标题 - 用于链接多个精灵以制作流程图像
  • 程序标题 - 用于加载过程映像

这里我们只关注节标题结构:

    mapping<var_name, offset, size...>
    // and special cases
    mapping<external_var_name, offset, size...>

每个程序都是单独编译的,这意味着地址分配是相似的(在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文件中找到符号,因此它会抱怨并停止链接过程。

实施例

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

    int entry(char *); // corresponds to main

    void _start(char *args) {
        entry(args);
        exit();
    }

参考

答案 1 :(得分:3)

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

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

现在:

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

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

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

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

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

gcc -c prog.c -nostartfiles

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

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

  

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

不提供纠正问题的完整信息,因为机器在识别错误​​点时并不像人类那样聪明。

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

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

答案 2 :(得分:3)

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

对'main&#39;

的未定义引用
/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运行时文件here / here

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

注意 :这对我来说是一项使命(学习机会) - 我花了几天时间在有限的免费研究和理解我有时间。这是我很长一段时间都在想的东西,但从来没有考虑过......希望我的理解是正确的(虽然显然不完整 - 如果可以,我会更新它。)

这有点复杂,而且隐藏起来。

只需阅读该消息,我们就可以发现它与调试信息有关(.debug_info .debug_linecrt1.o部分提到了debug 文件。请注意/usr/lib/debug/路径,其中只包含调试信息,另一个crt1.o是&#34; 剥离&#34;文件...

格式字符串位于binutils项目中,具体为bfd/elfcode.h。 BFD是Binary File Descriptor - 在许多系统架构中处理目标文件的GNU方式。

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

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

  

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

  

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

这意味着由于与特定(缺少?)符号相关的重定位而发出这些错误。 A&#39;符号&#39;在这种情况下,任何命名的东西都是&#39; - 例如:函数和变量。

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

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

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

$ 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'