赞
踩
一个可执行文件或共享目标文件的程序头表(program header table)是一个数组,数组中的每一个元素称为“程序头(program header)”,每一个程序头描述了一个“段(segment)”或者一块用于准备执行程序的信息。
在elf header中e_phentsize 和 e_phnum 成员指定了程序头的大小和数量。
每个segment的结构如下:
- struct Elf64_Phdr {
- Elf64_Word p_type;
- Elf64_Word p_flags;
- Elf64_Off p_offset;
- Elf64_Addr p_vaddr;
- Elf64_Addr p_paddr;
- Elf64_Xword p_filesz;
- Elf64_Xword p_memsz;
- Elf64_Xword p_align;
- };
描述的段的类型。
- #define PT_NULL 0
- #define PT_LOAD 1
- #define PT_DYNAMIC 2
- #define PT_INTERP 3
- #define PT_NOTE 4
- #define PT_SHLIB 5
- #define PT_PHDR 6
- #define PT_TLS 7
- #define PT_NUM 8
- #define PT_LOOS 0x60000000
- #define PT_GNU_EH_FRAME 0x6474e550
- #define PT_GNU_STACK 0x6474e551
- #define PT_GNU_RELRO 0x6474e552
- #define PT_GNU_PROPERTY 0x6474e553
- #define PT_LOSUNW 0x6ffffffa
- #define PT_SUNWBSS 0x6ffffffa
- #define PT_SUNWSTACK 0x6ffffffb
- #define PT_HISUNW 0x6fffffff
- #define PT_HIOS 0x6fffffff
- #define PT_LOPROC 0x70000000
- #define PT_HIPROC 0x7fffffff
结尾案例会说其中一部分。
此数据成员给出了本段内容的权限。
标志 | 值 | 权限 | 实际权限 |
PF_None | 0 | 无 | 无 |
PF_Exec | 1 | 可执行 | 可读,可执行 |
PF_Write | 2 | 可写 | 可读,可写,写执行 |
PF_Write_Exec | 3 | 可写,可执行 | 可读,可写,写执行 |
PF_Read | 4 | 可读 | 可读,可执行 |
PF_Read_Exec | 5 | 可读,可执行 | 可读,可执行 |
PF_Read_Write | 6 | 可读,可写 | 可读,可写,写执行 |
PF_Read_Write_Exec | 7 | 可读,可写,写执行 | 可读,可写,写执行 |
对上表总结一下,有两条:
1、可读与可执行是通用的,有其中一个就等于也有了另一个
2、可写权限是最高权限,可以覆盖另外两个,有了可写权限,所有权限就都有了
段(segment)内容的开始位置相对于文件开头的偏移量,在010Edit解析后如下所示
其中(p_offset)为0x0
此数据成员给出本段内容的开始位置在进程空间中的虚拟地址。
听着感觉要长脑子了,小案例来喽~
可以看到在文件中的位置是0x3C78
但是在内存中时,对于so基址的偏移是0x4C78。
一般与p_vaddr的值相同,貌似无用。
此数据成员给出本段内容在文件中的大小,单位是字节。
p_offset 偏移后的位置 加上 p_filesz 就是本segment 在文件中的内容
此数据成员给出本段内容在内存中的大小,单位是字节。
在内存中 p_vaddr 对于基址偏移后的位置 加上 p_memsz 就是本segment 在内存中的内容
对于可装载的段来说,其 p_vaddr 和 p_offset 的值至少要向内存页面大小对齐。
此数据成员指明本段内容如何在内存和文件中对齐。
如果该值为 0 或 1,表明没有对齐要求;
否则,p_align 应该是一个正整数,并且是 2 的幂次数。p_vaddr 和 p_offset 在对 p_align 取模后应该相等。
0x3C78 % 0x1000 (4096) = C78
0x4C78 % 0x1000 (4096) = C78
根据上一篇的 elf header 分析,我们知道program header table 的起始位置是 0x40,其中每个表项的大小相同。
上图黑色背景的内容是和蓝色背景的内容都是程序头表的表项。
在这个元素中的p_type 类型为 PT_LOAD
此类型表明本程序头指向一个可装载的段。
段的内容会被从文件中拷贝到内存中。如前所述,段在文件中的大小是 p_filesz,在内存中的大小是p_memsz。如果 p_memsz 大于 p_filesz,在内存中多出的存储空间应填 0 补充。
也就是说,段在内存中可以比在文件中占用空间更大;而相反,p_filesz永远不应该比 p_memsz 大,因为这样的话,内存中就将无法完整地映射段的内容。在程序头表中,所有 PT_LOAD 类型的程序头按照 p_vaddr 的值做升序排列。
从(p_offset)0x0的位置开始加载31C8(12744)个字节 加载进内存。此内存页的权限为 可读,可执行。 当p_vadder 不为 0 时情况如下;
从(p_offset)0x3C78的位置开始加载3C8(968)个字节 加载进内存 对于 so基地址 加上 4C78偏移的位置。此内存页的权限为 可读,可写,可执行。
p_data 是圈出来的段的内容。
此段也是init_array。
p_type 类型为 PT_DYNAMIC。
如果一个目标文件参与动态连接的话,它的程序头表中一定会包含一个类型为PT_DYNAMIC 的表项,其所对应的段称为动态段(dynamic segment),段的名字为.dynamic。
动态段的作用是提供动态连接器所需要的信息,比如依赖于哪些共享目标文件、动态连接符号表的位置、动态连接重定位表的位置等等。
它包含一个由 如下结构体组成的数组。
- typedef struct {
- Elf64_Sxword d_tag;
- union {
- Elf64_Xword d_val;
- Elf64_Addr d_ptr;
- } d_un;
- } Elf64_Dyn;
对于每一个这种类型的目标项,d_tag 控制着对 d_un 的解析。
截取了一部分tags
tags[13] 的type是 DT_STRTAB 也就是字符串表所在的地址。
d_ptr 是 0x978
tags[14] 的type是 DT_SYMTAB 也就是符号表所在的地址
d_ptr 是 0x480
tags[16] 的type是 DT_SYMENT 也就是每个符号表条目的大小
d_val 是 24
名称 | 值 | |
DT_NULL | 0 | Terminating entry. |
DT_NEEDED | 1 | 所需共享库的字符串表偏移量 |
DT_PLTRELSZ | 2 | PLT重定位的总大小(以字节为单位) |
DT_PLTGOT | 3 | 处理器相关地址 |
DT_HASH | 4 | 符号哈希表的地址 |
DT_STRTAB | 5 | 字符串表的地址 |
DT_SYMTAB | 6 | 符号表的地址 |
DT_RELA | 7 | ElfNN_Rela 重定位的地址 |
DT_RELASZ | 8 | ElfNN_Rela 重定位的总大小 |
DT_RELAENT | 9 | 每个 ElfNN_Rela 重定位项的大小 |
DT_STRSZ | 10 | 字符串表的总大小 |
DT_SYMENT | 11 | 每个符号表项的大小 |
DT_INIT | 12 | init函数的地址 |
DT_FINI | 13 | fini函数的地址 |
DT_SONAME | 14 | 以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于标识共享目标文件的名称。 |
DT_RPATH | 15 | 以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。此元素的用途已被 DT_RUNPATH 取代。 |
DT_SYMBOLIC | 16 | 表示目标文件包含在其链接编辑过程中应用的符号绑定。 |
DT_REL | 17 | 与 DT_RELA 类似,但其表中包含隐式加数。此元素要求同时存在 DT_RELSZ 和 DT_RELENT 元素。 |
DT_RELSZ | 18 | DT_REL 重定位表的总大小 |
DT_RELENT | 19 | DT_REL 重定位项的大小 |
DT_PLTREL | 20 | 表示过程链接表指向的重定位项的类型(DT_REL 或 DT_RELA)。过程链接表中的所有重定位都必须使用相同的重定位项。此元素要求同时存在 DT_JMPREL 元素 |
DT_DEBUG | 21 | 用于调试 |
DT_TEXTREL | 22 | 表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备 |
DT_JMPREL | 23 | 与过程链接表单独关联的重定位项的地址,此元素要求同时存在 DT_PLTRELSZ 和 DT_PLTREL 元素 |
DT_BIND_NOW | 24 | 表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项 |
DT_INIT_ARRAY | 25 | init_array函数的指针数组的地址,此元素要求同时存在 DT_INIT_ARRAYSZ 元素 |
DT_FINI_ARRAY | 26 | fini_array函数的指针数组的地址,此元素要求同时存在 DT_FINI_ARRAYSZ 元素 |
DT_INIT_ARRAYSZ | 27 | DT_INIT_ARRAY 数组的总大小 |
DT_FINI_ARRAYSZ | 28 | DT_FINI_ARRAY 数组的总大小 |
DT_RUNPATH | 29 | 以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移 |
DT_FLAGS | 30 | 特定于此目标文件的标志值。 |
DT_MAXPOSTAGS | 34 | 正动态数组标记值的数量 |
DT_RELRSZ | 35 | ElfNN_Relr 重定位的总大小 |
DT_RELR | 36 | ElfNN_Relr 重定位的地址 |
DT_RELRENT | 37 | 每个 ElfNN_Relr 重定位的大小 |
DT_LOOS | 0x6000000d | 此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义 |
DT_SUNW_AUXILIARY | 0x6000000d | 以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个辅助 filtee |
DT_SUNW_RTLDINF | 0x6000000e | 保留供运行时链接程序内部使用 |
DT_SUNW_FILTER | 0x6000000f | 符号过滤器名称,以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个标准 filtee |
DT_SUNW_CAP | 0x60000010 | 功能节的地址 |
DT_SUNW_ASLR | 0x60000023 | ASLR控制 |
DT_HIOS | 0x6ffff000 | 特定于操作系统 |
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。