gcc生成动态库必须先用-fpic编译源文件为.o,再用-shared链接成.so;-fpic是强制要求,非可选优化,否则会因重定位错误失败。

gcc -shared 生成动态库的基本命令结构
直接用 gcc -shared 无法单独生成可用的动态库,它必须配合 -fPIC 和目标文件(.o)一起使用。漏掉 -fPIC 是最常见错误,会导致链接失败并报错 relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object。
正确流程是两步:
- 先用
gcc -fPIC -c xxx.c -o xxx.o生成位置无关的目标文件 - 再用
gcc -shared -o libxxx.so xxx.o链接成动态库
例如:gcc -fPIC -c math_utils.c -o math_utils.o → gcc -shared -o libmath.so math_utils.o
-fPIC 是 mandatory,不是可选优化
-fPIC(Position Independent Code)不是“建议加”,而是 -shared 的硬性前提。x86_64 上不加会直接报错;即使在某些旧架构或低版本 GCC 中侥幸通过,运行时也可能因地址冲突崩溃。
原因在于:动态库加载地址在运行时才确定,所有代码和数据访问都必须基于相对偏移,不能含绝对地址引用。而默认编译出的 .o 是为可执行文件设计的,含大量绝对寻址。
注意:-fPIE 不行 —— 它只适用于可执行文件,对动态库无效。
导出符号控制:避免把内部函数暴露出去
默认情况下,gcc -shared 会导出所有非 static 全局符号(函数、变量),这容易造成命名污染或意外调用内部实现。
推荐做法是显式控制可见性:
- 源码中用
__attribute__((visibility("hidden")))标记内部函数 - 编译时加
-fvisibility=hidden,再用__attribute__((visibility("default")))显式标记要导出的接口 - 或者用
-Wl,--exclude-libs=ALL或版本脚本(.map文件)精细过滤
否则,一个没加 static 的辅助函数可能被外部 dlsym 拿到并误用。
链接依赖库时 -L 和 -l 的顺序很关键
如果动态库本身依赖其他库(比如用了 sqrt() 就依赖 libm),在生成 .so 时必须把 -L 和 -l 放在 -shared 命令的末尾,且 -l 必须在对应目标文件之后。
错误写法:gcc -shared -L/usr/local/lib -lm -o libfoo.so foo.o → libm 可能被忽略
正确写法:gcc -shared -o libfoo.so foo.o -L/usr/local/lib -lm
原因:GNU ld 是从左到右扫描,只对右侧已知未满足的符号尝试解析。把 -l 放前面,它还没看到 foo.o 里的未定义符号,就跳过了。
最后提醒一句:生成的 .so 文件名最好带主版本号(如 libxxx.so.1),并用 soname 控制运行时链接行为;否则升级后旧程序可能因找不到符号而直接启动失败,这种问题往往在部署时才暴露。











