Редактор связей Редактор связей СОДЕРЖАНИЕ 1. Основные понятия 2 1.1. Конфигурация памяти 2 1.2. Секции 3 1.3. Адреса 3 1.4. Связывание 3 1.5. Объектный файл 3 2. Управляющий язык редактора связей 5 2.1. Выражения 5 2.2. Оператор присваивания 6 2.3. Описание конфигурации памяти 7 2.4. Предложения определения секций 9 2.4.1. Спецификации файлов 9 2.4.2. Указание адреса загрузки секции 10 2.4.3. Выравнивание выходной секции 11 2.4.4. Группировка выходных секций 12 2.4.5. Создание пустот в выходных секциях 14 2.4.6. Создание и определение имен при редактировании связей 15 2.4.7. Размещение секций в именованных областях памяти 16 2.4.8. Инициализация пустот и секций .bss 17 3. Прочие возможности редактора связей 19 3.1. Определение точки входа 19 3.2. Использование библиотек объектных файлов 19 3.3. Обход неконфигурируемых областей памяти 21 3.4. Алгоритм размещения 22 3.5. Инкрементальное редактирование связей 22 3.6. Секции DSECT, COPY, INFO и OVERLAY 24 3.7. Выравнивание секций в выходном файле 25 3.8. Ненастраиваемые входные файлы 26 4. Синтаксис управляющего языка редактора связей 27 1. ОСНОВНЫЕ ПОНЯТИЯ В статье ld(1) Справочника пользователя перечислены опции ко- мандной строки редактора связей, некоторые из которых можно указывать также при вызове C-компилятора cc(1). В настоящей публикации рассматривается управляющий язык редактора связей. Управляющий язык редактора связей предоставляет следующие воз- можности: Описание конфигурации памяти целевого компьютера. Объединение секций объектного файла в порядке, отличном от подразумеваемого. Назначение для секций определенных адресов или диапазо- на адресов памяти. Определение или переопределение глобальных имен. При обычных обстоятельствах нет надобности в жестком контроле за объектными файлами и их последующим расположением в памяти. Управляющий язык используется, когда все же необходимо детально контролировать ход и результат работы редактора связей. Предложения управляющего языка редактора связей помещаются в файл, имя которого указывается в командной строке ld(1). Если файл указан в командной строке и не опознан в качестве объект- ного модуля или библиотеки, то предполагается, что он содержит предложения управляющего языка. В следующих разделах определяются основные понятия, знакомство с которыми необходимо для использования управляющего языка. 1.1. Конфигурация памяти Для целей размещения программ и данных виртуальная память целе- вого компьютера подразделяется на конфигурируемую и неконфигу- рируемую. По умолчанию вся память считается конфигурируемой, то есть допускается ее использование редактором связей. В микроп роцессорных приложениях, однако, области памяти, расположенные по разным адресам, зачастую неоднородны. Например, с нулевого адреса может располагаться ППЗУ размером 3K, а с адреса 20K - ПЗУ на 8K. Память в диапазоне от 3K до 20K-1 целесообразно сделать неконфигурируемой, то есть запретоть редактору связей ld(1) использовать ее. Ничто и никогда не может быть связано с неконфигурируемой памятью. Иными словами, указание того, чт некоторая область памяти неконфигурируема, делает соответствую- щие адреса некорректными или несуществующими с точки зрения ре- дактора связей. Конфигурацию памяти, отличную от подразумевае- мой, необходимо специфицировать явно. Если не оговорено противное, все дальнейшие рассуждения о памя- ти, адресах и т.д. относятся к конфигурируемым областям адрес- ного пространства. 1.2. Секции Секция объектного файла должна занимать непрерывный участок па- мяти и является минимальным объектом, который подвергается пе- ремещению. Координатами секции являются ее начальный адрес и длина. В начале файла располагаются заголовки секций, описываю щие все секции этого файла. В процессе обработки из секций входных файлов вырабатываются выходные секции, которые могут содержать команды, данные или смесь того и другого. Как между входными, так и между выходными секциями могут быть пустоты ("дыры"), однако в пределах одной выходной секции память выде- ляется последовательно и с пустотами перекрываться не может. 1.3. Адреса Применительно к редактированию внешних связей термин физический адрес трактуется нестандартным образом. Физический адрес секции или имени определяется как смещение относительно начала (нул вого адреса) адресного пространства. Физический адрес объекта не обязательно совпадает с тем адресом, по которому объект бу- дет помещен во время выполнения. Так, в системах со страничной виртуальной памятью адрес есть смещение относительно нулевого адреса виртуальной памяти, которое затем преобразуется аппара- турой и/или операционной системой. 1.4. Связывание Часто бывает необходимо, чтобы начало секции приходилось на оп ределенный, заранее известный адрес. Установление соответствия между объектом и начальным адресом называется связыванием. в таком случае говорят, что объект связан с определенным адресом. Обычно связыванию подвергаются выходные секции, однако имеется возможность связывать с адресами и некоторые абсолютные гло- бальные имена, для чего используется оператор присваивания уп- равляющего языка ld(1). 1.5. Объектный файл Объектные файлы генерирует как ассемблер (обычно вызываемый ка- ким-либо компилятором), так и редактор внешних связей ld(1). Редактор связей, получая на вход настраиваемые объектные файлы, создает выходной объектный файл, который может требовать, а мо жет и не требовать дополнительной настройки. При определенных условиях объектные файлы, подаваемые на вход ld(1), могут быть и абсолютными. Файлы, которые создаются компиляторами, могут среди прочих со держать секции с именами .text и .data. Секция .text содержит выполняемые команды, а секция .data - инициализированные дан- ные. Пусть, например, программа на языке C содержит глобальное, то есть не входящее в функцию, описание и оператор присваива- ния: |int i=100; | . . . |i=0; Команды, сгенерированные для оператора присваивания, попадут в секцию .text, а место для переменной i будет отведено в секции .data. 2. УПРАВЛЯЮЩИЙ ЯЗЫК РЕДАКТОРА СВЯЗЕЙ 2.1. Выражения В выражениях могут использоваться глобальные имена, константы и большинство основных операций языка C (см. Синтаксис управляю щего языка редактора связей). Как и в языке C, числовые конс- танты считаются десятичными, если только им не предшествует 0 для восьмеричных и 0x для шестнадцатеричных. Все числа тракту- ются как длинные целые. Имена могут содержать прописные и строчные буквы, цифры и символ подчеркивания, _. Если имя появ ляется внутри выражения, то в качестве значения используется его адрес. Редактор связей не просматривает таблицу имен и не пытается выяснить значения переменных, размерности массивов и т.п. Для распознавания имен, чисел, операций и т.п. редактор связей использует сканер, сгенерированный с помощью утилиты lex(1). Ниже перечислены слова, которые сканер считает зарезервирован- ными, и которые нельзя поэтому использовать в качестве имен или названий секций: |ADDR BLOCK GROUP NEXT RANGE SPARE |ALIGN COMMON INFO NOLOAD REGIONS PHY |ASSIGN COPY LENGTH ORIGIN SECTIONS TV |BIND DSECT MEMORY OVERLAY SIZEOF | | addr block length origin sizeof | align group next phy spare | assign l o range | bind len org s В следующей таблице приведены, в порядке убывания приоритета, знаки допустимых операций: | +-------------------------+ | | Знак операции | | +-------------------------| | | ! ~ - (унарный минус) | | | * / % | | | + - (бинарный минус) | | | >> << | | | == != > < <= >= | | | & | | | | | | | && | | | || | | | = += -= *= /= | | +-------------------------+ Перечисленные операции имеют тот же смысл, что и в языке C. Операции, знаки которых находятся на одной строке, имеют одина- ковый приоритет. 2.2. Оператор присваивания Оператор присваивания позволяет определять внешние имена и свя- зывать их с определенными адресами. Оператор присваивания может записываться одним из следующих двух способов: |имя = выражение; | |имя операция= выражение; Здесь операция - это один из знаков +, -, * или /. Оператор присваивания должен оканчиваться точкой с запятой. Все операторы присваивания (за исключением одного случая, кото- рый описывается в следующем абзаце), выполняются после того, как произведено размещение всех объектов, определенных во вход- ных файлах, но перед перед настройкой ссылок из команд и дан- ных. Поэтому, при вычислении выражения в правой части оператора присваивания, значениями встречающихся там имен будут их адреса в выходном объектном файле. Ссылки же из команд или данных на имя, которое посредством оператора присваивания связывается с новым адресом, будут настроены на этот новый адрес. Операторы присваивания обрабатываются в том порядке, в котором они посту- пают на вход ld(1). Операторы присваивания обычно помещаются вне сферы действия предложений, определяющих секции (см. раздел Предложения опре деления секций). Однако, существует специальное имя, точка, ., которое может появляться только в пределах определения секций. Значением этого имени является текущее значение счетчика разме- щения ld(1), поэтому ld(1) вычисляет значения выражений, содер- жащих ., в процессе размещения. Присваиванием значения имени . в предложении определения секции можно увеличить (но не умень- шить) счетчик размещения и в результате создавать пустоты внут- ри секции, как это описывается в разделе Предложения определе- ния секций. Присваивание значения . некоторой переменной позво- ляет в любой момент работы редактора связей сохранить текущее значение счетчика размещения. Функция align используется для выравнивания по границе n байт, где n есть степень двойки. Следующие два выражения равносильны: |ALIGN (n) | |(. + n - 1) & ~(n - 1) Псевдофункции SIZEOF и ADDR в качестве аргумента получают имя некоторой секции, а возвращают, соответственно, ее длину и ад- рес. Они могут использоваться в определениях имен вне пределов определения секций. Выражения редактора связей могут иметь либо абсолютные, либо перемещаемые (настраиваемые) значения. Если ld(1) создает новое имя в результате обработки оператора присваивания, то тип имени будет совпадать с типом значения выражения из правой части Этот тип может быть определен при помощи следующих правил: Выражения с единственным вхождением перемещаемого имени имеют перемещаемые значения, независимо от наличия и количества в этом выражении констант или абсолютных имен. Разность значений двух перемещаемых имен из одной и той же секции будет абсолютной. Другие выражения являются комбинациями вышеупомянутых. Примечание Сформулированные правила не только описывают тип значе- ния выражений, но и накладывают ограничения на способы их конструирования. 2.3. Описание конфигурации памяти Предложение MEMORY используется для указания: Общего размера виртуальной памяти целевого компьютера. Конфигурируемых и неконфигурируемых областей виртуаль- ной памяти. Если предложения MEMORY отсутствуют, вся память считается кон- фигурируемой. Подразумеваемый размер памяти зависит от целевого компьютера. Предложение MEMORY позволяет дать любому диапазону виртуальных адресов произвольное имя длиной не более восьми символов, кото- рое в дальнейшем можно использовать для связывания выходных секций с адресами из поименованного диапазона. Имена областей памяти могут состоять из прописных и строчных букв, цифр и спе- циальных символов $, ., _. Эти имена используются только в про- цессе работы редактора связей и не включаются ни в заголовки выходного объектного файла, ни в его таблицу имен. При наличии предложений MEMORY вся явно не описанная в них па- мять считается неконфигурируемой. Редактор связей не использует для размещения неконфигурируемые области памяти, поэтому с ними могут быть связаны только DSECT-секции. Посредством предложения MEMORY областям памяти могут назначать- ся атрибуты. В последующем, возможно, значения этих атрибутов будут использоваться для дополнительного поиска ошибок, пока же проверка правильности использования атрибутов не производится. В настоящее время возможные атрибуты таковы: R Память, допускающая чтение. W Память, допускающая запись. Х Память, в которой могут размещаться выполняемые команды. I Инициализируемая память. Например, память, отводи мая под стек, обычно не инициализируется. В дальнейшем, если понадобится, будут добавлены новые атрибуты. Если атрибуты не указаны в предложениях MEMORY, либо этих пред- ложений нет вообще, то по умолчанию области памяти получают ат рибуты R, W, Х и I. Cинтаксис предложения MEMORY таков: |MEMORY { | имя1 (атрибуты) : origin = n1, length = n2 | имя2 (атрибуты) : origin = n3, length = n4 | . . . |} После ключевого слова origin (которое можно сократить до org или o) указывается начальный адрес области памяти. После ключе- вого слова length (или, короче, len или l) указывается размер области. Операнд слова origin должен быть допустимым виртуаль- ным адресом. Значения начального адреса и длины указываются в виде десятичной, восьмеричной или шестнадцатеричной константы, записанной по правилам языка C. Предложения MEMORY, а также спецификации origin и length внутри них, должны отделяться друг от друга пробелами, символами табуляции, переводами строк или запятыми. Пользуясь предложением MEMORY, можно указать редактору связей, что конфигурация памяти отличается от подразумеваемой. Напри мер, пусть нужно не допустить связывания объектов с адресами в пределах первых 0x10000 слов. Этого можно добиться посредством следующего предложения MEMORY: |MEMORY { | valid: org = 0x10000, len = 0xFE0000 |} 2.4. Предложения определения секций Предложение SECTIONS предназначено для указания способа комби- нирования входных секций и размещения выходных секций (как в целом в виртуальной памяти, так и относительно других секций), а также для переименования выходных секций. По умолчанию, если предложения SECTIONS отсутствуют, все однои- менные входные секции объединяются в выходной секции с тем же именем. Если редактируются внешние связи двух объектных файлов, из которых первый содержит секции s1 и s2, а второй - секции s3 и s4, то выходной объектный файл будет содержать четыре секции: s1, s2, s3 и s4. Порядок этих секций будет зависеть от порядка, в котором редактор связей будет просматривать входные файлы. Синтаксис предложений SECTIONS таков: |SECTIONS { | имя_выходной_секции_1 : { | спецификации_файлов, | операторы_присваивания | } | имя_выходной_секции_2 : { | спецификации_файлов, | операторы_присваивания | } | . . . |} В оставшейся части этого раздела обсуждаются различные виды предложений определения секций. 2.4.1. Спецификации файлов При определении выходной секции, файлы и секции файлов, которые должны быть включены в нее, нужно перечислять в том порядке, в котором они должны следовать в выходной секции. Секции входного файла указываются одним из следующих способов: |имя_файла (имя_секции) | имя_файла (имя_секции1 имя_секции2 ...) Имена секций входного файла, как и сами спецификации файлов, должны отделяться друг от друга пробелами, символами табуляции, переводами строк или запятыми. Чтобы указать все неинициализированные и неразмещаемые глобаль- ные объекты из данного файла, можно использовать запись |имя_файла [COMMON] Если имя файла указывается без сопровождающего его списка сек- ций, то все секции этого файла (кроме неинициализированных и неразмещаемых глобальных объектов) вставляются в определяемую выходную секцию. Пример: |SECTIONS { | outsec1: { | file1.o (sec1) | file2.o | file3.o (sec1, sec2) | } |} В результате выполнения этого предложения порядок размещения входных секций в выходной секции outsec1 будет таким: Секция sec1 из файла file1.o. Все секции файла file2.o, в том же порядке, в котором они следуют в файле file2.o. Секция sec1 из файла file3.o, а затем секция sec2 того же файла file3.o. Если в других входных файлах окажутся секции с именем outsec1, то они будут следовать после секций, перечисленных в приведен- ном выше определении выходной секции outsec1. Если в файле file3.o или в файле file1.o окажутся, кроме перечисленных выше, еще какие-нибудь входные секции, то они будут помещены в однои- менные выходные, - в том, разумеется, случае, если пользователь не включит их в другие спецификации файлов. Для спецификации всех не размещенных ранее входных секций с оп- ределенным именем (независимо от имени входного файла) исполь- зуется запись вида |* (имя_секции) 2.4.2. Указание адреса загрузки секции Связывание выходной секции с определенным адресом виртуальной памяти достигается посредством следующей разновидности предло- жения SECTIONS: |SECTIONS { | имя_выходной_секции_1 адрес : { | . . . | } | . . . |} Адрес, с которым выполняется связывание, записывается в виде константы языка C. Редактор связей выдает соответствующее сооб щение об ошибке, если указанные выходная_секция и адрес не мо- гут быть связаны (например, из-за конфигурации памяти или из-за перекрытия с другими секциями). В качестве адреса можно также использовать слово BIND, за которым должно следовать выражение в скобках. В этом выражении могут встречаться псевдофункции SIZEOF, ADDR и NEXT. Аргументом SIZEOF и ADDR должна быть ранее определенная секция, а аргументом NEXT - константа. Псевдофунк- ция NEXT возвращает минимальный адрес конфигурируемой памяти, по которому еще ничего не размещено, кратный аргументу. Выходные секции можно разместить в любом месте конфигурируемой памяти, где они помещаются, не перекрываясь с другими секциями. Предложения SECTIONS могут подаваться на вход редактора связей в произвольном порядке, если только не используются псевдофунк- ции SIZEOF и ADDR. Редактор связей не обеспечивает автоматически ни четного разме- ра секции, ни четного адреса ее начала, в отличие от ассембле- ра, который гарантирует, что длина секции в байтах будет де- литься на четыре. Используя предложения управляющего языка, можно добиться того, что секция будет начинаться с нечетного адреса, однако делать это не рекомендуется. Если секция начина- ется с нечетного байта, то либо она будет неправильно выпол няться, либо неправильно будет осуществляться доступ к данным, находящимся в этой секции. Если пользователь все же указал не- четную границу, ld(1) выдаст предупреждение. 2.4.3. Выравнивание выходной секции Можно потребовать, чтобы начальный виртуальный адрес выходной секции был бы выравнен на границу n байт, где n есть степень 2. Это достигается использованием в предложении SECTIONS, на месте адреса, функции ALIGN. Следующие две формы записи адреса начала секции эквивалентны: |ALIGN (n) | |(. + n - 1) & ~(n - 1) Рассмотрим пример. |SECTIONS { | outsec ALIGN (0x20000): { | . . . | } | . . . |} Здесь выходной секции outsec не назначается никакой заранее оп ределенный адрес, но она будет размещена по некоторому адресу, кратному 0x20000 (например, может быть назначен адрес 0x0, 0x20000, 0x40000, 0x60000 и т.д.). 2.4.4. Группировка выходных секций Подразумеваемый алгоритм размещения секций для редактора связей ld(1) таков: В одну выходную секцию помещаются все входные секции .init, а за ними все входные секции .text. Эта выходная секция получает имя .text; она связывается с адресом, равным 0x0 плюс размер всех заголовков выходного файла. Все входные секции .data помещаются в одну выходную. Этой выходной секции дается имя .data, и, в системах со страничной виртуальной памятью, она связывается с адре- сом, который получается выравниванием на машинно-зави- симую границу плюс величина, определяемая размерами за- головков и секции .text. Все секции неинициализированных данных .bss, а также все неинициализированные и неразмещаемые глобальные имена помещаются в одну выходную секцию с именем .bss, которая располагается сразу после секции .data без вы- равнивания по какой-либо границе. Этот алгоритм не применяется, если во входном потоке есть хотя бы одно предложение SECTIONS. В случае самостоятельной обработ- ки объектных файлов обычного формата можно, не полагаясь на из- ложенный выше алгоритм, извлечь информацию об адресах и порядке следования секций из заголовков файла и его секций. Подразуме- ваемый алгоритм размещения эквивалентен следующему предложению: |SECTIONS { | .text размер_заголовков : { *(.init) *(.text) } | } | GROUP BIND (NEXT (граница_выравнивания) + | (SIZEOF (.text) + ADDR (.text)) % 0x2000) : { | .data : { } | .bss : { } | } |} где граница_выравнивания есть машинно-зависимая константа. Предложение GROUP обеспечивает группировку, то есть последова- тельное размещение, двух выходных секций, .data и .bss. Связы- вание с конкретным адресом и выравнивание по определенной гра нице производится для группы в целом, а не для отдельных входя- щих в нее секций. Секции, образующие группу, размещаются после- довательно в том порядке, в котором они указываются в предложе- нии GROUP. Если необходимо сгруппировать секции .text, .data и .bss, сле- дует использовать такое предложение SECTIONS: |SECTIONS { | GROUP: { | .text: {} | .data: {} | .bss: {} | } |} При этом выходной файл будет по-прежнему содержать три различ- ные секции (.text, .data и .bss), однако теперь они будут раз- мещаться в последовательных участках виртуальной памяти. Группу выходных секций как единое целое можно связать с конк- ретным адресом или произвести ее выравнивание на определенную границу, просто указав нужный адрес в предложении GROUP. Так, для связывания с адресом 0xС0000 достаточно воспользоваться конструкцией |GROUP 0xС0000: { а для выравнивания на границу, кратную 0x10000, - конструкцией |GROUP ALIGN (0x10000): { Если сделать одно из этих добавлений к указанному выше примеру, то выходная секция .text будет размещена по адресу 0xС0000 (со- ответственно выравнена на границу 0x10000); затем остальные члены группы, в порядке их указания, размещаются по ближайшим доступным адресам. Если предложение GROUP не используется, то каждая выходная сек- ция рассматривается отдельно: |SECTIONS { | .text: {} | .data ALIGN (0x2000): {} | .bss: {} |} Секция .text связывается с виртуальным адресом 0x0 (если он на- ходится в пределах конфигурируемой памяти). Секция .data связы- вается с виртуальным адресом, кратным 0x2000. Если хватит мес- та, секция .bss будет следовать сразу за секцией .text, если же не хватит - то сразу за секцией .data. Порядок, в котором имена выходных секций появляются в предложении SECTIONS, не определя- ет порядка следования этих секций в выходном файле. 2.4.5. Создание пустот в выходных секциях Специальный символ точка, ., может появляться только в предл жениях определения секций и в операторах присваивания. Его по- явление в левой части оператора присваивания предписывает ld(1) увеличить или переустановить счетчик размещения, что приводит к образованию пустого места в выходной секции. Отметим, что под такого рода пустоты физически отводится место в выходном файле; они инициализируются с помощью заполнителя - либо подразумевае- мого (0x00), либо явно указанного (см. описание опции -f в раз- деле Прочие возможности редактора связей и раздел Инициализация пустот и секций .bss). Рассмотрим следующее определение секции: |outsec: { | . += 0x1000; | f1.o (.text) | . += 0x100; | f2.o (.text) | . = align (4); | f3.o (.text) |} Результат выполнения этого предложения будет таков: В начале секции окажется "дыра" длиной 0x1000 байт, инизиализированная с помощью подразумеваемого заполни теля. Затем будет следовать входная секция .text из файла f1.o. Через 0x100 байт после окончания f1.o (.text) окажется входная секция .text из файла f2.o. Секция .text из файла f3.o будет размещена с ближайшей границы слова вслед за секцией .text из файла f2.o (вы- равнивание производится относительно начала секции outsec). Размещение и выравнивание в пределах одной выходной секции про- изводится относительно ее начала, то есть так, как если бы сек- ция начиналась с нулевого адреса. Поэтому, если в рассмотренном выше примере адрес выходной секции outsec окажется нечетным, то нечетным будет и адрес той части outsec, куда размещается вход- ная секция f3.o (.text), даже если f3.o (.text) и была ранее выравнена на границу слова. Этого можно избежать, потребовав выравнивания выходной секции в целом: |outsec ALIGN (4): { Заметим, что ассемблер as(1) всегда производит выравнивание ге- нерируемых им секций на границу слова, так что можно не указы вать это требование явно. То же верно и в отношении C-компиля- тора. Операторы, уменьшающие счетчик размещения, являются некоррект- ными, поскольку повторное размещение по какому-либо адресу не допускается. Как правило, для изменения счетчика размещения ис- пользуются операции += и align. 2.4.6. Создание и определение имен при редактировании связей Операторы присваивания ld(1) нужны для того, чтобы имя могло получить значение, которое вычисляется при редактировании свя зей. Обычно употребляются три вида операторов присваивания: С символом . в левой части - для управления счетчиком размещения. С символом . в правой части - для присваивания имени настраиваемого значения. Без символа . - для присваивания имени абсолютного зна- чения. Первый вид присваиваний обсуждался в предыдущем разделе. В операторах второго вида имени присваивается адрес, который становится известным только после размещения. Пример: |SECTIONS { | outsc1: { ... } | outsc2: { | file1.o (s1) | s2_start = . ; | file2.o (s2) | s2_end = . - 1; | } |} Значением s2_start будет адрес начала секции file2.o (s2), а значением s2_end - адрес последнего байта file2.o (s2). Рассмотрим следующий пример. |SECTIONS { | outsc1: { | file1.o (.data) | mark = .; | . += 4; | file2.o (.data) | } |} Здесь создается имя mark и его значением становится адрес бай- та, следующего за окончанием секции .data из file1.o. Затем ре- зервируются четыре байта для будущего использования mark при выполнении. Тип имени mark - длинное целое (32 бита). Значение выражения, содержащего счетчик размещения, вычисляется в процессе размещения, поэтому такие выражения могут встречать- ся только внутри предложений SECTIONS. В предложениях SECTIONS могут быть и операторы присваивания, не содержащие ., хотя обычно так не делается. Значения выражений из правых частей та- ких операторов определяются после окончания размещения. Изме- нять подобным образом адрес какого-либо имени опасно. Пусть, например, имя определено в секции .data, где для него отведен и проинициализирован некоторый участок памяти. Тогда, если редак- тируется ряд объектных файлов, содержащих ссылки на это имя, соответствующий ему элемент таблицы имен с момента присваивания будет отражать новый, измененный адрес этого имени. В то же время инициализированные данные не переписываются по новому ад- ресу; кроме того, могут остаться ссылки на старый адрес. ld(1) выдает предупреждение при изменении значения каждого имени, ра- нее уже определенного. Заметим, однако, что присваивание абсо- лютных значений новым именам совершенно безопасно, так как с такими именами не связаны ни ссылки, ни данные. 2.4.7. Размещение секций в именованных областях памяти Можно потребовать, чтобы секция была размещена где-либо внут именованной области памяти, определенной ранее предложением MEMORY. По аналогии с принятым в операционной системе UNIX син- таксисом переназначения стандартного вывода, для этого применя- ется символ >. Пример: |MEMORY { | mem1: o=0x000000 l=0x10000 | mem2 (RW): o=0x020000 l=0x40000 | mem3 (RW): o=0x070000 l=0x40000 | mem1: o=0x120000 l=0x04000 |} | |SECTIONS { | outsec1: {f1.o (.data)} > mem1 | outsec2: {f2.o (.data)} > mem3 |} Эти предложения предписывают ld(1) разместить секцию outsec1 где-либо внутри области памяти mem1, то есть между 0x0 и 0xFFFF или между 0x120000 и 0x123FFF. Секция outsec2 будет размещена в диапазоне адресов от 0x70000 до 0xAFFFF. 2.4.8. Инициализация пустот и секций .bss Пустоты в выходных секциях (см. пример в разделе Создание пус- тот в выходных секциях) редактор связей обычно заполняет нуле выми байтами. По умолчанию, секции .bss не инициализируются вовсе, то есть ни ассемблер, ни редактор связей не генерируют для них каких-либо (в том числе и нулевых) данных. Пустоты, а равно и выходные секции .bss, можно заполнить произ- вольными двухбайтными значениями, указав опцию инициализации в предложении SECTIONS. Подчеркнем, что опция инициализации воз- действует только на пустоты и секции .bss. Опция может понадо- биться, например, если необходимо заполнить определенным обра- зом таблицу неинициализированных данных без перекомпиляции программ, или если нужно заполнить "дыру" в секции .text коман- дами переходе к подпрограмме обработки ошибок. Потребовать инициализации можно как для выходной секции в це- лом, так и для отдельной ее части. Однако в связи с тем, что неинициализированная секция .bss физически не занимает места в выходном дайле, ее нельзя проинициализировать частично. Даже если заказана инициализация только части секции .bss, она будет проинициализирована целиком. Итак, если секция .bss объединяет- ся с секциями .text или .data (разумеется, инициализируемыми), или если инициализируется часть секции .bss, то произойдет одно из двух: Если опция инициализации задана, она будет отнесена ко всем частям выходной секции, полученным из входных сек- ций .bss и не имеющим явной инициализации. Если опция инициализации не задана, ld(1) заполнит то же место подразумеваемым заполнителем. Рассмотрим следующее определение секции: |SECTIONS { | sec1: { | f1.o | . += 0x200; | f2.o (.text) | } = 0xDFFF | sec2: { | f1.o (.bss) | f2.o (.bss) = 0x1234 | } | sec3: { | f3.o (.bss) | . . . | } = 0xFFFF | sec4: {f4.o (.bss)} |} Здесь "дыра" размером 0x200 байт в секции sec1 заполняется зна- чениями 0xDFFF. В секции sec2 f1.o (.bss) заполняется подразу- меваемым значением 0x00, а f2.o (.bss) инициализируется после- довательностью 0x1234. Все секции .bss, входящие в sec3, как и пустоты, заполняются значением 0xFFFF. Секция sec4 не инициали- зируется, иными словами, в объектном файле не будет данных для этой секции. 3. ПРОЧИЕ ВОЗМОЖНОСТИ РЕДАКТОРА СВЯЗЕЙ 3.1. Определение точки входа Вспомогательный заголовок объектных файлов обычного формата, применяемый в ОС UNIX и имеющий структуру a.out, содержит поле для (основной) точки входа этого файла. Правила заполнения это- го поля редактором связей (в порядке их применения) таковы: Берется значение имени, заданного в опции -e, если эта опция используется. Используется значение имени _start, если это имя опре- делено. Используется значение имени main, если это имя опреде- лено. В остальных случаях поле получает нулевое значение. Таким образом, можно задать значение точки входа, используя оп- цию -e или предложение управляющего языка вида |_start = выражение Когда редактор связей вызывается на выполнение командой cc(1), программа пользователя объединяется с инициализирующей програм- мой. Эта последняя после обращения к программе пользователя вы- полняет системный вызов exit [см. exit(2)], чтобы закрыть файлы и осуществить другие терминирующие действия. Если пользователь вызывает редактор связей сам и/или изменяет точку входа, он должен гарантировать, что программа завершает выполнение сис- темным вызовом exit. 3.2. Использование библиотек объектных файлов Каждый элемент такой библиотеки (например, библиотеки libc.a) является полноценным объектным файлом. Команда ar(1) создает библиотеки из объектных файлов, генерируемых компиляторами. Библиотеки обрабатываются редактором связей избирательно: ис пользуются только те элементы, которые разрешают внешние ссыл- ки. Библиотеки могут упоминаться как внутри предложений, опре- деляющих секции, так и вне их. В обоих случаях объектный файл - элемент библиотеки используется для редактирования внешних свя- зей, если выполнены следующие два условия: Существует неразрешенная ссылка на имя, определенное в этом файле. Такая ссылка обнаружена ld(1) до завершения просмотра библиотеки. При использовании элемента библиотеки, просматриваемой в про- цессе обработки предложения SECTIONS, все входные секции из этого объектного файла помещаются в ту выходную секция, которая создается в этом предложении. Если же элемент используется при просмотре библиотеки, упомянутой вне предложения SECTIONS, то каждая входная секция из этого объектного файла помещается в одноименную выходную. В последнем случае, если это необходимо, создаются новые выходные секции. Необходимо запомнить следующее: В предложениях управляющего языка редактора связей мож но указать только библиотеку целиком, но не отдельные элементы. Не существует способа изменить изложенные выше подразу меваемые правила редактирования связей элементов библи- отек объектных файлов и их секций. Опция -l используется как средство сокращения записи при специ- фикации входных файлов, принадлежащих предопределенному набору каталогов и имеющих предопределенные имена. Обычно таким обра- зом задаются библиотеки, хотя это и не обязательно. Библиотеки объектных файлов могут быть указаны и без опции -l просто путем задания их маршрутных имен. Важен порядок указания библиотек, так как из них извлекаютс лишь те элементы, на которые есть неразрешенные ссылки к момен- ту просмотра библиотеки. ld(1) просматривает находящуюся в на- чале библиотеки объектных файлов таблицу имен несколько раз, пока не обнаружит, что никакие внешние ссылки не могут более быть разрешены за счет элементов этой библиотеки. Рассмотрим следующий пример: В каждом из входных файлов file1.o и file2.o есть ссыл ки на внешнюю функцию FCN. Входной файл file1.o содержит ссылку на внешнее имя ABC. Входной файл file2.o содержит ссылку на внешнее имя XYZ. Элемент 0 библиотеки liba.a содержит определение имени XYZ. Элемент 0 библиотеки libc.a содержит определение имени ABC. В обеих библиотеках в элементе 1 определяется функци FCN. Пусть командная строка с вызовом ld(1) выглядит следующим обра- зом: |ld file1.o -la file2.o -lc Тогда ссылки на FCN разрешаются элементом 1 библиотеки liba.a, ссылка на ABC - элементом 0 библиотеки libc.a, а ссылка на XYZ остается неразрешенной, так как библиотека liba.a просматрива- ется раньше редактирования связей файла file2.o. Если же коман- да ld вводится таким образом: |ld file1.o file2.o -la -lc то ссылки на FCN и ABC разрешаются как в предыдущем примере, а ссылка на XYZ разрешается элементом 0 библиотеки liba.a. Пусть, наконец, команда ld(1) введена так: |ld file1.o file2.o -lc -la Отличие от предыдущего примера выразится в том, что для разре- шения ссылки на FCN будет извлечен элемент 1 библиотеки libc.a, а не liba.a. Опция -u используется, чтобы вызвать принудительное редактиро- вание связей тех элементов библиотек, на которые, быть может, нет реально существующих внешних ссылок. Например, в случае вы- зова |ld -u rout1 -la создается неопределенное имя rout1 и, если в каком-либо объект- ном файле библиотеки liba.a это имя определяется, то этот файл (а с ним, быть может, и некоторые другие) извлекается для ре дактирования связей. Без опции -u ld(1) не просматривал бы биб- лиотеку вообще ввиду отсутствия неразрешенных ссылок и неопре- деленных имен. 3.3. Обход неконфигурируемых областей памяти Когда в виртуальной памяти есть неконфигурируемые области, каж- дое приложение (или пользователь) должно самостоятельно обеспе- чивать такую структуру выходных секций, чтобы они помещались в конфигурируемых областях. Например, пусть конфигурация памяти такова: |MEMORY { | mem1: о=0x00000 l=0x02000 | mem2: о=0x40000 l=0x05000 | mem3: о=0x20000 l=0x10000 |} Пусть далее в каждом из файлов f1.o, f2.o, ..., fn.o содержатся три секции: .text, .data и .bss. Предположим также, что размер объединенной секции .text оказался бы равным 0x12000 байт. Лег- ко видеть, что не существует конфигурируемой области памяти достаточной длины. Чтобы ld(1) мог выполнить размещение, необ- ходимо эту секцию разделить посредством соответствующих предло жений, например: |SECTIONS { | txt1: { | f1.o (.text) | f2.o (.text) | f3.o (.text) | } | txt2: { | f4.o (.text) | f5.o (.text) | f6.o (.text) | } | . . . |} 3.4. Алгоритм размещения Выходная секция создается в результате выполнения предложени SECTIONS, или объединения одноименных входных секций или объ- единения секций .text и .init в выходную секцию .text. В выход ную секцию включаются несколько (возможно, одна или ни одной) входных. После того, как состав выходной секции определен, ей назначается для размещения участок конфигурируемой виртуальной памяти. ld(1) использует алгоритм, цель которого - минимизиро- вать фрагментацию памяти и таким образом повысить вероятность успешного размещения всех выходных секций, с учетом конфигура- ции памяти. Этот алгоритм заключается в следующем: Размещаются все выходные секции, связанные с конкретны- ми адресами. Размещаются все секции, связанные с именованными облас- тями памяти. Секции, размещаемые на этом или следующем шаге, связываются с первым доступным в (именованной) памяти адресом, с учетом требований выравнивания, если таковое имеются. Размещаются все остальные выходные секции. Если, как это предполагается по умолчанию, вся память образует одну непрерывную конфигурируемую область, а предложения SECTIONS отсутствуют, то выходные секции размещаются в том по- рядке, в котором их создает ld(1). В остальных случаях выходные секции размещаются в том порядке, в котором они определяются, или становятся известными ld(1), - в первой подходящей из дос- тупных областей памяти. 3.5. Инкрементальное редактирование связей Как уже отмечалось ранее, результат работы ld(1) можно исполь зовать в качестве исходной информации для последующего редакти- рования связей, при условии, что сохраняется информация о наст- ройке ссылок (то есть задана опция -r). Редактирование связей, использующее ранее полученную информацию, называется инкремен- тальным. Есть смысл разделять большие системы на несколько под- систем, связи внутри которых редактируются независимо, а затем, при необходимости, осуществлять пересборку системы в целом, например: Шаг 1: |ld -r -o outfile1 ifile1 infile1.o |/* ifile1 */ |SECTIONS { | ss1: { | f1.o | f2.o | . . . | fn.o | } |} Шаг 2: |ld -r -o outfile2 ifile2 infile2.o |/* ifile2 */ |SECTIONS { | ss2: { | g1.o | g2.o | . . . | gn.o | } |} Шаг 3: |ld -a -o final.out outfile1 outfile2 Если подсистемы формируются разумно, то после перекомпиляции нескольких файлов придется повторить лишь часть процесса редак- тирования связей. Рекомендуется придерживаться двух простых правил: Промежуточные вызовы редактора связей должны (посредст- вом предложений SECTIONS) управлять только построением выходных секций из входных файлов и их секций, но не назначать адреса этим секциям. Все операторы присваивания, а равно и предложения, уп- равляющие размещением секций и конфигурацией памяти, следует включать только в окончательный вызов ld(1). 3.6. Секции DSECT, COPY, INFO и OVERLAY При определении секций им может быть назначен тип. Пример: |SECTIONS { | name1 0x200000 (DSECT) : { file1.o } | name2 0x400000 (COPY) : { file2.o } | name3 0x600000 (NOLOAD): { file3.o } | name4 (INFO) : { file4.o } | name5 0x900000 (NOLOAD): { file5.o } |} Применение опции DSECT приводит к созданию так называемой фик- тивной секции. Свойства фиктивной секции таковы: Не размещается, то есть не принимается во внимание при распределении памяти для выходных секций, поэтому не занимает памяти и не фигурирует в карте распределения памяти, которую выдает ld(1). Может перекрываться с другими выходными секциями, вклю чая фиктивные, и даже с неконфигурируемыми областями памяти. Глобальные имена, определенные в фиктивной секции, пе- ремещаются обычным образом. В таблице имен выходного файла они имеют то же значение, которое имели бы, будь DSECT-секция действительно загружена по назначенному ей виртуальному адресу. Если в DSECT-секции найдены неоп- ределенные внешние имена, то библиотеки объектных фай- лов, если указаны, просматриваются, чтобы найти элемен- ты, где эти имена определяются. Связи найденных объект- ных файлов редактируются обычным образом, то есть не как фиктивные секции. В выходной файл не включается ни содержимое фиктивной секции, ни ассоциированная с ней информация о настройке ссылок и номерах строк, если таковая имеется. В приведенном примере ни одна секция из файла file1.o не разме- щается, но в то же время выполняется настройка всех ссылок, как если бы эти секции действительно связывались с назначенными ад- ресами. Другие секции могут ссылаться на определенные там гло- бальные имена, и эти ссылки разрешатся правильно. Секция с опцией COPY подобна фиктивной секции с той лишь разни- цей, что данные COPY-секции и вся связанная с ней информация включаются в выходной файл. Те же свойства, что и COPY, имеют INFO-секции, однако назначе- ние последних - хранить информацию о самом объектном файле, в то время как секции COPY могут содержать действительные данные или команды. Секция типа NOLOAD лишь в одном отношении отличается от обычной выходной секции: содержащиеся в ней команды и/или данные не включаются в выходной файл. Под NOLOAD-секцию, однако, отводит- ся виртуальная память, она фигурирует в карте распределения па- мяти и т.п. Секция типа OVERLAY настраивается и записывается в выходной файл. От обычной секции она отличается тем, что не размещается и может перекрываться с другими секциями и неконфигурируемыми областями памяти. 3.7. Выравнивание секций в выходном файле Для выравнивания секций внутри выходного файла используется оп- ция BLOCK, которую можно указывать как для отдельной секции, так и в предложении GROUP. Опция BLOCK не влияет ни на процесс редактирования связей, ни на адрес размещения выходной секции, и отражается только на расположении секции в пределах выходного файла. Пример: |SECTIONS { | .text BLOCK (0x200): {} | .data ALIGN (0x2000) BLOCK (0x200): {} |} Указанные предложения SECTIONS предписывают ld(1), чтобы каждая из секций .text и .data оказались в выходном файле со смещением от начала, кратным 0x200 - например, 0x0, 0x200, 0x400 и т.д. Полезно рассмотреть содержимое файла /lib/default.ld, с помощью которого можно изменять подразумеваемые соглашения редактора связей: |MEMORY { | valid : org = 0x0, len = 0x90000000 |} |SECTIONS { | GROUP BLOCK(1024): { | .text: {*(.init) *(.text) *(.fini)} | } | GROUP ALIGN(1048576) BLOCK(1024): { | .data: {} | .bss: {} | } |} 3.8. Ненастраиваемые входные файлы Если результат работы ld(1) предполагается использовать в ка- честве исходных данных для последующего редактирования связей, необходимо при первом вызове ld(1) указать опцию -r, чтобы сох- ранить информацию о настройке ссылок и дать возможность выпол- нить окончательное редактирование. Если во входном файле ld(1) отсутствует таблица имен или инфор- мация о настройке ссылок (вследствие применения команды strip(1) либо редактирования без опции -r или с опцией -s) ld(1) продолжает работу, используя в качестве исходных данных ненастраиваемый файл. Для того, чтобы такое редактирование связей завершилось успешно (то есть чтобы все входные файлы были обработаны правильно, все имена получили значения, все ссылки разрешены и т.д.), примени- тельно к ненастраиваемым файлам должны выполняться два условия: Ни в одном входном файле не должно быть неразрешенных внешних ссылок. Каждый входной файл должен быть связан с тем же адре- сом, что и при первом вызове ld(1). Примечание При нарушении перечисленных условий никаких сообщений об ошибках не появится, в связи с чем необходима край няя осторожность при редактировании связей ненастраива емых входных файлов. 4. СИНТАКСИС УПРАВЛЯЮЩЕГО ЯЗЫКА РЕДАКТОРА СВЯЗЕЙ Примечание В данном разделе символы квадратных и фигурных скобок несут двойную нагрузку: Обычные символы [ ] и { } являются частью синтак- сических конструкций и должны присутствовать во входных предложениях. Cимволы [ и ], выделенные жирным шрифтом, указыва- ют, что заключенная в них часть синтаксической конструкции является необязательной. Символы { и }, выделенные жирным шрифтом, указыва- ют, что заключенная в них часть синтаксической конструкции может быть повторена несколько раз. Символ перевода строки в середине правила эквивалентен символу альтернативы |. ::= {} ::= | | | | ::= MEMORY {[]} ::= []:, ::= ({ R | W | X | I }) ::= = ::= = ::= origin | o | org | ORIGIN ::= length | l | len | LENGTH ::= SECTIONS{{}} ::=
| | ::= GROUP:{} ::=
{[,]
}
::=[]:{[{}]} [][] ::= [] | [][] ::= [] [][][] ::= | () ::= () ::= align | ALIGN ::= () ::= block | BLOCK ::= (DSECT) | (NOLOAD) | (COPY) | (INFO) | (OVERLAY) ::= = ::= > | > ::= [[COMMON]][] ()[[COMMON]][] *()[[COMMON]][] | | пусто ::= [{}] ::= -l ::= bind | BIND ::= ::= | . ::= = | += | -= | *= | /= ::= ; | , ::= | ::= * | / | % | + | - | >> | << | == | != > | < | <= | >= | & | && || ::= | | () | () | () | () () | () ::= ! | - | ~ ::= phy | PHY ::= sizeof | SIZEOF ::= next | NEXT ::= addr | ADDR ::= {} ::= -e | -f | -h -l | -m | -o | -r | -s | -t -u | -z | -H | -L | -М | -N | -S | -V -VS | -a | -x ::= | , ::= произвольное допустимое имя переменной ::= произвольная допустимая длинная целая константа ::= пробел, символ табуляции, перевод строки ::= любое допустимое в ОС UNIX имя файла ::= любое допустимое в ОС UNIX маршрутное имя ::= любое допустимое имя секции