5. 其他
5.1. nm命令
nm命令可以列出一个函数库文件中的符号表。它对于静态的函数库和共享的函数库都起作用。对于一个给定的函数库,nm命令可以列出函数库中定义的所有符号,包括每个符号的值和类型。还可以给出在原程序中这个函数(符号)是在多少行定义的,不过这必须要求编译该函数库的时候加“-l”选项。
关于符号的类型,这里我们再多讨论一下。符号的类型是以一个字母的形式显示的,小写字母表示这个符号是本地(local)的,而大写字母则表示这个符号是全局的(global,externel)。一般来说,类型有一下几种:T、D、B、U、W。各自的含义如下:T表示在代码段中定义的一般变量符号;D表示时初始化过的数据段;B表示初始化的数据段;U表示没有定义的,在这个库里面使用了,但是在其他库中定义的符号;W,weak的缩写,表示如果其他函数库中也有对这个符号的定义,则其他符号的定义可以覆盖这个定义。
如果你知道一个函数的名字,但是你不知道这个函数在什么库中定义的,那么可以用mn的“-o”选项和grep命令来查找库的名字。-o选项使得显示的每一行都有这个函数库文件名。例如,你要查找“cos”这个是在什么地方定义的,大致可以用下面的命令:
nm -o /lib/* /usr/lib/* /usr/lib/*/* /usr/local/lib/* 2 /dev/null
| grep 'cos$'
关于nm的更详细的用法我们可以参考info文档,位置是info:binutils#nm。
5.2. 特殊函数_init和_fini
函数库里面有两个特殊的函数,_init和_fini,这个我们在前面已经说过了。主要是分别用来初始化函数库和关闭的时候做一些必要的处理,我们可以把自己认为需要的代码放到这两个函数里面,它们分别在函数库被加载和释放的时候被执行。具体说,如果一个函数库里面有一个名字为“_init”的函数输出,那么在第一次通过dlopen()函数打开这个函数库,或者只是简单的作为共享函数库被打开的时候,_init函数被自动调用执行。与之相对应的就是_fini函数,当一个程序调用dlclose()去释放对这个函数库的引用的时候,如果该函数库的被引用计数器为0了,或者这个函数库是作为一般的共享函数库被使用而使用它的程序正常退出的时候,_fini就会被调用执行。C语言定义它们的原型如下:
void _init(void); void _fini(void);
当用gcc编译源程序为“.o”文件的时候,需要加一个“-nostartfiles”选项。这个选项使得C编译器不链接系统的启动函数库里面的启动函数。否则,就会得到一个“multiple-definition”的错误。
5.3. 共享函数库也可以使脚本(Scripts)
GNU的loader允许使用特殊格式的脚本语言来写一个函数库。这对于那些需要间接包含其他函数库的情况还是有用的。例如,下面是一个/usr/lib/libc.so的例子:
/* GNU ld script Use the shared library, but some functions are only in
the static library, so try that secondarily. */GROUP ( /lib/libc.so.6
/usr/lib/libc_nonshared.a )
更多的信息可以参考texinfo文档中关于ld链接的脚本部分。一般的信息还可以参考: info:ld#Options 和info:ld#Commands,也可以参考info:ld#Option Commands。
5.4. GNU libtool
如果你正在编译的系统相很方便的移植到其他操作系统下,你可以使用GNU libtool来创建和安装这个函数库。GNU libtool是一个函数库支持的典型的脚本。Libtool隐藏了使用一个可移植的函数库的负责性。Libtool提供了一个可以移植的界面来创建object文件,链接函数库(静态或者共享的),并且安装这些库。它还包含了libltdl,一个可移植的动态函数库调入程序的wrapper。更多的详细讨论,可以在http://www.gnu.org/software/libtool/manual.html看到。
5.5. 删除一些符号
在一个生产的文件中很多符号都是为了debug而包含的,占用了不少空间。如果空间不够,而且这些符号也许不再需要,就可以将其中一些删除。
最好的方法就是先正常的生成你需要的object文件,然后debug和测试你需要的一些东西。一旦你完全测试完毕了,就可以用strip去删除一些不需要的符号了。Strip命令可以使你很方便的控制删除什么符号,而保留什么符号。Strip的具体用法可以参考其帮助文件。
另外的方法就是使用GNU ld的选项“-S”和“-s”;“-S”会删除一些debugger的符号,而“-s”则是将所有的符号信息都删除。通常我们可以在gcc中加这样的参数“-Wl,-S”和“-Wl,-s”来达到这个目的。