За последние дни мы узнали, как работает экспорт и импорт функций в 16-битных Windows, а также что экспорт функций в Win32 очень похож на экспорт функций в 16-битных Windows. Но 16-битный импорт был полностью переписан в мире 32-битных Windows.
Вспомните, что в 16-битных Windows правки для импортируемой функции размещались по всему сегменту цепочкой. Это прекрасно работало в Win16, потому что там было глобальное адресное пространство: сегменты кода разделялись глобально, так что будучи единожды загруженным, сегмент мог быть использован любым процессом. Но 32-битные Windows использовали изолированные адресные пространства. Если правки были расположены по всему сегменту, то загрузка сегмента кода с диска (вернее - страницы сегмента кода) означала его модификацию, что не давало разделять один сегмент несколькими процессами (механизм copy-on-write). Даже если бы таблица правок хранилась бы во внешнем сегменте, вам всё равно пришлось бы править сегмент кода, чтобы указать цели для переходов (если вы достаточно умны, то вы могли бы разделять страницы между процессами, если все правки были бы одинаковы для всех этих процессов; но учёт изменений для этой модели был бы полным кошмаром).
Но помимо простой неэффективности, идея применения правок импорта к сегменту кода просто невозможна. К примеру, Alpha AXP имеет инструкцию "call direct" (прямой вызов), но она ограничена функциями, которые находятся лишь на расстоянии не более 128 Кб от места вызова. Если вы хотите вызвать функцию, которая находится дальше от вас, то вы должны загрузить адрес назначения во временный регистр и вызвать функцию через этот регистр. И, как мы видели ранее, загрузка 32-битного значения в регистр на Alpha AXP является двухступенчатой операцией, которая зависит от того, установлен или очищен 15-й бит того значения, которое вы хотите загрузить. Поскольку это импортируемая функция, то на этапе компиляции или компоновки вы понятия не имеете будет ли адрес целевой функции иметь 15-й бит или нет.
И Alpha AXP была далеко не единственной архитектурой, которая ограничивала расстояние для прямых вызовов. К примеру, Intel ia64 (Itanium) мог делать прямые вызовы функций только в пределах 4 Мб, а популярные сегодня архитектуры AMD x86-64 и Intel EM64T ограничивают вас 2 Гб. Да, это не звучит как ограничение пока вы не вспомните, что это - 64-разрядные процессоры с 16 эксабайтами адресного пространства. И снова мы видим, что архитектура x86 является странной (прим.пер.: в том смысле, что на ней нет ограничения на прямые вызовы в отличие от многих других архитектур).
Оба момента выше сделали правки импорта в сегменте кода нежелательными (и невозможными). Вместо этого правки импорта пришлось применять к данным. Вместо того, чтобы применять правку к каждому месту, где вызывается импортируемая функция, применяется всего одна правка в таблице указателей на функции. Это означает, что вызовы импортируемых функций на самом деле являются косвенными вызовами через указатель. К примеру, на x86 это означает, что вместо
call ImportedFunction
генерируется код, который говорит call [__imp__ImportedFunction]
, где __imp__ImportedFunction - это имя переменной (места в таблице), которая содержит указатель на эту импортируемую функцию.Это означает, что правки импортируемых функций заключаются в простом поиске целевых адресов и их записи в таблицу импортируемых адресов (прим.пер.: хранимую в сегменте данных). Код не изменяется; он просто читает указатель на функцию в run-time и вызывает функцию через указатель.
С этой информацией мы теперь вооружены для более глубокого погружения в кое-какие последствия этого дизайна - что мы и сделаем в следующий раз.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.