Wine中PE格式文件的加載(四):DLL的裝入和連接

在加載完PE可執行文件後,回到kernel32的入口函數__wine_kernel_init中,接下來調用了函數LdrInitializeThunk。dll的裝入和連接過程主要是該函數實現的。

函數部分代碼如下圖所示:

 

先用main_exe_file判斷主模塊是否已經被建立了,這是在wine_process_init函數中被賦值的一個句柄類型。

get_moderf的作用正如註釋所說,是爲可執行文件分配一個模塊的引用結構。

在Wine裝入和連接dll的時候,每個用到的dll以及exe,都對應於一個“模塊的引用結構”,WINE_MODREF類型,並且裝載過程中也將單個dll或exe視作一個模塊。結構如下圖所示:

其中LDR_MODULE類型來源於Windows。由於每個dll中都描述了其直接依賴的一組dll,所以需要用一個指針數組deps來記錄。而nDeps顧名思義就是直接依賴的dll個數。

所以在裝入和連接過程中,應該是有這樣一顆樹

然後就是對PEB信息進行填充。

該函數中直接負責dll裝載和連接的,只有fixup_imports。函數代碼如下圖所示:

fixup_imports是用來將一個模塊所依賴的所有模塊都裝入連接進來。當然對於標誌位是LDR_DONT_RESOLVE_REFS,即不需要解析其引用的模塊,或者該模塊的引用數爲0(這種情況當然只有ntdll.dll纔可能),那麼就可以直接返回了。而對於大多數依賴其他dll的模塊而言,接下來就是要裝入和連接它們了。

從LdrInitializeThunk看到,fixup_imports會從第一個模塊,即exe開始,按照我們上面的圖的依賴關係把所有需要的dll都裝入連接起來。但這裏必然會有重複的情況,比如gdi32.dll依賴於ntdll.dll,而kernel32.dll也依賴於ntdll.dll。不過,這不是fixup_imports考慮的問題,它只管接下來調用import_dll,後者將按照導入表imports[]中記錄的模塊,將它們一一裝入,併爲裝入的模塊中的所有函數分配好地址空間。

import_dll中與連接相關的代碼涉及到PE格式的細節,就不展開討論。總體上說,它會調用load_dll將目標模塊裝入,如果目標模塊還依賴於其他dll,那麼load_dll還將進一步裝入被依賴的dll,直到所有被依賴的dll都被裝入到進程空間爲止。之後,import_dll再完成從函數名到地址的解析。循環往復過後,一個可執行文件依賴的dll都將被裝入,用到的函數都將被譯成地址,用於程序執行過程中的調用。

前面有講到,EXE文件的加載也調用到了load_dll,但沒有詳細展開。該函數代碼如下圖所示:


首先,load_dll需要確定是否能找到需要裝載的dll,並且該dll是否已經被裝入,即調用find_dll_file。後者會根據進程參數塊(ppb)中指定的各個路徑,去查找是否有該dll的文件。如果找到,那麼pwm就不爲空,說明該dll已經被裝入,不必再重複裝載,返回即可。

get_load_order這個函數是專門爲wineconfig準備的。我們知道,用wineconfig是可以爲每個應用程序配置各種dll的使用搭配,有些dll可以使用built-in的,有些則可以用native的。這些信息最終都寫到註冊表中,而get_load_order就會從註冊表中讀取該dll的選用信息。

這裏一個有意思的插曲,is_fake_dll函數判斷找到的dll文件是否是假的dll。在~/.wine目錄生成後,其下的drive_c/windows/system32下面,就產生了很多PE格式的dll。這些dll通常只有幾k字節,內容也是一些簡單的指令,很顯然是無法使用的。至於Wine爲什麼要產生這些dll,還沒進行過深入的分析,但是如果find_dll_file找到的是這樣的dll,那麼下面就會當作沒有找到該dll處理。

下面的代碼就是根據loadorder的值選擇裝入什麼格式的dll,native的或者built-in的。從函數名就可以看出,load_native_dll是用於裝入native dll的,而load_builtin_dll是用於裝入built-in dll的。在開頭提到過,Wine通過winebuilder將所有built-indll都做成了類似PE格式的dll,那麼爲什麼這裏裝入時還有不同呢?

其實,built-in dll仍然是ELF格式,只是winebuilder刻意爲它們各自寫入了一個類似於PE格式中的header,這樣,在裝入過程中,仍然可以獲得dll的描述信息,寫到LDR_MODULE結構中。但是最終連接時,還是要根據ELF的頭部來解決最終的裝入連接問題。

因此可以看到,load_native_dll是調用Windows的 section系統調用將dll裝入(前面有講到),而load_builtin_dll則要通過wine_dll_load裝載。

至此dll的裝入連接過程就可以結束了。函數最後做了一個判斷,需要說明一下。其實這時候,再回顧一下前面的過程會發現,如果代碼執行到這個判斷中,即nts == STATUS_SUCCESS的話,那麼肯定是這個dll被裝入到進程空間的那次。如果一個dll被多次引用而進入到load_dll的話,從第二次開始就不會進入到這個判斷了。所以對同一個dll,只會執行一次if語句塊中的代碼。

參考:longene兼容內核論壇
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章