从 DllMain 调用 LoadLibrary [英] Calling LoadLibrary from DllMain

查看:16
本文介绍了从 DllMain 调用 LoadLibrary的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

MSDN 说:<块引用>

不得调用 LoadLibrary 或 LoadLibraryEx 函数(或调用这些函数的函数),因为这可能会在 DLL 加载顺序中创建依赖循环.这可能会导致在系统执行其初始化代码之前使用 DLL.

我试图从 DllMain 调用 LoadLibrary 但什么也没发生.

我看到的唯一问题是加载的 DLL 将在我的 DllMain 的其余部分执行之前使用我的 DLL 中的函数.

为什么我不能在 DllMain 中调用 LoadLibrary?

好吧,我意识到我不能在 DllMain 中调用 LoadLibrary 只是因为我必须像其他信徒一样相信 MSDN(我在那里看到了一些错误的东西,但我也应该忘记它们).
并且因为在较新版本的 Windows 中可能会发生一些事情(尽管过去十年没有任何变化).

但是任何人都可以展示一个代码来重现在 DllMain 中调用 LoadLibrary 时会发生什么不好的事情?在任何现有的 Windows 操作系统中?
不只是在另一个内部调用一个单例初始化函数,而是在 DllMain 中调用 LoadLibrary?

解决方案

从 DllMain 调用 LoadLibrary 在一些简单甚至不那么简单的情况下是完全安全的.但设计是信任 DllMain 不会更改已加载模块的列表.

虽然拥有加载器锁确实限制了在 DllMain 中可以做什么,但它只是与 LoadLibrary 规则间接相关.加载器锁的相关目的是序列化对加载模块列表的访问.虽然 NTDLL 在一个线程中处理此列表,但拥有加载器锁可确保列表不会被在另一个线程中执行的 NTDLL 代码更改.但是,加载程序锁是一个关键部分.它不会阻止同一线程重新获取加载程序锁并更改列表.

如果 NTDLL 在处理列表时完全保留它自己,这无关紧要.但是,NTDLL 允许在此工作中涉及其他代码,例如在初始化新加载的 DLL 时.每次 NTDLL 在处理列表时调用外部,都会为设计做出选择.大体上,有两种选择.一种是稳定列表并释放加载器锁,调用外部,然后获取加载器锁并恢复对列表的工作,就像从头开始一样,因为外部调用可能已经改变了它.另一种是保持加载器锁定并相信被调用的代码不会做任何改变列表的事情.因此 LoadLibrary 成为 DllMain 中的禁区.

并不是说加载器锁对阻止 DllMain 调用 LoadLibrary 做任何事情,甚至加载器锁本身使这样的调用变得不安全.相反,通过保留加载程序锁,NTDLL 信任 DllMain 不会调用 LoadLibrary.

相比之下,请考虑关于不等待同步对象的 DllMain 规则.在这里,加载器锁在使这不安全方面具有直接作用.在 DllMain 中等待同步对象可能会导致死锁.所需要的只是另一个线程已经持有您正在等待的对象,然后这个另一个线程调用任何将等待加载器锁的函数(例如,LoadLibrary 以及诸如看似无关紧要的 GetModuleHandle 之类的函数).

想要扩展或打破 DllMain 规则可能是恶作剧甚至是彻头彻尾的愚蠢.然而,我必须指出,微软至少应为人们询问这些规则有多强或有多有意义而负部分责任.毕竟,有些并不总是被清楚而有力地记录下来,当我最后一次查看时,它们仍然没有在所有肯定需要它们的情况下记录下来.(我想到的例外是,至少在 Visual Studio 2005 之前,编写 DLL 的 MFC 程序员被告知将他们的初始化代码放在 CWinApp::InitInstance 中,但没有被告知此代码受 DllMain 规则约束.)

此外,对于微软的任何人来说,好像应该毫无疑问地遵循 DllMain 规则一样会有点丰富.微软自己的程序员违反规则的例子是存在的,即使在违反规则之后仍然继续被视为造成了严重的现实世界的麻烦.

MSDN says:

It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code.

I tried to call LoadLibrary from DllMain and nothing happened.

The only issue that I see is that loaded DLL will use functions in my DLL before rest of my DllMain executes.

Why I must not call LoadLibrary in DllMain?

EDIT:

OK, I realized that I must not call LoadLibrary in DllMain just because I must believe MSDN as other believers do (I saw some wrong things there, but I should forget them too).
And because something may happen in newer versions of Windows (although there nothing was changed for last ten years).

But can anyone show a code which will reproduce something bad what happens when LoadLibrary is called in DllMain? In any existing Windows OS?
Not just a call of one singleton initialization function inside another, but LoadLibrary in DllMain?

解决方案

There are simple, and even not so simple, circumstances in which calling LoadLibrary from DllMain is perfectly safe. But the design is that DllMain is trusted not to change the list of loaded modules.

Though possession of the loader lock does indeed constrain what can be done in DllMain, it is only indirectly relevant to the LoadLibrary rule. The relevant purpose of the loader lock is serialise access to the list of loaded modules. While NTDLL works on this list in one thread, possession of the loader lock ensures that the list won't be changed by NTDLL code that's executing in another thread. However, the loader lock is a critical section. It does nothing to stop the same thread from re-acquiring the loader lock and changing the list.

This would not matter if NTDLL kept entirely to itself while working on the list. However, NTDLL provides for involving other code in this work, as when initialising a newly loaded DLL. Each time NTDLL calls outside itself while working on the list, there is a choice to make for the design. Broadly, there are two options. One is to stabilise the list and release the loader lock, call outside, then acquire the loader lock and resume work on the list as if from scratch because the outside call may have changed it. The other is to keep the loader lock and trust the called code not to do anything that changes the list. And thus does LoadLibrary become off-limits in DllMain.

It's not that the loader lock does anything to stop DllMain from calling LoadLibrary or even that the loader lock itself makes such a call unsafe. It is instead that by retaining the loader lock, NTDLL trusts DllMain not to call LoadLibrary.

For contrast, consider the DllMain rule about not waiting on synchronisation objects. Here, the loader lock has a direct role in making this unsafe. Waiting on a synchronisation object in DllMain sets up the possibility of deadlock. All that's needed is that another thread already holds the object you're waiting on, and then this other thread calls any function that would wait on the loader lock (e.g., LoadLibrary but also such functions as the seemingly inocuous GetModuleHandle).

Wanting to stretch or break the DllMain rules may be mischievous or even outright stupid. However, I must point out that Microsoft is at least partly to blame for people asking how strong or meaningful are these rules. After all, some have not always been documented clearly and forcefully, and when last I looked they were still not documented in all the situations where they're surely needed. (The exception I have in mind is that at least until Visual Studio 2005, MFC programmers writing DLLs were told to put their initialisation code in CWinApp::InitInstance but were not told that this code is subject to the DllMain rules.)

Moreover, it would be a bit rich for anyone from Microsoft to speak as if the DllMain rules ought be followed without question. Examples exist where Microsoft's own programmers break the rules, and continue to even after breaking the rules is seen to have caused serious real-world trouble.

这篇关于从 DllMain 调用 LoadLibrary的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆