文章
服务器与存储开发
作者:Darryl Gove 和 Stephen Clamage,2011 年 5 月
第 1 部分 — 库和链接简介
第 2 部分 — 解析库中的符号链接应用程序时,链接程序检查应用程序中的所有符号,以验证这些符号是否能解析成链接行中指定的库中的符号。引发这一操作的标志是 -z defs,只要链接应用程序,就会自动包括此标志。链接库时,默认情况下不 包括此标志。这意味着,即使不能解析库的所有外部依赖项,也可照常链接库。
ldd 是用于检查可执行文件或库链接到哪些共享对象的命令。清单 1 显示在主可执行文件和其中一个共享库上运行 ldd 的结果。
$ ldd main lib2.so => codes/library/lib2.so lib1.so => codes/library/lib1.so libc.so.1 => /lib/libc.so.1 libm.so.2 => /lib/libm.so.2 $ ldd lib1.so $ |
输出显示该应用程序依赖于 lib1.so、lib2.so、libc.so 和 libm.so。当 ldd 用于 lib1.so 时,它报告该库没有依赖项。应当按照与应用程序相同的方式对库进行链接,以便记录它们的运行时依赖关系。通过在库的链接行上传递 -z defs,即可开始识别未解析的符号,如清单 2 所示。
$ cc -G -Kpic lib1.c -o lib1.so -z defs Undefined first referenced symbol in file printf lib1.o ld: fatal: Symbol referencing errors. No output written to lib1.so |
错误信息显示 printf() 在库中有未解析的符号。这可以通过使用 -lc 在链接行上显式列出 libc.so 来修复,如清单 3 所示。
$ cc -G -Kpic lib1.c -o lib1.so -lc -z defs $ ldd lib1.so libc.so.1 => /lib/libc.so.1 |
列出 libc.so 后,链接继续,并将 libc.so 记录为该库的依赖项。
建议在链接行包括相关的库,并使用标志 -z defs 创建库。这样做可以确保应用程序的任何支持库中不会有任何丢失的符号。如果未采用此方法,则可能要到链接主应用程序时才能检测到未解析的符号,或者在动态加载库的情况下,可能要到运行时才能检测到。清单 4 显示经过修改后存在未解析依赖项的 lib1.c 代码。
$ more lib1.c
#include ‹stdio.h›
void widget();
void f()
{
printf("In library 1\n");
widget();
} |
清单 5 显示了库的编译过程,以及随后编译应用程序的过程。链接应用程序时,链接程序会报告未解析的符号。
清单 5:编译使用了带有未解析符号的库的应用程序$ cc -G -Kpic lib1.c -o lib1.so $ cc -o main main.c -L. -R'$ORIGIN' -l1 -l2 Undefined first referenced symbol in file widget ./lib1.so ld: fatal: Symbol referencing errors. No output written to main |
本文所讨论的方法在构建依赖库的应用程序时非常有用。不过,应用程序的链接步骤给了链接程序检查所有已解析符号的机会。当应用程序在运行时通过调用 dlopen() 加载库时,这些方法将变得非常重要。在这种情况下,构建库时使用显式依赖项并使用标志 -z defs 警告存在未解析符号将非常重要。加载并非所有符号均已解析的库可能会导致应用程序失败。
另外值得调查的是,应用程序是否链接了任何未被使用的库。对应用程序或库使用实用程序 ldd 及 -r -U 选项时,ldd 将报告未使用的库以及已使用但当前未解析的库。
-z defs 来链接库,以便早期检测未解析的符号。ldd -r -U 可验证库是否有不需要的依赖项或未解析的符号。修订版 1,2011 年 4 月 27 日 |