使用--unresolved-symbols=ignore-in-object-files编译时为何需要-fPIC?
嘿,我来帮你拆解这个问题——你遇到的段错误其实是链接器选项误用导致的,咱们一步步说清楚:
核心问题:--unresolved-symbols=ignore-in-object-files的坑
你编译main.cpp时用的-Wl,--unresolved-symbols=ignore-in-object-files这个选项,本质是告诉链接器“别管那些找不到定义的符号,直接生成可执行文件就行”。但这么做的后果是:链接器不会为未定义的f()生成动态链接必需的PLT/GOT条目——这两个结构是程序运行时从共享库中查找符号地址的关键。
所以当你用LD_PRELOAD加载libfunctions.so时,你的a.out根本不知道要去这个共享库里找f()的定义,调用f()的时候就会跳到一个无效的内存地址(比如空指针),直接触发段错误。
正确的两种解决办法
办法一:编译时直接链接共享库(最稳妥)
不需要用那个忽略未解析符号的选项,直接在编译main.cpp时告诉链接器“f()的定义在libfunctions.so里”:
# 先编译main.cpp为目标文件 g++ -c main.cpp -o main.o # 链接时指定库的路径(-L.表示当前目录)和库名(-lfunctions对应libfunctions.so) g++ main.o -L. -lfunctions -o a.out
运行时只要让系统能找到共享库就行,比如设置LD_LIBRARY_PATH:
LD_LIBRARY_PATH=./ ./a.out
这样运行就能正常输出42了。
办法二:强制链接器生成动态符号引用(适合LD_PRELOAD场景)
如果你就是想用LD_PRELOAD来动态加载库,那可以在编译main.cpp时明确告诉链接器“f()是个未定义符号,要留到运行时解析”,用--undefined=f选项:
g++ main.cpp -Wl,--undefined=f -o a.out
然后再用LD_PRELOAD运行:
LD_PRELOAD=./libfunctions.so ./a.out
这时候链接器会为f()生成PLT/GOT条目,运行时就能正确从libfunctions.so里找到f()的地址,调用成功。
关于你提到的“编译main.c就没问题”的补充
其实这不是C和C的本质差异,而是如果用C编译时,你可能没加那个--unresolved-symbols=ignore-in-object-files选项?或者如果functions.cpp改成C编译的话,名字修饰会更简单,但在你的案例里,因为都是C编译,f()的名字修饰是一致的(比如会变成_Z1fv),所以名字修饰不是问题,核心还是那个错误的链接选项。
内容的提问来源于stack exchange,提问作者user2108462

