Рабочим названием платформы .NET было |
Формат метаданных. Взаимодействие программных компонентов
Метаданные служат для описания типов, находящихся в сборке .NET, и хранятся в исполняемых файлах. Для хранения метаданных используется достаточно сложный двоичный формат, изложение которого заняло бы слишком много времени, поэтому в этом разделе мы ограничимся лишь частичным и в большой степени поверхностным описанием формата метаданных.
Если поставить себе цель в двух словах охарактеризовать формат метаданных в сборках .NET, то можно сказать следующее: он был бы значительно проще, если бы его разработчики не уделяли чрезмерного внимания вопросу компактности хранения метаданных. Дело в том, что спецификация формата определяет по нескольку способов кодирования одной и той же информации, описывает способы сжатия отдельных элементов метаданных (например, сигнатур методов) и, тем самым, оказывается загроможденной множеством деталей, затрудняющим разработку метаинструментов.
Для того чтобы провести обзор формата метаданных в этом разделе, мы используем следующий прием: рассмотрим только те детали формата, которые используются в незатейливом примере, выводящем на экран надпись "Hello, World!", а затем ожидающим ввода данных с клавиатуры. Может показаться, что пример слишком прост, однако, как мы увидим в дальнейшем, генерация метаданных даже для такого несложного примера требует больших усилий. Сборку .NET, соответствующую нашему примеру, можно получить, если откомпилировать следующую программу, записанную в синтаксисе ассемблера ILASM:
.assembly HelloWorld { .hash algorithm 0x00008004 .ver 1:0:1:1 } .module HelloWorld.exe // hello() - единственный метод в нашей сборке .method public static void hello() cil managed { .entrypoint .maxstack 8 // Загружаем строку "Hello, World!" на стек ldstr "Hello, World!" // Выводим строку на экран call void [mscorlib]System.Console::WriteLine(string) // Ожидаем, пока пользователь введет строку call string [mscorlib]System.Console::ReadLine() // Выводим введенную строку на экран call void [mscorlib]System.Console::WriteLine(string) // Завершаем выполнение ret }
Расположение метаданных и кода внутри сборки
В предыдущем разделе данной главы мы рассмотрели формат исполняемых файлов .NET и выяснили, что он дает разработчику достаточно большую свободу для размещения отдельных элементов внутри исполняемого файла. В частности, исполняемые файлы могут содержать несколько секций, и расположение этих секций, а также данных внутри них является более или менее произвольным.
Давайте выберем для нашего учебного примера схему размещения данных внутри исполняемого файла, изображенную на рисунке 2.7. Мы будем использовать две секции: секция ".text" будет содержать всю основную информацию, а в секции ".reloc" будет размещена таблица релокаций.
На схеме видно, что в начале секции ".text" располагается тело метода hello, за которым следуют метаданные. Напомним, что расположение метаданных задается в заголовке CLI, который мы рассматривали в предыдущем разделе данной главы. Тем самым, мы вольны выбрать для метаданных практически любое место внутри секции, и их расположение, изображенное на схеме, является лишь одним из многих возможных вариантов.
Заметим, что метаданные и CIL-код практически не зависят от остальных элементов формата исполняемого файла. Они занимают часть сборки .NET, при этом заголовок CLI указывает на метаданные, а внутри метаданных хранятся RVA тел методов. Тело метода hello в нашем примере расположено в самом начале секции исключительно для того, чтобы облегчить вычисление RVA этого метода (RVA метода совпадает с RVA секции).