X86 分页和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,这样就可以在需要的任何地址加载 安全。将特定代码存

当使用虚拟内存时,我很难理解PIC可执行文件的需求。从我收集的信息来看,每个程序都在页表中分配了一个条目,因此有一种错觉,即当分页机制处理可能的重定位、页故障等问题时,它拥有整个内存可供使用。因此,如果任何程序都有拥有所有可能内存地址的错觉,为什么要使用PIC呢

两个主要原因:

  • 共享库。不能保证库在特定的地址加载——即使在64位系统上,也无法保证每个库都有一个唯一的加载地址,不会与任何其他库或动态内存分配冲突。因此,共享库中的代码被编译为PIC,这样就可以在需要的任何地址加载

  • 安全。将特定代码存在于内存中可预测的位置是一种安全风险,因为它使可以串在一起执行任意操作的代码成为可能。帮助击败这些攻击

  • 我们不需要它,直到最近一两年,所有Linux可执行文件都是位置相关的(不是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
    位移中作为寻址模式的一部分,对其进行索引