Как лучше всего передать таблицу Global Offset Table (GOT) для моего языка на x86? - PullRequest
2 голосов
/ 27 февраля 2009

Я пишу небольшой загрузчик программ для моего языка, потому что я разочаровался в понимании формата ELF (и, делая это, я, в конечном счете, могу понять его лучше). Я mmap файлы в памяти, и смокинг радуется что угодно ..

Я не хочу препятствовать совместному использованию программы, внося в нее какие-либо изменения. Поэтому в итоге я делаю то же самое, что и C и elf: глобальная таблица смещений.

Проблема в том, как я могу передать GOT для моей программы?

Первое, что приходит на ум, это передать его в аргументе регистра или стека. В регистре это было бы здорово, но x86 запаздывает по количеству регистров. Это может означать, что я потеряю ebx или ebp или что-то подобное. В разумной архитектуре это было бы справедливым компромиссом. В x86 ощущается небольшая ошибка.

Разборка разделяемой библиотеки показывает, что gcc делает это как IP-относительную адресацию. Если бы я сделал это, это было бы:

    call 0
here:
    pop eax
    ; do something with [eax + (got - here) + index*4]

Хотя частично это кажется сложным. Мне не нравится делать это.

Есть еще идеи, кто-нибудь?

Редактировать: Когда я справился с этим с несколькими библиотеками, я понял следующее: у меня будет несколько GOT для каждого приложения, и использование определенного GOT зависит от того, какой кусок кода я нахожусь. Поэтому сохранение GOT в отдельном реестре потребуются некоторые дополнительные уловки, о которых я не знаю. Я хотел бы знать, как они решают эту проблему при ведении GOT в регистрах.

1 Ответ

1 голос
/ 27 февраля 2009

Вы можете использовать один из регистров сегмента (или его базу) для базы вашего двоичного изображения. Таким образом, вы бы ссылались на ваши глобальные данные, например. как FS: ххх.

Эти регистры являются остатками так называемой модели сегментированной памяти. По сути, сегменты являются «окнами» в линейное адресное пространство с заданной базой (и пределом), и если вы используете их для адресации (например, если адрес 0010: 00000001), то результирующий адрес будет (база сегмента с селектором 0010 ) +00000001. База (а также другие параметры) сегмента хранятся в таблице дескрипторов (их больше), которая является специальной областью в памяти. Они могут быть изменены только в режиме ядра, в linux есть системные вызовы (modify_ldt, arch_prctl). В 64-битном режиме ситуация немного сложнее.

См. Руководство по архитектуре AMD64 , особенно том 2: Системное программирование.

...