How do RIP-relative variable references like “[RIP + _a]” in x86-64 GAS Intel-syntax work?

最后都变了- 提交于 2019-11-27 16:18:18

GAS syntax for RIP-relative addressing looks like symbol + RIP, but it actually means symbol with respect to RIP.

There's an inconsistency with numeric literals:

  • [rip + 10] or AT&T 10(%rip) means 10 bytes past the end of this instruction
  • [rip + a] or AT&T a(%rip) means to calculate a rel32 displacement to reach a, not RIP + symbol value.
  • [a] or AT&T a is an absolute address, using a disp32 addressing mode. This isn't supported on OS X, where the image base address is always outside the low 32 bits. (Or for mov to al/ax/eax/rax, possible a 64-bit absolute moffs encoding, but you don't want that). Unable to move variables in .data to registers with Mac x86 Assembly.

    Linux position-dependent executables do put static code/data in the low 31 bits of virtual address space, so you can/should use mov edi, sym there, but on OS X your best option is lea rdi, [sym+RIP] if you need an address in a register.

(In OS X, the convention is that C variable/function names are prepended with _ in asm. In hand-written asm you don't have to do this for symbols you don't want to access from C.)


NASM is much less confusing in this respect:

  • [rel a] means RIP-relative addressing for [a]
  • [abs a] means [disp32].
  • default rel or default abs sets what's used for [a]. The default is (unfortunately) default abs, so you almost always want a default rel.

Example with .set symbol values vs. a label

.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]

gcc -nostdlib foo.s && objdump -drwC -Mintel a.out (on Linux; I don't have 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>

(Disassembling the .o with objdump -dr will show you that there aren't any relocations for the linker to fill in, they were all done at assemble time.)

Notice that only .set z, sym resulted in a with-respect-to calculation. x and y were original from plain numeric literals, not labels, so even though the instruction itself used [x + RIP], we still got [RIP + 8].


(Linux non-PIE only): To address absolute 8 wrt. RIP, you'd need AT&T syntax incb 8-.(%rip). I don't know how to write that in GAS intel_syntax; [8 - . + RIP] is rejected with Error: invalid operands (*ABS* and .text sections) for '-'.

Of course you can't do that anyway on OS X, except maybe for absolute addresses that are in range of the image base. But there's probably no relocation that can hold the 64-bit absolute address to be calculated for a 32-bit rel32.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!