Win32動態連線庫基址重置技術 (轉)

worldblog發表於2007-12-29
Win32動態連線庫基址重置技術 (轉)[@more@]

理論

要載入動態連結庫操作必須完成以下各步:

  1. 在上定位動態連結庫可。
  2. 仔細檢視已經載入進應用地址空間中的動態連結庫列表,判斷動態連結庫是否已經載入了。
  3. 為動態連結庫分配駐留,並將動態連結庫二進位制檔案對映到記憶體中(在 NT中,對映跨越了段)。
  4. 為使動態連結庫正常執行,執行一系列必要的處理(例如,解析動態連結庫中做過的修正,等等)。

不同的引數決定了動態連結庫的載入時間不同。以下給出了需要考慮的各種因素,事實上可能還有一些因素也會影響到動態連結庫的載入時間:

  • 底層軟:本身的速度以及執行哪一種。
  • 當前系統和應用程式的狀態:在虛擬記憶體中系統的緊密情況,以及動態連結庫是否可以被載入在首選基地址。
  • 動態連結庫本身:動態連結庫本身有多大,動態連結庫中有多少位置需要修正(結合兩者綜合考慮),此動態連結庫是否隱式連結到另一個同樣需要載入的動態連結庫中。

由上述分析可知,對某一動態連結庫進行基址重置絕不是影響動態連結庫載入時間的唯一決定因素。在本文中,作者使用了很多資料,說明了動態連結庫載入時間的變化範圍以及應用程式可能對載入時間的影響程度。

讀者應該注意到,對某一動態連結庫進行基址重置,不僅可能造成載入時間的大幅度增加,還需要增加頁檔案(pagefile)的開銷。載入動態連結庫的第一步,需要建立區段物件(section ),區段物件是由動態連結庫可執行檔案支援的、記憶體中的一個相鄰的區段。一旦動態連結庫的某一頁被從應用程式工作集中移去了,在下一次訪問此頁時,作業系統就會從動態連結庫可執行檔案中重新載入這一頁。

當然,當動態連結庫進行基址重置時,這一策略不再起作用,這主要是因為包含重定位地址的頁與動態連結庫可執行檔案對映中的相應頁不同。因此,一旦在載入可執行檔案時,作業系統企圖修正地址,就會複製相應的頁(由於區段是透過COPY_ON_WRITE標誌開啟的)。在複製中進行的所有的修改,作業系統都會記住,從現在開始,頁將在系統頁檔案中換入換出,而不再在可執行映象中進行頁。

採用這種機制,潛在的命中有兩種:首先,每個包含需要重定位地址的頁都佔用一頁系統頁檔案(其結果是,減少了所有應用程式可用的虛擬記憶體數);另外,由於作業系統執行動態連結庫頁中的第一次修正操作,新的頁必須從頁檔案中分配,並將整個頁複製下來。

儘管掃描動態連結庫重定位區段以及進行記憶體修正的演算法都有很高的,執行修正操作還是會增加動態連結庫的載入時間。(跨段操作的複雜性是需要修復地址數量的線性。)

地址修正

關於動態連結庫基址重置的一類經常問到的問題是,“地址修正究竟是什麼含義?程式設計師是否有辦法調整程式碼,以減少可執行程式中的地址修正?”對於這兩個問題的回答是,這一切在很大程度上依賴於可執行程式建立在哪一種平臺上。在本文中,我們將平臺限制在 386,、486、和奔騰(Pentium)上,討論相應的可執行程式。(注意:為其他平臺建立的可執行程式,相應的地址修復概念與本文討論的概念不同。)

在386、486、或奔騰處理器上,兩種情況可能導致某地址被標識為“可重定位”的狀態:一是靜態物件(static objects),另一種情況是絕對跳轉(absolute jumps)。

首先,如果動態連結庫引用了靜態物件,就會使用物件的絕對地址(假設動態連結庫被載入到首選地址中)。例如,在如下的程式碼段中:

LPSTR lpName="Name";

動態連結庫的載入程式就會將“Name”字串分配到動態連結庫資料段中,並將此字串的起始地址填寫到lpName變數對應的位置中去。如果由於DLL不能被載入到基地址而使得“Name”字串必須重定位,lpName必須相應地進行調整。注意,在這種情況下,程式碼段中每個引用lpName的變數也必須作相應的地址修正。

可以重定位的物件包括文字字串(例如,在上例中的“Name”字串)、任何一種型別的全域性或靜態資料、包含靜態分配的C++物件。應該注意到,特別是在C++中,可能存在著許多從一個靜態物件到另一個靜態物件的交叉。未初始化的資料不需要在重定位過程中修正地址,但指向未初始化靜態資料的索引需要進行地址修正。

在i386可執行程式碼中,另一類可以進行重定位的項是絕對跳轉和函式,包括系統函式呼叫。注意到程式開發人員很難透過修改程式程式碼來避免地址重定位,唯一可以採用的辦法就是縮減靜態分配資料的數目。要縮減靜態分配資料,一種辦法就是儘量避免使用名字進行資源索引,而應該透過座標進行資源索引(因為,程式設計師在程式碼中顯式使用的每一個名字都會自動變成一個可重定位的項)。

儘管如此,作者並不建議程式開發人員帶著減少載入時間這一特殊目的開發動態連結庫程式碼,除非存在以下兩種情況:(1)靜態分配物件的資料可以大幅度減少;(2)程式設計師依據此種方式進行時,不會影響其他程式設計中需要考慮到的因素。

除此之外,程式開發人員還可以透過簡單的方法,減少載入時間。例如,可以將所有可以重新定位的資料集中到幾頁中。很顯然,如果動態連結庫需要進行基址重置,每頁含有一個重定位項的兩頁,都需要有頁檔案來支援。如果所有的重定位項都出現在同一頁中,只有一頁需要頁檔案來支援,因此只有一頁會受到影響。在必要的情況下,讀者可以使用pragma (data_seg,資料段)偽指令,確保儘可能多的可重定位項被分配到儘可能少的頁中去。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-996100/,如需轉載,請註明出處,否則將追究法律責任。

相關文章