簡體   English   中英

VA(虛擬地址)和RVA(相對虛擬地址)

[英]VA (Virtual Address) & RVA (Relative Virtual Address)

作為鏈接器輸入的文件稱為對象文件 鏈接器生成一個Image文件 ,該文件又被加載器用作輸入。

來自“ Microsoft可移植可執行文件和通用對象文件格式規范 ”的模糊

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

在目標文件中,RVA的意義不大,因為未分配內存位置。 在這種情況下,RVA將是一個部分內的地址(在本表后面描述),稍后在鏈接期間將重定位應用於該地址。 為簡單起見,編譯器應該只將每個部分中的第一個RVA設置為零。

VA(虛擬地址) 與RVA相同,但不刪除圖像文件的基址。 該地址稱為“VA”,因為Windows為每個進程創建了一個獨立的VA空間,與物理內存無關。 對於幾乎所有目的,VA應僅被視為地址。 VA不像RVA那樣可預測,因為加載程序可能無法將圖像加載到其首選位置。

即使在讀完這篇文章之后,我仍然沒有得到它。 我有很多問題。 任何人都可以用實際的方式解釋它。 請遵守所述的Object FileImage File術語。

我所知道的就是地址

  • 在目標文件和圖像文件中,我們都不知道確切的內存位置,所以
  • 生成對象文件時的匯編程序計算相對於部分.data.text (用於函數名稱)的地址。
  • 鏈接器將多個目標文件作為輸入生成一個Image文件。 在生成時,它首先合並每個目標文件的所有部分,並在合並它時重新計算相對於每個部分的地址偏移量。 並且,沒有像全球抵消那樣的東西。

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

編輯:

在閱讀弗朗西斯給出的答案之后,我清楚了解物理地址,VA和RVA是什么以及它們之間的關系。

所有變量和方法的RVAs必須在重定位期間由鏈接器計算。 那么, (方法/變量的RVA值)==(它從文件開頭的偏移量) 一定是真的。 但令人驚訝的是,它沒有。 為什么這樣?

我在c:\\WINDOWS\\system32\\kernel32.dll上使用PEView檢查了這一點,發現:

  1. RVA&的FileOffset是相同的,直到段的開始。( .text是在這個dll第一部分)。
  2. .text.data.rsrc直到最后一節的最后一個字節( .reloc )RVA和FileOffset是不同的。 並且第一部分的第一個字節的RVA“始終”顯示為0x1000
  3. 有趣的是,每個部分的字節在FileOffset中是連續的。 我的意思是另一個部分從一個部分的最后一個字節的下一個字節開始。 但是如果我在RVA中看到同樣的事情,那么這是一個區段的最后一個字節和下一節的第一個字節之間的巨大差距。

我猜:

  1. 所有,第一個( .text here)部分之前的數據字節“不”實際加載到進程的VA空間中,這些數據字節僅用於定位和描述這些部分。 它們可以被稱為“元部分數據”。

    因為它們沒有加載到VA空間的過程中。 術語RVA的使用也沒有意義,這就是RVA == FileOffset這些字節的原因。

  2. 以來,

    • RVA術語僅對將實際加載到VA空間的那些字節有效。
    • .text.data.rsrc.reloc的字節是這樣的字節。
    • 而不是從RVA 0x00000開始,PEView軟件從0x1000開始。
  3. 我無法理解為什么第3次觀察。 我無法解釋。

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

例如,可能的物理內存地址(由CPU可見):

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

操作系統可能有一個映射表:

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

當在基地址0x10000000處加載到進程A時,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).

當在基地址0x32000000加載到進程B時,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在加載到內存時相對於進程基址,但某些RVA可能與圖像或目標文件中的“部分”起始地址相關(您必須檢查PE格式規范的詳細信息)。 無論哪個,RVA都與“某些”基礎VA相關。

總結一下,

  1. 物理內存地址是CPU看到的
  2. 虛擬地址(VA)與每個進程的物理地址相關(由OS管理)
  3. RVA與VA(文件庫或部分庫)相關,每個文件(由鏈接器和加載器管理)

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

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

您的工具,PEView正在嘗試顯示每個字節的RVA以加載基址。 由於這些部分從不同的基部開始,因此當穿過部分時RVA可能變得不同。

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

  1. 通常我們不會在節之前討論“RVA”,但是PE標題仍將被加載到節標題的末尾。 節標題和節主體(如果有)之間的間隙不會被加載。 您可以通過調試器進行檢查。 但是,如果各部分之間存在一些差距,則可能無法加載。

  2. 正如我所說,RVA只是“相對於某些VA”,無論它是什么VA(盡管在談論PE時,VA通常是指負載基地址)。 當您閱讀PE格式規范時,您可能會發現一些“RVA”,它與某些特殊地址相關,如資源起始地址。 來自0x1000的PEView列表RVA是因為該部分從0x1000開始。 為什么是0x1000? 因為鏈接器為PE頭留下了0x1000字節,所以RVA從0x1000開始。

  3. 您錯過的是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字節的文件(當映射到內存時為0x1000),這是第1節的偏移量。 然后通過對齊其數據(c44c1字節),我們得到physsize C4600。 C4600 + 800 = C4E00,這正是第二部分的偏移量。

    好的,這與整個PE加載有關,所以可能有點難以理解......

(編輯)讓我再次制作一個新的簡單摘要。

  1. DLL / EXE(PE格式)文件中的“RVA”通常相對於“內存中的加載基址”(但並非總是如此 - 您必須閱讀規范)
  2. PE格式包含“部分”映射結構,用於將物理文件內容映射到內存中。 因此RVA並不是真正相對於文件偏移量。
  3. 要計算某個字節的RVA,您必須在該部分中找到其偏移量並添加部分基數。

相對虛擬地址是與加載文件的地址的偏移量。 獲得這個想法的最簡單方法可能就是一個例子。 假設您有一個在地址1000h加載的文件(例如,DLL)。 在該文件中,您有一個RVA 200h的變量。 在這種情況下,該變量的VA(在DLL映射到存儲器之后)是1200h(即DLL的1000h基址加上變量的200h RVA(偏移)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM