[英]VA (Virtual Address) & RVA (Relative Virtual Address)
作為鏈接器輸入的文件稱為對象文件 。 鏈接器生成一個Image文件 ,該文件又被加載器用作輸入。
來自“ Microsoft可移植可執行文件和通用對象文件格式規范 ”的模糊
RVA(相對虛擬地址) 。 在圖像文件中,將項目的地址加載到內存后,從中減去圖像文件的基地址。 項目的RVA幾乎總是與其在磁盤上的文件位置(文件指針)不同。
在目標文件中,RVA的意義不大,因為未分配內存位置。 在這種情況下,RVA將是一個部分內的地址(在本表后面描述),稍后在鏈接期間將重定位應用於該地址。 為簡單起見,編譯器應該只將每個部分中的第一個RVA設置為零。
VA(虛擬地址) 。 與RVA相同,但不刪除圖像文件的基址。 該地址稱為“VA”,因為Windows為每個進程創建了一個獨立的VA空間,與物理內存無關。 對於幾乎所有目的,VA應僅被視為地址。 VA不像RVA那樣可預測,因為加載程序可能無法將圖像加載到其首選位置。
即使在讀完這篇文章之后,我仍然沒有得到它。 我有很多問題。 任何人都可以用實際的方式解釋它。 請遵守所述的Object File
和Image File
術語。
我所知道的就是地址
.data
和.text
(用於函數名稱)的地址。 如果我知道有什么問題,請糾正我。
編輯:
在閱讀弗朗西斯給出的答案之后,我清楚了解物理地址,VA和RVA是什么以及它們之間的關系。
所有變量和方法的RVAs必須在重定位期間由鏈接器計算。 那么, (方法/變量的RVA值)==(它從文件開頭的偏移量) ? 一定是真的。 但令人驚訝的是,它沒有。 為什么這樣?
我在c:\\WINDOWS\\system32\\kernel32.dll
上使用PEView檢查了這一點,發現:
.text
是在這個dll第一部分)。 .text
到.data
, .rsrc
直到最后一節的最后一個字節( .reloc
)RVA和FileOffset是不同的。 並且第一部分的第一個字節的RVA“始終”顯示為0x1000
我猜:
所有,第一個( .text
here)部分之前的數據字節“不”實際加載到進程的VA空間中,這些數據字節僅用於定位和描述這些部分。 它們可以被稱為“元部分數據”。
因為它們沒有加載到VA空間的過程中。 術語RVA的使用也沒有意義,這就是RVA == FileOffset
這些字節的原因。
以來,
.text
, .data
, .rsrc
, .reloc
的字節是這樣的字節。 0x00000
開始,PEView軟件從0x1000
開始。 我無法理解為什么第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相關。
總結一下,
(編輯)關於爪子的新問題:
方法/變量的RVA值並不總是偏離文件的開頭。 它們通常相對於某些VA,它可能是默認的加載基地址或部分基礎VA - 這就是為什么我說你必須檢查PE格式規范的細節。
您的工具,PEView正在嘗試顯示每個字節的RVA以加載基址。 由於這些部分從不同的基部開始,因此當穿過部分時RVA可能變得不同。
關於你的猜測,他們非常接近正確的答案:
通常我們不會在節之前討論“RVA”,但是PE標題仍將被加載到節標題的末尾。 節標題和節主體(如果有)之間的間隙不會被加載。 您可以通過調試器進行檢查。 但是,如果各部分之間存在一些差距,則可能無法加載。
正如我所說,RVA只是“相對於某些VA”,無論它是什么VA(盡管在談論PE時,VA通常是指負載基地址)。 當您閱讀PE格式規范時,您可能會發現一些“RVA”,它與某些特殊地址相關,如資源起始地址。 來自0x1000的PEView列表RVA是因為該部分從0x1000開始。 為什么是0x1000? 因為鏈接器為PE頭留下了0x1000字節,所以RVA從0x1000開始。
您錯過的是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加載有關,所以可能有點難以理解......
(編輯)讓我再次制作一個新的簡單摘要。
相對虛擬地址是與加載文件的地址的偏移量。 獲得這個想法的最簡單方法可能就是一個例子。 假設您有一個在地址1000h加載的文件(例如,DLL)。 在該文件中,您有一個RVA 200h的變量。 在這種情況下,該變量的VA(在DLL映射到存儲器之后)是1200h(即DLL的1000h基址加上變量的200h RVA(偏移)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.