静态库并入共享库时需编译为PIC吗?两种调用场景解析
静态库并入共享库的编译疑问
代码文件
a.c
// a.c int a() { return 123; }
b.c
// b.c extern int a(); int b() { return a(); }
main.c
// main.c #include <stdio.h> extern int b(); int main() { printf("%d\n", b()); return 0; }
编译流程与Makefile
编译要求:将a.c编译为静态库liba.a,b.c编译为共享库libb.so(静态库liba.a会被链接并入libb.so);main.c编译为可执行文件,动态链接libb.so。对应的Makefile如下:
all: gcc -c -fPIC -o a.o a.c ar rcs liba.a a.o gcc -c -fPIC -o b.o b.c gcc -shared -o libb.so b.o liba.a gcc -o main -L. -lb
疑问
已知b.o因生成共享库使用-fPIC编译,现提出两个问题:
- 因为
liba.a会并入共享库,是否必须用-fPIC编译a.o?测试中去掉该选项仍可正常运行。 - 若修改
main.c,声明extern int a()并调用(假设libb.so使其可见),此时是否需用-fPIC编译a.o?测试中去掉该选项也能正常运行。
解答
问题1:并入共享库的静态库成员是否必须用-fPIC编译?
不是必须,但强烈建议这么做。
你测试时能正常运行,只是当前系统的编译器/链接器兼容了非PIC目标文件并入共享库的操作,但这属于依赖平台的特殊行为,不具备可移植性。比如在x86平台,非PIC代码可以通过重定位在共享库中工作,但换到x86_64、ARM等架构,链接器会直接报错,拒绝将非PIC代码打包进共享库。
就算平台允许,非PIC代码并入共享库后会让共享库失去位置无关性的核心优势:加载时需要额外的重定位操作,拖慢启动速度;同时多个进程加载该共享库时,无法共享内存页(每个进程的重定位结果不同),会浪费内存资源。
问题2:main直接调用共享库中导出的a()时,是否需要a.o用-fPIC编译?
本质和问题1一致,依然建议必须用-fPIC,测试能跑只是平台兼容的特例。
当main直接调用a()时,a()的符号会从libb.so导出。如果a.o是非PIC的,在严格要求PIC的平台上,链接libb.so阶段就会失败。就算当前平台能运行,同样会面临加载重定位、内存页无法共享的问题,属于有隐患的写法。
总结:测试通过不代表写法正确,-fPIC是编写跨平台、高性能共享库的标准要求,不要依赖平台的兼容行为。
内容的提问来源于stack exchange,提问作者tmlen
相关产品推荐
相关产品推荐

