首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

DLL 載入時機大揭秘:隱式連結 vs 顯式連結,誰先執行?EXE 還是 DLL?

     編輯:組合語言

核心問題:Windows 啟動程式時,先跑 EXE 還是先載入 DLL?

這是許多初學者困惑的地方。答案取決於連結方式:

  • 隱式連結:靜態連結 .lib,includelib + invoke Func。Windows 載入器會在 EXE 進入點執行前,先載入所有靜態相依的 DLL 並呼叫其 DllEntry (DLL_PROCESS_ATTACH)。
  • 顯式連結:程式碼中呼叫 LoadLibrary / GetProcAddress。EXE 先在進入點開始執行,直到執行到 LoadLibrary 那一行,DLL 才會被載入。

實驗設計:同一個 DLL,兩種呼叫方式對比

修改前一篇的 MyDll.asm 與測試程式,在 DllEntry 與 OutEdxEax 中加入 MessageBox 觀察執行順序。

修改版 MyDll.asm (關鍵片段)

.DATA
szText0 db "哈哈哈...MyDLL加載",0
szText1 db "MyDLL第一次插入進程的地址空間",0
szText2 db "MyDLL從進程的地址空間卸出",0
szText3 db "同一進程的新線程生成",0
szText4 db "同一進程的線程銷毀",0
szText5 db "MyDLL的函數OutEdxEax調用成功",0

.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
    .if reason == DLL_PROCESS_ATTACH
        invoke MessageBox, NULL, addr szText1, addr szText0, MB_OK
        mov eax, TRUE
        ret
    .elseif reason == DLL_PROCESS_DETACH
        invoke MessageBox, NULL, addr szText2, addr szText0, MB_OK
        mov eax, FALSE
        ret
    .elseif reason == DLL_THREAD_ATTACH
        invoke MessageBox, NULL, addr szText3, addr szText0, MB_OK
        mov reason, TRUE
        ret
    .elseif reason == DLL_THREAD_DETACH
        invoke MessageBox, NULL, addr szText4, addr szText0, MB_OK
        mov eax, FALSE
        ret
    .endif
    ret
DllEntry Endp

OutEdxEax proc uses ebx esi edi, lpString
    ; ... 轉換邏輯同上 ...
    invoke MessageBox, NULL, addr szText5, addr szText0, MB_OK ; 驗證函式被呼叫
    ret
OutEdxEax endp

測試程式 A:10.asm (隱式連結)

include mydll.inc
includelib mydll.lib

.DATA
szText db "哈哈哈...10.exe運行",0

.code
start:
    invoke MessageBox, NULL, addr szText, addr szText, MB_OK  ; EXE 入口第一個動作
    mov edx, 12345678h
    mov eax, 87654321h
    invoke OutEdxEax, addr CharOut
    invoke StdOut, addr CharOut
    invoke ExitProcess, NULL
end start

觀察結果順序:

  1. 彈出:「MyDLL第一次插入進程的地址空間」 (DLL 載入,DllEntry 執行)
  2. 彈出:「哈哈哈...10.exe運行」 (EXE start 執行)
  3. 彈出:「MyDLL的函數OutEdxEax調用成功」 (呼叫 DLL 函式)

結論:隱式連結下,DLL 載入與初始化 早於 EXE 進入點代碼。

測試程式 B:10_01.asm (顯式連結)

; 不 include mydll.inc, 不 includelib mydll.lib
.DATA
MyDllFileName db "e:\masm32\xlfancy\mydll.dll",0
CallFuncName  db "OutEdxEax",0

.data?
MyDLLHandle   dd ?
OutEdxEaxAddr dd ?

.code
start:
    invoke MessageBox, NULL, addr szText, addr szText, MB_OK  ; EXE 先跑

    invoke LoadLibrary, offset MyDllFileName  ; 這裡才載入 DLL
    or eax, eax
    jz ExitPro
    mov MyDLLHandle, eax

    invoke GetProcAddress, eax, offset CallFuncName
    or eax, eax
    jz FreeLib
    mov OutEdxEaxAddr, eax

    ; 手動準備參數呼叫
    lea eax, CharOut
    push eax
    mov edx, 12345678h
    mov eax, 87654321h
    call OutEdxEaxAddr

    invoke StdOut, addr CharOut
FreeLib:
    invoke FreeLibrary, MyDLLHandle
ExitPro:
    invoke ExitProcess, NULL
end start

觀察結果順序:

  1. 彈出:「哈哈哈...10_01.exe運行」 (EXE start 先執行)
  2. 彈出:「MyDLL第一次插入進程的地址空間」 (LoadLibrary 觸發 DLL 載入)
  3. 彈出:「MyDLL的函數OutEdxEax調用成功」 (呼叫函式)

結論:顯式連結下,EXE 進入點代碼 早於 DLL 載入。

深度解析:載入器內部機制

特性隱式連結顯式連結
載入時機進程建立初期,載入器解析 IAT執行期呼叫 LoadLibrary 時
相依性解析靜態:EXE 匯入表列出所有 DLL動態:程式碼決定載入誰
錯誤處理DLL 缺失 -> 進程啟動失敗 (彈系統錯誤框)LoadLibrary 返回 NULL,程式可自行處理
適用場景核心功能、必須存在的庫外掛、選用功能、延遲載入優化啟動速度
GetProcAddress 需要?不需要,連結器處理必要,取得函式位址

實務建議

  • 預設用隱式連結:程式碼乾淨、效能最好 (無額外呼叫開銷)、載入器保證相依性。
  • 需彈性時用顯式連結:插件架構、根據系統版本載入不同 DLL (如 XP 用 A.dll, Win10 用 B.dll)、減少啟動時記憶體佔用。
  • 混合策略:核心庫隱式連結,擴充功能顯式連結。

原作者的幽默自白

「以上都在本機編譯通過,可就是看不到結果。(折騰了半天,不只所以,JMP...) 不管了,沒犯路線錯誤就行...(馬馬乎乎,也敢貼出來...『砰』,怎麼有磚打到腦袋上?)」

這段話道出了底層開發的真實寫照:理論懂了,環境配對、路徑正確、權限足夠,才能跑出預期結果。實驗環境 (Win2000/XP 時代) 與現代 Windows (簽名驅動、強制驅動簽名、PPL 保護) 差異極大,請在虛擬機或測試機驗證。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved