Как работают относительные переменные RIP, такие как "[RIP + _a]" в x86-64 GAS Intel-синтаксис?

Рассмотрим следующую ссылку на переменную в сборке Intel x64, где переменная a объявлен в .data раздел:

mov eax, dword ptr [rip + _a]

У меня проблемы с пониманием, как работает эта переменная ссылка. поскольку a является символом, соответствующим адресу времени выполнения переменной (с перемещением), как можно [rip + _a] разыменовать правильную ячейку памяти a? В самом деле, rip содержит адрес текущей инструкции, который является большим положительным целым числом, поэтому добавление приводит к неверному адресу a?

И наоборот, если я использую синтаксис x86 (который очень интуитивно понятен):

mov eax, dword ptr [_a]

, Я получаю следующую ошибку: 32-разрядная абсолютная адресация не поддерживается в 64-разрядном режиме.

Любое объяснение?

  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

1 ответ

Синтаксис GAS для RIP-относительной адресации выглядит symbol + RIP, но это на самом деле означает symbolв отношенииRIP,

Существует несоответствие с числовыми литералами:

  • [rip + 10] или AT&T 10(%rip) означает 10 байтов после конца этой инструкции
  • [rip + a] или AT&T a(%rip) означает рассчитать rel32 смещение, чтобы достичь a, а не RIP + значение символа.
  • [a] или AT&T a является абсолютным адресом, использующим режим адресации disp32. Это не поддерживается в OS X, где базовый адрес образа всегда находится за пределами младших 32 бит. (Или для mov для al/ax/eax/rax, возможен 64-битный абсолют moffs кодирование, но вы не хотите этого). Невозможно переместить переменные в.data в регистры с Mac x86 Assembly.

    Позиционно-зависимые исполняемые файлы Linux действительно помещают статический код / ​​данные в младшие 31 бит виртуального адресного пространства, так что вы можете / должны использовать mov edi, sym там, но на OS X ваш лучший вариант lea rdi, [sym+RIP] если вам нужен адрес в реестре.

(В OS X соглашение состоит в том, что имена переменных / функций C начинаются с _ в ассм. В написанном от руки asm вам не нужно делать это для символов, к которым вы не хотите получать доступ из C.)


NASM гораздо менее запутан в этом отношении:

  • [rel a] означает RIP-относительную адресацию для [a]
  • [abs a] средства [disp32],
  • default rel или же default abs устанавливает то, что используется для [a], По умолчанию (к сожалению) default abs, так что вы почти всегда хотите default rel,

Пример с .set Значения символов против метки

.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 (в 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>

(Разборка .o с objdump -dr покажет вам, что для компоновщика нет никаких перемещений, все они были сделаны во время сборки.)

Обратите внимание, что только .set z, sym привели в отношении к расчету. x а также y были оригинальными из простых числовых литералов, а не меток, поэтому даже при использовании самой инструкции [x + RIP]мы все еще получили [RIP + 8],


(Только для Linux без PIE) 8 WRT. RIP, вам нужен AT&T синтаксис incb 8-.(%rip), Я не знаю, как написать это в газе intel_syntax; [8 - . + RIP] отклонено с Error: invalid operands (*ABS* and .text sections) for '-',

Конечно, вы не можете сделать это в любом случае в OS X, за исключением, может быть, абсолютных адресов, которые находятся в диапазоне базы изображений. Но, вероятно, нет перемещения, которое могло бы содержать 64-битный абсолютный адрес для расчета для 32-битного rel32.

Другие вопросы по тегам