08-使用一个共享库
41.4.3 使用一个共享库
为了使用一个共享库就需要做两件事情,而使用静态库的程序则无需完成这两件事情。
- 由于可执行文件不再包含它所需的目标文件的副本,因此它必须要通过某种机制找出在运行时所需的共享库。这是通过在链接阶段将共享库的名称嵌入可执行文件中来完成的。(在ELF中,库依赖性是记录在可执行文件的DT_NEEDED标签中的。)一个程序所依赖的所有共享库列表被称为程序的动态依赖列表。
- 在运行时必须要存在某种机制来解析嵌入的库名——即找出与在可执行文件中指定的名称对应的共享库文件——接着如果库不在内存中的话就将库加载进内存。
将程序与共享库链接起来时自动会将库的名字嵌入可执行文件中。
如果现在运行这个程序,那么就会收到下面的错误消息。
解决这个问题就需要做第二件事情:动态链接,即在运行时解析内嵌的库名。这个任务是由动态链接器(也称为动态链接加载器或运行时链接器)来完成的。动态链接器本身也是一个共享库,其名称为/lib/ld-linux.so.2,所有使用共享库的ELF可执行文件都会用到这个共享库。
路径名/lib/ld-linux.so.2通常是一个指向动态链接器可执行文件的符号链接。这个文件的名称为ld-version.so,其中version表示安装在系统上的glibc的版本——如ld-2.11.so。在一些架构上,动态链接器的路径名是不同的。如在IA-64上,动态链接器符号链接的名称为/lib/ld-linux-ia64.so.2。
动态链接器会检查程序所需的共享库清单并使用一组预先定义好的规则来在文件系统上找出相关的库文件。其中一些规则指定了一组存放共享库的标准目录。如很多共享库位于/lib和/usr/lib中。之所以出现上面的错误消息是因为程序所需的库位于当前工作目录中,而不位于动态链接器搜索的标准目录清单中。
一些架构(如zSeries、PowerPC64以及x86-64)同时支持执行32位和64位的程序。在此类系统上,32位的库位于/lib子目录中,64位的库位于/lib64子目录中。
LD_LIBRARY_PATH环境变量
通知动态链接器一个共享库位于一个非标准目录中的一种方法是将该目录添加到LD_LIBRARY_PATH环境变量中以分号分隔的目录列表中。(也可以使用分号来分隔,在使用分号时必须将列表放在引号中以防止shell将分号解释了其他用途。)如果定义了LD_LIBRARY_PATH,那么动态链接器在查找标准库目录之前会先查找该环境变量列出的目录中的共享库。(稍后会介绍一个生产应用程序永远都不应该依赖于LD_LIBRARY_PATH,但此刻通过这个变量可以方便地开始使用共享库了。)因此可以使用下面的命令来运行程序。
上面的命令中使用的shell(bash、Korn以及Bourne)语法在执行prog的进程中创建了一个环境变量定义。这个定义告诉动态链接器在.,即当前工作目录中搜索共享库。
在LD_LIBRARY_PATH列表中的空目录(如dirx::diry中间的空目录)等价于.,即当前工作目录(但注意将LD_LIBRARY_PATH的值设置为空字符串并不能达到同样效果)。需要避免这种用法(SUSv3同样不建议在PATH环境变量中使用这种方式)。
静态链接和动态链接比较
通常,术语链接用来表示使用链接器ld将一个或多个编译过的目标文件组合成一个可执行文件。有时候会使用术语静态链接从动态链接中将在运行时加载可执行文件所需的共享库这一步骤给区分出来。(静态链接有时候也被称为链接编辑,像ld这样的静态链接器有时候被称为链接编辑器。)每个程序——包括那些使用共享库的程序——都会经历一个静态链接的阶段。在运行时,使用共享库的程序会经历额外的动态链接阶段。