段并不是目标文件的新内容,段只是程序加载角度的内容块,一个段可以包含多个section,也可以包含头。段通过Program header table索引。
通常,可执行文件和共享库目标文件才具有段。
Program header table是一个数组,每个成员描述了一个段。ELF头中的e_phoff存储了表在文件中的偏移。e_phentsize存储了表中一个条目的大小。e_phnum存储了有多个条目。Program header的结构体表示如下:
1 2 3 4 5 6 7 8 9 10 | typedef struct elf32_phdr { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } Elf32_Phdr; |
p_type,段的类型。
p_offset,段在目标文件中的偏移。
p_vaddr,段的起始虚拟地址。
p_paddr,段的物理地址,和操作系统相关。
p_filesz,段在文件中的大小,可能是0。
p_memsz,段加载到内存后的大小,可能是0。
p_flags,段相关的标识。
p_align,段在文件和内存中的对齐要求,因为p_vaddr可能并不是最后加载到内存的地址,所以p_align给出对齐的需求。
p_type字段,给出了段的类型,由于段是用来加载进程的,所以类型与加载处理紧密相关,如下:
Name | Value |
PT_NULL | 0 |
PT_LOAD | 1 |
PT_DYNAMIC | 2 |
PT_INTERP | 3 |
PT_NOTE | 4 |
PT_SHLIB | 5 |
PT_PHDR | 6 |
PT_LOPROC | 0X70000000 |
PT_HIPROC | 0X7FFFFFFF |
PT_NULL,program header没有被使用,其它字段没有意义。
PT_LOAD,可加载段,将文件p_offset处p_filesz大小的文件内容加载到内存,p_memsz可以比p_filesz大,多出的内容初始化为0。显而易见,这是为.bss section设计的。
PT_DYNAMIC,描述动态链接信息。
PT_INTERP,这个段包含一个'\0'结尾的字符串。这个字符串是动态加载器的路径。
PT_NOTE,包含辅助信息。
PT_SHLIB,保留。
PT_PHDR,指定Program header table本身的大小和位置,这个段不能出现多次,而且只有在Program header table是内存镜像的一部分时,才能出现,而且必须在任何可加载段前。
一些厂商或系统创建者,需要标记一些特殊的信息,Note section就是做这个用的。ELF标准没有规范note的内容,但是给出了note的格式。
namesz |
descsz |
type |
name |
desc |
。。。 |
上述格式中,每个条目至少有4个字节,不足的填充0。
对于可执行文件和共享目标文件,有下述特殊的section name,一部分在之前sections章节的已经描述了。
name |
.bss .comment .data .data1 .debug .dynamic .dynstr .dynsym .fini .got .hash .init .interp .line .note .plt .relname .relaname .rodata .rodata1 .shstrtab .strtab .symtab .text |
新的section在后续相关文章描述。
通过readelf,读取一个可执行文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4 INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1 [Requesting program interpreter: /lib/ld-linux.so.2] LOAD 0x000000 0x08048000 0x08048000 0x00604 0x00604 R E 0x1000 LOAD 0x000f08 0x08049f08 0x08049f08 0x0011c 0x00120 RW 0x1000 DYNAMIC 0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW 0x4 NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4 GNU_EH_FRAME 0x0004e4 0x080484e4 0x080484e4 0x00034 0x00034 R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 GNU_RELRO 0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R 0x1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07 08 .init_array .fini_array .jcr .dynamic .got |
可以发现,不同的段可以有重叠的区域,因为不同的段有不同的用途。段也只是各个section和header的集合。