Windows下的動態鏈結

2021-09-30 06:11:36 字數 3546 閱讀 8305

windows

下的dll

檔案和exe

檔案實際上是乙個概念,它們都是有

pe格式的二進位制檔案,稍微有些不同的是

pe檔案頭部有個符號位表示該檔案是

exe或是

dll,而

dll檔案的副檔名不一定是

.dll

,也可能是

.ocx(ocx控制項)

或是.cpl(

控制面板程式)。

在elf中,共享庫中所有的全域性函式和變數在預設情況下都可以被其他模組使用,也就是說

elf預設匯出所有的全域性符號。但是在

dll中情況有所不同,我們需要顯示地「告訴」編譯器我們需要匯出某個符號,否則編譯器預設所有符號都不匯出。當我們在程式中使用

dll匯出的符號時,這個過程被稱為匯入(

import

)。除了使用「

__declspec

」擴充套件關鍵字指定匯入匯出符號之外,我們也可以使用「

.def

」檔案來宣告匯入匯出符號。「

.def

」副檔名的檔案是類似於

ld鏈結器的鏈結指令碼語言,可以被當作

link

鏈結器的輸入檔案,用於控制鏈結過程。「

.def

」檔案中的

import

或者exports

段可以用來宣告匯入匯出符號,這個方法不僅對

c/c++

有效,對其他語言也有效。

dll的簡單例子

假設我們的乙個

dll提供數**算的函式,源**如下

(math.c)

:__declspec(dllexport) double sub(double a, double b)

經過msvc

編譯器cl

進行編譯,結果生成了「

math.dll

」、「math.obj

」、「math.exp

」和「math.lib」這4

個檔案.

math.dll

——就是生成的

dll檔案;

math.obj

——是編譯的目標檔案;

math.exp

——exp

檔案實際上是鏈結器在建立

dll時的臨時檔案。鏈結器在建立

dll時與靜態鏈結時一樣採用兩遍掃瞄過程,

dll一般都有匯出符號,鏈結器在第一遍時會遍歷所有的目標檔案並且收集所有匯出符號資訊並且建立

dll的匯出表。為了方便起見,鏈結器把這個匯出表放到乙個臨時的目標檔案叫做「

.edata

」的段中,這個目標檔案就是

exp檔案,

exp檔案實際上是乙個標準的

pe/coff

目標檔案,只不過它的副檔名不是

.obj

而是.exp

。math.lib

——在靜態鏈結的時候,「

.lib

」檔案是一組目標檔案的集合。在動態鏈結的

」math.lib」

裡面的目標檔案是什麼呢?「

math.lib

」中並不真正包含「

math.c

」的**和資料,它用來描述「

math.dll

」的匯出符號,它把包含了應用程式鏈結

math.dll

時所需要的匯入符號以及一部分「樁」**,又被稱作「膠水」**,以便與將程式與

dll粘在一起。因此又被稱為導入庫(

import library

)dll

顯示執行時鏈結

·loadlibrary(

或者loadlibraryex),

這個函式用來裝載乙個

dll到程序的位址空間。

·getprocaddress,

用來查詢某個符號的位址。

·freelibrary,

用來解除安裝某個已載入的模組。

匯入函式的呼叫

編譯器在產生導入庫時,同乙個匯出函式會產生兩個符號的定義,比如對於函式

foo來說,它在導入庫中有兩個符號,乙個是

foo,另外乙個是

__imp__foo

。這兩個符號的區別是,

foo這個符號指向

foo函式的樁**(**於產生

dll檔案時伴隨的那個

lib檔案,即導入庫),而

__imp__foo

指向foo

函式在iat

(匯入位址陣列

import address table

)中的位置。程式設計師可以通過

「__declspec(dllimport)

」來宣告匯入函式

,也可以不使用。通過

「__declspec(dllimport)

」來宣告匯入函式,那麼編譯器就知道他是外部匯入的,以便於產生相應的指令以確保跟導入庫中的「

__imp__foo

」能夠正確鏈結;如果不使用,鏈結器會把匯入函式的目標位址導向一小段樁**(

stub

),由這個樁**再將控制權交給

iat中的真正目標位址。因此,推薦使用「

__declspec(dllimport)

」,畢竟從效能上來講,它比不使用該宣告少了一次跳轉指令。 使用

c++編寫共享庫會經常遇到各種相容性問題,這是因為

c++的標準只規定了語言層面的規則,而對二進位制級別卻沒有任何規定。為了解決這些問題,更大程度上使得程式能夠有更好的重用性,微軟公司提出了元件物件模型(

com, component object model

)。為了指導我們使用

c++編寫動態鏈結庫,在

windows

平台下(有些意見對

linux/elf

也有效),要盡量遵循以下指導意見:

·所有的藉口函式都應該是抽象的。所有的方法都應該是純虛的。(或者是

inline

的方法也可以)

·所有的全域性函式都應該使用

extern 「c」

來防止名字修飾的不相容。並且匯出函式都應該是

__stdcall

呼叫規範的(

com的

dll都是用這樣的規範)。

·不要使用

c++標準庫

stl

·不要使用異常。

·不要使用虛析構函式。可以建立乙個

destroy()

方法並且過載

delete

操作符並且呼叫

destroy() ·

不要在dll

裡面申請記憶體,而在

dll外釋放(或者相反)。不同的

dll和可執行檔案可能使用不同的堆,在乙個堆裡面申請記憶體而在另外乙個堆裡面釋放會導致錯誤。比如,對於記憶體分配相關的寒暑不應該是

inline

的,以防止它在編譯時被展開到不同的

dll和可知性檔案。

·不要在介面中使用過載方法(

overloaded methods

,乙個方法多重引數)。因為不同的編譯器對於

vtable

的安排可能不同。

Windows下的動態鏈結

dll的設計目的與共享物件有些出入,dll更加強調模組化,即微軟希望通過dll機制加強軟體的模組化設計,使得各種模組之間能夠鬆散地組合 重用和公升級。基位址 base address 和相對位址 rva,relative virtual address dll共享資料段 匯出 declspec dl...

Windows下動態鏈結庫與靜態鏈結庫的構建

bool winapi dllmain handle hdllhandle,dword dwreason,當乙個程式試圖載入解除安裝dll時,系統會呼叫dll庫中的dllmain函式 然後編寫實際功能函式 libiary win32 dll 模組名稱 exports 下面的函式才能在dll外使用 f...

Windows下gcc編譯鏈結

在windows的dos下實現gcc編譯和鏈結 這裡主要看的是兩篇寫的很詳細的文章 c語言多檔案編譯初探 一 c語言多檔案編譯初探 二 3.此時就可以在dos中使用gcc了。gcc可以將c c 檔案編譯為.o檔案,然後鏈結生成可執行檔案.exe。4.接下來我們寫兩個原始檔,乙個標頭檔案,用來模擬多檔...