КаталогИндекс раздела
НазадОглавлениеВперед


Тема 4. Загрузчики и Редакторы связей

Основные понятия

Определение (ГОСТ)
Загрузчик - программа, которая подготавливает объектную программу к выполнению и инициирует ее выполнение.

Более детально функции Загрузчика следующие:

Не обязательно функции Загрузчика должны выполняться именно в той последовательности, в какой они описаны. Опишем эти функции более подробно.

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

Функция загрузки сводится к считыванию образа программы с диска (или другого внешнего носителя) в оперативную память.

Функция связывания состоит в компоновки программы из многих объектных модулей. Поскольку каждый из объектных модулей в составе программы был получен в результате отдельного процесса трансляции, который работает только с одним конкретным модулем, обращения к процедурам и данным, расположенным в других модулях, в объектных модулях не содержат актуальных адресов. Загрузчик же "видит" все объектные модули, входящие в состав программы, и он может вставить в обращения к внешним точкам правильные адреса. Загрузчики, которые выполняют функцию связывания вместе с другими функциями, называются Связывающими Загрузчиками. Выполнение функции связывания может быть переложено на отдельную программу, называемую Редактором связей или Компоновщиком. Редактор связей выполняет только функцию связывания - сборки программы из многих объектных модулей и формирование адресов в обращениях к внешним точкам. На выходе Редактора связей мы получаем загрузочный модуль.

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

Существуют программы, которые при написании рассчитываются на размещение в определенных адресах памяти, так называемые, абсолютные программы. Подготовка таких программ к выполнению значительно проще и выполняется она Абсолютным Загрузчиком. Функции такого Загрузчика гораздо проще:

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

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

Основные типы Загрузчиков - настраивающие и непосредственно связывающие.

Настраивающие Загрузчики

Настраивающий Загрузчик является первым шагом в сторону усложнения от Абсолютного Загрузчика. Функции связывания и перемещения решаются в нем не самым эффективным, но простейшим способом.

Связывание в Настраивающем Загрузчике.

Проблема связывания в Настраивающем Загрузчике решается при помощи Вектора Переходов. Вектор Переходов включается в состав объектного модуля и содержит список всех внешних имен, к которым есть обращение в модуле с полем адреса для каждого имени. Вектор Переходов заполняется при обработке директив типа EXT (перечисления внешних имен). В команды программы, обращающиеся к внешним именам вставляется обращение к адресному полю соответствующего элемента Вектора Переходов с признаком косвенного обращение. (Косвенное обращение означает, что обращение идет не по адресу, который задан в команде, а по адресу, который записан в ячейке, адрес которой задан в команде.)

При загрузке в оперативную память Вектор Переходов загружается вместе с кодами программы и остается в памяти все время выполнения программы.

Когда Загрузчик компонует программу из нескольких объектных модулей, он "узнает" все фактические адреса входных точек и в Вектора Переходов тех модулей, которые обращаются к данной входной точке вставляет эти адреса. Обращение к внешней точке, таким образом, производится косвенное через Вектор Переходов.

Перемещенне в Настраивающем Загрузчике.

Принятые в Настраивающих Загрузчиках методы позволяют легко реализовать настройку реальных адресов, заданных относительно начала программы. Сущность метода перемещения состоит в том, что с каждым словом кода программы (размер слова обычно равен размеру реального адреса) связывается "бит перемещения". Значение этого бита 0/1 является признаком неперемещаемого/перемещаемого слова. Если слово является неперемещаемым, оно оставляется Загрузчиком без изменений. Если слово является перемещаемым, то к значению в слове прибавляется стартовый адрес модуля в оперативной памяти. Биты перемещения могут упаковываться - например, описание 8 слов в одном байте.

Непосредственно Связывающие Загрузчики

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

Формат объектного модуля

Объектный модуль, поступающий на вход Загрузчика должен в той или иной форме содержать:

Один из вариантов организации объектного модуля описывается ниже.

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

Кодовая запись содержит адрес относительно начала модулей и коды программы. Кодовые записи строятся Ассемблером при генерации объектного кода - кодов команд и директив типа DC. Каждая кодовая запись начинается с адреса (относительно начала модуля), с которого начинается размещение ее содержимого. Разрывы в линейном пространстве адресов могут быть обусловлены директивами выделения памяти без записи в нее значений (директивы типа BSS) или разрывами, управляемыми программистом (директивы типа ORG).

Запись связываний содержит один или несколько элементов Таблицы внешних символов. Элемент Таблицы внешних символов имеет следующий формат:

Ассемблер формирует новые элементы Таблицы перемещений при обработке директив типа SEGMENT, ENT, EXT.

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

Относительный адрес и длина - адрес и длина того кода, который должен быть модифицирован. Имя символа - имя из Таблицы внешних символов, которое прибавляется к значению кода или вычитается из него. Бит операции - признак операции сложения или вычитания. Ассемблер генерирует элемент Таблицы перемещений, когда обрабатывает адресные выражения. Адресное выражение может быть абсолютным (независящими от адреса загрузки) или перемещаемым (зависящими от адреса загрузки) . Элементы Таблицы перемещений генерируются только для перемещаемых выражений. Рассмотрим адресное выражение:

    ADDR1 - ADDR2

Запись окончания формируется Ассемблером при обработке директивы END, она содержит стартовый адрес программы. Естественно, эта запись должна быть заполнена только в одном из объектных модулей, составляющих программу.

Алгоритм работы Непосредственно Связывающего Загрузчика

Наиболее простой алгоритм работы Загрузчика - двухпроходный.

На вход Загрузчика обязательно подается список объектных модулей, из которых составляется программа. Этот список может быть параметром программы-Загрузчика или находиться в отдельном файле. На первом проходе Загрузчик просматривает все объектные модули по списку и решает 2 задачи:

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

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

На 2-ом проходе Загрузчик снова читает все объектные модули по списку. При этом он уже размещает коды модуля в памяти и формирует для каждого модуля Таблицу внешних символов (локальную для модуля) и Таблицу перемещений. Адресные поля в этих таблицах он заполняет или корректирует с учетом фактического адреса модуля в памяти и содержимого Глобальной таблицы внешних символов. Затем он выполняет обработку Таблицы перемещений, используя для коррекции адресных кодов в программе значения из Локальной Таблицы внешних символов.

Алгоритм выполнения 1-го прохода - следующий:

  1. 1-й проход Загрузчика.
  2. Начальные установки. Создание пустой Глобальной таблицы. Стартовый адрес=пусто. Относительный адрес 1-го сегмента - 0. Размер программы - 0.
  3. Выборка следующего имени из списка объектных модулей. Если весь список объектных модулей обработан - переход на окончание 1-го прохода.
  4. Чтение заголовка очередной записи объектного модуля, если объектный модуль обработан полностью - переход к следующему модулю.
  5. Чтение остальной части записи (размер записи содержится в ее заголовке).
  6. Разветвление в зависимости от типа записи.
  7. При обработке записи окончания проверяется, имеется ли в записи стартовый адрес. Если стартового адреса нет - никакая другая обработка записи не производится.
  8. Если в записи есть стартовый адрес, проверяется, не был ли он уже установлен.
  9. Если стартовый адрес не был установлен, он устанавливается.
  10. Если стартовый адрес был установлен, выдается сообщение об ошибке. (Ни эта, ни последующие рассмотренные ошибки не приводят к немедленному завершению 1-го прохода, однако, если на 1-ом проходе были ошибки, 2-й проход не выполняется).
  11. При обработке записи связывания выполняется перебор элементов Таблицы внешних символов...
  12. ... и разветвление - в зависимости от типа элемента.
  13. Для элемента - сегмента вычисляется начальный адрес следующего сегмента и длина сегмента прибавляется к общему размеру программы.
  14. Для элемента - входной точки ищется имя точки в Глобальной таблице .
  15. Если имя не найдено в Глобальной таблице, в таблицу добавляется новый элемент.
  16. Если имя найдено в Глобальной таблице, - ошибка, неуникальное внешнее имя.
  17. При окончании 1-го прохода проверяется, установился ли адрес стартовой точки программы.
  18. Если этот адрес не установлен - ошибка.
  19. Если этот адрес установлен и в ходе выполнения 1-го прохода не было других ошибок, Загрузчик продолжает работу.
  20. Выделяется память для программы в соответствии с ее размером.
  21. В Глобальную таблицу внешних символов записываются фактические адреса.
  22. Выполняется 2-й проход.
  23. Освобождается Глобальная таблица
  24. Если не было ошибок на 2-ом проходе
  25. ... управление передается на стартовый адрес программы
  26. Загрузчик завершает работу.

Алгоритм выполнения 2-го прохода - следующий:

  1. 2-й проход Загрузчика
  2. Установка на начало списка имен объектных модулей.
  3. Выборка следующего имени из списка объектных модулей. Если весь список объектных модулей обработан - переход на окончание 2-го прохода.
  4. Создание для модуля Локальной таблицы внешних символов и Таблицы перемещений - пустых.
  5. Чтение заголовка очередной записи объектного модуля, если все записи модуля прочитаны - переход к обработке перемещений в модуле.
  6. Чтение остальной части записи (размер записи содержится в ее заголовке).
  7. Разветвление в зависимости от типа записи.
  8. Для кодовой записи считывается относительный адрес записи и переводится в фактический.
  9. Тело кодовой записи считывается и размещается в памяти по фактическому адресу.
  10. Для записи связывания перебираются находящиеся в ней элементы Таблицы имен
  11. Обработка разветвляется в зависимости от типа имени.
  12. Для имен сегментов или входных точек относительный адрес переводится в фактический.
  13. Имя внешней точки ищется в Глобальной таблице внешних имен.
  14. Если имя не найдено в Глобальной таблице, выдается сообщение об ошибке.
  15. Если имя найдено в Глобальной таблице, в значение адреса из Глобальной таблицы становится значением этого имени.
  16. Элемент с откорректированным адресом заносится в Локальную таблицу имен.
  17. Для записи перемещения перебираются находящиеся в ней элементы Таблицы перемещения.
  18. Относительный адрес в элементе заменяется на фактический...
  19. ... и элемент добавляется в Таблицу перемещений.
  20. После того, как весь модуль прочитан, выполняется перебор Таблицы перемещений модуля.
  21. Для каждого элемента Таблицы перемещений имя, записанное в его поле имени ищется в Локальной таблице имен и из Локальной таблицы имен выбирается связанный с этим именем адрес.
  22. Из кода программы выбирается код, адрес и длина которого записаны в элементе Таблицы перемещений.
  23. Над выбранным кодом и адресом, выбранным из Таблицы имен выполняется операция сложения или вычитания, результат записывается на место кода.
  24. После перебора всей Таблицы перемещений, освобождаются Таблица перемещений и Локальная таблица имен модуля, и управление передается на обработку следующего модуля.
  25. После обработки всех объектных модулей 2-й проход заканчивается.


НазадОглавлениеВперед
КаталогИндекс раздела