Операционные системы. Курс лекций

Компоновщики и загрузчики ОС. Форматы COFF (command object file format) и PE (portable executable).


Компоновщик должен создать из объектных модулей формата COFF исполнительный объектный модуль формата PE. Компоновщик устанавливает связи между программным кодом и данными, объединяет фрагменты программного кода, объединяет форматы данных, объединяет объектные модули, выполняет формирование настроечных структур, необходимых загрузчику и т.д.

Компоновщик должен создать модуль, содержащий код, привязанный к конкретному процессору и ряд структур, необходимых для того, чтобы ОС смогла представить данный код для выполнения.

Компоновщик обеспечивает связывание различных типов. Тип связывания или тип компоновки определяет соответствие имени объекту или функции в программе, исходный текст которой располагается в нескольких модулях.

Различают статическое и динамическое связывание.

Статическое связывание бывает внешним и внутренним. Оно обеспечивается на стадии формирования исполняемого модуля еще до этапа выполнения программы.

Если объект локализован в одном модуле, используется внутренне связывание. Внешнее связывание выполняется компоновщиком, который на этапе сборки многомодульной программы устанавливает связь между уникальным объектом и обращениями к объекту из разных модулей программы.

При динамическом связывании компоновщик не имеет представления о том, какой конкретно объект будет соответствовать данному обращению. Динамическое связывание обеспечивается компилятором в результате подстановки специального кода, который выполняется непосредственно в ходе работы программы.

Компоновщик формирует необходимые структуры для работы с динамическими библиотеками и образует статические структуры.

Особенностью компоновщика exe-модулей для Windows является то, что он берет на себя часть функций загрузчика. Это сделано для того, чтобы максимально упростить загрузчик ОС, увеличив скорость загрузки программы и для упрощения процесса распределения памяти для различных программных модулей.

Поэтому компоновщик должен самостоятельно подготовить практически все необходимые структуры для выполнения программы.


Важнейшим понятием в исполняемом модуле является относительный виртуальный адрес RVA. RVA является смещением какого-либо элемента модуля относительно базового адреса в памяти или относительно начала файла на диске.

Наименьшая часть объектного файла, которая подвергается перемещению – это секция. Можно рассматривать секцию как элемент COFF или PE модуля, который содержит однотипную информацию. Объектный модуль COFF начинается со структуры Image File Header. – основной заголовок – информация о типе процессора, время создания, количество секций.



Таблица секций – это массив структур Image Section Header, каждая из которых полностью описывает конкретную секцию: имя секции – физический адрес секции в модуле, размер секции и тип содержимого в секции. Кроме параметров содержимого секции в Image Section Header указывается наличие таблицы поправок для данной секции и таблицы номеров строк.

В объектном модуле COFF таблица поправок и таблица номеров строк хранятся для каждой секции внутри самой секции непосредственно после данных.

Таблица поправок представляет собой массив структур Image Relocation. Данные поправки или привязки основаны на использовании таблицы символов и используются компоновщиком для настройки связей.

Таблица номеров строк является массивом структур Image Line Number – ставит в соответствие одной строке программного кода источник ее RVA в исполняемом отображении. Таблица номеров строк является отладочной информацией.

После заголовка Image Section Header последовательно располагаются сами секции. После секции идет таблица символов, которая представляет собой массив структур Image Symbol. Каждая такая структура описывает один символ, которым может являться имя секции, имя процедуры, имя переменной, области памяти и т.д.

Элементы в таблице символов совершенно различны и имеют разные типы. Поправки или привязки основаны на использовании таблицы символов. Элемент таблицы символов, описывающий поправку, содержит номер секции элемента, к которому осуществляется привязка и его RVA.



В свою очередь элемент таблицы поправок ссылающийся на элемент таблицы символов содержит смещение области кода, к которой необходима привязка и  тип привязки.

После таблицы символов идет таблица ASCII-Z строк, которые представляют собой длинные имена элементов таблицы символов. Если какой-то элемент таблицы символов имеет более 8 символов, то вместо самого имени в таблице символов содержится RVA на строку из таблицы ASCII-Z (zero) строк.

В простейшем случае компоновщик PE модулей выполняет простейшие функции:

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

- проанализировав таблицу символов компоновщик получит предварительную информацию о количестве секций PE, их расположении, последовательности и т.д.

Для того, чтобы иметь всю информацию о будущих секциях, нужно знать, какие секции компоновщик должен добавить от себя. Важнейшей секцией, формируемой компоновщиком, является секция импорта, где хранятся записи импорта.  

Так как таблица символов описывает функции типа external в объектных модулях, то можно построить секцию импорта на основе анализа таблицы символов.



1.    Выявляются символы external и сопоставляются с именами функций dll.

2.    Запись в секцию имен внешних функций и имен dll где они находятся.

3.    Создание 2х массивов указателей на структуры Image_Import_by_name

После уточнения количества будущих секций, их последовательностей и размеров можно приступить к сборке модулей секций PE. Секция PE может быть любого размера, поэтому компоновщик собирает всю однотипную информацию в одну секцию.

После создания секций нужно произвести привязку адресов в командах секции кода, например, к данным из секции данных. Хотя могут быть и другие привязки.

Привязки выполняются в соответствии с таблицами привязок объектных модулей Image Relocation. Далее можно добавить еще одну, содержащую базовые поправки адресов PE модуля на случай его отображения не по указанному в модуле адресу.

После того, как секции созданы и произведена настройка связей можно создать заголовки Image File Header, Image Optional Header, Image Section Header, хотя некоторые поля заголовком могут заполняться в начале или во время любого этапа.

Создание секции PE из секции COFF состоит из следующих шагов:

1.    установка указателя на первую секцию первого модуля;

2.    копирование/добавление информации в создаваемую PE секцию. Если есть привязки, то происходит редактирование связей;

3.    поиск следующих секций с информацией того же типа;

4.    если такие секции есть, то установка указателя на следующую подходящую секцию текущего модуля и переход к п.2;

5.    установка указателя на следующую подходящую секцию следующего модуля, т.е. PE секция собирается из секций COFF всех объектных модулей.


Содержание раздела