dll 加载与卸载的顺序研究

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18218335/article/details/79578404

之前写过一篇DLL 加载卸载的文章,不太好,重写一下,更深入一点。

两个组或者两个公司独立的开发可能用来组成同一个产品的组件,但是他们必须独立的构建、测试和提交他们的工作。组件粒度很难是正确的且与怎么对组件是最好的这样的问题无关。取而代之的是,一个组件是怎样才能对公司最好(团队不喜欢使用多个dll,且他们想自主的写一个单独的DLL,以便他们的测试人员可以对测试组件所意为的工作量感到满意)以及怎样才对一个高性能的团队最好的集合(一个功能一个DLL将使系统的性能下降不少)。

当一个加载器发现这种现象的时候,它会怎么做?

谁更深,谁被更早的添加到列表中??

装了火绒就是上面的效果,因为火绒会注入dtrampo.dll 到进程中。

不装火绒的时候就是这种状态。

为什么DLL 以错误的顺序卸载?

当一个程序开始运行,或者一个DLL 被加载,加载器为DLL 或者程序建立一个依赖树。之后加载器找到一个正确的加载顺序,使得,直到一个DLL 的所有的依赖的DLL 都被加载了之后该DLL才会被初始化。如果你有一个循环依赖,下面将思考这个问题??如果你在DLL_PROCESS_ATTACH 通知中调用LoadLibrary,将使得这个情形更加混乱,而且,会导致死锁。

首先:循环加载:
DemoProgram:

#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
int main()
{
       printf("DemoProgram world\r\n");
       hello();
       getchar();
       return 0;
}

helloworld.dll
// dllmain.cpp : 定义 DLL 应用程序的入口点。

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
extern "C" __declspec(dllexport) void hello()
{
       printf("Hello World\r\n");
       return;
}
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
       switch (fdwReason)
       {
       case DLL_PROCESS_ATTACH:
              printf("HelloWorld Module\r\n");
              // hello();
              ref_hello();
              break;
       case DLL_PROCESS_DETACH:
              break;
       case DLL_THREAD_ATTACH:
              break;
       case DLL_THREAD_DETACH:
              break;
       }
       return TRUE;
}

ref_hello.dll:
// dllmain.cpp : 定义 DLL 应用程序的入口点。

#include "stdafx.h"
#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
       {
              printf("ref_hello Module\r\n");
              hello();
              break;
       }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
extern "C" __declspec(dllexport) void ref_hello()
{
       printf("ref_hello \r\n");
}

输出为:
ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
Hello World

我们改变DemoProgram ,修改对helloworld 模块的引用为:对ref_hello 模块的引用:

#include <stdio.h>
// #pragma comment(lib,"helloworld.lib")
// extern "C" __declspec(dllimport) void hello();
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       getchar();
       return 0;
}

输出如下:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
ref_hello

我们再次改变DemoProgram,使其同时引用两个模块:

#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       hello();
       getchar();
       return 0;
}

输出为:

ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
ref_hello
Hello World

修改上面对于两个模块的引用的顺序:

#include <stdio.h>
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       hello();
       getchar();
       return 0;
}

输出如下:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
Hello World
ref_hello

似乎发现了什么。。。。。。。。。。。。。。。。。。
其PE 文件中的DLL 的加载顺序同上,

接下来在项目属性中修改两个模块的顺序如下:

这里写图片描述

ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
Hello World
ref_hello

再次修改顺序如下:
这里写图片描述
输出结果为:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
Hello World
ref_hello

我们似乎又发现了什么。。。。。。。。。。。。。。。。。。。。。。。。。。
使用CFF Explorer_CN.exe 查看两种EXE 其导入表如下:
这里写图片描述
这里写图片描述

再对比EXE 的输出我们可以发现导入顺序的玄机。

这个没有考虑到一个问题是:如果A,B 循环依赖,且在DLL_PROCESS_DETACH 消息中依然引用对方的代码,将可能导致程序崩溃,因为引用了已经卸载的DLL 的代码。
下一篇:

1. 如何在不修改PE 文件的情况下修改其导入的模块的加载顺序??????

2. dllmain 中调用LoadLibrary

猜你喜欢

转载自blog.csdn.net/qq_18218335/article/details/79578404