【问题标题】:Windows DLL & Dynamic Initialization OrderingWindows DLL 和动态初始化排序
【发布时间】:2014-07-19 02:32:34
【问题描述】:

我对动态初始化(即 main 之前的构造函数)和 DLL 链接排序有一些疑问 - 适用于 Windows 和 POSIX。

为了便于讨论,我将定义几个术语:

加载时库: 在编译时已“链接”的库 这样,当系统加载我的应用程序时,它们会被加载 自动进入。 (即放在 CMake 的 target_link_libraries 中的那些 命令)。

运行时库: 我通过 dlopen 或手动加载的库 等价物。出于讨论的目的,我会说我只 曾经在 main 中使用 dlopen 手动加载库,所以这应该 简化事情。

动态初始化:如果您不熟悉 C++ 规范 这个的定义,请不要试图回答这个问题。

好的,假设我有一个应用程序 (MyAwesomeApp),它链接到动态库 (MyLib1),而动态库又链接到另一个库 (MyLib2)。所以依赖树是:

MyAwesomeApp -> MyLib1 -> MyLib2

对于此示例,假设 MyLib1 和 MyLib2 都是加载时库。

上面的初始化顺序是什么?很明显,所有静态初始化,包括导出/导入函数的链接(仅限 Windows)都将首先发生......但是动态初始化会发生什么?我希望整体排序:

所有导入/导出符号链接
ALL 静态初始化
MyLib2 的所有动态初始化
MyLib1 的所有动态初始化
MyAwesomeApp 的所有动态初始化
MyAwesomeApp 的 main() 函数

但我在规范中找不到任何强制执行此操作的内容。我确实看到了一些关于 elf 的暗示,但我需要在规范中找到保证,以便我做我想做的事情。

为了确保我的思路清晰,我希望库加载的工作方式与 Python 中的“导入”非常相似,如果尚未加载,它将被完全加载(包括任何初始化)在我做任何事情之前......如果它已经加载,那么我将链接到它。

举一个更复杂的例子,以确保我的第一个例子没有其他定义会产生不同的响应:

MyAwesomeApp 依赖于 MyLib1 & MyLib2 MyLib1 依赖于 MyLib2

我希望进行以下初始化:

所有导入/导出符号链接
ALL 静态初始化
MyLib2 的所有动态初始化
MyLib1 的所有动态初始化
MyAwesomeApp 的所有动态初始化
MyAwesomeApp 的 main() 函数

我很乐意提供任何帮助指出规格说明它是这样的。或者,如果这是错误的,任何规格说明真正发生了什么!

提前致谢!

-克里斯托弗

【问题讨论】:

  • 标准中的相关句子似乎是“否则,变量的初始化相对于在不同翻译单元中定义的变量的初始化是不确定的。”当然,我不理解该段的其余部分,但我相信这意味着无法保证来自 MyLib1 的动态初始化是在 MyLib2 之前还是之后发生。
  • 首先,这主要是指在库中——而不是跨 DLL 边界。其次,虽然 C++ 标准不能保证它跨越所有可能的 C++ 目标,但 ELF 和 Microsoft 的二进制标准可以,这就是我所追求的。
  • 好的。在 Windows 上,任何此类保证(如果可能)都必须由编译器提供,因为 Windows 不要求编译器以任何特定方式进行动态初始化。似乎确实按照您建议的顺序调用了 DLL 入口点,请参阅The NT DLL Loader: basic operation,尽管我不清楚这是合同规定的。 (但它不太可能改变。)因此,可以期望以最明显方式进行动态初始化的编译器会按照您的建议行事。
  • 我添加了一个赏金,因为我认为这个问题很有趣并且没有受到太多关注。也就是说,考虑到静态链接模块似乎没有这样的保证,编译器提供这种关于加载时动态库的保证对我来说似乎很奇怪,所以我的猜测是,在大多数情况下,至少行为将是无证的,并且可能会发生变化。希望编译器专家可以在这里插话。 :-)

标签: windows dll posix dylib shared-libraries


【解决方案1】:

C++ 标准中没有规定动态链接的工作方式。

话虽如此,Visual Studio 附带 C 运行时(又名 CRT)源,您可以在 dllcrt0.c 中查看静态初始化程序的运行位置。

如果您考虑运行每个阶段需要满足哪些约束,您还可以推导出操作的相对顺序:

  1. 导入/导出解析只需要.dll。
  2. 静态初始化只需要 .dll。
  3. 动态初始化需要为 .dll 解析所有导入。

第 1 步和第 2 步不相互依赖,因此它们可以独立发生。第 3 步需要为每个 .dll 提供 1 和 2,因此它必须在 1 和 2 之后发生。

因此,任何满足上述约束的特定加载顺序都是有效的加载顺序。

换句话说,如果您需要关心特定步骤的特定顺序,您可能正在做一些危险的事情,它依赖于不会在操作系统的主要或次要修订版中保留的实现特定细节。例如,加载程序锁对 .dll 的工作方式在不同的 Windows 版本中发生了显着变化。

【讨论】:

  • Nothing in the C++ standard mandates how dynamic linking works. 实际上标准对动态链接一无所知。此外,非常好的和准确的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
相关资源
最近更新 更多