段并不是目标文件的新内容,段只是程序加载角度的内容块,一个段可以包含多个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的集合。