Загрузка CLR в процесс. Подробности.

Ваш отзыв

clr

В известной книге Джеффри Рихтера «CLR via C#» достаточно подробно описан процесс загрузки CLR в процесс, создание хоста, затронуты вопросы его тонкой настройки. Но в процессе работы возник вопрос: а что же происходит между созданием процесса в Windows и тем, когда приложение начнет работу? Откуда система знает, что загружая сборку, надо просмотреть определенную часть заголовка исполняемого файла, да и что это за часть? Попробуем разобраться…

Для начала запустим любой exe-шник, представляющий из себя управляемую сборку и откроем, например, ProcessExplorer. У меня это пустая формочка Windows WinForm. Найдем в списке работающих процессов наш exe-шник, щелкнем по нему правой кнопкой мыши и выберем пункт контекстного меню «Launch Depends…». Откроется окно, как на рисунке ниже:

processexplorer

В левой части окна мы видим, что цепочка зависимостей включает в себя MsCorEE.dll, а также NtDll.dll. Запомним это.

Управляемые сборки имеют тот же формат. что и неуправляемые, а именно PE (Portable Executable) для 32-разрядных сборок (PE32) и PE32+ для 64-разрядных. PE-файлы обязательно имеют PE-заголовок. Подробности устройства PE-заголовка можно начать изучать, например, с Википедии wp-monalisa icon.

В заголовке PE есть массив DataDirectory, расположенный со смещением 96 в 32-х разрядных машинах и 112 в 64-разрядных. Современные компиляторы «порождают» данный массив 16-элементным, хотя теоретически можно и побольше (источник — книга .NET IL Assembler).

Особое внимание стоит уделить 13-му элементу данного массива IAT (Import Address Table), ссылающегося на 2-й — Import Table Address and size. Неуправляемые компиляторы типа VC++ могут сюда импортировать внешние функции каких-либо неуправляемых сборок. Управляемые компиляторы, типа компилятора языка C# сюда ничего кроме точки вызова CLR (mscoree!_CorExeMain) не записывают.

Итак, я попросила Вас запомнить названия некоторых dll-ек при загрузке нашей сборки. В Ntdll.dll находится загрузчик сборок Windows, в т.ч. и управляемых. При загрузке файла загрузчик начинает читать, что же написано в PE-заголовке. Современные версии загрузчика в Windows (XP и моложе) 100% распознают CLR-заголовок и автоматически начинают загрузку MsCorEE.dll, которая, в свою очередь, начинает загрузку CLR, как будет описано ниже. Старшие выпуски Windows не понимают этого заголовка. Именно поэтому управляемые сборки в своем заголовке в разделе IAT имеют запись со специальным вызовом функции  mscoree!_CorExeMain, чтобы сборка все же запустилась при наличии установленного .NET. Более молодые Windows попросту игнорируют данную инструкцию.

16-ый элемент зарезервирован и в управляемых сборках не используется. А вот 15-й — это как раз искомый CLR-заголовок! Если раздел RVA (Relative Virtual Address) пустой, стало быть данный файл не является управляемой сборкой .NET.

В заголовок CLR, располагаемый в readonly-секции .text PE-файла, включены требуемая версия CLR для исполнения кода сборки, определено расположение точки входа сборки,  некоторые флаги, расположение файлов ресурсов, модулей и проч. Посмотреть его содержимое можно воспользовавшись, например, консольной утилитой dumpbin, поставляемой вместе с Visual Studio, запускаемой с флагом /clrheader и полным адресом до сборки. Вот пример такого заголовка:

File Type: EXECUTABLE IMAGE

clr Header:

48 cb
2.05 runtime version
2678 [ 1D14] RVA [size] of MetaData Directory
20003 flags
IL Only
32-Bit Required
32-Bit Preferred
6000001 entry point token
2050 [ B8] RVA [size] of Resources Directory
0 [ 0] RVA [size] of StrongNameSignature Directory
0 [ 0] RVA [size] of CodeManagerTable Directory
0 [ 0] RVA [size] of VTableFixups Directory
0 [ 0] RVA [size] of ExportAddressTableJumps Directory
0 [ 0] RVA [size] of ManagedNativeHeader Directory

Summary

2000 .reloc
4000 .rsrc
4000 .text

Также на заголовок исполняемого файла можно взглянуть при помощи утилиты ildasm.exe. Для этого необходимо в меню Вид -> Метаданные поставить галочку напротив пункта «Исходные данные: заголовок» и нажать комбинацию клавиш Ctrl + M. Откроется окно с PE-заголовком и CLR-заголовком исполняемого файла. Но это все визуализация. Точный состав этого заголовка можно посмотреть в файле CorHdr.h, распространяемый с Microsoft .NET Framework SDK, структура IMAGE_COR20_HEADER. Замечу, что разбор заголовка CLR не входит в рамки данной статьи, если это будет необходимо, этому будет посвящена отдельная статья на данном блоге wp-monalisa icon.

Итак, Windows тем или иным способом узнала, что запускаемый файл является управляемой сборкой. Уже легче wp-monalisa icon. Предполагаем, что на исследуемой машине установлен .NET 4.0 и моложе, т.к. эмм… 2016-ый на дворе! MsCorEE.dll вызывает функцию CLRCreateInstance, которая способна нам вернуть один из трех интерфейсов — ICLRMetaHost, ICLRMetaHostPolicy или ICLRDebugging. Первый метод предоставляет методы для непосредственной активации CLR, второй — для указания предпочтительной версии рантайма, третий — методы для загрузки/выгрузки модулей отладки. Нас в данном случае интересует интерфейс ICLRMetaHost. Его метод GetRuntime возвращает указатель на интерфейс ICLRRuntimeInfo. После вызова его метода GetInterface MsCorEE.dll загружает в текущий процесс нужную версию CLR, возвращая при этом один из запрошенных интерфейсов, среди которых стоит отметить ICLRRuntimeHost. Затем у этого интерфейса можно вызвать метод Start(), который заставит «стартануть» CLR, а затем, например, методом ExecuteInDefaultAppDomain вызвать какой-нибудь метод нашей сборки. Метод Start() в большинстве случаев не обязателен, т.к. CLR при вызове метода управляемого кода должна сама начать свою работу. Перед тем, как выполнить какой-либо код, CLR создает ТРИ домена приложений:

  1. SystemDomain (синглетон)
  2. SharedDomain (синглетон)
  3. DefaultDomain

Первый домен инициализирует предыдущие два, затем в shareddomain загружает mscorlib.dll. В процессе работы он следит за работами всех остальных доменов и при необходимости их выгружает, а также загружает новые.

Во второй домен загружается нейтральные к доменам сборки.

В третий домен загружается пользовательская сборка.

Более подробно про эти домены можно почитать в старенькой статье Microsoft Внутреннее устройство .NET Framework.

И вот только теперь перед нами возникает наша Windows-формочка, которую мы так усердно формировали wp-monalisa icon. Вот такой это продолжительный и интересный процесс.

Подпишитесь на обновления блога!


Оставьте комментарий

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url=""> <img src="" alt="" class="" width="" height="">

http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ab.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ac.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ag.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ah.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ai.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ak.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/am.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/an.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ao.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/aq.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ar.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/at.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/av.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/aw.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/ay.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/az.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/bb.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/bc.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/bd.gif 
http://noservice.ru/wp-content/plugins/wp-monalisa/icons/be.gif 
больше...