虽然编译器驱动程序通常会确保对链接编辑器指定适当的库,但您必须经常提供自己的库。通过显式指定链接编辑器需要的输入文件可以指定共享目标文件和归档。但是,更常见且更灵活的方法涉及使用链接编辑器的 –l 选项。
根据约定,共享目标文件通常通过前缀 lib 和后缀 .so 指明。归档指定有前缀 lib 和后缀 .a。例如,libfoo.so 是共享目标文件版的 "foo" 实现,可用于编译环境。libfoo.a 是库的归档版本。
这些约定可由链接编辑器的 –l 选项识别。此选项通常用于为链接编辑提供其他库。以下示例指示链接编辑器搜索 libfoo.so。如果链接编辑器未找到 libfoo.so,则在继续搜索下一个目录之前将搜索 libfoo.a。
$ cc -o prog file1.c file2.c -lfoo
以动态模式进行链接编辑时,可以选择同时链接共享目标文件和归档。以静态模式进行链接编辑,仅接受归档库作为输入。
在动态模式下使用 –l 选项时,链接编辑器首先搜索给定目录以查找与指定名称匹配的共享目标文件。如果未找到任何匹配项,链接编辑器将在相同目录中查找归档库。在静态模式下使用 –l 选项时,将仅查找归档库。
动态模式下的库搜索机制会在给定目录中搜索共享目标文件,然后搜索归档库。使用 –B 选项可以更精确地控制搜索。
通过在命令行中指定 –B dynamic 和 –B static 选项,可以分别在共享目标文件或归档之间切换库搜索。例如,要将应用程序与归档 libfoo.a 和共享目标文件 libbar.so 链接,可发出以下命令。
$ cc -o prog main.o file1.c -Bstatic -lfoo -Bdynamic -lbar
–B static 和 –B dynamic 选项并不完全对称。指定 –B static 时,链接编辑器要等到下一次出现 –B dynamic 时才接受共享目标文件作为输入。但是,指定 –B dynamic 时,链接编辑器首先在任何给定目录中查找共享目标文件,然后查找归档库。
对上一个示例的准确说明如下:链接编辑器首先搜索 libfoo.a,然后链接编辑器将搜索 libbar.so,如果此搜索失败,则搜索 libbar.a。
命令行中归档的位置可以影响要生成的输出文件。链接编辑器搜索归档只是为了解析先前遇到过的未定义或暂定的外部引用。完成此搜索并提取所有需要的成员后,链接编辑器将继续处理命令行中的下一个输入文件。
因此,缺省情况下,不能使用归档来解析命令行中归档后面的输入文件中的任何新引用。例如,以下命令指示链接编辑器只有在解析从 file1.c 中获取的符号引用时才搜索 libfoo.a。不能使用 libfoo.a 归档来解析 file2.c 或 file3.c 中的符号引用。
$ cc -o prog file1.c -Bstatic -lfoo file2.c file3.c -Bdynamic
归档之间可以存在相互的依赖性,这样,要从一个归档中提取成员,还必须从另一个归档中提取相应成员。如果这些依赖性构成循环,则必须在命令行中重复指定归档以满足前面的引用。
$ cc -o prog .... -lA -lB -lC -lA -lB -lC -lA
确定和维护重复指定的归档是一项非常繁琐的任务。–z rescan-now 选项可以简化此过程。在命令行中遇到 –z rescan-now 选项时,链接编辑器会立即处理该选项。命令行中该选项之前的所有已处理归档都将立即重新处理。此处理过程会尝试查找可解析符号引用的其他归档成员。继续重新扫描此归档,直到扫描归档列表一遍但未提取到任何新成员为止。因此,上一个示例可进行如下简化:
$ cc -o prog .... -lA -lB -lC -z rescan-now
另外,还可以使用 –z rescan-start 和 –z rescan-end 选项将相互依赖的归档一起划分到一个归档组中。链接编辑器在命令行中遇到结束分隔符时,会立即重新处理这些组。在组中找到的归档会重新进行处理,以尝试查找可解析符号引用的其他归档成员。继续重新扫描此归档,直到扫描归档组一遍但未提取到任何新成员为止。如果使用归档组,上一个示例的编写如下:
$ cc -o prog .... -z rescan-start -lA -lB -lC -z rescan-end
上述所有示例都假定链接编辑器知道在哪里搜索命令行中列出的库。缺省情况下,在链接 32 位目标文件时,链接编辑器只知道在两个标准目录中查找库:先搜索 /lib,再搜索 /usr/lib。在链接 64 位目标文件时,只使用两个标准目录:先搜索 /lib/64,再搜索 /usr/lib/64。必须显式地将要搜索的所有其他目录添加到链接编辑器的搜索路径中。
可以使用命令行选项或环境变量更改链接编辑器的搜索路径。
可以使用 –L 选项将新的路径名添加到库搜索路径中。在命令行中遇到此选项时,此选项将改变搜索路径。例如,以下命令将先搜索 path1,再搜索 /lib,最后搜索 /usr/lib,以查找 libfoo。此命令先搜索 path1,再搜索 path2,接着搜索 /lib 和 /usr/lib,以查找 libbar。
$ cc -o prog main.o -Lpath1 file1.c -lfoo file2.c -Lpath2 -lbar
使用 –L 选项定义的路径名仅由链接编辑器使用。这些路径名不会记录在要创建的输出文件映像中。因此,运行时链接程序不能使用这些路径名。
可以使用 –Y 选项更改链接编辑器搜索的缺省目录。随此选项提供的参数采用以冒号分隔的目录列表形式。例如,以下命令仅在 /opt/COMPILER/lib 和 /home/me/lib 目录中搜索 libfoo。
$ cc -o prog main.c -YP,/opt/COMPILER/lib:/home/me/lib -lfoo
可以使用 –L 选项补充通过使用 –Y 选项指定的目录。编译器驱动程序通常使用 –Y 选项提供特定于编译器的搜索路径。
还可以使用环境变量 LD_LIBRARY_PATH 来添加链接编辑器的库搜索路径。通常,LD_LIBRARY_PATH 采用冒号分隔的目录列表形式。LD_LIBRARY_PATH 最常见的形式是以分号分隔的两个目录列表。在命令行中提供的 –Y 列表之前和之后搜索这些列表。
以下示例说明在同时设置了 LD_LIBRARY_PATH 且使用多个 –L 调用链接编辑器的情况下所得到的结果。
$ LD_LIBRARY_PATH=dir1:dir2;dir3 $ export LD_LIBRARY_PATH $ cc -o prog main.c -Lpath1 .... -Lpath2 .... -Lpathn -lfoo
有效搜索路径为 dir1:dir2:path1:path2:....:pathn:dir3:/lib:/usr/lib。
如果在 LD_LIBRARY_PATH 定义中未指定分号,那么将在解释所有 –L 选项之后解释指定的目录列表。在以下示例中,有效搜索路径为 path1:path2:....:pathn:dir1:dir2:/lib:/usr/lib。
$ LD_LIBRARY_PATH=dir1:dir2 $ export LD_LIBRARY_PATH $ cc -o prog main.c -Lpath1 .... -Lpath2 .... -Lpathn -lfoo
运行时链接程序在两个缺省位置中查找依赖项。在处理 32 位目标文件时,缺省位置为 /lib 和 /usr/lib。在处理 64 位目标文件时,缺省位置为 /lib/64 和 /usr/lib/64。其他所有要搜索的目录必须显式添加到运行时链接程序的搜索路径中。
动态可执行文件或共享目标文件与其他共享目标文件链接时,这些共享目标文件将被记录为依赖项。运行时链接程序执行进程期间必须找到这些依赖项。链接动态目标文件时,可以在输出文件中记录一个或多个搜索路径。这些搜索路径称为运行路径。运行时链接程序使用目标文件的运行路径查找该目标文件的依赖项。
可以使用 –z nodefaultlib 选项生成专用目标文件,以便在运行时不搜索任何缺省位置。此选项的用法表示可以使用目标文件的运行路径来查找该目标文件的所有依赖项。如果不使用此选项,则无论如何扩充运行时链接程序的搜索路径,所用的最后一个搜索路径始终是缺省位置。
可以使用 –R 选项(采用冒号分隔的目录列表形式)将运行路径记录在动态可执行文件或共享目标文件中。以下示例将运行路径 /home/me/lib:/home/you/lib 记录在动态可执行文件 prog 中。
$ cc -o prog main.c -R/home/me/lib:/home/you/lib -Lpath1 \ -Lpath2 file1.c file2.c -lfoo -lbar
运行时链接程序使用这些路径(后接缺省位置)来获取任意共享目标文件依赖项。在本例中,此运行路径用于查找 libfoo.so.1 和 libbar.so.1。
链接编辑器接受多个 –R 选项。指定的多个选项串联在一起,用冒号分隔。因此,上一个示例还可采用如下表示方式。
$ cc -o prog main.c -R/home/me/lib -Lpath1 -R/home/you/lib \ -Lpath2 file1.c file2.c -lfoo -lbar
对于可以安装在各种位置的目标文件,$ORIGIN 动态字符串标记提供了一种记录运行路径的灵活方法。请参见查找关联的依赖项。