ELF头和进程虚拟内存中的不同地址

ven*_*s.w 6 operating-system kernel memory-management process elf

我在linux下通过pmap看到进程映像:

08048000       0       4       0 r-x--  [my program]

08049000       0       4       4 rw---  [my program]
Run Code Online (Sandbox Code Playgroud)

上面的三个段是代码段、rodata段和数据段,它们都是按照PAGESIZE(4K)对齐的,但是当我输入命令objdump -h时,ELF头显示如下:

read-only code segment
Load off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
     filesz 0x00000448 memsz 0x00000448 flags r-x

read/write data segment
Load off 0x00000448 vaddr 0x08049448 paddr 0x08049448 align 2**12
     filesz 0x000000e8 memsz 0x00000104 flag rw-
Run Code Online (Sandbox Code Playgroud)

据说ELF头中,代码段和数据段分别从虚拟地址0x08048000、0x049448开始寻址,这与内存中的进程映像不同。我知道代码/数据段应该分配给不同的PAGESIZE,这可以给他们不同的保护权限。但是,如果真实的虚拟文件与 elf 二进制文件不同,程序如何执行呢?

Chr*_*odd 5

ELF 程序加载(以及通常从文件进行内存映射)的方式是基于页的。因此,涉及的地址、文件中的偏移量以及大小都必须是页面大小的倍数。

然而,程序加载器足够聪明,可以通过将未完全在页面边界开始或结束的部分四舍五入到页面边界来处理那些不完全在页面边界上开始或结束的部分,从而映射超出所需的部分。因此,将从文件中加载一些额外的数据来填充页面,但不应访问它,因此这应该不重要。

在您的示例中,代码段从偏移量 0x0 加载到地址 0x08048000,大小为 0x448。地址和偏移量是对齐的,因此只需将大小向上舍入为整页。数据段从偏移量 0x448 加载到 0x08049448 处。它们没有对齐,但兼容——加载程序将两者向下舍入为页面倍数(0x08049000 和 0x000)并映射到该页面中。请注意,这最终是文件中与代码段相同的页面,因此该页面加载到两个不同的地址,一个是只读的,另一个是读写非共享的。因此,代码和数据最终都在过程映像中的两个位置可见,但这并不重要 - 代码最终在 0x8048000..0x8048447 处 rx,数据最终在 0x8049448..0x804954b 处 rw-,仅此而已很重要。