VA(虚拟地址)&RVA(相对虚拟地址)

input到链接器的文件称为对象文件 。 链接器产生一个Image文件 ,这个文件又被加载器用作input。

从“ 微软可移植可执行文件和通用目标文件格式规范

RVA(相对虚拟地址) 。 在一个图像文件中,将一个项目的地址加载到内存中,并从中减去图像文件的基地址。 一个项目的RVA几乎总是不同于它在磁盘上文件的位置(文件指针)。

在目标文件中,RVA没有意义,因为内存位置未分配。 在这种情况下,RVA将是一个部分(稍后在此表中描述)中的地址,在链接期间稍后将对其应用重定位。 为了简单起见,编译器应该将每个部分中的第一个RVA设置为零。

VA(虚拟地址) 。 与RVA相同,不同之处在于不会减去映像文件的基址。 该地址称为“VA”,因为Windows为每个进程创build独立于物理内存的独特VA空间。 几乎所有的目的,一个VA应该被视为一个地址。 VA并不像RVA那样可预测,因为加载器可能不会将图像加载到其首选位置。

即使阅读了这个,我仍然不明白。 我有很多问题。 任何人都可以用实际的方式来解释它吗? 请遵守所述的Object FileImage File术语。

我所知道的地址就是这样

  • 无论是在对象文件还是在图像文件中,我们都不知道确切的内存位置,
  • 生成目标文件时,汇编程序计算相对于.data.text (用于函数名称)部分的地址。
  • 以多个目标文件作为input的链接器生成一个图像文件。 在生成时,首先合并每个目标文件的所有部分,并在合并时重新计算相对于每个部分的地址偏移量。 而且,没有什么东西像全球抵消。

如果我所知道的东西有问题,请纠正我。

编辑:

看完弗朗西斯的回答后,我很清楚什么是Physical Address,VA&RVA以及它们之间的关系。

链接器在重定位期间必须计算所有variables和方法的RVA。 那么, (一个方法/variables的RVA的值)==(它从文件开始的偏移量) ? 一定是真的 但令人惊讶的是,它不是。 为什么这样?

我通过在c:\WINDOWS\system32\kernel32.dll上使用PEView检查了这一点,发现:

  1. RVA和FileOffset是相同的,直到部分的开始( .text是在这个DLL的第一部分)。
  2. .text开始.data.rsrc到最后一节( .reloc )的最后一个字节RVA和FileOffset是不同的。 也是第一段第一个字节的RVA总是显示为0x1000
  3. 有趣的是每个部分的字节在FileOffset中是连续的。 我的意思是另一节开始在一个节的最后一个字节的下一个字节。 但是如果我在RVA中看到同样的东西,那么这些是在一个段的最后一个字节的RVA和下一段的第一个字节之间的巨大差距。

我猜:

  1. 所有在第一个( .text here)部分之前的数据的字节都是“not”实际加载到进程的VA空间,这些字节的数据只是用来定位和描述这些部分。 他们可以被称为“元节资料”。

    由于它们没有加载到进程的VA空间中。 RVA这个术语的用法也没有意义,这就是为什么RVA == FileOffset为这些字节的原因。

  2. 以来,

    • RVA项只对那些实际加载到VA空间的字节有效。
    • .text.data.rsrc.reloc的字节是这样的字节。
    • 而不是从RVA 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,它的devise简单易于搬迁。 当加载可重定位模块(例如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在加载到内存中时与进程基地址相关,但是某些RVA可能与图像或目标文件中的“section”起始地址有关(您必须查看PE格式规范的详细信息)。 无论哪个,RVA都是相对于“一些”基地的VA。

总而言之,

  1. 物理内存地址是CPU所看到的
  2. 虚拟Addreess(VA)相对于物理地址,每个进程(由OSpipe理)
  3. RVA是相对于VA(文件基础或部分基础),每个文件(由链接器和加载器pipe理)

(编辑)关于爪子的新问题:

一个方法/variables的RVA值并不总是从文件开始的偏移量。 通常是相对于一些VA,这可能是一个默认的加载基地址或部分基地VA – 这就是为什么我说你必须检查PE格式规格的细节。

你的工具PEView试图显示每个字节的RVA来加载基地址。 由于各部分在不同的基础上开始,所以跨越路段时RVA可能会有所不同。

关于你的猜测,非常接近正确的答案:

  1. 通常我们不会讨论段之前的“RVA”,但PE头仍然会被加载,直到段头结束。 节标题和节体之间的间隙(如果有)将不会被加载。 你可以通过debugging器来检查。 更重要的是,当部分之间存在一定差距时,可能不会加载。

  2. 正如我所说,RVA只是“相对于一些VA”,不pipe它是什么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开始。这些部分在加载到内存中时应该是连续的,所以它们的RVA是连续的。 但是由于内存是由页面分配的,因此它将是页面大小的倍数(4096 = 0x1000字节)。 这就是为什么#2部分开始于1000 + C5000 = C6000(C5000来自C44C1)。

    为了提供内存映射,这些部分仍然必须以一定的大小(文件alignment大小 – 由链接器决定,在我上面的例子中是0x200 = 512字节),它控制PhysSize字段。 偏移意味着“偏移到物理PE文件的开始”。

    所以头文件占用文件的0x800字节(映射到内存时为0x1000),这是第1节的偏移量。 然后通过alignment其数据(c44c1字节),我们得到physsize C4600。 C4600 + 800 = C4E00,这正好是第二部分的偏移量。

    好吧,这与整个PE加载的东西有关,所以它可能有点难以理解…

(编辑)让我再次做一个新的简单总结。

  1. DLL / EXE(PE格式)文件中的“RVA”通常是相对于“内存中的加载基地址”(但并不总是 – 您必须阅读规范)
  2. PE格式包含一个“部分”映射结构,将物理文件内容映射到内存中。 所以RVA并不是真正的相对于文件偏移量。
  3. 要计算某个字节的RVA,必须在该部分find其偏移量并添加部分基础。

相对虚拟地址是从文件加载地址的偏移量。 可能最简单的方法是通过一个例子。 假设你有一个在地址1000h加载的文件(例如一个DLL)。 在那个文件中,你在RVA 200h有一个variables。 在这种情况下,该variables的VA(在DLL被映射到内存之后)是1200h(即DLL的1000h基地址加上variables的200h RVA(偏移量)。