函数库连接的五个特殊秘密:(UNIX链接的真实情况)
1.动态库文件的扩展名“.so”,而静态库文件的扩展名是“.a”
共享archive的文件名扩展名“.sa”,共享archive只是一种过渡形式,帮助人们从静态库转变成动态库。
2.例如,通过-lthread选项,告诉编译链接到libthread.so
实际上,编译器被告知根据选项-lname链接到相应的函数库,函数库的名字是libname.so---换句话说,"lib"部分和文件的扩展名被省略了,但在前面加了一个“-l”。
3.编译器在确定的目录找到库
它查看一些特殊的位置,在/usr/lib中查找库。例如,线程库位于/usr/lib/libthread.so.
编译器选项-Lpathname告知链接器一些其他的目录,如果命令中加入了-l选项,链接器就往这些目录查找函数库。系统中的环境变量LD_LIBRARY_PATH和LD_RUN_PATH用于提供这类信息。处于安全,性能和创建运行独立性方面的考虑,使用环境变量的做法现在已经不提倡。一般还是在链接时使用-Lpathname和-Rpathname选项。
4.观察头文件,确认所使用的函数库
一个很好的建议就是可以观察程序所使用的#include指令。在程序中所包含的每个头文件都可能代表一个必须链接的库。但是头文件的名字通常并不与它所对应的函数库名相似。
Solaris 2x下的库约定
#include文件名 库路径名 所用的编译器选项
"/user/openwin/include/X11.h" /usr/openwin/lib/libX11.so -L/usr/openwin/lib-1X11
函数库链接所存在的另一个不一致就是函数库包含许多函数的定义,但这些函数的原型声明
却散布于多个头文件中。
nm命令在/usr/lib的每个函数库中浏览所有的符号,从中寻找所丢失的符号。在缺省情况下,链接器会在/usr/cc/lib和/usr/lib中查找,你也应该从这两个地方着手,如果找不到就进一步扩展查找范围。(如/usr/openwin/lib)
%cd /usr/lib
%foreach i (lib?*)
?echo $i
?nm $i | grep xdr_refrence | grep -v UNDEF
?end
...
这会在该目录中的所有函数库上运行nm命令,它显示函数库中已知的符号列表。通过grep设定需要搜索的符号,并过滤标记为UNDEF符号(在该函数库中有引用,但不是在此处定义)。结果显示xdr_reference位于libnsl库。需要在编译器命令行的末尾加上-lnsl。
5.与提取动态库中的符号相比,静态库中的符号提取的方法限制更严
在动态链接中,所有的库符号进入输出文件的虚拟地址空间中,所有的符号对于链接在一起的所有文件都是可见的。相反,对于静态链接,在处理archive时,它只是在archive中查找载入器当时所知道的未定义符号。简而言之,在编译器命令行中各个静态链接库出现的顺序是非常重要的。符号是通过从左到右的顺序进行解析的。如果相同的符号在两个不同的函数库中有不同的定义,且静态库出现的顺序不同,其结果就有可能不同。
如果在自己的代码之前引入了静态库,又会带来一个问题。因为此时尚未出现未定义的符号,所以它不会从函数库中提取任何符号。接着,当目标问题被链接器处理时,他所有的对函数库的引用都将是未实现的!
libm经常是以静态链接的archive形式存在。如果你的程序使用了一些数学函数如sin()等,若像下面这样进行静态链接:
cc -lm main.c
则会得到一条错误信息,如下:
Undefined first referenced
symbol in file
sin main.o
ld:fatal: Symbol referencing errors. No output written to a.out
为了能从math库中提取所需的符号,首先需要让文件包含未解析的引用,如下所示:
cc main.c -lm
链接器采用<命令><文件><选项>这样的约定。
/*函数库选项应置于何处*/
始终将-l函数库选项放在编译命令行的最右边
工作区可以在被载入链接器的模式里声明像下面这样的函数,从而向链接器提供更多的线索:
在PC上,当Borland的编译器驱动器试图猜测需要链接的浮点数时,也会出现这样的问题。
scanf: floating point formats not linked
abnormal program termination(scanf:浮点格式未链接,程序异常中止)
当程序在scanf()或者printf()中使用浮点数格式,但并不调用任何浮点数函数时,就有可能
猜测错误
static void forcefloat(float *p) {
float f = *p; forcefloat(&f);
}
不需要实际调用这个函数,只要保证它被链接即可。这样就能给Borland PC的链接器提供
一个足够可靠的线索,即该浮点数库确实是需要的。
另外还有一条类似的信息,当软件需要数值协处理器而计算机却未安装它时,Microsoft C运行时系统会打印一条信息,表示“浮点数未载入”。可以使用浮点数仿真库重新链接程序来解决这个问题。