在普通的C语言中,如果您希望将函数或者变量限制在当前文件中,需要对其使用static关键字。然而,在一个包含很多文件的共享库中,如果您希望某个符号可以被共享库内部的几个文件访问,而又不提供给外部,则对符号进行隐藏处理就会比较困难。大多数的连接器都提供一些便利的方法来隐藏或者显示模块中所有的符号,但如果希望更加具有选择性,则需要更多的处理。
幸运的是,GCC 4.0提供了一些新的、用于改变符号可见性的方法。下面部分将对这些新方法进行描述
GCC 4.0编译器支持一些用于隐藏和显示符号名称的新选项,同时还支持一个新的pragma和一些编译器属性,用于改变代码符号的可见性。
请注意:下面的特性只在GCC 4.0和更新的版本提供。
编译器选项
GCC 4.0支持一个新的选项,用于设置源文件中符号的缺省可见性。这个选项是-fvisibility=vis ,您可以用它来设置当前编译的符号的可见性。这个选项的值可以是default(缺省)或者hidden(隐藏),设置为default时,没有显式标识为hidden的符号都处理为可见;设置为hidden时,没有显式标识为可见的符号都处理为隐藏。如果您在编译中没有指定-fvisibility选项,编译器会自行处理为缺省的可见性。
请注意:default设置不是指编译器缺省的处理方式。和hidden设置一样,default来自ELF格式定义的可见性名称。具有default可见性的符号和所有不使用特殊机制的符号具有相同的可见性类型;也就是说,它将作为公共接口的一部分输出。
编译器还支持-fvisibility-inlines-hidden选项,用于强制隐藏所有的嵌入函数。当您希望对大多数项目使用缺省的可见性,但又希望隐藏所有的嵌入函数时,您可能会用到这个选项。
可见性属性
如果用GCC 4.0编译代码,可以用可见性属性将个别的符号标识为default或hidden:
__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("hidden"))) void MyFunction2() {}
可见性属性会覆盖编译时通过-fvisibility选项指定的值。因此,增加default可见性属性会使符号在所有情况下都被输出,反过来,增加hidden可见性属性会隐藏相应的符号。
可见性属性可以应用到函数,变量,模板,以及C++类。如果一个类被标识为hidden,则该类的所有成员函数,静态成员变量,以及编译器生成的元数据,比如虚函数表和RTTI信息也都会被隐藏。
为了演示这些属性如何在编译时进行工作,请看一下下面的声明:
int a(int n) {return n;}
__attribute__((visibility("hidden"))) int b(int n) {return n;}
__attribute__((visibility("default"))) int c(int n) {return n;}
class X
{
public:
virtual ~X();
};
class __attribute__((visibility("hidden"))) Y
{
public:
virtual ~Y();
};
class __attribute__((visibility("default"))) Z
{
public:
virtual ~Z();
};
X::~X() { }
Y::~Y() { }
Z::~Z() { }
用-fvisibility=default选项编译这段代码会使函数a和c ,还有类X和Z的符号输出到库的外部。而如果用-fvisibility=hidden选项进行编译,则会输出函数c和类Z的符号(使用“readelf -s test.so”查看)
限制符号可见性的原因
从动态共享库中尽可能少地输出符号是一个好的实践经验。输出一个受限制的符号会提高程序的模块性,并隐藏实现的细节。在库中减少符号的数目还可以减少库的内存印迹,减少动态连接器的工作量。动态连接器装载和识别的符号越少,程序启动和运行的速度就越快。
Linux下编写动态链接库的简单过程
(1) 使用编译选项 -fPIC ,产生与位置无关的代码
(2) 使用编译选项 -fvisibility=hidden 隐藏符号
(3) 使用链接选项 -shared 表示生成动态链接库
(4) 使用链接选项 -wl 将参数传递给链接器
(5) 使用链接选项 -soname 指定库名,注意不是库文件名
(6) 保险起见,显式链接 C 库,即指定 -lc ,保证得到所需的启动代码,避免程序在有不兼容版本 C 库的系统上无法启动
(7) 为对外输出符号加上特殊声明, __attribute__((visibility("default")))
(8) 为了让 GCC 编译链接时能找到非默认路径下的动态链接库,在 /etc/profile 中添加 GCC 的 LIBRARY_PATH 环境变量
(9) 为了让程序运行时能自动找到非默认路径下的动态链接库,在 /etc/ld.so.conf 中添加路径,然后用 /sbin/ldconfig 更新缓存
(10) 例子:
a) int func() __attribute__((visibility("default")));
b) gcc –fPIC –fvisibility=hidden –g –c xxx.c –o xxx.o
c) gcc –g –shared –Wl,-soname,libxyz.so –o libxyz.so.1.0 xxx.o
export LD_LIBRARY_PATH=thirdparty/lib 增加自己的 动态库路径
http://shake863.iteye.com/blog/213266
http://blog.csdn.net/zdragon2002/article/details/6061962
阅读(1590) | 评论(0) | 转发(0) |