Реклама:

В начале первого прохода ассемблирования счетчик адресов команд устанавливается на 0. Этот шаг эквивалентен предположению, что объектный модуль во время выполнения будет находиться в ячейке с адресом 0. На рис. 7.3 показаны 4 объектных модуля, подготовленных для типичной машины. В этом примере каждый модуль начинается с команды перехода BRANCH к команде MOVE в том же модуле.

Задачи компоновщика

Рис. 7.3. Каждый модуль имеет собственное адресное пространство, начинающееся с нулевого адреса

Чтобы можно было запускать программу, компоновщик помещает объектные модули в основную память, формируя образ исполняемого двоичного кода (рис. 7.4, а). Цель - создать точный образ виртуального адресного пространства исполняемой программы внутри компоновщика и разместить все объектные модули по соответствующим адресам. Если физической или виртуальной памяти для формирования образа недостаточно, можно использовать дисковый файл. Обычно небольшой раздел памяти, начинающийся с нулевого адреса, используется для векторов прерываний, взаимодействия с операционной системой, обнаружения неинициализированных указателей и других целей, поэтому, как правило, программы начинаются не с нулевого адреса, а выше. В нашем примере программы начинаются с адреса 100.

Посмотрите на рис. 7.4, а. Хотя программа уже загружена в образ исполняемого двоичного файла, она еще не готова для выполнения. Посмотрим, что произойдет, если выполнение программы начнется с команды в начале модуля А. Программа не совершит перехода к команде MOVE, поскольку эта команда находится в ячейке с адресом 300. Фактически все команды обращения к памяти по той же причине выполнены не будут.

Здесь возникает проблема перераспределения памяти, поскольку каждый объектный модуль на рис. 7.3 занимает собственное адресное пространство. В машине с сегментированным адресным пространством (например, в Pentium 4) каждый объектный модуль теоретически может иметь собственное адресное пространство, если его поместить в отдельный сегмент. Однако для Pentium 4 только система OS/2 поддерживает такую структуру1.

Все версии Windows и UNIX поддерживают одно линейное адресное пространство, поэтому все объектные модули должны быть объединены в одном адресном пространстве.

Более того, команды вызова процедур на рис. 7.4, а вообще не будут работать. В ячейке с адресом 400 программист намеревается вызвать объектный модуль В, но поскольку каждая процедура транслируется отдельно, ассемблер не может определить, какой адрес вставлять в команду CALL В, поскольку адрес объектного модуля В до компоновки неизвестен. Эта проблема называется проблемой внешней ссылки. Обе проблемы решаются с помощью компоновщика.

Компоновщик объединяет отдельные адресные пространства объектных модулей в единое линейное адресное пространство. Для этого совершаются следующие шаги:

1. Компоновщик строит таблицу объектных модулей и их размеров.

2. На основе этой таблицы он приписывает начальные адреса каждому объектному модулю.

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

Необходимо отметить, что сегментный способ организации был использован только в первой версии OS/2, которая была 16-разрядной и разрабатывалась для микропроцессора 286. Поэтому относить эту систему к Pentium 4 представляется не вполне правильным. Начиная с 1993 года, все последующие версии OS/2 были 32-разрядными и, как и другие современные операционные системы, не поддерживают сегментирование, а используют только страничный механизм. - Примеч. научн. ред.

Задачи компоновщика

Рис. 7.4. Объектные модули после размещения в двоичном образе, но до перераспределения памяти и компоновки (а); те же объектные модули после компоновки и перераспределения памяти (б). В результате получается исполняемый двоичный код, который можно запускать

4. Компоновщик находит все команды, которые обращаются к процедурам, и вставляет в них адреса этих процедур.

Таблица 7.6 соответствует построенной на первом шаге таблице объектных модулей, представленных на рис. 7.4. В ней даются имя, длина и начальный адрес каждого модуля.

На рис. 7.4, б показано, как выглядит адресное пространство после завершения работы компоновщика.

Таблица 7.6. Имя, длина и начальный адрес каждого модуля на рис. 7.4

Модуль

Длина

Начальный адрес

А

В

С

1100

D

1600

Компоновка и загрузка || Оглавление || Структура объектного модуля