可执行文件的ELF文件有一个程序(段)头和一个节头,可以通过
readelf -a
看到,这里是一个例子:
上面两张图分别是节头和程序(段)头。可以看出,一个段头是由若干个节头组成的,用于将程序加载到内存中。
是否只需要将.text、.rodata、.data、.bss节加载到内存中?
该段中的所有其他部分(例如第三段中的 .ctors、.dtors .jcr)是否用于对齐?
截面和段是完全不同的两个概念。节与存储在那里的数据的语义有关(即它将用于什么),并且一旦链接了程序或共享库(除非出于调试目的),实际上就无关紧要了。您甚至可以完全删除节标题(或用随机垃圾覆盖它们),程序仍然可以工作。
段(即程序头加载指令)是内核和/或动态链接器在加载程序时实际查看的内容。例如,在您的情况下,您有两个加载指令。第一个导致文件的前 4k(1 页)映射到地址 0x08048000,并指示实际上仅使用此映射的前 0x4b8 字节(其余部分是对齐)。第二个导致文件的前 8k(2 页)映射到地址 0x08049000。其中绝大多数是对齐。前 0xf14 字节不是加载指令的一部分(只是对齐),将被浪费。从 0x08049f14 开始,实际使用从文件映射的 0x108 字节,另外 0x10 字节(以达到 MemSize 0x118)由加载器(内核或动态链接器)填充零。这最多跨越 0x0804a02c(在第二个映射页面中)。第二个映射页面的其余部分未使用/浪费(但是
malloc
可能能够将其恢复以用作堆的一部分)。
最后,虽然根本不会使用节标题,但您的程序在运行时可能会使用许多不同节的内容。请注意,
.ctors
和.dtors
的地址范围位于第二个加载映射的开头,因此它们在运行时被程序映射和访问(运行时启动/退出代码将使用它们来运行全局构造函数和析构函数) ,如果使用带有 ctor/dtor 属性的 C++ 或“GNU C”代码)。另请注意,.data
从第二个映射页面中的地址 0x0804a00c 开始。这允许在应用重定位(程序头中的 RELRO 指令)后将第一页保护为只读。