加载 DLL 未初始化静态 C++类
我有一个在运行时加载的 DLL。 DLL 依赖于一个静态变量来进行内部工作(它是一个 std::map),该变量是在 DLL 中定义的。
当我加载后调用 DLL 中的第一个函数时,我从 DLL 中得到一个 SegFault,映射从未初始化。从我从 DLL 加载中读到的所有内容来看,静态和全局数据初始化甚至应该在调用 DLLMain 之前发生。
为了测试静态初始化,我添加了一个静态结构来打印一条消息,甚至还添加了一个断点以进行良好的测量。
static struct a
{
a(void) { puts("Constructing\n"); }
}statica;
在调用 DLLMain 或函数之前没有消息或中断。
这是我的加载代码:
dll = LoadLibrary("NetSim");
//Error Handling
ChangeReliability = reinterpret_cast<NetSim::ChangeReliability>
(GetProcAddress(dll, "ChangeReliability"));
ChangeReliability(100);
我已经验证了dll版本是正确的,多次重建整个项目,但没有区别。我没有什么想法。
I have a DLL that is being loaded at run-time. The DLL relies on a static variable for internal workings (it is a std::map), this variable is defined within the DLL.
When I call the first function from the DLL after loading, I get a SegFault from the DLL, the map was never initialized. From everything I have read from DLL Loading, static and global data initialization should happen before even the call to DLLMain.
To test static initialization I added a static struct that prints out a message, and even threw in a breakpoint for good measure.
static struct a
{
a(void) { puts("Constructing\n"); }
}statica;
There was no message, or break before DLLMain or the function is called.
Here is my loading code:
dll = LoadLibrary("NetSim");
//Error Handling
ChangeReliability = reinterpret_cast<NetSim::ChangeReliability>
(GetProcAddress(dll, "ChangeReliability"));
ChangeReliability(100);
I have verified that the dll version is the correct one, rebuilt the entire project multiple times, but no difference. I am fresh out of ideas.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
当您链接 DLL 时,是否指定了 /ENTRY 开关?如果是这样,它将覆盖链接器的默认值 DllMainCRTStartup。因此,_CRT_INIT 将不会被调用,反过来,全局初始化程序也不会被调用,这将导致未初始化的全局(静态)数据。
如果您为自己的入口点指定 /ENTRY,则需要在进程附加和进程分离期间调用 _CRT_INIT。
如果您未指定 /ENTRY,则链接器应使用 CRT 的入口点,该入口点在调用 DllMain 之前在进程附加/分离时调用 _CRT_INIT。
When you linked your DLL, was the /ENTRY switch specified? If so, it'll override the linker's default which is DllMainCRTStartup. As a result, _CRT_INIT won't be called and, in turn, the global initializers won't be called which will result in uninitialized global (static) data.
If you are specifying /ENTRY for your own entry point, you need to call _CRT_INIT during process attach and process detach.
If you are not specifying /ENTRY, the linker should be using the CRT's entry point which calls _CRT_INIT on process attach/detach before calling into your DllMain.
我想指出的是,应该避免在 DLL 中使用复杂的静态对象。
请记住,DLL 中的静态初始化程序是从 DllMain 调用的,因此对 DllMain 代码的所有限制都适用于静态对象的构造函数和析构函数。特别是,std::map 可以在构造期间分配动态内存,如果 C++ 运行时尚未初始化(如果您使用动态链接运行时),这可能会导致不可预测的结果。
有一篇关于编写 DllMain 的好文章:创建 DLL 的最佳实践。
我建议将静态映射对象更改为静态指针(可以安全地进行零初始化),并添加单独的 DLL 导出函数进行初始化,或使用延迟初始化(即在访问指针之前检查指针并创建对象)如果它为空)。
I'd like to point out that complex static objects in DLLs should be avoided.
Remember that static intializers in a DLL are called from DllMain, and thus all restrictions on DllMain code apply to constructors and destructors of static objects. In particular, std::map can allocate dynamic memory during construction, which can lead to unpredictable results if C++ runtime is not initialized yet (in case you are using dynamically-linked runtime).
There is a good article on writing DllMain: Best Practices for Creating DLLs.
I would suggest changing your static map object to a static pointer (which can be safely zero-initialized), and either adding a separate DLL-exported function for initialization, or using lazy initialization (i.e. check the pointer before accessing it and create the object if it's null).
事实上,你很可能做出了错误的假设:
请参阅《Effective C++》第 4 条:
原因是确保“正确”的初始化顺序是不可能的,因此 C++ 标准简单地放弃了它,并将其保留为未定义。
因此,如果您的 DllMain 与声明静态变量的代码位于不同的文件中,则行为是未定义的,并且您很有可能在初始化实际完成之前调用 DllMain。
解决方案非常简单,在《Effective C++》的同一项目中进行了概述(顺便说一句:我强烈建议您阅读那本书!),并且需要在函数内声明静态变量,该变量只需返回它。
Actually, chances are you are making a wrong assumption:
See item 4 of Effective C++:
The reason is that ensuring a "correct" initialization order is impossible, and therefore the C++ standard simply gives up on it, and just leave that as undefined.
So, if your DllMain is in a different file than the code where the static variable is declared, the behavior is undefined, and you have very good chances of getting DllMain called before the initialization is actually done.
Solution is quite simple, and outlined in the same item of Effective C++ (btw: I strongly recommend you reading that book!), and requires declaring the static variable inside a function, that simply returns it.
虽然我不确定初始化失败的原因,但一种解决方法是在 DllMain 中显式创建并初始化变量。根据 Microsoft 最佳实践论文,您应该避免在 DllMain 中使用 CRT 分配函数,因此我使用 Windows 堆分配函数,以及自定义分配器(注意:未经测试的代码,但应该或多或少是正确的):
Although I'm not sure why initialization fails, one workaround would be to explicitly create and initialize the variables in your DllMain. As per Microsoft's best practices paper, you should avoid using CRT allocation function in DllMain, so I use windows heap allocation functions instead, with a custom allocator (note: untested code, but should be more or less right):
“经典”的简单 singleton 实现将起作用:
当然,您不应该从 DllMain 调用它。
The "classic" simple singelton implemention will work:
Of course, you shouldn't call this from DllMain.