2010-01-31 367 views
39

輸入到鏈接器的文件被稱爲目標文件。 鏈接器產生一個圖像文件,該文件又被加載器用作輸入。VA(虛擬地址)&RVA(相對虛擬地址)

從A導語 「Microsoft可移植可執行文件和通用對象文件格式規範

RVA(相對虛擬地址)。在圖像文件中, 被加載到內存後的地址,其中 的圖像文件的基地址被從中減去 。項目 的RVA幾乎總是不同於其在磁盤上的文件內的 位置(文件 指針)。

在一個目標文件中,RVA小於 有意義,因爲未分配內存位置 。在這種情況下,RVA 將是 (稍後在此表中描述)內的地址,至 ,其中重新定位稍後在鏈接期間應用 。爲簡單起見, 編譯器應將每個部分中的第一個RVA 設置爲零。

VA(虛擬地址)。與RVA相同,但不會減去 圖像文件的基址。 地址被稱爲「VA」,因爲 Windows爲每個進程創建獨立的VA空間 ,獨立於 物理內存。幾乎所有的 目的,一個VA應該被認爲是 只是一個地址。 VA不是可作爲RVA預測的 ,因爲 加載程序可能不會在其 首選位置加載圖像。

即使在閱讀本文後,我仍然不明白。我有很多問題。任何人都可以用實際的方式解釋它嗎?請遵守所述的術語Object File & Image File

所有我知道的地址,是

  • 無論在目標文件,也沒有在圖像文件,我們不知道確切的內存位置的話,
  • 彙編而產生目標文件的地址計算相對於部分.data & .text(對於函數名稱)。
  • 將多個目標文件作爲輸入的鏈接器生成一個圖像文件。在生成時,它首先合併每個對象文件的所有部分,並在合併時重新計算相對於每個部分的地址偏移量。而且,沒有任何東西像全球偏移。

如果我知道什麼有問題,請糾正我。

編輯:

讀給弗朗西斯的回答後,我很清楚什麼物理地址,VA & RVA,什麼是它們之間的關係。

所有變量的RVA &方法在重定位期間必須由鏈接器計算。所以,(方法/變量的RVA的值)==(它從文件開始的偏移量)?一定是真的。但令人驚訝的是,它不是。爲什麼這樣?

我通過使用c:\WINDOWS\system32\kernel32.dllPEView檢查這一點,並發現:

  1. RVA &的FileOffset是相同的,直到第年初(.text是在這個dll第一部分)。
  2. .text開始到.data,.rsrc直到最後一節的最後一個字節(.reloc)RVA & FileOffset是不同的。 &也是第一節的第一個字節的RVA是「始終」顯示爲0x1000
  3. 有趣的是每個節的字節在FileOffset中是連續的。我的意思是另一節開始於節的最後一個字節的下一個字節。但是,如果我在RVA中看到同樣的事情,則這些是節的最後一個字節的RVA與下一節的第一個字節之間的巨大差距。

我的猜測:

  1. 所有,數據的第一個(.text這裏)之前已 字節 節「而不是」實際加載 到進程的VA空間,這些 數據字節僅用於 定位&描述這些部分。 它們可以被稱爲「meta section data」。

    因爲它們沒有裝入VA 過程的空間。 術語RVA的用法也沒有意義,這是 之所以爲RVA == FileOffset這些字節。

  2. 由於,

    • RVA術語有效期爲只有那些將被實際裝載 到VA空間字節。
    • .text,.data,.rsrc,.reloc的字節是這樣的字節。
    • 取代從RVA開始0x00000 PEView軟件從0x1000開始 。
  3. 我不明白爲什麼第三次觀察。我無法解釋。

回答

54

大多數Windows進程(*。 exe)被加載到(用戶模式)內存地址0x00400000,這就是我們所說的「虛擬地址」(VA) - 因爲它們只對每個進程可見,並且將被操作系統轉換爲不同的物理地址(可見內核/驅動器層)

例如,一個可能的物理存儲器地址(可見由CPU):

0x00300000 on physical memory has process A's main 
0x00500000 on physical memory has process B's main 

並且OS可以具有mappi NG表:

process A's 0x00400000 (VA) = physical address 0x00300000 
process B's 0x00400000 (VA) = physical address 0x00500000 

然後,當你嘗試在進程A閱讀0x004000000,你會得到它位於物理內存0x00300000的內容。

關於RVA,它的設計簡單易於搬遷。加載可重定位模塊(例如DLL)時,系統將嘗試將其滑過整個進程內存空間。所以在文件格式中,它會提供一個「相對」地址來幫助計算。

例如,DLL C可具有此地址:

RVA 0x00001000 DLL C's main entry 

當被加載到處理A在基地址0x10000000的,C的主入口成爲

VA = 0x10000000 + 0x00001000 = 0x10001000 
(if process A's VA 0x10000000 mapped to physical address was 0x30000000, then 
    C's main entry will be 0x30001000 for physical address). 

當被加載到處理B在基地址0x32000000處,C的主要條目變爲

VA = 0x32000000 + 0x00001000 = 0x32001000 
(if process B's VA 0x32000000 mapped to physical address was 0x50000000, then 
    C's main entry will be 0x50001000 for physical address). 

通常,當加載到內存中時,年齡文件與進程基地址相關,但某些RVA可能與圖像或目標文件中的「部分」起始地址有關(您必須檢查PE格式規範的詳細信息)。無論哪個,RVA都是相對於「某些」基礎VA的。

總之,

  1. 物理內存地址是什麼CPU看到
  2. 虛擬Addreess(VA)是相對於物理地址,每個進程(由OS管理)
  3. RVA是相對於VA(文件基本或部分基),每個文件(通過鏈接器和加載)管理

(編輯)關於爪的新問題:

方法/變量的RVA值並不總是偏離文件的開頭。這通常是相對於一些VA,這可能是一個默認的加載基地址或節基VA - 這就是爲什麼我說你必須檢查PE格式規格的細節。

您的工具PEView正試圖顯示每個字節的RVA來加載基地址。由於各部分在不同的基礎上開始,因此在穿越路段時RVA可能會有所不同。

關於你的猜測,則非常接近正確答案:

  1. 通常我們會不段之前討論「RVA」,但PE頭依然會加載,直到節結束頭。節標題和節體之間的間隙(如果有)將不會被加載。你可以通過調試器來檢查。但是,當各部分之間存在一些差距時,它們可能不會被加載。如我所說,RVA只是「相對於某些VA」,無論它是什麼VA(雖然在談到PE時,VA通常是指負載基地址)。當你閱讀PE格式規範時,你可能會發現一些與某些特殊地址相關的「RVA」,比如資源起始地址。來自0x1000的PEView列表RVA是因爲該部分從0x1000開始。爲什麼是0x1000?由於鏈接器爲PE標頭留下了0x1000字節,因此RVA從0x1000開始。

  2. 你已經錯過了什麼是PE加載階段「一節」的概念。 PE可能包含多個「部分」,每個部分都映射到一個新的起始VA地址。例如,這是從WIN7 KERNEL32.DLL傾倒:

    # Name VirtSize RVA  PhysSize Offset 
    1 .text 000C44C1 00001000 000C4600 00000800 
    2 .data 00000FEC 000C6000 00000E00 000C4E00 
    3 .rsrc 00000520 000C7000 00000600 000C5C00 
    4 .reloc 0000B098 000C8000 0000B200 000C6200 
    

    有一個看不見的「0頭RVA = 0000,SIZE = 1000」,它被迫的.text在RVA 1000開始,這些部分應是連續的時被加載到內存中(即VA),因此它們的RVA是連續的。但由於內存是由頁面分配的,因此它將是頁面大小的倍數(4096 = 0x1000字節)。這就是爲什麼#2部分開始於1000 + C5000 = C6000(C5000來自C44C1)。

    爲了提供內存映射,這些部分仍然必須按照一定的大小(文件對齊大小 - 由鏈接器決定,在我上面的示例中它是0x200 = 512字節),它控制PhysSize字段。偏移意味着「偏移到物理PE文件開始」。

    所以標頭佔據的0x800個字節的文件(和0×1000當被映射到存儲器),其的部分#1的偏移。然後通過對齊其數據(c44c1字節),我們得到physsize C4600。 C4600 + 800 = C4E00,這正是第二部分的偏移量。

    OK,所以它可能是一個很難理解這是關係到整個PE裝載的東西...

(編輯)讓我再次做出一個新的簡單的總結。

  1. 的「RVA」在DLL/EXE(PE格式)文件通常是相對於「內存中加載基址」(但不總是 - 你必須閱讀的規範)
  2. 的PE格式包含「部分」映射結構將物理文件內容映射到內存中。所以RVA並不是真的與文件偏移相關。
  3. 要計算某個字節的RVA,必須在該部分中找到其偏移量並添加部分基礎。
+0

謝謝你這麼清楚的解釋。只有一個建議,你可以在RVA例子中把'Process A'的名字改成'Process X'。因爲DLL A和進程A都可能導致混淆。 :) – claws 2010-02-01 06:01:38

+1

我已經將DLL A更改爲DLL C.還對文本的某些部分進行了細化以防止混淆。 – Francis 2010-02-01 06:07:06

+0

我已經擴展了我的查詢。你可以看看嗎? – claws 2010-02-01 08:59:32

10

相對虛擬地址是從文件加載地址的偏移量。可能最簡單的方法就是用一個例子。假設您有一個在地址1000h加載的文件(例如,一個DLL)。在那個文件中,你在RVA 200h有一個變量。在那種情況下,該變量的VA(在DLL被映射到內存之後)是1200h(即,DLL的1000h基地址加上變量的200h RVA(偏移量)

+1

>>'你有RVA可變200h.'這是什麼相對? dll的開始(dll的第一個字節)還是相對於它所在的部分? (.data)在這種情況下。 – claws 2010-01-31 07:08:30

+3

@claws:大多數RVA都是相對於文件的開頭給出的,但偶爾(特別是在查看目標文件而不是可執行文件時),您將看到基於該部分的RVA。 – 2010-01-31 16:29:10