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

pou*_*ler 12 c gcc

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

$ echo "void whatever() {}" > prog.c
$ gcc prog.c
Run Code Online (Sandbox Code Playgroud)

这会在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
Run Code Online (Sandbox Code Playgroud)

请注意,在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
Run Code Online (Sandbox Code Playgroud)

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

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

gcc-4.8 prog.c没有main()内部功能的情况下会发生此错误prog.c.


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

错误的数量似乎并不依赖于程序,这表明考虑的重定位不是源于此文件,而是缺少main()函数的直接结果.我曾推测,这些重与错误的指标3可能是main(),argcargv,但许多仍然存在,而这仅仅是一个未经证实的假说.

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

Ton*_*ony 5

C程序功能(类Unix)

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

ELF格式

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

  • section header - 用于链接多个elf以生成过程映像
  • 程序头 - 用于加载过程映像

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

    mapping<var_name, offset, size...>
    // and special cases
    mapping<external_var_name, offset, size...>
Run Code Online (Sandbox Code Playgroud)

每个程序都是单独编译的,这意味着地址分配是相似的(在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
Run Code Online (Sandbox Code Playgroud)

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

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

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

    void _start(char *args) {
        entry(args);
        exit();
    }
Run Code Online (Sandbox Code Playgroud)

参考


小智 3

不要直接编译代码,而是遍历编译的所有阶段来找出错误出现的位置(据我所知,此类错误发生在链接期间)。以下gcc论点会有所帮助:

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

现在:

gcc -E prog.c
gcc -S prog.c
gcc -c prog.c
Run Code Online (Sandbox Code Playgroud)

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

gcc prog.c -lc -nostartfiles
Run Code Online (Sandbox Code Playgroud)

这会产生警告:

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

这是因为顺序的原因。即,init调用_start函数,_start函数调用main函数。此警告意味着该_start函数无法定位main函数,其中init调用无法定位_start。请注意,这只是一个警告。为了避免这个警告,我们需要将命令改为编译时不出现警告,如下所示。

gcc prog.c -lc --entry whatever -nostartfiles
Run Code Online (Sandbox Code Playgroud)

prog.c使用此命令,我们通过以 function 为起点gcc链接库来指示内核进行编译,其中该代码不包含函数。libc.sowhatevermain

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

就 gcc 6.2.0 而言,我认为所有这些链接内容都是由编译器本身处理的。因此,您可以简单地提及编译命令,如下所示。

gcc -c prog.c -nostartfiles
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

提供的所有数据都是通过逐步分析完成的。您可以重新创建提到的所有步骤。希望这能消除您的疑虑。再会!

  • 虽然这个答案本身相当彻底且有趣,但我真的不明白它是如何回答这个问题的。很明显,错误来自链接器,因为错误位于“ld”内部。您用“_start”和“--entrywhatever-nostartfiles”提到的内容似乎致力于解决显示的错误,而不是解释“重定位 N 具有无效符号索引 M”的实际含义。但我提供赏金并不是因为我想修复错误,而是因为我想更多地了解链接器本身的这一部分。 (3认同)