请考虑以下x64 Intel程序集中的变量引用,其中在a
部分中声明了变量.data
:
mov eax, dword ptr [rip + _a]
我很难理解此变量引用的工作方式。由于
a
是对应于变量运行时地址(带有重定位)的符号,因此[rip + _a]
如何取消引用a
的正确存储位置?确实,rip
保留当前指令的地址,它是一个大的正整数,因此加法会导致错误的a
地址?相反,如果我使用x86语法(非常直观):
mov eax, dword ptr [_a]
,出现以下错误:64位模式下不支持32位绝对寻址。
有什么解释吗?
1 int a = 5;
2
3 int main() {
4 int b = a;
5 return b;
6 }
编译:
gcc -S -masm=intel abs_ref.c -o abs_ref
: 1 .section __TEXT,__text,regular,pure_instructions
2 .build_version macos, 10, 14
3 .intel_syntax noprefix
4 .globl _main ## -- Begin function main
5 .p2align 4, 0x90
6 _main: ## @main
7 .cfi_startproc
8 ## %bb.0:
9 push rbp
10 .cfi_def_cfa_offset 16
11 .cfi_offset rbp, -16
12 mov rbp, rsp
13 .cfi_def_cfa_register rbp
14 mov dword ptr [rbp - 4], 0
15 mov eax, dword ptr [rip + _a]
16 mov dword ptr [rbp - 8], eax
17 mov eax, dword ptr [rbp - 8]
18 pop rbp
19 ret
20 .cfi_endproc
21 ## -- End function
22 .section __DATA,__data
23 .globl _a ## @a
24 .p2align 2
25 _a:
26 .long 5 ## 0x5
27
28
29 .subsections_via_symbols
最佳答案
用于RIP相对寻址的GAS语法类似于symbol + RIP
,但实际上相对于symbol
表示RIP
。
数字文字之间存在不一致之处:[rip + 10]
或AT&T 10(%rip)
表示该指令末尾的10个字节[rip + a]
或AT&T a(%rip)
表示要计算达到rel32
的a
位移,而不是RIP +符号值。 (GAS手册documents this特殊解释)[a]
或AT&T a
是绝对地址,使用disp32寻址模式。 OS X不支持此功能,因为OS X的图像基址始终在低32位之外。 (或对于mov
与al / ax / eax / rax之间的往返,可以使用64位绝对moffs
编码,但您不希望这样做)。
Linux位置相关的可执行文件确实将静态代码/数据放在虚拟地址空间的低31位(2GiB)中,因此您可以/应该在其中使用mov edi, sym
,但是在OS X上,如果需要使用lea rdi, [sym+RIP]
,则最佳选择是_
地址在寄存器中。 Unable to move variables in .data to registers with Mac x86 Assembly。
(在OS X中,惯例是C变量/函数名称在asm中以[rel a]
开头。在手写asm中,您不必为不想从C访问的符号执行此操作。)
NASM在这方面的困惑要少得多:[a]
表示[abs a]
的RIP相对寻址[disp32]
表示default rel
。default abs
或[a]
设置default abs
使用的内容。默认值是(不幸的是)default rel
,因此您几乎总是需要.set
。gcc -nostdlib foo.s && objdump -drwC -Mintel a.out
符号值与标签的示例
.intel_syntax noprefix
mov dword ptr [sym + rip], 0x11111111
sym:
.equ x, 8
inc byte ptr [x + rip]
.set y, 32
inc byte ptr [y + rip]
.set z, sym
inc byte ptr [z + rip]
.o
(在Linux上;我没有OS X):0000000000001000 <sym-0xa>:
1000: c7 05 00 00 00 00 11 11 11 11 mov DWORD PTR [rip+0x0],0x11111111 # 100a <sym> # rel32 = 0; it's from the end of the instruction not the end of the rel32 or anywhere else.
000000000000100a <sym>:
100a: fe 05 08 00 00 00 inc BYTE PTR [rip+0x8] # 1018 <sym+0xe>
1010: fe 05 20 00 00 00 inc BYTE PTR [rip+0x20] # 1036 <sym+0x2c>
1016: fe 05 ee ff ff ff inc BYTE PTR [rip+0xffffffffffffffee] # 100a <sym>
(用
objdump -dr
分解.set z, sym
会告诉您,链接器没有要填充的重定位,它们都是在组装时完成的。)请注意,只有
x
会导致with-to-to计算。 y
和[x + RIP]
是纯数字文字而不是标签的原始内容,因此即使指令本身使用了[RIP + 8]
,我们仍然得到8
。(仅限Linux非PIE):解决绝对
incb 8-.(%rip)
wrt。 RIP,您需要AT&T语法intel_syntax
。我不知道该如何在GAS [8 - . + RIP]
中编写该代码; Error: invalid operands (*ABS* and .text sections) for '-'
被拒绝。当然,在OS X上无论如何都不能执行此操作,除非绝对地址在映像范围内。但是可能没有重定位可以容纳要为32位rel32计算的64位绝对地址。
关于assembly - x86-64 GAS Intel语法中的RIP相对变量引用(例如“[RIP + _a]”)如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54745872/