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


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

В прошлый раз мы рассмотрели каким образом функции экспортировались из 16-битных DLL. Сейчас мы посмотрим, как они импортировались.

Когда каждый сегмент загружается в память, все его содержимое загружается с диска, а затем выполняется привязка адресов. Данные адресной привязки для импортируемой функции состоят из имени запрашиваемой DLL библиотеки, запрашиваемой функции (либо имя функции, либо ее номер) и указателя на первую ячейку памяти в данном (импортирующем) сегменте, где необходимо произвести привязку. Все импортируемые адреса являются дальними адресами, так как они располагаются в другом сегменте. (Если бы они располагались в том же самом сегменте, они бы были в этой же DLL библиотеке, и тогда бы они не импортировались!) В 16-битной Windows, дальний адрес представляет собой 4 байта (двухбайтовый селектор и двухбайтовое смещение), а так как окончательный адрес на момент генерации DLL неизвестен, эти четыре байта просто резервируются до тех пор, пока туда не будет записан фактический адрес, когда будет выполнен импорт. Кроме того, эти четыре байта имеют еще одно предназначение.

Все вызовы в сегменте, которые импортируют одну и ту же функцию, объединены в связный список. В структуре данных для адресной привязки содержится адрес на первый элемент. Элементами в этом списке являются четырехбайтовые ячейки памяти, зарезервированные для адресов импортируемых функций. Каждый элемент содержит адрес следующего элемента. Например, предположим, у нас есть сегмент, которые требует двух коррекций адресов для функции GetPrivateProfileInt, которая находится в KERNEL.DLL под номером 127. Запись в таблице настройки адресов гласит: "Этому сегменту требуется функция 127 из KERNEL; начальный адрес находится по смещению 01D1". На диске данный сегмент выглядит примерно так:

01D0  9A

01D1  FE

01D2  01

01D3  00

01D0  00

01FD  9A

01FE   FF

01FF   FF

0200  00

0201  00

Чтобы выполнить привязку адреса, сперва вызывается GetProcAddress, чтобы получить адрес функции из сегмента KERNEL под номером 127. Затем мы обращаемся к ячейке, где расположен первый элемент списка (0x01D1), записываем  туда адрес функции и смотрим на значение, которое там было до того. Там был адрес 0x01FE, значит теперь мы идем по смещению 0x01FE и также записываем туда адрес. До этого там хранилось значение 0xFFFF, что означает конец цепочки подстройки адресов.

А что будет, если вызов GetProcAddress окажется неудачным? (Скажем, в модуле KERNEL нет функции с номером 127.) Тогда вместо записи адреса запрашиваемой функции загрузчик поместит туда адрес функции, которая выведет на экран сообщение о неустранимой ошибке: "Call to Undefined Dynalink".

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

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

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s