C++ 为什么我需要-fPIC来编译对象文件中的--unresolved symbols=ignore?

C++ 为什么我需要-fPIC来编译对象文件中的--unresolved symbols=ignore?,c++,linker,loading,elf,C++,Linker,Loading,Elf,我有以下两个文件: main.cpp: #include <iostream> int f(); int main(){ std::cout<<f(); } 我将functions.cpp编译为libfunctions.so,使用以下命令: g++ -fPIC -shared functions.cpp -o libfunctions.so g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files

我有以下两个文件:

main.cpp:

#include <iostream>
int f();
int main(){
  std::cout<<f();
}
我将functions.cpp编译为libfunctions.so,使用以下命令:

g++ -fPIC -shared functions.cpp -o libfunctions.so
g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
LD_PRELOAD=./libfunctions.so ./a.out
g++ -fPIC main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
我使用以下命令将main.cpp编译为.out:

g++ -fPIC -shared functions.cpp -o libfunctions.so
g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
LD_PRELOAD=./libfunctions.so ./a.out
g++ -fPIC main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
使用此命令运行.out时:

g++ -fPIC -shared functions.cpp -o libfunctions.so
g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
LD_PRELOAD=./libfunctions.so ./a.out
g++ -fPIC main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
我有一个分割错误

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

g++ -fPIC -shared functions.cpp -o libfunctions.so
g++ main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
LD_PRELOAD=./libfunctions.so ./a.out
g++ -fPIC main.cpp -Wl,--unresolved-symbols=ignore-in-object-files
然后它就起作用了

我理解为什么共享库必须用-fPIC编译,因为人们不知道加载时加载它们的地址。但是我不明白为什么
main.cpp
也必须编译为PIC。我认为既然
a.out
的加载地址在链接时已知,那么肯定没有必要使用
-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链接器错误,但我认为它将被视为极低优先级。

它应该为未知符号生成动态重定位吗?通常,不可能为未知符号生成正确的动态重定位。尺寸和版本信息会丢失。您能再详细说明一下吗?我不确定为什么它需要知道符号的大小,而库版本可能会有所不同。我认为使用动态链接的主要原因之一是,您可以使用不同的
。因此
文件通过不同的实现提供相同的功能。对象符号有一个大小,而且,您不能在不影响ABI兼容性的情况下更改大小(即使大小在C级别不可见)。如果没有定义,链接编辑器将不知道符号是指函数还是指对象。