Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Раздел 1 Системы программирования.doc
Скачиваний:
14
Добавлен:
01.04.2025
Размер:
471 Кб
Скачать
☆

Тема 1.4 Назначение и функции компоновщика

Компоновщик (или редактор связей) предназначен для связывания между со­бой объектных файлов, порождаемых компилятором, а также файлов библиотек, входящих в состав системы программирования. Название «компоновщик» про­исходит от англ. «link» (связывать, сцеплять). Во многих системах программи­рования файл компоновщика носит имя «linker», а разработчики на жаргонном языке нередко называют его «линковщик», или «линкер».

Объектный файл (или набор объектных файлов) не может быть исполнен до тех пор, пока все модули и секции не будут в нем увязаны между собой. Это и делает редактор связей (компоновщик). Результатом его работы является единый файл (часто называемый «исполняемым файлом»), который содержит весь текст ре­зультирующей программы на языке машинных кодов.

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

Ссылки (вызовы процедур или функций, обращения к переменным), которым компоновщик не смог найти соответствие, называются «неразрешенными»1. В гото­вой к выполнению результирующей программе не должно быть неразрешенных ссылок. Компоновщик порождает сообщение об ошибке, если при попытке со­брать объектные файлы в единое целое он не смог обнаружить какой-либо необ­ходимой составляющей.

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

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

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

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

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

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

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

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