为什么我需要 -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 链接器错误,但我认为它将被视为非常低的优先级。
我有以下 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 链接器错误,但我认为它将被视为非常低的优先级。