这是一个重现问题的测试:
$ 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(),argc和argv,但许多仍然存在,而这仅仅是一个未经证实的假说.
这完全超出了我的想法,任何有助于我更好地理解它的信息,或者在GCC的后续版本中发生了哪些变化,都将受到热烈欢迎.
main不像你原先想的那样是程序的开始,c lib有一个启动程序(crt1.o),它有一个_start程序可以调用我们的程序main并在之后执行清理工作mainELF有两个标题,如下所示:
这里我们只关注节头结构:
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 crtN(N取决于 ELF 文件)在init调用之前执行_start,它提供有关内存和其他机器相关参数的元数据。链接错误显示为
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 重定位 0 具有无效的符号索引 11
不提供纠正问题的完整信息,因为机器在识别错误点方面不如人类聪明。
这会生成一个完整的可执行文件。请注意,当我们在项目中处理库/模块时,会使用此类代码(没有主函数)。
提供的所有数据都是通过逐步分析完成的。您可以重新创建提到的所有步骤。希望这能消除您的疑虑。再会!