Assembly 在静态饼图二进制文件中,重新定位应该如何工作?

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

考虑这个适用于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
    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"