Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
шпора 97.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
778.75 Кб
Скачать

Что такое импорт

Импортируя идентификатор, необязательно прибегать к __declspec(dllimport) – можно использовать стандартное ключевое слово extern языка С. Но компилятор создаст чуть более эффективный код, если ему будет заранее известно, что идентификатор, на который мы ссылаемся, импортируется из LIB-файла DLL –модуля.

Разрешая ссылки на импортируемые идентификаторы, компоновщик создаст в конечном EXE-модуле раздел импорта (import section).

В нем перечисляются DLL, необходимые этому модулю, и идентификаторы, на которые есть ссылки из всех используемых DLL.

Выполнение EXE-модуля

При запуске EXE-файла загрузчик операционной системы создает для его процесса виртуальное пространство и проецирует на него исполняемый модуль. Далее загрузчик анализирует раздел импорта и пытается спроецировать все необходимые DLL на адресное пространство процесса.

Поскольку в разделе импорта указано только имя DLL (без пути), загрузчику приходится самому искать ее на дисковых устройствах в компьютере пользователя. Поиск DLL осуществляется в следующей последовательности:

  1. Каталог, содержащий EXE-файл

  2. Текущий каталог процесса

  3. Системный каталог Windows

  4. Основной каталог Windows

  5. Каталоги, указанные в переменной окружения PATH

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

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

Обнаружив идентификатор, загрузчик отыскивает его RVA и прибавляет к виртуальному адресу, по которому данная DLL размещена в адресное пространство процесса, а затем сохраняет полученный адрес в разделе импорта EXE-модуля. И с этого момента ссылка в коде на импортируемый идентификатор приводит к выборке его адреса из раздела импорта вызывающего модуля, открывая таким образом доступ к импортируемой переменной, функции или функции-члену С++ класса. Как только динамические связи будут установлены, первичный поток процесса начинает выполняться.

Загрузчик всех этих DLL и настройка ссылок занимает какое-то время. Чтобы сократить время загрузки приложения нужно модифицировать базовые адреса EXE- и DLL-модулей и провести их связывание.

Явная загрузка DLL и связывание идентификаторов

Чтобы поток мог вызвать функцию из DLL-модуля, DLL надо спроецировать на адресное пространство процесса, которому принадлежит этот поток. Делается это двумя способами:

  1. Код приложения просто ссылается на идентификаторы, содержащиеся в DLL, и тем самым заставляет загрузчик неявно загружать (и связывать) нужную DLL при запуске приложения.

  2. Явная загрузка и связывание требуемой DLL в период выполнения приложения. Иначе говоря, поток явно загружает DLL в адресное пространство процесса, получает виртуальный адрес необходимой DLL-функции и взывает ее по этому адресу. Изящество этого подхода в том, что все происходит в уже выполняемом приложении.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]