为什么在 NASM 中使用 RIP 相对寻址?
Why use RIP-relative addressing in NASM?
我有一个 Mac OS X 的程序集 hello world 程序,如下所示:
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msg]
mov rdx, msg.len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, World!", 10
.len: equ $ - msg
我想知道 lea rsi, [rel msg]
行。为什么 NASM 强迫我这样做?据我了解,msg
只是指向可执行文件中某些数据的指针,执行 mov rsi, msg
会将那个地址放入 rsi
。但是,如果我将行 lea rsi, [rel msg]
替换为 ,NASM 会抛出此错误(注意:我正在使用命令 nasm -f macho64 hello.asm
):
hello.asm:9: fatal: No section for index 2 offset 0 found
为什么会这样? lea
有什么特别之处是 mov
做不到的?我怎么知道什么时候使用每一个?
What is so special about lea that mov can't do?
LEA r, [rel symbol]
可以在 运行 时访问 RIP。 mov r, imm
不能。立即数常量被编码为指令的二进制表示,这意味着如果代码+数据映射到 link 时未知的地址,它将无法工作。 (即它是位置相关的代码。)
这就是为什么 RIP 相对寻址对 PIC(位置无关代码)非常有用:而不需要通过全局偏移 Table 的间接级别来访问同一对象中定义的静态数据文件,您可以只使用 RIP 相对地址。
它还可以有效地为您提供 64 位地址,而无需在指令中嵌入完整的 64 位绝对地址。 MacOS X 需要 64 位地址,因为它将“映像库”映射到虚拟地址的低 4GiB 之外 space.
如果可执行文件(不仅仅是共享库)是 PIC 是件好事,这样 MacOS 就可以随机化它们的基址以提高安全性。 (无需在出现的任何地方重写绝对地址。)
在依赖于位置的 Linux 可执行文件(不是 MacOS)中,您可以作为优化使用
mov esi, msg
。注意 ESI,不是 RSI。
mov rsi, msg
使用 10 字节 mov rsi, imm64
而不是 7 字节 lea rsi, [RIP + rel32]
时 效率较低 。 ()
在 x86-64 中访问静态数据的“正常”方式是使用 RIP 相对寻址,例如mov eax, [rel my_global_var]
。如果目标允许 32 位绝对值,则仅用于将地址放入您有时可能会利用 32 位绝对值的寄存器。
其他相关问答:
- Why is the address of static variables relative to the Instruction Pointer?
- 32-bit absolute addresses no longer allowed in x86-64 Linux?
- Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array)
What is so special about lea
that mov
can't do?
mov reg,imm
加载一个 immediate 常量到它的目标操作数。立即数直接在操作码中编码,例如如果 someVar
的地址是 0x00ABCDEF
,mov eax,someVar
将被编码为 B8 EF CD AB 00
。 IE。要使用 imm
作为 msg
的地址对这样的指令进行编码,您需要知道 msg
的确切地址。在与位置无关的代码中,您事先并不知道。
mov reg,[expression]
加载位于 expression
描述的地址处的值。 x86指令的复杂编码方案允许有相当复杂的expression
:一般是reg1+reg2*s+displ
,其中s
可以是0,1,2,4,reg1
和reg2
可以是通用寄存器也可以是零,displ
是立即数位移。在 64 位模式下 expression
可以有另一种形式:RIP+displ
,即地址是相对于下一条指令计算的。
lea reg,[expression]
使用所有这些计算地址的复杂方法将 地址本身 加载到 reg
中(不像 mov
,它取消引用地址计算)。因此,在编译时不可用的信息,即 RIP
中的绝对地址,可以在不知道其值的情况下编码到指令中。 nasm 表达式 lea rsi,[rel msg]
被翻译成
lea rsi,[rip+(msg-nextInsn)]
nextInsn:
它使用相对地址msg-nextInsn
而不是msg
的绝对地址,因此允许汇编程序不知道实际地址但仍然对指令进行编码。
我有一个 Mac OS X 的程序集 hello world 程序,如下所示:
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msg]
mov rdx, msg.len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, World!", 10
.len: equ $ - msg
我想知道 lea rsi, [rel msg]
行。为什么 NASM 强迫我这样做?据我了解,msg
只是指向可执行文件中某些数据的指针,执行 mov rsi, msg
会将那个地址放入 rsi
。但是,如果我将行 lea rsi, [rel msg]
替换为 ,NASM 会抛出此错误(注意:我正在使用命令 nasm -f macho64 hello.asm
):
hello.asm:9: fatal: No section for index 2 offset 0 found
为什么会这样? lea
有什么特别之处是 mov
做不到的?我怎么知道什么时候使用每一个?
What is so special about lea that mov can't do?
LEA r, [rel symbol]
可以在 运行 时访问 RIP。 mov r, imm
不能。立即数常量被编码为指令的二进制表示,这意味着如果代码+数据映射到 link 时未知的地址,它将无法工作。 (即它是位置相关的代码。)
这就是为什么 RIP 相对寻址对 PIC(位置无关代码)非常有用:而不需要通过全局偏移 Table 的间接级别来访问同一对象中定义的静态数据文件,您可以只使用 RIP 相对地址。
它还可以有效地为您提供 64 位地址,而无需在指令中嵌入完整的 64 位绝对地址。 MacOS X 需要 64 位地址,因为它将“映像库”映射到虚拟地址的低 4GiB 之外 space.
如果可执行文件(不仅仅是共享库)是 PIC 是件好事,这样 MacOS 就可以随机化它们的基址以提高安全性。 (无需在出现的任何地方重写绝对地址。)
在依赖于位置的 Linux 可执行文件(不是 MacOS)中,您可以作为优化使用
mov esi, msg
。注意 ESI,不是 RSI。
mov rsi, msg
使用 10 字节 mov rsi, imm64
而不是 7 字节 lea rsi, [RIP + rel32]
时 效率较低 。 (
在 x86-64 中访问静态数据的“正常”方式是使用 RIP 相对寻址,例如mov eax, [rel my_global_var]
。如果目标允许 32 位绝对值,则仅用于将地址放入您有时可能会利用 32 位绝对值的寄存器。
其他相关问答:
- Why is the address of static variables relative to the Instruction Pointer?
- 32-bit absolute addresses no longer allowed in x86-64 Linux?
- Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array)
What is so special about
lea
thatmov
can't do?
mov reg,imm
加载一个 immediate 常量到它的目标操作数。立即数直接在操作码中编码,例如如果 someVar
的地址是 0x00ABCDEF
,mov eax,someVar
将被编码为 B8 EF CD AB 00
。 IE。要使用 imm
作为 msg
的地址对这样的指令进行编码,您需要知道 msg
的确切地址。在与位置无关的代码中,您事先并不知道。
mov reg,[expression]
加载位于 expression
描述的地址处的值。 x86指令的复杂编码方案允许有相当复杂的expression
:一般是reg1+reg2*s+displ
,其中s
可以是0,1,2,4,reg1
和reg2
可以是通用寄存器也可以是零,displ
是立即数位移。在 64 位模式下 expression
可以有另一种形式:RIP+displ
,即地址是相对于下一条指令计算的。
lea reg,[expression]
使用所有这些计算地址的复杂方法将 地址本身 加载到 reg
中(不像 mov
,它取消引用地址计算)。因此,在编译时不可用的信息,即 RIP
中的绝对地址,可以在不知道其值的情况下编码到指令中。 nasm 表达式 lea rsi,[rel msg]
被翻译成
lea rsi,[rip+(msg-nextInsn)]
nextInsn:
它使用相对地址msg-nextInsn
而不是msg
的绝对地址,因此允许汇编程序不知道实际地址但仍然对指令进行编码。