问题起源
在Linux下编译动态库的时候,所有的符号默认都是导出的,也就是动态库中的函数名,类名等,在外部都是可见的。
当程序引用多个动态库时,由于各个动态库可能属于不同的团队来开发,不同团队使用相同的第三方库的可能性也是有的(例如openssl,libcurl,cjson等),
不同的团队使用的第三方库的版本大概率都是不同的。在编译期间可能不会产生编译错误,但是在运行时可能会导致符号冲突,
引发的现象是调用相同的函数本地测试的结果和运行时的结果不同。
通过修改链接顺序解决问题
既然是不同版本的库相互影响,假设高版本的库能向下兼容,是否可以修改链接顺序,将高版本的库放在前面,低版本的库放在后面,这样就会先加载高版本的库,问题也可以解决
例如libtest1.so,libtest2.so都有openssl库,但是libtest2.so库中openssl版本比较高,那么可以通过修改链接顺序解决问题
LDFLAGS += -Wl,-rpath . -L . -ltest2 -ltest1
修改链接顺序对于能向下兼容的第三方库是可以解决问题,但是某些第三方库也是不向下兼容的,例如CJSON
通过链接过程中指定优先使用本动态库中的符号
一般而言,单个动态库中并不会出现符号冲突,动态库内部符号冲突一般在编译过程中就会暴露出来,因此当上层程序调用动态库时,
我们可以设置上层程序优先使用本动态库中的符号,而不是全局符号。这样即使其他动态库导出的符号和自己动态库中的符号同名,冲突也不会发生,
运行自己动态库程序的时候会使用自己本动态库中的函数和类。
LDFLAGS += -Wl,-Bsymbolic
注意:这边编译选项必须在被链接的动态库的编译过程中添加,而不是在上层程序链接第三方库的时候添加,例如test引用liba.so,那么这边编译选项需要在编译liba.so过程中添加
通过隐藏符号表解决问题
既然是符号冲突导致的异常,那么可以把库符号信息进行修改,把库内部调用的符号限定为只能内部调用,外部调用的符号限定为可公共调用。一般方法是在函数前增加 __attribute__ 前缀来控制。
在编译过程中加入-fvisibility=hidden选项,因此符号的可见性。对于想要对外暴露的函数通过函数前增加__attribute__((visibility("default")))来指定输出。
gcc -g -O0 -Wall -fPIC -fvisibility=hidden -c test.c -o test.o
附件
查看动态库中的符号表
nm libxxx.so |grep [函数名]
备注: t表示符号是本地符号,T表示符号是全局符号