CRT初始化和DLLMain

5

引用:

来自微软的"Best Practices for Creating DLLs"文档http://download.microsoft.com/download/a/f/7/af7777e5-7dcd-4800-8a0a-b18336565f5b/DLL_bestprac.doc

"DLL 经常具有复杂的相互依赖关系,这些关系隐式地定义了它们必须被加载的顺序。库加载器有效地分析这些依赖关系,计算出正确的加载顺序,并按照该顺序加载 DLL。"[1]

"(在 DLLMain 内部) 使用来自动态 C 运行时 (CRT) 的内存管理函数。如果未初始化 CRT DLL,则对这些函数的调用可能导致进程崩溃。"[2]

来自 MSDN:http://msdn.microsoft.com/en-us/library/988ye33t.aspx

"_DllMainCRTStartup 函数会执行一些操作,包括调用 _CRT_INIT,该函数初始化 C/C++ 运行时库并在静态非局部变量上调用 C++ 构造函数。如果没有此函数,则运行时库将处于未初始化状态。"[3]

"除了初始化 C 运行时库之外,_DllMainCRTStartup 还调用一个名为 DllMain 的函数。"[4]

问题:

如果您的 DLL 依赖于 CRT DLL,则根据[1],CRT DLL 将首先被加载 (被初始化),因此[2]如何发生?

根据[3][4],_DllMainCRTStartup 将调用 _CRT_INIT 来初始化 CRT,那么[2]如何发生?

如果可执行文件通过"隐式链接"加载您的 DLL,则基于 [3] - _DllMainCRTStartup 调用 _CRT_INIT 来初始化 CRT,且 mainCRTStartup 也将初始化 CRT,那么 CRT 实际上会发生什么?

如果您的 DLL 将在 mainCRTStartup 之前被加载,则在 DLLMain 或其他导出函数内部调用 CRT 函数是否安全?

谁将实际初始化 CRT DLL?

---
1. 如果 DLL 依赖于 CRT DLL,则 CRT DLL 最终会首先被加载,以确保正确的加载顺序和避免由于缺少所需的 DLL 而导致进程崩溃等问题。然而,如果 CRT DLL 未初始化,对 CRT 函数的调用可能会导致进程崩溃,因为此时 CRT 数据结构未初始化。
2. 在 DLLMain 中使用 CRT 的内存管理函数并不总是安全的,这是因为如果 CRT DLL 未初始化,那么 CRT 内部数据结构也未初始化,这将导致调用 CRT 函数时出现错误。因此,建议在 DLLMain 中避免使用 CRT 函数,或者确保 CRT DLL 已正确初始化。
3. 如果可执行文件通过"隐式链接"加载 DLL,则 _DllMainCRTStartup 将被优先调用以初始化 CRT,并在静态非局部变量上调用 C++ 构造函数。然后,操作系统将继续调用主程序入口点 (mainCRTStartup 或 WinMainCRTStartup) 来初始化 CRT,从而完成整个初始化过程。
4. 如果您的 DLL 将在 mainCRTStartup
1个回答

11
你的工作假设DLL的入口点总是_DllMainCRTStartup,这并不是事实,这只是链接器的默认值。程序员可以将其更改为任何内容,并通过链接器的/ENTRYPOINT选项轻松更改。微软无法防止这种情况发生。指出这一点并不是一种很好的做法,这就是文件的目的。

如果这样的自定义入口点没有确保显式初始化CRT,则会很容易引发[2]错误。这不仅涉及初始化CRT运行时状态,还包括初始化DLL的全局状态,如调用C初始化程序、静态C++对象的构造函数和分配线程本地变量。这是DLL版本的CRT无法完成的。请记住,_DllMainCRTStartup和_CRT_INIT被链接到DLL本身中,该代码不在CRT DLL版本中。

CRT DLL自身的运行时状态由CRT DLL自身的入口点初始化,Windows加载器确保它首先运行。


很简单且有说服力,你的回复很有帮助。 - amanjiang
我已经创建了一个关于这个问题/答案的后续问题:https://dev59.com/dKjja4cB1Zd3GeqP8Ur7 - 如果您愿意,请查看它。 - Martin Ba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接