一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

關(guān)于顯示加載動(dòng)態(tài)鏈接庫(kù)模塊及卸載的問題

 小仙女本仙人 2021-05-05

問題起因是,在一次模塊卸載后,程序運(yùn)行異常。遂對(duì)動(dòng)態(tài)鏈接庫(kù)做一些測(cè)試。

動(dòng)態(tài)庫(kù)加載方式有兩種,隱式加載和顯示加載,隱式加載包含xxx.lib導(dǎo)入庫(kù),在程序執(zhí)行之前由動(dòng)態(tài)加載器完成所有加載;顯示加載則使用LoadLibrary方式;具體數(shù)據(jù)可參考《程序員的自我修養(yǎng):鏈接,裝載與庫(kù)》一書。

動(dòng)態(tài)庫(kù)頭文件:

 1 #ifdef DYNAMICLIBRARYTEST_EXPORTS
 2 #define DYNAMICLIBRARYTEST_API __declspec(dllexport)
 3 #else
 4 #define DYNAMICLIBRARYTEST_API __declspec(dllimport)
 5 #endif
 6 
 7 // 此類是從 dll 導(dǎo)出的
 8 class DYNAMICLIBRARYTEST_API Base {
 9 public:
10     Base(void);
11     
12     virtual int* virtualFunc();
13     virtual ~Base();
14     
15 
16     int a = 8;
17     int b = 9;
18     char c[10] = {'H','e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd' };
19     // TODO: 在此處添加方法。
20 };
21 
22 class DYNAMICLIBRARYTEST_API Derive  : public Base
23 {
24 public:
25     Derive(void);
26     int* normalFunc()
27     {
28         return nullptr;
29     }
30 
31     int* virtualFunc() override;
32      ~Derive();
33     // TODO: 在此處添加方法。
34 };
35 
36 extern "C" DYNAMICLIBRARYTEST_API int i_global;
37 
38 extern "C" DYNAMICLIBRARYTEST_API double d_global;
39 
40 extern "C" DYNAMICLIBRARYTEST_API char c_global[6];
41 
42 extern "C" DYNAMICLIBRARYTEST_API int func1(void);
43 extern "C" DYNAMICLIBRARYTEST_API Derive* createDerive();
View Code

 動(dòng)態(tài)庫(kù)實(shí)現(xiàn)文件:

 1 // DynamicLibraryTest.cpp : 定義 DLL 的導(dǎo)出函數(shù)。
 2 //
 3 
 4 #include "DynamicLibraryTest.h"
 5 // 這是導(dǎo)出變量的一個(gè)示例
 6 DYNAMICLIBRARYTEST_API int i_global = 1;
 7 int i_global_1 = 9;
 8 DYNAMICLIBRARYTEST_API double d_global = 2 ;
 9 DYNAMICLIBRARYTEST_API char c_global[6] = {'G', 'l','o', 'b', 'a', 'l'};
10 
11 // 這是導(dǎo)出函數(shù)的一個(gè)示例。
12 DYNAMICLIBRARYTEST_API int func1(void)
13 {
14     return -1;
15 }
16 
17 Derive * createDerive()
18 {
19     return new Derive;
20 }
21 
22 Base::Base()
23 {
24     return;
25 }
26 
27 
28 int* Base::virtualFunc()
29 {
30     return nullptr;
31 }
32 
33 Base::~Base()
34 {
35 }
36 
37 Derive::Derive(void)
38 {
39 }
40 
41 int* Derive::virtualFunc()
42 {
43     int c = a + b;
44     c--;
45     return new int[10];
46 }
47 
48 Derive::~Derive()
49 {
50 }
View Code

查看導(dǎo)出符號(hào):

 

 可以看到導(dǎo)出的變量命名比較正常,這是因?yàn)槭且訡風(fēng)格導(dǎo)出的。不然就是C++的詭異風(fēng)格修飾。

主程序?qū)崿F(xiàn):project.cpp

 1 // project.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。
 2 //
 3 
 4 #include <iostream>
 5 #include "DynamicLibraryTest.h"
 6 #include <Windows.h>
 7 
 8 #define LIBNAME "C:/Users/Admin/source/repos/DynamicLibraryTest/Release/DLL_1.dll"
 9 
10 typedef int*(*NormalFunc)();
11 typedef Derive*(*CreateDerive)();
12 int main()
13 {
14     const char* szStr = LIBNAME;
15     WCHAR wszClassName[256]; 
16     memset(wszClassName, 0, sizeof(wszClassName));
17     MultiByteToWideChar(CP_ACP, 0, szStr, strlen(szStr) + 1, wszClassName, sizeof(wszClassName) / sizeof(wszClassName[0])); 
18     HMODULE hmodule = ::LoadLibrary(wszClassName);
19     if (NULL == hmodule) 
20     {
21         printf("LoadLibrary failed/n"); 
22         return -1; 
23     }
24 
25     CreateDerive funcDerive = (CreateDerive)GetProcAddress(hmodule, "createDerive");
26     NormalFunc nor = (NormalFunc)GetProcAddress(hmodule, "?normalFunc@Derive@@QAEPAHXZ");
27     Derive* d = funcDerive();//分配在堆上
28     Derive* d2 = funcDerive();
29     //d->normalFunc();//不能直接調(diào)用非虛函數(shù)
30     //本模塊保存了一份虛表地址在堆上,每次訪問虛函數(shù),通過堆上的保存的虛表地址查找真正的虛表,
31     //而虛表保存在映射區(qū)域(dll模塊的全局常量區(qū),不過映射的數(shù)據(jù)區(qū)域?yàn)閭浞荩S著模塊的卸載,該映射區(qū)域也會(huì)消失,導(dǎo)致訪問異常。
32     //至于為什么顯示加載dll的方式不能調(diào)用非虛函數(shù),是因?yàn)檎{(diào)用這種函數(shù)不需要查虛表,直接調(diào)函數(shù)地址,但該函數(shù)導(dǎo)出名字經(jīng)過修飾,
33     //會(huì)造成無(wú)法解析的引用; 子類和父類都有一套虛表,存的是各自的函數(shù)地址。
34     int* vb = d->virtualFunc();//ecx寄存器保存的是this指針,即d;
35     d2->a = 2;
36     _asm
37     {
38         mov ecx, dword ptr[d2];
39     }
40     nor();//此時(shí)調(diào)用的是d2的成員函數(shù)。
41     delete d;
42     int *local = new int[10];
43     vb[0] = 1;
44     local[0] = 2;
45     int c = vb[0] + local[0];
46 
47     ::FreeLibrary(hmodule);
48     //int* va = d->virtualFunc();//報(bào)錯(cuò)
49     return 0;
50 }

 顯示加載后,得到類對(duì)象d,是不能直接通過該對(duì)象調(diào)用其非虛成員函數(shù)的(鏈接不通過),但是能直接調(diào)用虛函數(shù)。問題是因?yàn)檎{(diào)用虛函數(shù)是要查虛表的。下圖是project.obj的main部分反匯編代碼:

 

 可以看到對(duì)于一般的函數(shù)調(diào)用會(huì)生成函數(shù)符號(hào),相當(dāng)于一個(gè)占位標(biāo)記,該符號(hào)地址在鏈接前,用默認(rèn)地址00 00 00 00 代替(32位機(jī)器下),在執(zhí)行鏈接后,該默認(rèn)地址會(huì)修改為正確的位置。

鏈接后的main部分反編譯代碼:

 

 回到之前的那個(gè)問題,為什么一般的成員函數(shù)不能直接調(diào)用,因?yàn)檎也坏椒?hào)(無(wú)法解析的引用符號(hào)),會(huì)導(dǎo)致鏈接不過。

 第一,導(dǎo)出該符號(hào)(整個(gè)類都是導(dǎo)出的話,該成員函數(shù)自然也是導(dǎo)出的)。第二,該符號(hào)的名字要寫對(duì);

NormalFunc nor = (NormalFunc)GetProcAddress(hmodule, "?normalFunc@Derive@@QAEPAHXZ");

強(qiáng)行獲取該方法。那么又有一個(gè)問題,這個(gè)函數(shù)該怎么調(diào)用?對(duì)于任意一個(gè)成員函數(shù)來(lái)講,調(diào)用會(huì)存在一個(gè)this指針。直接調(diào)用會(huì)出現(xiàn)奇怪的現(xiàn)象。其實(shí)通常調(diào)用成員函數(shù),從匯編的角度,會(huì)將this指針賦值給ecx寄存器。接著調(diào)用該函數(shù)。

 

 

 

 上圖可以看到ecx與this的關(guān)系。通過證實(shí)nor()執(zhí)行的確實(shí)是d2的成員函數(shù)。

接著下一個(gè)問題,卸載模塊后,在該模塊申請(qǐng)的堆內(nèi)存數(shù)據(jù)還在不在?以及能不能繼續(xù)調(diào)用該模塊的成員函數(shù)。

下圖先給出該進(jìn)程的內(nèi)存布局(x64Dbg反編譯工具):

 

 執(zhí)行完LoadLibrary后的內(nèi)存布局:

 

 可以看到dll_1映射到了某個(gè)內(nèi)存地址。

 查看dll中normalFunc的函數(shù)地址:

 對(duì)應(yīng)于dll的代碼段映射區(qū)域。

查看d和d2的內(nèi)存區(qū)域:

 

 可以看到這兩個(gè)變量所對(duì)應(yīng)的首4字節(jié)值是一樣的,這就是虛表地址。

轉(zhuǎn)到虛表地址:

 

 發(fā)現(xiàn)該虛表存儲(chǔ)在DLL_1的內(nèi)存區(qū)域“.rdata ”段(從前面的內(nèi)存布局看出)。

那么當(dāng)真?zhèn)€DLL被卸載時(shí)發(fā)生了什么?執(zhí)行完Freelibrary后:

 

 

 

 

 

 那么顯而易見,卸載dll模塊后,變量d2是不能調(diào)用任何函數(shù)的,因?yàn)榇藭r(shí)地址都清空了,包括虛函數(shù),虛表不存在。而d2這個(gè)變量所對(duì)應(yīng)的內(nèi)存空間依然存在。但是意味著該類對(duì)象沒法調(diào)用析構(gòu)函數(shù),造成內(nèi)存泄漏。

其實(shí),在dll申請(qǐng)的內(nèi)存,最好在該dll里釋放,不然會(huì)出現(xiàn)奇怪的現(xiàn)象。

。。。待續(xù)

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    黄色国产一区二区三区| 亚洲一区二区三区熟女少妇| 国产精品不卡高清在线观看| 91精品国自产拍老熟女露脸 | 91亚洲国产成人久久精品麻豆| 亚洲欧美日本国产不卡| 久久三级国外久久久三级| 国产欧美韩日一区二区三区| 久久国产精品熟女一区二区三区| 欧美一级不卡视频在线观看| 欧美六区视频在线观看| 美日韩一区二区精品系列| 亚洲熟女少妇精品一区二区三区| 日韩精品毛片视频免费看| 国产精品视频一区二区秋霞 | 国产又粗又深又猛又爽又黄| 五月婷婷综合激情啪啪| 出差被公高潮久久中文字幕| 国产精品内射婷婷一级二级| 在线免费看国产精品黄片| 懂色一区二区三区四区| 成年人黄片大全在线观看| 国产欧美日韩精品成人专区| 欧美乱码精品一区二区三| 在线观看中文字幕91| 男人把女人操得嗷嗷叫| 国内九一激情白浆发布| 国产一级不卡视频在线观看| 日本高清视频在线播放| 亚洲乱妇熟女爽的高潮片| 91欧美亚洲精品在线观看| 国产成人在线一区二区三区| 91老熟妇嗷嗷叫太91| 美女激情免费在线观看| 福利一区二区视频在线| 久久精品中文扫妇内射| 精品亚洲一区二区三区w竹菊| 视频在线观看色一区二区| 日韩中文字幕人妻精品| 91亚洲国产成人久久| 爱草草在线观看免费视频|