Я думаю, что вам нужна запись о перемещении, которая дает абсолютный адрес, оставляя его до динамического c linker / runtime-fixup-applier, чтобы вычислить правильное относительное смещение для достижения этого абсолютного адреса из места, где это применяется перемещение.
Возможно, не так просто . например, x86-64 RIP-относительная адресация относится к концу инструкции , но, например, mov [RIP+rel32], imm32
кодируется непосредственно после части rel32 режима адресации. Но если нет немедленного, это обычно в конце инструкции. Таким образом, точка, к которой относится режим адресации, может не быть фиксированной позицией по отношению к. положение, которое вы должны применить.
Но мы можем оставить это на усмотрение ассемблера и позволить ему добавить некоторое смещение, чтобы учесть это различие в базе, так что перемещение приведет к правильному абсолютному адресу.
Это позволяет сохранить компактность записи о перемещении такого же размера, как и у «обычного»: просто место, где ее нужно применить, тип и 32-битный абсолютный адрес или любую ширину, которую использует машина. (Вы можете даже просто закодировать абсолютный адрес в месте, где идет относительное смещение P C, если оно всегда достаточно широкое.)
Или правильное относительное смещение, чтобы достичь желаемого абсолютного адреса относительно некоторого base, например 0. Это то, что используют файлы GNU / Linux ELF .o
, как и исполняемые файлы P IE. Это также решает проблему смещения перемещения, чтобы учесть любое переменное расстояние между тем, где оно хранится и где оно относительно.
Так, например, чтобы переместить все изображение с 0
на 0x10000
, вы просто вычтите 0x10000
из каждого относительного перемещения абсолютной цели.
Кстати, вы можете сделать это на практике для инструкций относительного вызова i386 в GNU / Linux с помощью GAS. Ближайшие вызовы на x86 всегда используют кодировку call rel32
, но хорошие ассемблеры на платформах, которые поддерживают необходимые перемещения, позволяют вам написать абсолютную цель и предоставить компоновщику правильные перемещения для вас. ( Вызов абсолютного указателя в машинном коде x86 )
# foo.s
.globl _start
_start:
nop # some padding so the base of the call address doesn't start at 0
nop
call 0x123456 # relative call to that absolute address
Сборка с gcc -m32 -c foo.s
, разборка с objdump -drwC -Mintel
foo.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 90 nop
1: 90 nop
2: e8 52 34 12 00 call 123459 <_start+0x123459> 3: R_386_PC32 *ABS*
readelf -a foo.o
показывает раздел перемещений выглядит следующим образом:
Relocation section '.rel.text' at offset 0x94 contains 1 entry:
Offset Info Type Sym.Value Sym. Name
00000003 00000002 R_386_PC32
Целевой адрес не является частью этой записи перемещения; он закодирован в существующий машинный код. Это работает для i386, но, возможно, не всегда для x86-64. Построение без -m32
дает нам:
Relocation section '.rela.text' at offset 0xb0 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000000003 000000000002 R_X86_64_PC32 123452
В любом случае, обратите внимание, что "offset" - это место, где его нужно применять (2 байта NOP плюс байт опкода "call" означает, что rel32 запускает 3
байтов в раздел, начиная с основания 0.) 0x123452
в перемещении x86-64 является реальной целью ... 6 минус длина rel32 (4 байта).
Имя " + addend "заголовок столбца имеет смысл для перемещений, которые вы получите от ориентации на имя символа со смещением. например, mov eax, [global_array + 12]
необходимо загрузить с 12 байт после того, как компоновщик положит начало global_array
.
Также обратите внимание, что мы смотрим на несвязанный файл .o
, а не на связанный исполняемый файл. x86-64 разделяемые объекты ELF не допускают исправлений во время выполнения для 32-битных абсолютных целей; весь объект может быть случайно расположен на расстоянии более + -2 ГБ. (Вот почему я использовал -m32
).
Кажется, что 32-битные исполняемые файлы P IE также не поддерживают это должным образом. Вероятно, потому что позиция независима от названия. : P Построение с gcc -m32 -pie -nostdlib foo.s
дало нам кодировку e8 4f 24 12 00
, которая работает для базы изображений 0x1000
. (Даже изнутри GDB после установки точки останова и запуска исполняемого файла P IE, чтобы разрешить применение перемещений.)
Но если мы строим с помощью gcc -m32 -shared -nostdlib foo.s
для создания общей библиотеки, перенос текста по-прежнему разрешается:
$ gcc -m32 -shared -nostdlib foo.s && objdump -drwC -Mintel a.out
a.out: file format elf32-i386
Disassembly of section .text:
00001000 <_start>:
1000: 90 nop
1001: 90 nop
1002: e8 4f 24 12 00 call 123456 <_DYNAMIC+0x1204ae>
Обратите внимание, что при разборке использовалась информация о перемещении для вычисления правильной конечной цели вызова.
Но я думаю, что это на самом деле не работает, потому что вывод readelf не показывает никаких перемещений. Выполнение этого все еще не удается (даже перейти на правильный адрес); мы получаем 0xf7ffc002 <+2>: e8 4f 24 12 00 call 0xf811e456
В любом случае, сбои перемещения во время выполнения в GNU / Linux происходят только потому, что я злоупотребляю перемещением текста, я думаю. Записи перемещения для .o
объектных файлов полностью работают.