为什么我需要-fPIC来编译--unresolved-symbols = ignore-in-object-files?

时间:2018-04-01 17:38:36

标签: c++ linker loading elf

我有以下两个文件:

main.cpp中:

#include <iostream>
int f();
int main(){
  std::cout<<f();
}

functions.cpp:

int f(){
   return 42;
}

我使用以下命令将functions.cpp编译成libfunctions.so:

g++ -fPIC -shared functions.cpp -o libfunctions.so

我使用以下命令将main.cpp编译成a.out:

g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files

当我使用此命令运行a.out时:

LD_PRELOAD=./libfunctions.so ./a.out

我遇到了分段错误。

但是如果我使用这个命令将main.cpp编译成a.out:

g++ -fPIC main.cpp -Wl,--unresolved-symbols=ignore-in-object-files

然后它有效。

我理解为什么必须使用-fPIC编译共享库,因为无法知道加载时它们将被加载到的地址。但是我不明白为什么main.cpp也必须编译为PIC。我认为,因为a.out的加载地址在链接时是已知的,所以当然不需要使用-fPIC进行编译。

我错过了什么?

1 个答案:

答案 0 :(得分:2)

我假设您使用的是GNU工具链。遗憾的是,binutils ld有时会为无效输入生成损坏的二进制文件,而不是失败并显示错误消息。

在你的情况下,我得到:

./a.out: error while loading shared libraries: unexpected PLT reloc type 0x00

此错误消息正确无误:

Relocation section '.rela.plt' at offset 0x628 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
…
000000000000  000000000000 R_X86_64_NONE                        0

R_X86_64_NONE的值为零,如果遇到错误,它有时会被ld使用而不是真正的重定位。

这是否是一个ld bug,值得商榷。 ld生成了你要求的二进制文件,忽略了错误。它确实产生了无效的重定位。使用-fno-plt进行编译时,根本没有重定位,但程序仍然崩溃,因为已解析的符号相对于可执行文件或文本部分的偏移量为0。

我怀疑使用-fPIC时,它恰好适合您,因为ld会为未知符号生成动态重定位。 (不过,我无法获得binutils 2.30来进行此搬迁。)

通常,不可能生成对未定义符号的正确动态重定位。如果没有定义,在许多体系结构中,无法判断目标是函数还是对象。如果使用复制重定位,则对对象的未定义引用需要准确的大小信息。函数和对象引用都需要符号定义才能获得正确的符号版本(如果有的话)。有很多原因可以解释欠连接问题。

可能值得将此报告为binutils链接器错误,但我认为它将被视为非常低优先级。