Как DLL функции импортировались в 32-битной Windows?


Перевод с блога The Old New Thing. Оригинал здесь.

В прошлых частях мы изучили, как 16-битная Windows экспортирует и импортирует функции из DLL и обнаружили, что экспортирование функций из 32-битных DLL практически ничем не отличается от экспортирования из 16-битных DLL. Но вот тот способ, при помощи которого функции импортируются в 16-битных DLL просто не работает в 32-битном мире.

Вспомните, что в 16-битной Windows, подстройка адресов для импортируемой функции затрагивала весь кодовый сегмент. Это отлично работало в 16-битной Windows, так как там было одно адресное пространство: сегменты кода разделялись глобально для всей системы, и как только сегмент был загружен, каждый процесс мог его использовать. Но 32-битная Windows использует раздельные адресные пространства. И если бы подстройка адресов затрагивала кодовый сегмент, то тогда загрузка страницы кода с диска обязательно влекла бы за собой ее модификацию для подстройки адресов, и такие страницы нельзя было бы разделять между несколькими процессами. Даже если бы таблицы подстройки адресов хранились вне кодового сегмента, все равно пришлось бы модифицировать кодовые страницы для установки адресов переходов. (Обладая достаточной сноровкой, вы смогли бы управлять разделением таких страниц между процессами в тех случаях, когда все модификации на странице являются одинаковыми для нескольких процессов, но учет таких страниц – тот еще геморрой.)

Но, кроме того, что это неэффективно, идея модификации кодовых сегментов для подстройки адресов еще и просто невозможна. У процессор Alpha AXP есть команда "call direct", однако она может применяться для вызова функций, расположенных в пределах 128 килобайт. Если вы хотите вызвать функцию, которая расположена дальше, вы должны загрузить ее адрес во временный регистр и вызвать через этот регистр. Но дело в том, что загрузка 32-битного значения в регистр Alpha AXP – это двухшаговая операция, которая зависит от значения бита 15. Так как мы имеем дело с импортируемой функцией, то во время компиляции или компоновки мы понятия не имеем о том, какое значение имеет 15-ый бит в адресе импортируемой функции.

(Причем Alpha AXP вряд ли единственная архитектура, ограничивающая прямые вызовы. Intel IA64 может выполнять прямые вызовы функций не далее чем на 4МБ, AMD x86-64 и Intel EM64T могут достигать 2ГБ. На первый взгляд это довольно много до тех пор, пока не вспоминаешь, что они являются 64-битными процессорами с адресным пространством в 16 экзабайт (16*10^18). Все-таки архитектура x86 – это извращение.)

Обе вышеприведенные причины делают нежелательным (или невозможным), модификацию кода при подстройке адресов. Значит необходимо модифицировать данные. Вместо того, чтобы записывать адрес функции в каждом месте, откуда она вызывается, мы записываем его в таблицу указателей на функции. Это означает, что вызовы импортируемых функций на самом деле являются косвенными через указатель на функцию. На х86 это означает, что вместо команды call ImportedFunction, кодогенератор вставляет команду call [__imp__ImportedFunction], где __imp__ImportedFunction – это имя переменной, содержащей адрес импортируемой функции.

Это означает, что позднее связывание – это просто поиск фактических адресов и запись их в таблицу адресов импортируемых функций. Сам код не меняется, он просто извлекает адрес функции во время исполнения программы и осуществляет вызов по этому адресу.

Обладая этой несложной информацией, мы теперь готовы рассмотреть более глубокие последствия такого подхода, что мы и сделаем в следующих частях.

Реклама
Запись опубликована в рубрике DLL. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s