近日在编译ACE库时,依赖库总会产生“undefined reference to XXX”错误,通常这个问题是所引用的函数没有定义导致的,但是这个库中这些函数确实定义了的,排除了这种可能性。
那么接下来看一下这个库中这些函数的情况。
初看之下,似乎一切正常,函数也是存在的,
但是仔细观察bind的值,似乎有些不对了,这些函数是动态库导出函数,但是bind显示的值却是local。
linux环境下动态库中的函数默认都是全局可见的,是什么改变了函数的这种属性呢?
首先看一下编译过程的输出信息,编译每一份文件时都设置了-fvisibility=hidden属性,
如果这个设置成hidden,函数确实不会导出。
接着向上追溯,从makefile中寻找问题根源,发现如下逻辑。
结合注释很容易理解,g++高版本支持了visibility属性,当g++版本大于4就会开启此属性,也就是默认的话动态库符号就是local。
在linux下,源文件中的所有函数都有一个默认的visibility属性为public,在编译命令中加入-fvisibility=hidden参数,会将所有默认的public的属性变为hidden。此时,如果对函数设置__attribute__ ((visibility("default")))参数,使特定的函数仍然按默认的public属性处理,则-fvisibility=hidden参数不会对该函数起作用。所以,设置了-fvisibility=hidden参数之后,只有设置了__attribute__ ((visibility("default")))的函数才是对外可见的,如此则效果等同于Visual Studio下的__declspec(dllexport)定义。
问题本来到这里就可以结束了,我们再深入思考几个问题:
1.用c++写的(准确来说是g++编译)导出库供c或其他语言调用时,有时也会出现“undefined reference to XXX”
这是c++为实现函数重载而采用了name mangling方案,把命名空间、函数名和参数组合在一起重新命名,编译器处理之后的函数名称实际上已经不是你调用的那个函数命了。要解决这种问题,可以在导出函数用extern “C”声明解决。
2.如何指定linux中哪些符号可见,哪些不可见呢
__attribute__((visibility("default"))) void Function(){};
__attribute__((visibility("hidden"))) void Function(){};
设置为default时,没有显式标识为hidden的符号都处理为可见;设置为hidden时,没有显式标识为可见的符号都处理为隐藏。如果您在编译中没有指定-fvisibility选项,编译器会自行处理为缺省的可见性。
编译器还支持-fvisibility-inlines-hidden选项,用于强制隐藏所有的嵌入函数。当希望对大多数项目使用缺省的可见性,但又希望隐藏所有的嵌入函数时,您可能会用到这个选项。
仿造windows下导出dll的做法,利用宏定义
#if defined SO_EXPORT_VISI
#define SO_EXPORT_VISI_ __attribute__((visibility("default")))
#else
#define SO_EXPORT_VISI_
#endif
3.常见的链接器的含义
-Wl,-Bdynamic/-Bstatic 这两个分别指定了链接是采用动态库还是静态库,也可以用来把静态库编译成动态库。
示例如下:
-Bstatic -la -lb -lc -lm -Bdynamic -lrt -lstdc++
这样就可以把静态库liba、libb、libc合并成一个库,避免了常规的解压再合并的繁琐操作
-Wl,-R 连接时指定运行时依赖的路径,其他调用者就不用再指定依赖库路径了。
-Wl,soname 向so库中写入库名,可以用来做版本控制。
4.更快速的定位问题的方法
nm -D soname
直接用来查看库的导出函数名。