1

考虑这个适用于 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 -nostdlib -static-pie并运行它时,它失败了,并且 strace 告诉我发生了这种情况:

execve("/bin/echo", [0x301d, 0x3022], 0x7fff9bbe5a08 /* 28 vars */) = -1 EFAULT (Bad address)

不过,如果我将它构建为静态非 PIE 二进制文件或动态 PIE 二进制文件,它工作正常。看起来问题是重定位没有得到处理。

在动态 PIE 二进制文件中,动态链接器会执行此操作,而在非 PIE 静态二进制文件中,您不需要运行时重定位;静态地址是链接时间常数。

但是静态 PIE 二进制文件应该如何工作?他们只是根本不应该有任何搬迁,还是应该有其他东西来处理它们?

4

2 回答 2

3

显然静态 PIE 仍然将运行时重定位到用户空间。如果省略 CRT 启动代码(带有-nostdlib),则根本不会发生。这大概就是为什么gcc -nostdlib默认情况下不制作静态 PIE 的原因。

如果您确实链接了 glibc 的 CRT 起始代码,它将使用专门用于该目的的代码为您处理它。

测试用例:您的代码_start更改为main,或来自评论的 Nate 的 C 示例。(我将全局变量名称更改为长且易于在搜索readelfnm输出中找到。)

#include <stdio.h>

int global_static_a = 7;
int *static_ptr = &global_static_a;

int main(void) {
  printf("%d\n", *static_ptr);   // load and deref the statically-initialized pointer
}
  • 编译gcc -g -fpie -static-pie print.c (我在 Arch GNU/Linux for x86-64 上使用 gcc 10.1.0 和 glibc 2.31-5)

  • 运行gdb ./a.out。在 GDB 中:

  • starti (我想确保 GDB在设置观察点之前可以看到正确的地址,以防万一)

  • watch static_ptr

  • continue

观察点被 击中_dl_relocate_static_pie+540 mov QWORD PTR [rcx],rdx

Hardware watchpoint 2: static_ptr

Old value = (int *) 0xb7130
New value = (int *) 0x7ffff7ffb130 <global_static_a>

一个名为的函数被_dl_relocate_static_pie链接到我的可执行文件中这一事实非常清楚地证明 glibc 提供了该代码。

于 2020-06-26T02:34:17.700 回答
1

问题是您正在使用-nostdlib,因此您缺少在可执行文件本身中执行重定位的代码。如果您使用以下方法构建一个稍微不同的示例,它将起作用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"
于 2020-06-26T02:39:40.973 回答