【发布时间】: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