天天看点

Linux下控制动态库导出

在Linux中动态库的确给程序带来了良好的扩充性,并减少了内存的使用量,但这是有代价的。例如:

1

2

3

4

5

6

<code>#include &lt;stdio.h&gt;</code>

<code>int</code> <code>main(</code><code>int</code> <code>argc, </code><code>char</code> <code>*argv[])</code>

<code>{</code>

<code>       </code><code>printf</code><code>(“hello\n”);</code>

<code>       </code><code>return</code> <code>0;</code>

<code>}</code>

我们知道printf是在glibc中定义的,如果不适用动态库,而是将glibc静态链接到进程中的话,那么printf函数的地址在编译时就是已知的了,使用很简单的依据地址转移,就可以进行函数调用。

可是如果采用动态库的话,在程序编译阶段,编译器就无法得知printf的函数地址,因为动态库加载的内存地址时随机的。那么对于动态库的情况,针对printf是如何寻址的呢?

在程序运行时,当调用printf的时候,程序会将处理权交给linker,由其负责在执行文件以及其连接的动态库中查找printf的函数地址。由于linker不知道printf具体是在哪个动态库,所以它将在整个执行文件和动态库的范围内查找。更糟糕的是在C++程序中,符号的命名是类名+函数名,这导致在做字符串比较时,往往直到字符串的结尾才能获得结果。

这也就导致了在进程启动过程中,符号查找占据了一大部分时间。在Linux的KDE进程启动中,符号查找甚至占据了进程启动80%的时间。

因此就针对上述的情况,有以下优化解决方案:

1、减少导出符号的数量

在动态库编译和生成时,默认所有的函数和全局变量都是导出的。而实际上有很多函数只是动态库内部使用,通过去掉那些动态库中不必要的导出符号,从而减少动态库在做链接时所查找的符号数量,可以加快动态库链接的速度。

可以使用ld的ld --retain-symbols-file --version-script两个选项实现。写一个导出符号文件,如 symbol 指定你只导出的函数,如 func1。使用 ld 的--retain-symbols-file  参数可以在 static section 里取消 func1 以外的所有函数。这时你用 readelf 看编译好后的 .so 文件 static section 里没有了,使用 nm 看 .so文件它无法查出导出函数。但这并不完全。因为在 dynamic section 里还是会看到所有符号被导出。如果想在 dynsym section 里也不让他导出的话,需要再编写一个 script 文件,指定 global 与 local 在 global 中指定你要导出的函数,简单的格式如下:

VERSION{

VER_1.0{

global: 导出函数名;

local: *;

};

}

再在 ld 时用 --version-script  选项来 load 你的文件。都完事后再使用 readelf 观察static 与 dynamic section 发现只导出了你指定的函数名即符号。

例:

ld -shared --retain-symbols-file  符号文件 --version-script  脚本文件 -o 动态库文件。so filename

2、减少符号的长度

3、使用prelink

在这里另外在提一个问题,很有趣的东西。

gcc -fvisibility=hidden 只在链接时传入的.c文件起作用,对.o文件不其作用;

比如test.c test1.c,使用以下命令:

 gcc -shared -fvisibility=hidden -otest.so test.c test1.c

和命令

 gcc -c test.c test1.c

 gcc -shared -fvisibility=hidden -otest.so test.o test1.o

生成的test.so中的对应可见性是不一样的,使用“readelf -s test.so”查看发现:

第一个达到预期目的,即将两个.c文件中的functions设为HIDDEN,

而第2个则不行,-fvisibility=hidden不起作用;

再用gcc -shared -fvisibility=hidden -o test.so test.o test1.c

生成的so,则可发现test1.c中的函数为HIDDEN,但test.o中的函数仍为DEFAULT;

     本文转自 驿落黄昏 51CTO博客,原文链接:http://blog.51cto.com/yiluohuanghun/1198295,如需转载请自行联系原作者