如何在 64 位汇编程序中使用 RIP 相对寻址?
Posted
技术标签:
【中文标题】如何在 64 位汇编程序中使用 RIP 相对寻址?【英文标题】:How to use RIP Relative Addressing in a 64-bit assembly program? 【发布时间】:2011-03-16 02:00:21 【问题描述】:如何在 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"
我猜测使用 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"
正常版本在编译时运行良好:
as -o hello.o hello.s && ld -s -o hello hello.o && ./hello
但我无法让 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)
这显示了我试图完成的事情:lea 0x10(%rip),%rsi 在 lea 指令之后加载地址 17 个字节,即地址 0x400096,可以找到 Hello world 字符串,从而产生与位置无关的代码。
【问题讨论】:
为什么 17 字节后(0x10 是 16)? tortall.net/projects/yasm/manual/html/nasm-effaddr.html 说:RIP is the instruction pointer register, which contains the address of the location immediately following the current instruction
但lea
指令有七个字节长,而不是一个。
【参考方案1】:
我相信您想将字符串的地址加载到%rsi
;您的代码尝试从该地址而不是地址本身加载四字。你想要:
lea msg(%rip), %rsi
如果我没记错的话。但是,我没有要测试的 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 字节。但我在文档中找不到 rip 和其他寄存器在计算上存在差异 @StephenCanon 这适用于 x86_64 什么是 lea msg(%rip), %rsi 在 32 位汇编器中的等价物? @Zibri:没有位置无关的方式,这就是 AMD64 添加 RIP 相对寻址的原因。在 Linux 下编译器使用相对于 GOT 的偏移量。当然,在依赖于位置的 32 位代码中,您只需使用mov $msg, %esi
,就像在依赖于位置的 64 位代码中一样(在 Linux 下,已知静态符号地址位于非虚拟地址空间的低 2GiB 中) -PIE 可执行文件)。
@user2808671: 是的,msg(%rip)
是一种特殊情况,表示 相对于 RIP 的符号,而不是绝对地址 + RIP。 sourceware.org/binutils/docs/as/i386_002dMemory.html 的底部记录了这一点。【参考方案2】:
由于您需要 %rsi 中 msg 的地址,只需替换:
mov msg(%rip),%rsi
与:
lea msg(%rip),%rsi
【讨论】:
以上是关于如何在 64 位汇编程序中使用 RIP 相对寻址?的主要内容,如果未能解决你的问题,请参考以下文章
NASM x86_64在32位模式下组装:为什么该指令产生RIP相对寻址代码?
x86-64 GAS Intel 语法中的 RIP 相对变量引用(如“[RIP + _a]”)如何工作?