Абсолютная адресация с использованием относительной адресации P C в перемещаемой программе. Какой будет запись модификации? - PullRequest
0 голосов
/ 01 февраля 2020

Я пытаюсь выполнить следующее упражнение из раздела 2.2 Системное программное обеспечение: Введение в программирование систем (для VTU), 3 / e , автор Leland L. Beck.

Существует ассемблер для машины, которая имеет только относительную адресацию счетчика программ. Если нам нужно собрать инструкцию, операнды которой являются абсолютным адресом в памяти - например, LDA 100, загрузить регистр A с адреса (шестнадцатеричного) 100 в памяти. Как такая инструкция могла бы быть собрана в перемещаемой программе?

Какие операции перемещения потребовались бы?

Я немного запутался о том, как абсолютная адресация может быть сделана с использованием P C -относительная адресация. Я полагаю, что ассемблеру необходимо создать команду для загрузчика, инструктирующую его сохранить начальный адрес программы (скажем, BEGIN) и перейти к адресу 100, когда он встречает LDA 100, уменьшив PC на PC - 100, тем самым прыгая по абсолютному адресу 100 в памяти. Однако я не уверен, как будет выглядеть запись модификации.

Может ли кто-нибудь дать разъяснения по этому поводу?

1 Ответ

3 голосов
/ 01 февраля 2020

Я думаю, что вам нужна запись о перемещении, которая дает абсолютный адрес, оставляя его до динамического 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 объектных файлов полностью работают.

...