Assembly 在静态饼图二进制文件中,重新定位应该如何工作?
考虑这个适用于AMD64 Linux的GNU汇编程序:Assembly 在静态饼图二进制文件中,重新定位应该如何工作?,assembly,x86-64,static-linking,relocation,position-independent-code,Assembly,X86 64,Static Linking,Relocation,Position Independent Code,考虑这个适用于AMD64 Linux的GNU汇编程序: .globl _start _start: movl $59, %eax # SYS_execve leaq .pathname(%rip), %rdi # position-independent addressing leaq .argv(%rip), %rsi movq (%rsp), %rdx leaq 16(%rsp,%rdx,8), %rdx syscall mov
.globl _start
_start:
movl $59, %eax # SYS_execve
leaq .pathname(%rip), %rdi # position-independent addressing
leaq .argv(%rip), %rsi
movq (%rsp), %rdx
leaq 16(%rsp,%rdx,8), %rdx
syscall
movl $60, %eax # SYS_exit
movl $1, %edi
syscall
.section .data
.argv:
.quad .argv0 # Absolute address as static data
.quad .argv1
.quad 0
.pathname:
.ascii "/bin/"
.argv0:
.asciz "echo"
.argv1:
.asciz "hello"
当我使用gcc-nostlib-static pie构建它并运行它时,它失败了,strace告诉我这会发生:
execve(“/bin/echo”[0x301d,0x3022],0x7fff9bbe5a08/*28 vars*/)=-1默认值(错误地址)
但是,如果我将其构建为静态非饼图二进制文件或动态饼图二进制文件,它就可以正常工作。看起来问题在于重新定位没有得到处理
在动态饼图二进制文件中,动态链接器可以做到这一点,而在非饼图静态二进制文件中,您不需要运行时重新定位;静态地址是链接时间常数
但是静态饼图二进制文件应该如何工作呢?它们是根本不应该有任何重新定位,还是应该有其他东西来处理它们?显然,静态PIE仍然将运行时重新定位留给用户空间。如果省略CRT启动代码(使用-nostlib
),则根本不会发生。这大概就是为什么默认情况下,gcc-nostlib
不会生成静态饼图的原因
如果您确实链接了glibc的CRT启动代码,它将使用专门用于此目的的代码为您处理。
测试用例:您的\u start
代码更改为main
,或者从注释中选择Nate的C示例。(我在搜索readelf
或nm
输出时,将全局变量名称更改为长且易于查找。)
一个名为\u dl\u relocate\u static\u pie
的函数被链接到我的可执行文件中,这一事实非常清楚地证明了glibc提供了该代码。显然,static pie仍然将运行时重定位留给了用户空间。如果省略CRT启动代码(使用-nostlib
),则根本不会发生。这大概就是为什么默认情况下,gcc-nostlib
不会生成静态饼图的原因
如果您确实链接了glibc的CRT启动代码,它将使用专门用于此目的的代码为您处理。
测试用例:您的\u start
代码更改为main
,或者从注释中选择Nate的C示例。(我在搜索readelf
或nm
输出时,将全局变量名称更改为长且易于查找。)
一个名为\u dl\u remocate\u static\u pie
的函数被链接到我的可执行文件中,这一事实非常清楚地证明了glibc提供了该代码。问题在于,您使用的是-nostlib
,因此您缺少在可执行文件中执行重定位的代码。如果您使用gcc-static pie
构建一个稍有不同的示例,它将起作用:
.globl main
main:
movl $59, %eax # SYS_execve
leaq .pathname(%rip), %rdi # position-independent addressing
leaq .argv(%rip), %rsi
syscall
movl $60, %eax # SYS_exit
movl $1, %edi
syscall
.section .data
.argv:
.quad .argv0 # Absolute address as static data
.quad .argv1
.quad 0
.pathname:
.ascii "/bin/"
.argv0:
.asciz "echo"
.argv1:
.asciz "hello"
问题是您使用的是-nostlib
,因此缺少在可执行文件本身中执行重定位的代码。如果您使用gcc-static pie
构建一个稍有不同的示例,它将起作用:
.globl main
main:
movl $59, %eax # SYS_execve
leaq .pathname(%rip), %rdi # position-independent addressing
leaq .argv(%rip), %rsi
syscall
movl $60, %eax # SYS_exit
movl $1, %edi
syscall
.section .data
.argv:
.quad .argv0 # Absolute address as static data
.quad .argv1
.quad 0
.pathname:
.ascii "/bin/"
.argv0:
.asciz "echo"
.argv1:
.asciz "hello"
我猜数据(和文本)重新定位在静态饼图中根本不起作用。在用户空间执行开始之前,唯一可以处理它们的是内核,可能没有人希望在内核中有更多的代码。这可能是GCC在默认情况下不会生成静态饼图的原因,即使没有共享库。@PeterCordes:但不知何故,它做的事情与我的系统上的-fpie-static pie
编译并运行得很好(虽然不是在godbolt上)。@NateEldredge这似乎暗示着(1)glibc会处理它,它对我来说不起作用,因为我没有链接glibc,或者(2)GCC隐藏了一个神奇的函数调用。另外,有趣的是,它在godbolt上不起作用。我想知道这是为什么。一个快速的测试似乎指向理论(1)。如果我使用我的程序,将\u start
更改为main
,停止尝试从堆栈中设置envp
,并去掉-nostlib
,那么它就可以正常工作。我猜数据(和文本)重定位在静态饼图中根本不起作用。在用户空间执行开始之前,唯一可以处理它们的是内核,可能没有人希望在内核中有更多的代码。这可能是GCC在默认情况下不会生成静态饼图的原因,即使没有共享库。@PeterCordes:但不知何故,它做的事情与我的系统上的-fpie-static pie
编译并运行得很好(虽然不是在godbolt上)。@NateEldredge这似乎暗示着(1)glibc会处理它,它对我来说不起作用,因为我没有链接glibc,或者(2)GCC隐藏了一个神奇的函数调用。另外,有趣的是,它在godbolt上不起作用。我想知道这是为什么。一个快速的测试似乎指向理论(1)。如果我使用我的程序,将\u start
更改为main
,停止尝试从堆栈中设置envp
,并删除-nostlib
,则它可以正常工作。
.globl main
main:
movl $59, %eax # SYS_execve
leaq .pathname(%rip), %rdi # position-independent addressing
leaq .argv(%rip), %rsi
syscall
movl $60, %eax # SYS_exit
movl $1, %edi
syscall
.section .data
.argv:
.quad .argv0 # Absolute address as static data
.quad .argv1
.quad 0
.pathname:
.ascii "/bin/"
.argv0:
.asciz "echo"
.argv1:
.asciz "hello"