Linux 中的应用程序以以下两种方式之一链接到外部函数:要么在构建时与静态库( lib*.a
) 静态地链接,并且将库代码包含在该应用程序的可执行文件里;要么在运行时与共享库( lib*.so
) 动态地链接。通过动态链接装入器,将动态库映射进应用程序的可执行内存中。在启动应用程序之前,动态链接装入器将所需的共享目标库映射到应用程序的内存,或者使用系统共享的目标并为应用程序解析所需的外部引用。现在应用程序就可以运行了。
作为示例,下面有一个演示 Linux 中对动态链接库的缺省使用的小程序:
main() { printf("Hello world "); } |
当使用 gcc 编译 hello.c 时,就创建了一个名为 a.out
的可执行文件。通过使用 Linux 命令 ldd a.out
(该命令打印出共享库的相互依赖性),可以看出所需的共享库是:
libc.so.6 => /lib/libc.so.6 (0x4001d000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) |
使用相同的动态链接装入器在应用程序运行之后将 dll 映射进应用程序的内存。通过使用 Linux 动态装入器例程,应用程序控制装入哪一个动态库以及调用库中的哪一个函数,以执行装入和链接以及返回所需入口点的地址。
|
Linux 提供 4 个库函数( dlopen
, dlerror
, dlsym
和 dlclose
),一个 include 文件( dlfcn.h
)以及两个共享库(静态库 libdl.a
和动态库 libdl.so
),以支持动态链接装入器。这些库函数是:
- dlopen 将共享目标文件打开并且映射到内存中,并且返回句柄
- dlsym返回一个指向被请求入口点的指针
- dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针
- dlclose关闭句柄并且取消共享目标文件的映射
动态链接装入器例程 dlopen 需要在文件系统中查找共享目标文件以打开文件并创建句柄。有 4 种方式用以指定文件的位置:
dlopen call
中的绝对文件路径- 在 LD_LIBRARY_PATH 环境变量中指定的目录中
- 在 /etc/ld.so.cache 中指定的库列表之中
- 先在 /usr/lib 之中,然后在 /lib 之中
|
动态链接装入器示例程序是一个小的 C 程序,该程序被设计用来练习 dl 例程。该程序基于每个人都编写过的一个 C 程序,它将“Hello World”打印到控制台上。最初打印的消息是“HeLlO WoRlD”。该测试程序链接到再次打印该消息的两个函数上:第一次都用大写字符,第二次都用小写字符。
以下是该程序的概要:
- 使用绝对路径“/home/dlTest/lib库.so”和选项 RTLD_LAZY,
dlopen
打开 dll 的共享目标文件并返回句柄。-
选项 RTLD_LAZY 推迟解析 dll 的外部引用,直到 dll 被执行。
- 选项 RTLD_NOW 在
dlopen
返回之前解析所有的外部引用。
-
dlsym
返回入口点 函数的地址。dlclose
关闭到 lib.so 的句柄,并且从内存中取消 dll 映射。dlopen
使用基于环境变量 LD_LIBRARY_PATH 的相对路径查找共享目标路径,来打开 dll 的共享目标文件 lowercase.so,并且返回句柄。
注意,每次调用 dlopen
、 dlsym
或 dlclose
之后,调用 dlerror
以获取最后的错误信息,并且打印该错误信息字符串。