動態連結庫(轉)
本課中,我們將學習DLLs,它們到底是什麼和如何建立它們。
理論:
如果您程式設計的時間非常長,就會發現很多的程式之間其實有相當多的重複程式碼。每編一個程式就重寫一遍這些程式碼既沒必要又浪費時間。在DOS時代,一般的做法是把這些重複的程式碼寫成一個個的函式,然後把它們按類別放到不同的庫檔案中去。當要使用這些函式時,只要把您的目標檔案(.obj)檔案和先前存放在庫檔案中的函式進行連結,連結時連結器會從庫檔案中抽取相關的資訊並把它們插入到可執行檔案中去。這個過程叫做靜態連結。C執行時庫就是一個好例子。這樣的庫的缺點是您在每一個呼叫庫函式的程式中都必須嵌入同一函式的複製,這顯然很浪費磁碟。在DOS時代畢竟每一時刻僅有一個程式在執行,所以浪費的還只是磁碟而已,在多工的WINDOWS時代就不僅浪費磁碟,還要浪費寶貴的記憶體了。
在WINDOWS中,由於有多個程式同時執行,如果您的程式非常大的話,那將消耗相當多的記憶體。WINDOWS的解決辦法是:使用動態連結庫。動態連結庫從表面上看也是一大堆的通用函式,不過即使有多個程式呼叫了它,在記憶體中也僅僅只有動態連結庫的唯一一份複製。WINDOWS是透過分頁機制來作到這一點的。當然,庫的程式碼只有一份,但是每一個應用程式要有自己單獨的資料段,要麼就會亂掉。
不象舊時的靜態連結庫,它並不會把這些函式的可執行程式碼放入到應用程式中去,而是當程式已經在記憶體中執行時,如果需要呼叫該函式時才調入記憶體也即連結。這也就是為什麼把它叫做“動態”的原因所在。另外您還可以動態地解除安裝動態連結庫,當然要求這時沒有其它的應用程式在使用它,否則就要一直等到最後一個使用它的函式也不再使用該動態連結庫時才能去解除安裝它。
為了正確的呼叫庫和給庫函式分配記憶體空間,在編譯和連結應用程式時,必須把重定位等一些訊息插入到執行程式碼中去,以便載入正確的庫,並給庫函式分配正確的地址。
那麼這些資訊從哪裡得到呢?引入庫。引入庫包含足夠的資訊,連結器從中抽取足夠的資訊(注意區別:靜態連結庫放入的是可執行程式碼)把它們放入到可執行檔案中去。當WINDOWS的載入器裝入應用程式檢視到有DLL時,它會查詢該庫檔案,如果沒有查到,就報錯退出,否則就把它對映進程式的地址空間,並修正函式呼叫語句的地址。
如果沒有引入庫呢?當然我們也可以呼叫動態連結庫中的任意函式。只不過您必須知道呼叫的函式是否在庫中而且是否在庫的引出名字表中,另外還需要知道該函式的引數個數和引數的型別。
(譯者加:說到這裡,讓我想起了一件很有名的事。<
當您讓系統的載入器為您載入動態庫時,如果不能找到庫檔案,它就會提示一條“A required .DLL file, xxxxx.dll is missing”,這樣您的應用程式就無法執行,即使該庫對您的應用程式來說並不重要。
如果您選擇在程式執行時自己載入該庫,就沒有這種問題了。
如果您知道足夠的資訊,就可以呼叫系統未公開的函式。
如果您呼叫LoadLibrary函式載入庫,就必須再呼叫GetProcAddress函式來得到每一個您想呼叫的函式的地址,GetProcAddress會在動態連結庫中查詢函式的入口地址。由於多餘的步驟,這樣您的程式執行起來會慢一點,但是並不明顯。
明白了LoadLibrary函式的優缺點,下面我們就來看看如何產生一個動態連結庫。下面的程式碼是一個動態連結庫的框架:
;--------------------------------------------------------------------------------------
; DLLSkeleton.asm
;--------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib
.data
.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
;---------------------------------------------------------------------------------------------------
;下面是一個空函式,您可以象下面一樣插入您的函式。
;----------------------------------------------------------------------------------------------------
TestFunction proc
ret
TestFunction endp
End DllEntry
;-------------------------------------------------------------------------------------
; DLLSkeleton.def
;-------------------------------------------------------------------------------------
LIBRARY DLLSkeleton
EXPORTS TestFunction
上面是一個動態連結庫的框架,每一個DLL必須有一個入口點函式,WINDOWS每一次在做下面的動作時會呼叫該入口點函式:
當動態連結庫被載入時
當動態連結庫解除安裝時
同一程式的執行緒生成時
同一程式的執行緒退出時
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
入口點函式的名稱無所謂只要您讓語句“END”中的函式名和前面的相同就可以了。該函式共有三個引數,只有前面兩個是重要的。
hInstDLL是該動態連結庫模組的控制程式碼。它和程式的例項控制程式碼不一樣。如果您以後要用,可以儲存它,因為以後再要獲得它不容易。
根據不同的時機,reason傳入的值可能是下面的四個值中的一個:
DLL_PROCESS_ATTACH 動態連結庫第一次插入程式的地址空間時。當傳入的引數是該值時,您可以做一些初始化的工作。
DLL_PROCESS_DETACH 動態連結庫從程式的地址空間卸出時。您可以在此做一些清理的工作。譬如:釋放記憶體等。
DLL_THREAD_ATTACH 新執行緒生成。
DLL_THREAD_DETACH 執行緒銷燬。
如果想要庫中的程式碼繼續執行,返回TRUE,否則返回FALSE,那樣動態連結庫就不會載入了。譬如:您想分配一塊記憶體,如果不成功的話就退出,這時您就可以返回FALSE。那樣動態連結庫就不會載入了。
您可以加入的函式,它們的位置並不重要,把它們放在入口點函式的前面或後面都可以。只是如果您想要它們能被其它的程式呼叫的話,就必須把它們的名字放到模組定義檔案(.def)中去。
動態連結庫在它們自己的編譯過程就需要,而不只是提供給其它要引用它的程式參考。他們如下:
LIBRARY DLLSkeleton
EXPORTS TestFunction
第一行是必須的。LIBRARY 定義了DLL的模組名稱。它必須和動態連結庫的名稱相同。
EXPORTS關鍵字告訴連結器該DLL的引出函式,也就是其它程式可以呼叫的函式。舉個例子:其它的程式想要呼叫函式TestFunction ,我們就把它放到EXPORTS中。
還有就是,連結器的選項中必須放入開關項:/DLL 和/DEF
link /DLL /SUBSYSTEM:WINDOWS /DEF:DLLSkeleton.def /LIBPATH:c:masm32lib DLLSkeleton.obj
編譯器的開關選項是一樣的,即:/c /coff /Cp。在您連結好後,連結器會生成.lib 和.dll檔案。前者是引入庫,當其它的程式要呼叫您的動態連結庫中的函式時就需要該引入庫,以便把必要的資訊加入到其可執行檔案中去。
接下來我們來看看如何使用LoadLibrary函式來載入一個DLL。
;---------------------------------------------------------------------------------------------
; UseDLL.asm
;----------------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libkernel32.lib
includelib masm32libuser32.lib
.data
LibName db "DLLSkeleton.dll",0
FunctionName db "TestHello",0
DllNotFound db "Cannot load library",0
AppName db "Load Library",0
FunctionNotFound db "TestHello function not found",0
.data?
hLib dd ? ; 動態連結庫的控制程式碼 (DLL)
TestHelloAddr dd ? ; TestHello 函式的地址
.code
start:
invoke LoadLibrary,addr LibName
;---------------------------------------------------------------------------------------------------------
; 呼叫LoadLibrary,其引數是欲載入的動態連結庫的名稱。如果呼叫成功,將返回該DLL的控制程式碼。 否則返回NULL。該控制程式碼可以傳給 :library函式和其它需要動態連結庫控制程式碼的函式。
;-----------------------------------------------------------------------------------------------------------
.if eax==NULL
invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
.else
mov hLib,eax
invoke GetProcAddress,hLib,addr FunctionName
;-----------------------------------------------------------------------------------------------------------
; 當您得到了動態連結庫的控制程式碼後,把它傳給GetProcAddress函式,再把您要呼叫的函式的名稱 也傳給該函式。如果成功的話,它:會返回想要的函式的地址,失敗的話返回NULL。除非解除安裝該 動態連結庫否則函式的地址是不會改變的,所以您可以把它儲存到一個:全域性變數中以備後用。
;-----------------------------------------------------------------------------------------------------------
.if eax==NULL
invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
.else
mov TestHelloAddr,eax
call [TestHelloAddr]
;-----------------------------------------------------------------------------------------------------------
; 以後您就可以和呼叫其它函式一樣呼叫該函式了。其中要把包含函式地址資訊的變數用方括號括起來。
;-----------------------------------------------------------------------------------------------------------
.endif
invoke FreeLibrary,hLib
;-----------------------------------------------------------------------------------------------------------
;呼叫FreeLibrary解除安裝動態連結庫。
;-----------------------------------------------------------------------------------------------------------
.endif
invoke ExitProcess,NULL
end start
使用LoadLibrary函式載入動態連結庫,可能要自己多做一些工作,但是這種方法確實是提供了許多的靈活性。
[@more@]來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928731/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 動態連結庫與靜態連結庫
- cmake 連結動態連結庫
- linux下靜態連結庫和動態連結庫的區別有哪些Linux
- 【連結 1】與靜態連結庫連結
- 載入動態連結庫——dlopen dlsym dlclose
- C#呼叫C++動態連結庫C#C++
- 動態連結庫的生成和使用(二)
- 動態連結庫(DLL)的建立和使用
- 如何連結兩個名字一樣動態庫
- 在 Linux中如何使用動態連結模組庫?Linux
- P/Invoke之C#呼叫動態連結庫DLLC#
- 靜態連結動態連結的連結順序問題和makefile示例
- lua——alien庫實現lua呼叫C動態連結庫(dll、so)
- PHP獲取動態跳轉後的真實連結PHP
- 使用js動態新增連結隨機連結JS隨機
- Gazebo新增模型並控制模型運動作為動態障礙物(Ubuntu16.04, Gazebo7.16),附錄動態連結庫和靜態連結庫區別模型Ubuntu
- IIS無法訪問動態連結庫DLL的原因
- 動態連結的相關結構
- 編譯 pyav 成 wheel 並使用 auditwheel 固化動態連結庫編譯
- JNI呼叫c動態連結庫函式程式碼實踐函式
- [pwn基礎]動態連結原理
- 動態連結的PLT與GOTGo
- 用動態連結動態洩露system地址並利用
- FFmpeg開發筆記(四)FFmpeg的動態連結庫介紹筆記
- 【技術向】Linux動態連結庫預載入型後門Linux
- Linux環境下:程式的連結, 裝載和庫[靜態連結]Linux
- 關於動態連結串列的理解
- 動態連結串列的建立(程式碼)
- 安卓動態連結庫檔案體積最佳化探索實踐安卓
- windows和linux gcc生成動態連結庫DLL和SO並用python呼叫WindowsLinuxGCPython
- Windows環境下,動態連結庫(DLL)的“匯入”與“匯出”概念Windows
- 動態連結的步驟與實現
- C編譯: 動態連線庫 (.so檔案)編譯
- 在Java中利用動態代理實現資料庫連線與事務的自動管理【轉】Java資料庫
- jQuery動態修改連結的href屬性值jQuery
- 靜態庫與動態庫
- gcc庫連結GC
- 如何把一個長連結轉短連結 短連結轉化器該如何使用
- 反轉連結串列