Как работают относительные переменные 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&T10(%rip)
означает 10 байтов после конца этой инструкции[rip + a]
или AT&Ta(%rip)
означает рассчитатьrel32
смещение, чтобы достичьa
, а не RIP + значение символа.[a]
или AT&Ta
является абсолютным адресом, использующим режим адресации 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.