2014-03-27 74 views
0

用於調用DLL函數的地址修正是一個多階段過程:鏈接器將調用指令定向到間接跳轉指令,並將間接跳轉指令指向在導入表中的內存單詞。 rdata部分,其中Windows程序加載器將在運行時加載DLL時放置該函數的地址。用於DLL函數調用的間接跳轉

間接跳轉指令必須由鏈接器生成,因爲編譯器不知道該函數將變成DLL。程序文件大小通過爲每個函數僅生成一個間接跳轉指令而被最小化,無論它調用了多少個地方。

鑑於此,明顯的方式做到這一點是收集所有的間接跳轉指令的文本部分結束後,在所有目標文件中的所有編譯器生成的代碼,這似乎是發生了什麼當我嘗試使用Microsoft鏈接器/ nodefaultlib開關(生成足夠小的可執行文件以便我能夠理解完全反彙編)的簡單測試用例時。

當我用普通方式將一個小程序鏈接到C標準庫時,生成的可執行文件足夠大,我無法關注所有的反彙編,但據我所知,間接跳轉指令似乎一次只能以三個小組分散在整個代碼中。

這是我有遺漏的原因嗎?

回答

2

間接跳轉指令必須由鏈接器生成,因爲 編譯器不知道函數會變成DLL。

實際上,情況並非總是如此。如果標記與__declspec(dllimport)的函數,編譯知道這將是一個DLL的進口和在這種情況下,它可以產生一個間接調用:

; HMODULE = LoadLibrary("mylib"); 
push offset $SG66630 
call [[email protected]] 

[email protected]是指針在IAT進口)

如果你不使用dllimport那麼編譯器生成一個相對函數調用:

push offset $SG66630 
call [email protected] 

,在這種情況下,鏈接有可能產生跳躍存根:

LoadLibraryA proc near 
       jmp  [[email protected]] 
LoadLibraryA endp 

而且,事實上,它確實組這樣的跳躍存根在一起(儘管可能由編譯單元和/或進口的DLL,不是100%肯定在這裏)。

注意:過去,鏈接程序沒有顯式生成跳轉存根,而是從導入庫中獲取它們。它們包含完整的對象文件以及生成PE導入目錄所需的存根和結構。看到這篇文章是如何工作的:https://www.microsoft.com/msj/0498/hood0498.aspx

現在,導入庫只有API和DLL名稱,鏈接器知道如何生成必要的代碼和元數據來導入它們。