Отладка приложений


         

Подключение импортированных функций


Существует много способов подключения вызовов функций в программу. Довольно сложный способ заключается в следующем: нужно отыскать все CALL-инструкции вызовов и заменить указанный в них адрес одним из своих собственных. Этот подход сложен и чреват ошибками. К счастью, в случае DeadlockDetection функции, которые надо подключать, являются импортированными и их намного легче обрабатывать, чем CALL-инструкции.

Импортированной называют функцию, которая приходит из DLL. Например, когда программа вызывает функцию outputoebugstring, то она обращается к функции, которая постоянно находится в KERNEL32.DLL. Только начав заниматься программированием для Microsoft Win32, я думал, что вызов импортированной функции ведет себя точно так же, как вызов любой другой функции: инструкция CALL или инструкция ветвления передает управление на нужный адрес и начинает выполнять импортированную функцию. Единственным различием могло бы быть то, что в случае импортированной функции программный загрузчик операционной системы должен был бы пробежаться через выполняемый файл и подправить адреса вызовов так, чтобы учесть адрес загруженной в память DLL. Когда же я посмотрел, как на самом деле организовано обращение к импортированной функции, то был поражен простотой и красотой ее реализации.

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

Как же загрузчик сообщает вашему приложению, где можно найти импортированную функцию? Решение дьявольски остроумно. Подумав, куда направлены обращения к OutputDebugstring, вы скоро поймете, что каждое обращение должно направляться по одному и тому же адресу — адресу загрузки OutputDebugstring в памяти.

Содержание  Назад  Вперед