我有以下两个文件:
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
进行编译。
我错过了什么?
答案 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链接器错误,但我认为它将被视为非常低优先级。