ELF 文件(Unix 可执行文件或共享对象)有两个主要概念:
Section:可执行文件内具有特定作用的区域。 ELF 文件内可能有不同的部分(可以在man elf)。 ELF 文件中的常见部分有:
-
.text
(SHT_PROGBITS):ELF 文件中的实际可执行代码。
-
.dynsym
(SHT_DYNSYM):保存有关应动态检索的符号的信息。
-
.rela.dyn
and .rela.plt
(SHT_RELA):保存动态链接器在将 ELF 文件加载到内存时使用的重定位信息。
-
.dynamic
(SHT_DYNAMIC):保存动态链接器的信息,例如其他依赖项、运行时不同部分的偏移量等。
-
.symtab
(SHT_SYMTAB):保存符号表。
-
.strtab
(SHT_STRTAB):保存字符串表。
还有更多的部分,以上只是一些常见的部分。
Using readelf
人们可以看到 ELF 文件中的所有部分:
readelf --sections -W <file>
在我的计算机中的共享对象上运行此命令会产生以下输出(简化):
There are 29 section headers, starting at offset 0x1898:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[ 3] .dynsym DYNSYM 0000000000000230 000230 000168 18 A 4 2 8
[ 4] .dynstr STRTAB 0000000000000398 000398 0000b0 00 A 0 0 1
...
[ 7] .rela.dyn RELA 0000000000000488 000488 0000c0 18 A 3 0 8
[ 8] .rela.plt RELA 0000000000000548 000548 000030 18 AI 3 22 8
...
[12] .text PROGBITS 00000000000005e0 0005e0 000121 00 AX 0 0 16
...
[20] .dynamic DYNAMIC 0000000000200e18 000e18 0001c0 10 WA 4 0 8
...
[23] .data PROGBITS 0000000000201028 001028 000008 00 WA 0 0 8
...
[27] .symtab SYMTAB 0000000000000000 001068 000570 18 28 45 8
[28] .strtab STRTAB 0000000000000000 0015d8 0001c6 00 0 0 1
Segment:可执行文件内的一个区域,包含动态链接器的加载指令。意思是,段只是 ELF 文件内的一个区域,应该加载到首选地址内存中,并具有特定的权限、对齐方式等。
每个部分(ELF 文件中具有逻辑角色的区域)都应该是段的一部分,具有正确的权限和特征。一个段内可以包含多个段,并且一个段位于单个段内(一对多关系)。
Using readelf
我们可以看到 ELF 文件中的所有段:
readelf --segments -W <file>
在我的计算机中的共享对象上运行此命令会产生以下输出:
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00079c 0x00079c R E 0x200000
LOAD 0x000e00 0x0000000000200e00 0x0000000000200e00 0x000230 0x000238 RW 0x200000
DYNAMIC 0x000e18 0x0000000000200e18 0x0000000000200e18 0x0001c0 0x0001c0 RW 0x8
NOTE 0x0001c8 0x00000000000001c8 0x00000000000001c8 0x000024 0x000024 R 0x4
GNU_EH_FRAME 0x000718 0x0000000000000718 0x0000000000000718 0x00001c 0x00001c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000e00 0x0000000000200e00 0x0000000000200e00 0x000200 0x000200 R 0x1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .dynamic .got
在这里,我们可以看到所有与可执行代码相关的部分,以及许多与文件动态加载相关的部分都在段00(PT_LOAD)中,该段具有读取和可执行权限(R E
)。加载程序应修改的部分位于段 01 (PT_LOAD) 中,该段具有读写权限(RW
)。段 02 的类型为 PT_DYNAMIC,保存动态链接信息 -.dynamic
部分。
动态链接器在将 ELF 文件加载到内存中时会考虑所有这些信息。它将 ELF 文件的不同段从磁盘加载到内存中,并使用正确的权限保护它们的页面。然后,它迭代不同的部分并根据它们的角色使用它们(重定位、解析动态符号等......)。
内存保护本身是由操作系统和硬件本身进行的。与使用Linux方法类似保护()。有关内存保护的更多信息可以找到here.