【问题标题】:In what order are shared libraries initialized and finalized?共享库按什么顺序初始化和完成?
【发布时间】:2019-03-30 19:15:12
【问题描述】:

流程中的动态对象来自多个来源:

  1. 可执行文件本身
  2. 它需要的任何库(DT_NEEDED 用于 ELF)
  3. 明确加载的库(dlopen 或类似)
  4. 此类显式加载所需的任何库

当进程exits 运行它们的终结(atexit 函数,静态持续时间析构函数时,它们可以被显式dlclose)或隐式卸载在 C++ 和 __attribute__((destructor)) 函数中)在任何一种情况下。

在这些不同的情况下,是什么决定了动态对象的初始化和最终确定顺序?显然,库的最后一个dlclose 会立即卸载它,但是它的依赖树呢(其中一些也可能是其他加载库的依赖)?如果一个库是 dlopened 但随后被 exit 卸载怎么办?

我倾向于期待通常的 reverse 初始化顺序,但 DT_NEEDEDdlopen 之间可能存在差异,因为“插件”由后者加载并且可能依赖于可执行文件的数据,而不是相反。

【问题讨论】:

    标签: shared-libraries destructor exit dlopen


    【解决方案1】:

    算法在所有情况下都是相同的。映射主可执行文件(或dlopened 库)后,动态链接器将按照拓扑顺序执行初始化函数,以便在依赖项之前初始化依赖项。请注意,在某些情况下,这可能会导致未指定顺序,并且链接器会在那里进行任意选择。

    库的析构函数在它的一个构造函数中注册(通过调用__cxa_atexit)。注册是通过将累积库 dtor 函数添加到 Glibc 中的特殊列表来执行的。在exit 上,列表正向遍历,因此依赖的析构函数将在它们的依赖项之前被调用。

    【讨论】:

    • 拓扑顺序不限制dlopen 提及的任何内容。那个子目是未指定的吗?
    • @DavisHerring 不,如果两个库之间没有依赖关系,它们可以以任意相对顺序初始化。有趣的是,我曾经尝试对这个排序进行模糊测试,并在现有程序中发现了一些错误。
    • 我相信实际的订购图片有点复杂,例如DF_1_INITFIRST 可能还有其他标志。
    • @EmployedRussian 是的,但是 GNU 加载器只支持一个 DF_1_INITFIRST 库,而 libpthread 已经有了它,所以实际上这个标志在普通库中是不可用的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-08
    • 1970-01-01
    相关资源
    最近更新 更多