请考虑以下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)表示要计算达到rel32a位移,而不是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/

10-10 09:01