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

Why do I need -fPIC for compiling with --unresolved-symbols=ignore-in-object-files?

我有以下 2 个文件:

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 的加载地址在 link 时已知,那么肯定不需要用 -fPIC 编译。

我错过了什么?

我假设您使用的是 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 错误,值得商榷。 ld 生成了您要求的二进制文件,忽略了错误。它确实产生了无效的重定位。当使用 -fno-plt 编译时,我根本没有重定位,但程序仍然崩溃,因为已解析的符号相对于可执行文件或文本部分似乎具有偏移量 0。

我怀疑 -fPIC,它恰好对你有用,因为 ld 为未知符号生成动态重定位。 (不过,我无法让 binutils 2.30 生成此重定位。)

一般来说,不可能 生成到未定义符号的正确动态重定位。如果没有定义,在许多体系结构上,无法判断目标是函数还是对象。如果使用复制重定位,未定义的对象引用需要准确的大小信息。函数和对象引用都需要一个符号定义来获得正确的符号版本(如果有的话)。链接不足问题严重的原因有很多。

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