Далее используется термин RVA (relative virtual address), я его поясню. Все адреса в защищенных многозадачных системах (не только на x286..586) обычно делаются `виртуальными`: То есть, пользовательское приложение не должно иметь шанс узнать что-либо о физических адресах — иначе оно теоретически может разрушить любую защиту операционной системы. В Windows строгой защиты в этом смысле нет, но есть еще одна причина `виртуальности` адресов — динамическая загрузка/выгрузка данных из ОЗУ на диск для организации виртуальной памяти. Процессор аппаратно, `на лету`, транслирует виртуальные адреся в физические по таблицам, созданным ядром операционной системы.
Теперь о слове `relative`. Операционной системе, по большому счету, без разницы, какой именно виртуальный адрес дать первому байту образа исполняемого файла в ОЗУ. А линкеру и самой программе, в ряде случаев, удобнее работать с конкретным значением. Оно называется `ImageBase`; линкер записывает его в заголовке PE-файла. По техническим причинам, оно не может быть произвольным для Windows-программ. В Delphi есть директива `{$ImageBase …}`. Так вот, RVA объекта – это его смещение относительно значения `ImageBase`. Обычный адрес объекта (он, кстати, тоже виртуальный) есть сумма значений глобальной переменной `ImageBase` и `RVA` данного объекта.
В тексте использована ассемблерная мнемоника: `DD` и `DW` (Define Double и Define Word), что означает, соответственно, 32– и 16-разрядное слово. Символ `|` означает `или`, `либо`.
Описание формата ресурсов в MS PE COFF.
Я делаю сокращенное изложение фрагмента документации PE COFF. Я полагаю, этого более-менее достаточно, чтобы разобраться, при желании, с текстом примера Delphi. Файл PE.TXT (author Micheal J. O'Leary) взят из документации Microsoft C. Он же входит в MS Software Developers Kit (SDK) и в комплект поставки большинства компиляторов C для Win32. Если Вам интересно положение корневого каталога ресурсов в заголовке PE COFF или более подробный формат заголовка – можно смотреть исходные тексты проекта проекта RSEXPLOR или, разумеется, сам первоисточник — PE.TXT
Ресурсы индексированы как многоуровневое двоичное дерево. Технологически возможно 2**31 уровней, но в Windows стандартно используются только три: первый — TYPE (тип), далее — NAME (имя), далее — LANGUAGE (язык). Ресурсы должны быть отсортированы по определенным правилам – для ускорения поиска.
Типичное расположение ресурсов в файле: сначала лежит `RESOURCE DIRECTORY` (каталог/каталоги ресурсов), затем – `RESOURCE DATA` (собственно данные ресурсов).
Каталог ресурсов довольно похож, по структуре, на каталоги дисков. Он содержит записи (`DIR ENTRIES` – см. далее), которые указывают либо на ресурсы, либо на другие каталоги (точнее – подкаталоги) ресурсов. В отличие от дисков, сами данные не разносятся по кластерам, а наоборот – их стараются плотнее прижать друг к другу, поскольку никто не собирается вставлять туда дополнительные данные после сборки (линковки) исполняемого файла.
Каталог ресурсов начинается с заголовка (четыре 32-битных слова):
DD RESOURCE FLAGS
DD TIME/DATE STAMP
DW MAJOR VERSION, DW MINOR VERSION
DW # NAME ENTRY, DW # ID ENTRY
декларация в RXTypes.Pas:
IMAGE_RESOURCE_DIRECTORY = packed record
Characteristics : DWORD;
TimeDateStamp : DWORD;
MajorVersion : WORD;
MinorVersion : WORD;
NumberOfNamedEntries : WORD;
NumberOfIdEntries : WORD;
end;
Здесь важны два поля: `# NAME ENTRY` — число точек входа, имеющих имена, и `# ID ENTRY` — число точек входа, имеющих вместо имен целочисленные идентификаторы.
За заголовком следует массив из записей `RESOURCE DIR ENTRIES` (точек входа каталога). Там лежат `# NAME ENTRY`+ `# ID ENTRY` записей типа `DIR ENTRY`. Формат записи `DIR ENTRY` — два 32-битных слова:
DD NAME RVA | INTEGER ID
DD DATA ENTRY RVA | SUBDIR RVA
декларация в RXTypes.Pas:
IMAGE_RESOURCE_DIRECTORY_ENTRY = packed record
Name: DWORD; // Or ID: Word (Union)
OffsetToData: DWORD;
end;
Первое поле содержит либо `NAME RVA` — адрес строки (UNICODE) с именем, либо — `INTEGER ID` – целочисленный идентификатор. `INTEGER ID` может быть, например, одним из стандартных кодов типа ресурса или заданным пользователем кодом строки в таблице строк.
Самый старший бит второго поля (31-й бит) называется `Escape-флагом`. Если он установлен в `1`, считается что данная `DIR ENTRY` — ссылка на другой подкаталог ресурсов. Если сброшен в `0` — данная запись ссылка на данные ресурса. Понятно, при вычислении адреса этот бит всегда должен считаться `0`.
Строка, на которую указывает `NAME RVA`, очень похожа на паскалевскую short-string, только вместо байтов она состоит из 16-битные слов. Самое первое слово – длина строки, за ним лежат 16-битные символы UNICODE. Физически линкер кладет эти строки переменной длиины между каталогами и собственно данными ресурсов.
Читать дальше