Как загрузчик отображает DLL в адресное пространство обработки

Мне любопытно узнать, как загрузчик отображает DLL для обработки адресного пространства. Как загрузчик делает эту магию. Пример высоко ценится.

спасибо заранее.

4 ответов


какой уровень детализации вы ищете? На базовом уровне все динамические компоновщики работают практически одинаково:

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

хорошо, я предполагаю, что сторона Windows здесь. Что происходит при загрузке PE-файла, так это то, что загрузчик (содержащийся в NTDLL) будет делать следующее:

  1. найдите каждую из библиотек, используя семантику поиска DLL (системную и специфичную для уровня исправления), известные библиотеки DLL освобождены от этого
  2. сопоставьте файл в память (MMF), где страницы копируются при записи (CoW)
  3. пересеките каталог импорта и для каждого начала импорта (рекурсивно) в пункт 1.
  4. разрешите перемещения, которые большую часть времени представляют собой очень ограниченное количество объектов, поскольку сам код является независимым от позиции кодом (PIC)
  5. (IIRC) исправьте EAT от RVA (относительный виртуальный адрес) до VA (виртуальный адрес в текущем пространстве памяти процесса)
  6. исправьте IAT (таблицу адресов импорта), чтобы ссылаться на импорт с их фактическим адресом в пространстве памяти процесса
  7. для вызова DLL DLLMain() для EXE создайте поток, начальный адрес которого находится в точке входа PE-файла (это также упрощено, потому что фактический начальный адрес находится внутри kernel32.dll для процессов Win32)

теперь при компиляции кода зависит от компоновщика, как ссылается внешняя функция. Некоторые компоновщики создают заглушки, так что теоретически попытка проверить адрес функции на NULL всегда будет говорить, что это не NULL. Это причуда, о которой вы должны знать, если и когда ваш компоновщик затронут. Другие ссылка на запись IAT напрямую, в этом случае адрес неферментированной функции (думаю, загруженные с задержкой библиотеки DLL) может быть нулевым, и обработчик SEH затем вызовет помощник по задержке загрузки и (попытка) разрешить адрес функции, прежде чем возобновить выполнение в точке он не удался.

существует много бюрократии, связанной с вышеуказанным процессом, который я упрощал.

суть того, что вы хотели знать, заключается в том, что отображение в процесс происходит как ММФ, хотя вы можете искусственно имитировать поведение с пространством кучи. Однако, если вы помните точку зрения о корове, это суть идеи DLL. На самом деле то же самое копия (большинства) страниц библиотеки DLL будет совместно использоваться процессами, которые загружают библиотеку DLL particula. Страницы, которые не являются общими, - это те, на которые мы писали, например, при разрешении перемещений и подобных вещей. В этом случае каждый процесс имеет-теперь измененную-копию оригинала страница.

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


Если вы действительно заинтересованы, вы должны читать книги компоновщики и загрузчики.


предполагая, что это в Windows (DLL намекает на это), вы можете прочитать Динамическое Связывание Во Время Выполнения на странице документации. Он не указывает подробно как DLL отображается в адресное пространство; я думаю, вам не нужно это знать.