应用程序启动时发生什么情况?

库、链接、初始化和 C++ 系列的第 3 部分

作者:Darryl Gove 和 Stephen Clamage,2011 年 5 月

第 1 部分 — 库和链接简介
第 2 部分 — 解析库中的符号

当进程因为库的某些问题而不能启动时,检查实际发生的情况非常重要。环境变量 LD_DEBUG 告知运行时链接程序输出有关应用程序启动时所发生的情况的信息。可以用各种选项来指定提供哪些信息。信息可以简短,也可以冗长,因此选择恰当的选项仅获取所需信息是非常重要的。大多数情况下,首先需要检查的是加载了哪些库。这可以通过将 LD_DEBUG 的值设置为 files 来查看,如清单 1 所示。

清单 1:查看已加载的库
 $ LD_DEBUG=init ./main
 ...
 16211: 1: calling .init (from sorted order): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (done): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (from sorted order): codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (done):codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (from sorted order): /lib/libc.so.1
 16211: 1:
 16211: 1: calling .init (done): /lib/libc.so.1
 16211: 1:
 16211: 1:
 16211: 1: transferring control: main
 16211: 1:
 16211: 1:
 In library 1
 In library 2
 In main
 16211: 1: calling .fini: /codes/library/lib2.so
 16211: 1:
 16211: 1: calling .fini: /codes/library/lib1.so
 16211: 1:
 16211: 1: calling .fini: /lib/libc.so.1

在本示例中,检查了 main 应用程序,该应用程序指明需要 lib1.solib2.so。在生成该应用程序的命令行中,-l1-l2 之前,这种排序在加载库的时候得以保留。

默认情况下,输出到 stderr,但可以使用环境变量 LD_DEBUG_OUTPUT 对输出进行重定向。此环境变量将输出定向到指定的文件名,该文件名后面附加了进程 PID。

下一个问题通常是哪些符号绑定到哪个库。可以使用 LD_DEBUGbindings 输出来获取此信息,如清单 2 所示。输出相当冗长,因为它会列出所绑定的每个符号。

清单 2:确定所绑定的符号
 $ LD_DEBUG=bindings ./main
 13147:
 ...
 13147: 1: binding file=main to file=codes/library/lib1.so: symbol `f1'
 13147: 1: binding file=codes/library/lib1.so to file=/lib/libc.so.1: symbol `printf'
 ...
 13147: 1: binding file=main to file=codes/library/lib2.so: symbol `f2'
 13147: 1: binding file=codes/library/lib2.so to file=/lib/libc.so.1: symbol `printf'

该输出显示 main 绑定到 lib1.so 中的符号 f1 以及库 lib2.so 中的符号 f2。它还显示对这些库中的 printf() 的绑定。

加载库的时候,这些库可能有要运行的初始化部分。本示例中的这两个库都没有显式的初始化部分。将 LD_DEBUG 设置为 init 使我们能够查看初始化活动,如清单 3 所示。

清单 3:查看初始化顺序
 $ LD_DEBUG=init ./main
 ...
 16211: 1: calling .init (from sorted order): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (done): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (from sorted order): codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (done):codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (from sorted order): /lib/libc.so.1
 16211: 1:
 16211: 1: calling .init (done): /lib/libc.so.1
 16211: 1:
 16211: 1:
 16211: 1: transferring control: main
 16211: 1:
 16211: 1:
 In library 1
 In library 2
 In main
 16211: 1: calling .fini: /codes/library/lib2.so
 16211: 1:
 16211: 1: calling .fini: /codes/library/lib1.so
 16211: 1:
 16211: 1: calling .fini: /lib/libc.so.1

这些库按照它们的加载顺序进行初始化。它们以相反的顺序结束。库的加载顺序取决于在命令行中指定的库的顺序,此顺序可能被链接程序所确定的依赖关系所覆盖。

建议总结

使用环境变量 LD_DEBUG 调查运行时链接程序活动。

修订版 1,2011 年 5 月 5 日