作为链接器输入的文件称为对象文件。 链接器生成图像文件,然后由加载器用作输入。
来自“ Microsoft可移植可执行文件和通用对象文件格式规范”的简介
RVA(相对虚拟地址)。在图像文件中,项目的地址 在它加载到内存后,用 图像文件的基地址 减去它。物品的RVA 几乎总是与它不同 在磁盘上的文件中的位置(文件 指针)。
在目标文件中,RVA较少 因为记忆位置有意义 未分配。在这种情况下,一个RVA 将是一个部分内的地址 (在本表后面描述),到 稍后应用重定位 在链接期间。为简单起见,a 编译器应该只设置第一个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
在这里)
部分是“不”实际加载
进入VA空间的过程,这些
数据字节只是用来
定位&描述这些部分。
他们可以被称为“meta section”
数据“。
因为他们没有加载到VA
过程空间。用法
这个术语RVA也没有意义
RVA == FileOffset
这些字节的原因。
因为,
.text
,.data
,.rsrc
,.reloc
的字节数是这样的字节。0x00000
开始,PEView软件正在启动
来自0x1000
。我无法理解为什么第3次观察。我无法解释。
答案 0 :(得分:62)
大多数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 format spec的详细信息。
您的工具,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加载有关,所以可能有点难以理解......
(编辑)让我再次制作一个新的简单摘要。
答案 1 :(得分:11)
相对虚拟地址是与加载文件的地址的偏移量。获得这个想法的最简单方法可能就是一个例子。假设您有一个在地址1000h加载的文件(例如,DLL)。在该文件中,您有一个RVA 200h的变量。在这种情况下,该变量的VA(在DLL映射到内存之后)是1200h(即DLL的1000h基址加上变量的200h RVA(偏移)。