如何在64位汇编程序中使用RIP相对寻址?

Eri*_*rik 27 64-bit assembly x86-64 gnu-assembler

如何在AMD64架构的Linux汇编程序中使用RIP相对寻址?我正在寻找一个使用AMD64 RIP相对地址模式的简单示例(Hello world程序).

例如,以下64位汇编程序将与普通(绝对寻址)一起使用:

.text
    .global _start

_start:
    mov $0xd, %rdx

    mov $msg, %rsi
    pushq $0x1
    pop %rax
    mov %rax, %rdi
    syscall

    xor %rdi, %rdi
    pushq $0x3c
    pop %rax
    syscall

.data
msg:
    .ascii    "Hello world!\n"
Run Code Online (Sandbox Code Playgroud)

我猜测使用RIP相对寻址的相同程序将是这样的:

.text
    .global _start

_start:
    mov $0xd, %rdx

    mov msg(%rip), %rsi
    pushq $0x1
    pop %rax
    mov %rax, %rdi
    syscall

    xor %rdi, %rdi
    pushq $0x3c
    pop %rax
    syscall

msg:
    .ascii    "Hello world!\n"
Run Code Online (Sandbox Code Playgroud)

编译时,正常版本运行正常:

as -o hello.o hello.s && ld -s -o hello hello.o && ./hello
Run Code Online (Sandbox Code Playgroud)

但我无法使RIP版本正常工作.

有任何想法吗?

---编辑----

Stephen Canon的回答使RIP版本起作用.

现在当我反汇编RIP版本的可执行文件时,我得到:

objdump -d你好

0000000000400078 <.text>:
  400078: 48 c7 c2 0d 00 00 00  mov    $0xd,%rdx
  40007f: 48 8d 35 10 00 00 00  lea    0x10(%rip),%rsi        # 0x400096
  400086: 6a 01                 pushq  $0x1
  400088: 58                    pop    %rax
  400089: 48 89 c7              mov    %rax,%rdi
  40008c: 0f 05                 syscall 
  40008e: 48 31 ff              xor    %rdi,%rdi
  400091: 6a 3c                 pushq  $0x3c
  400093: 58                    pop    %rax
  400094: 0f 05                 syscall 
  400096: 48                    rex.W
  400097: 65                    gs
  400098: 6c                    insb   (%dx),%es:(%rdi)
  400099: 6c                    insb   (%dx),%es:(%rdi)
  40009a: 6f                    outsl  %ds:(%rsi),(%dx)
  40009b: 20 77 6f              and    %dh,0x6f(%rdi)
  40009e: 72 6c                 jb     0x40010c
  4000a0: 64 21 0a              and    %ecx,%fs:(%rdx)
Run Code Online (Sandbox Code Playgroud)

这显示了我想要完成的事情:lea 0x10(%rip),%rsi在地址0x400096之后的地址为0x400096之后加载地址17个字节,其中可以找到Hello世界字符串,从而产生与位置无关的代码.

Ste*_*non 26

我相信你想加载你的字符串的地址%rsi ; 您的代码尝试从该地址加载四字而不是地址本身.你要:

lea msg(%rip), %rsi
Run Code Online (Sandbox Code Playgroud)

如果我没错的话.但是,我没有要测试的linux盒子.

  • 如果使用lea msg(%rsp),%rsi而不是lea msg(%rip),%rsi(或任何寄存器但不是rip),则mes标签本身的地址将被添加而不是当前提供的寄存器值的偏移量.例如,如果msg在地址0x1FF然后使用lea msg(%rsp),%rsi导致rsi =*(rsp + 0x1FF)而不是rsi =*((rsp - 0x1FF)+ rsp)因为反汇编程序给出了0x10(%rip),因为距离当前rip和msg的距离是0x10 byts.但我在文件中找不到rip和其他寄存器之间的计算差异 (4认同)
  • @Zibri:没有位置无关的方式,这就是AMD64添加RIP相对寻址的原因。在 Linux 下编译器使用相对于 GOT 的偏移量。当然,在位置相关的 32 位代码中,您只需使用 `mov $msg, %esi` ,就像在位置相关的 64 位代码中一样(在 Linux 下,已知静态符号地址位于以下 2GiB 中)非 PIE 可执行文件中的虚拟地址空间)。 (2认同)
  • @user2808671:是的,`msg(%rip)`是一种特殊情况,意味着*关于*RIP的符号,而不是绝对地址+RIP。https://sourceware.org/binutils/docs/as/i386_002dMemory.html 的底部记录了这一点。 (2认同)