Рабочим названием платформы .NET было |
Формат метаданных. Взаимодействие программных компонентов
Структура метаданных
На рисунке 2.8 представлена схема структуры метаданных. Метаданные начинаются с заголовка, называемого корнем метаданных (metadata root). Корень метаданных начинается с 32-разрядной сигнатуры 0x424A5342. Если каждый байт сигнатуры рассматривать в виде ASCII-кода, то получится строка "BSJB", составленная из начальных букв имен четырех основных разработчиков .NET Framework: Брайана Харри (Brian Harry), Сьюзан Радке-Спроулл (Susan Radke-Sproull), Джейсона Зандера (Jason Zander) и Билла Эванса (Bill Evans). Далее в корне метаданных следует информация, относящаяся к версии .NET, а заканчивается корень метаданных 16-разрядным целым числом, содержащим количество так называемых потоков метаданных, заголовки которых располагаются непосредственно после корня метаданных.
Потоки метаданных (metadata streams) предназначены для хранения определенных видов информации. Заголовок каждого потока метаданных представляет собой структуру, состоящую из трех полей:
long Offset;
Смещение потока метаданных относительно начала метаданных в файле (то есть относительно начала корня метаданных).
long Size;
Размер потока метаданных в байтах (должен быть кратен четырем).
char Name[x];
ASCIIZ-строка, содержащая имя потока метаданных. Это поле имеет переменную длину.
В спецификации CLI определено пять видов потоков метаданных. Четыре потока метаданных представляют собой так называемые кучи, то есть хранилища однородных объектов, таких как строки и GUID'ы, и один поток метаданных имеет реляционную структуру и содержит таблицы метаданных. В таблице 2.2 приведено описание каждого из пяти потоков.
Таблицы метаданных
В спецификации CLI определены несколько десятков видов таблиц метаданных. Мы ограничимся рассмотрением только тех из них, которые используются в нашем учебном примере.
На рис. 2.9 представлена структура потока таблиц метаданных для учебного примера. Поток таблиц начинается с заголовка таблиц, непосредственно после которого следуют сами таблицы.
Заголовок таблиц метаданных содержит большое количество полей, из которых нас интересуют в первую очередь три поля:
char HeapSizes;
Различные биты этого поля задают размеры индексов, используемых для адресации куч метаданных. Бит 0 соответствует куче строк, бит 1 - куче GUID'ов, бит 3 - куче двоичных данных.
Если некоторый бит установлен, это означает, что соответствующая куча адресуется 32-разрядными индексами. В противном случае куча адресуется 16-разрядными индексами.
char Valid[8];
Размер этого поля - 64 бита. При этом каждый бит соответствует одной таблице метаданных. Если некоторый бит установлен, значит, соответствующая ему таблица присутствует в метаданных.
unsigned long Rows[7];
Массив 32-разрядных целых чисел, содержащих количество записей в каждой из присутствующих таблиц метаданных. В нашем учебном примере этот массив имеет размер 7, так как мы используем семь таблиц.
На рис. 2.10 представлено распределение элементов метаданных, используемых в учебном примере, по таблицам метаданных. При этом каждая таблица имеет порядковый номер, который соответствует номеру описывающего ее бита в массиве Valid.
Давайте подробнее рассмотрим каждую из семи используемых в примере таблиц метаданных.
Таблица сборок (Assembly - 0x20)
В этой таблице содержится только одна запись, описывающая нашу сборку. В этой записи для нас интерес представляют следующие поля:
short MajorVersion; short MinorVersion; short BuildNumber; short RevisionNumber;
Эти четыре поля хранят информацию о версии сборки.
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя сборки ("HelloWorld").
Таблица модулей (Module - 0x00)
Таблица модулей содержит только одну запись, описывающую модуль. В этой записи существенными являются два поля:
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя модуля ("HelloWorld.exe").
short Mvid;
Это поле содержит индекс в куче GUID'ов, по которому хранится глобальный уникальный идентификатор модуля.
Таблица определенных в сборке типов (TypeDef - 0x02)
В этой таблице каждая запись соответствует одному типу, объявленному в сборке. В нашем учебном примере нет классов, но для того чтобы объявить глобальную функцию, нам требуется специальный абстрактный тип <Module>. Дело в том, что все глобальные функции и поля считаются принадлежащими этому типу. Запись, описывающая этот тип, содержит следующие интересующие нас поля:
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя типа ("<Module>").
short FieldList; short MethodList;
Эти два поля содержат индексы в таблицах полей и методов, начиная с которых расположены описатели полей и методов типа.
Таблица методов (Method - 0x06)
Таблица методов описывает методы, объявленные в сборке. Каждая запись этой таблицы содержит информацию об одном методе, представленную в следующих полях:
long RVA;
RVA тела метода в исполняемом файле.
short Flags;
Набор флагов, задающих область видимости метода и другие его атрибуты.
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя метода.
short Signature;
Индекс в куче двоичных данных, по которому расположена сигнатура метода.
short ParamList;
В этом поле хранится индекс в таблице описателей параметров метода.
Таблица импортируемых сборок (AssemblyRef - 0x23)
Все импортируемые сборки должны быть перечислены в таблице импортируемых сборок. Каждая запись этой таблицы содержит следующие поля:
short MajorVersion; short MinorVersion; short BuildNumber; short RevisionNumber;
Эти четыре поля хранят информацию о версии импортируемой сборки.
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя импортируемой сборки (в нашем случае это "mscorlib").
Таблица импортируемых типов (TypeRef - 0x01)
Каждая запись в этой таблице соответствует одному из импортируемых типов и содержит следующие поля:
short ResolutionScope;
Специальным образом закодированная информация о том, какой сборке или какому модулю принадлежит данный тип.
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя импортируемого типа (в нашем случае это "Console").
short Namespace;
Это поле содержит индекс в куче строк, по которому хранится имя пространства имен; данному пространству принадлежит импортируемый тип (в нашем случае это "System").
Таблица членов импортируемых типов (MemberRef - 0x0A)
В таблице членов импортируемых типов перечислены все методы, поля и свойства этих типов, которые используются в программе. Каждая запись этой таблицы содержит следующие поля:
short Class;
Специальным образом закодированная информация об импортируемом типе.
short Name;
Это поле содержит индекс в куче строк, по которому хранится имя члена импортируемого типа.
short Signature;
Индекс в куче двоичных данных, по которому расположена сигнатура члена импортируемого типа.