重定位应该如何在静态 PIE 二进制文件中工作? [英] How are relocations supposed to work in static PIE binaries?

查看:53
本文介绍了重定位应该如何在静态 PIE 二进制文件中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个用于 AMD64 Linux 的 GNU 汇编程序:

Consider this GNU Assembler program for AMD64 Linux:

.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 告诉我发生了这种情况:

When I build it with gcc -nostdlib -static-pie and run it, it fails, and strace shows me that this happens:

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

不过,如果我将其构建为静态非 PIE 二进制文件或动态 PIE 二进制文件,它就可以正常工作.问题似乎是没有处理重定位.

It works fine if I build it as a static non-PIE binary or as a dynamic PIE binary, though. It looks like the problem is that relocations aren't getting processed.

在动态 PIE 二进制文件中,动态链接器会这样做,而在非 PIE 静态二进制文件中,您不需要运行时重定位;静态地址是链接时间常数.

In dynamic PIE binaries, the dynamic linker does that, and in non-PIE static binaries, you don't need runtime relocations; static addresses are link time constants.

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

But how are static PIE binaries supposed to work? Are they just not supposed to have any relocations at all, or is something else supposed to process them?

推荐答案

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

Apparently static-PIE still leaves runtime relocation to user-space. If you omit CRT startup code (with -nostdlib), it doesn't happen at all. That's presumably why gcc -nostdlib doesn't make a static-PIE by default.

如果您确实链接了 glibc 的 CRT 启动代码,它会使用专门用于此目的的代码为您处理.

测试用例:将带有 _start 的代码更改为 main,或者来自注释的 Nate 的 C 示例.(我将全局变量名称更改为长且易于在搜索 readelfnm 输出时找到.)

Test case: your code with _start changed to main, or Nate's C example from comments. (I changed the global var names to be long and easy to find in searching readelf or nm output.)

#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)

    • Compile with gcc -g -fpie -static-pie print.c (I used gcc 10.1.0 with glibc 2.31-5 on Arch GNU/Linux for x86-64)

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

      Run gdb ./a.out. In GDB:

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

      starti (I wanted to make sure GDB could see the correct addresses before setting watch points, in case that's necessary)

      观看 static_ptr

      继续

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

      The watchpoint was hit by _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 提供了该代码.

      The fact that a function called _dl_relocate_static_pie got linked into my executable is pretty clear evidence that glibc provided that code.

      这篇关于重定位应该如何在静态 PIE 二进制文件中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆