DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响
《windows核心编程》作者在讨论DllMain执行序列化的时候,曾说过一个他的故事:他试图通过调用DisableThreadLibraryCalls以使得新线程不在调用DllMain从而解决死锁问题。但是该方案最后失败了。思考作者的思路,他可能一开始认为:因为线程要调用DllMain而加锁,于是windows在发现DllMain不用调用时就不用加锁了。本文将探讨DisableThreadLibraryCalls对DllMain死锁的影响。首先我们需要定位是什么函数调用了DllMain。(转载请指明出于breaksoftware的csdn博客)为了方便分析,我设计了以下代码
DLL中
在LoadLibrary之后Sleep了一下,是为了让工作线程有机会执行起来。在执行到Sleep之后,程序中断在DLL中。我们看调用堆栈
我将关注下从ntdll进入DllWithoutDisableThreadLibraryCalls_A.dll的逻辑调用。双击_LdrpCallInitRoutine这行查看其汇编由以上流程可以发现,标红色的7C939A10前一句和7C9399FD前一句是重要的跳转条件判断。它们分别是
VOIDLdrpInitialize ( IN PCONTEXT Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ){ …… Peb->LoaderLock = (PVOID)&LoaderLock; if ( !RtlTryEnterCriticalSection(&LoaderLock) ) { if ( LoaderLockInitialized ) { RtlEnterCriticalSection(&LoaderLock); } else { // // drop into a 30ms delay loop // DelayValue.QuadPart = Int32x32To64( 30, -10000 ); while ( !LoaderLockInitialized ) { NtDelayExecution(FALSE,&DelayValue); } RtlEnterCriticalSection(&LoaderLock); } } …… LdrpInitializeThread(Context); …… RtlLeaveCriticalSection(&LoaderLock); …我们看到在11或13或25行进入临界区后,在29行调用了LdrpInitializeThread,而在31行退出临界区。这就是说整个LdrpInitializeThread的逻辑都在临界区中执行的,也就是说DisableThreadLibraryCalls将无权干涉是否会进入临界区。这就解释了为什么不能使用DisableThreadLibraryCalls来使上例解决死锁的原因。