必须加 -fpic 和 -shared:-fpic 生成位置无关代码以支持 aslr 和共享内存,-shared 告知链接器构建动态对象并设置 elf 关键字段;二者缺一不可,否则 .so 文件加载失败或被 ld.so 拒绝。

必须加 -fPIC 和 -shared,缺一不可。其他参数都是次要的,但这两个不写,生成的 .so 文件要么加载失败,要么根本不能被 ld.so 识别。
为什么一定要用 -fPIC
Linux 动态库强制要求位置无关代码。没加 -fPIC 编译出的 .o 文件,即使强行用 gcc -shared 打包成 .so,运行时大概率报错:cannot make segment writable for relocation 或直接 Segmentation fault。
-
-fPIC让编译器生成通过 GOT(全局偏移表)寻址的代码,支持 ASLR 和多进程共享内存 - 嵌入式或老系统可能允许非 PIC,但现代 x86_64 / aarch64 发行版(如 Ubuntu 22.04+、CentOS 8+)会直接拒绝加载
- 错误示范:
gcc -c hello.c -o hello.o→gcc -shared -o libhello.so hello.o(缺少-fPIC,不可用)
gcc -shared 的实际用法
-shared 不是可选开关,而是告诉链接器“这不是可执行文件,也不是静态库,我要做动态对象”。它会跳过入口符号检查(比如不需要 main),并设置 ELF 的 `DT_SONAME` 等关键字段。
- 正确写法:
gcc -shared -fPIC -o libhello.so hello.o - 可以一次链接多个
.o:gcc -shared -fPIC -o libmath.so add.o sub.o mul.o - 不能混用静态库:
gcc -shared -fPIC -o libfoo.so libbar.a会失败,必须先解包或确保所有输入都是 PIC 目标文件 - 如果源文件里用了全局变量或函数指针,
-fPIC还得配合-DPIC宏(某些头文件依赖此宏判断调用方式)
生成后为什么运行时报 libhello.so: cannot open shared object file
不是编译错了,是运行时找不到路径。Linux 默认只查 /usr/lib、/lib64 和 /usr/local/lib,当前目录 . 不在搜索列表里。
- 临时解决:
LD_LIBRARY_PATH=. ./main(注意=.后面不能有空格) - 永久解决:把
libhello.so拷到系统路径(如/usr/local/lib),再运行sudo ldconfig - 更安全的做法:用
-Wl,-rpath,'$ORIGIN'把相对路径硬编码进可执行文件:gcc -o main main.c -L. -lhello -Wl,-rpath,'$ORIGIN' - 验证是否生效:
ldd ./main应该显示libhello.so => ./libhello.so(而不是not found)
最容易被忽略的是:-fPIC 必须加在编译 .o 阶段,不是链接阶段;-shared 必须加在链接 .so 阶段,不能漏。两者错位或遗漏,都会导致看似成功实则不可用的 .so 文件。











