X86 分页和PIC可执行文件
当使用虚拟内存时,我很难理解PIC可执行文件的需求。从我收集的信息来看,每个程序都在页表中分配了一个条目,因此有一种错觉,即当分页机制处理可能的重定位、页故障等问题时,它拥有整个内存可供使用。因此,如果任何程序都有拥有所有可能内存地址的错觉,为什么要使用PIC呢 两个主要原因:X86 分页和PIC可执行文件,x86,operating-system,x86-64,virtual-memory,position-independent-code,X86,Operating System,X86 64,Virtual Memory,Position Independent Code,当使用虚拟内存时,我很难理解PIC可执行文件的需求。从我收集的信息来看,每个程序都在页表中分配了一个条目,因此有一种错觉,即当分页机制处理可能的重定位、页故障等问题时,它拥有整个内存可供使用。因此,如果任何程序都有拥有所有可能内存地址的错觉,为什么要使用PIC呢 两个主要原因: 共享库。不能保证库在特定的地址加载——即使在64位系统上,也无法保证每个库都有一个唯一的加载地址,不会与任何其他库或动态内存分配冲突。因此,共享库中的代码被编译为PIC,这样就可以在需要的任何地址加载 安全。将特定代码存
gcc-fno-PIE-no-PIE
构建非PIE可执行文件,静态ELF可执行文件始终是非PIE的,其加载地址在链接时选择。通常默认将文本段的开头置于401000
独立于位置的ELF可执行文件最初是一个hack:一个带有入口点的ELF共享对象。但是现在它被广泛使用,而且大多数Linux发行版都默认使用了gcc
。加载地址可以在运行时随机化
还请注意,许多操作系统在加载非首选地址的可执行文件或库时支持运行时修复 例如,Linux上的ELF共享对象可以包含64位绝对地址的重定位,因此您可以在使用针对x86和x86-64的
gcc-fPIC
编译的代码中使用传统的跳转表(代码指针数组)或静态初始化的指针数组(指向数据或函数)
请注意,
gcc-fPIC
还支持符号插入,因此函数不能直接访问全局变量;他们必须从GOT加载地址,除非符号具有“隐藏”ELF可见性。(当然,如果将其设置为静态
而不是全局)
看
(该博客中提出的一些想法已经实现,例如GCC支持-fno plt
)
使用-fpie
仅定位独立性的实际成本非常小。但在操作系统上仍然是非零的,在操作系统上,位置相关的可执行文件保证加载在32位的低位虚拟地址空间(例如Linux),因此您可以利用5字节mov r32、imm32
的32位绝对地址,而不是7字节RIP相对LEA将静态地址放入寄存器,或[array+reg]
将静态数组的地址置于disp32
位移中作为寻址模式的一部分,对其进行索引