The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа
Вперед Назад Содержание

3. Язык управления линкером

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

Вы можете представить командный файл (обычно называемый скриптом) линкеру, либо явно с помощью опции '-T', либо неявно, как обычный файл. Если линкер открывает файл, который не может распознать как файл с поддерживаемым объектным форматом или как библиотеку, он сообщает об ошибке.

3.1 Скрипты линкера

Язык управления линкером (ЯУЛ) LD - это набор команд; некоторые из этих команд устанавливают отдельную опцию, некоторые используются для выбора группы входных файлов или для установки имени выходного файла. Два типа управления имеют фундаментальное значение в процессе линковки.

Самая фундаментальная команда LD - это команда SECTIONS (см. 3.4). Каждый осмысленный скрипт линкера должен иметь команду SECTIONS: она определяет "карту" выходного файла и изобилует множеством деталей. Ни одна другая команда ЯУЛ не является необходимой в таком большинстве случаев, как эта.

Команда MEMORY дополняет команду SECTIONS описывая доступную память в целевой архитектуре. Эта команда не является обязательной. Если Вы не будете использовать команду MEMORY, тогда LD выделит необходимый блок доступной памяти для всего вывода (см. 3.3).

Вы можете вставлять комментарии в скрипты линкера, как в языке C, используя для начала комментария символы '/*' и для завершения - символы '*/'. Как и в языке C комментарии синтаксически эквивалентны пробелу.

3.2 Выражения

Множество полезных команд используют арифметические выражения. Синтаксис выражений в ЯУЛ индентичен синтаксису выражений в языке Cи со следующими особенностями:

Целые числа

Восьмиричное целое - это число, начинающееся с '0', после которого идет 0 или более восьмеричных цифр ("01234567").

 _as_octal = 0157255;
Десятичное число начинается с ненулевой цифры, за которой следует 0 или более десятичных цифр ("0123456789").

 _as_decimal = 57055;
Шестнадцатиричное число начинается с '0x' или '0X', за которыми следует 1 или более шестнадцатиричных цифр ("0123456789abcdefABCDEF").

 _as_hex = 0xdead;

Для того, чтобы записать отрицательное число, используйте префиксный оператор '-' (см. 3.2.4).

 _as_neg = -57005;
Кроме того, суффиксы 'K' и 'M' могут быть использованы для умножении константы на 1024 или на 1024*1024. Например, следующие константы равны:

 _fourk_1 = 4K;
 _fourk_2 = 4096;
 _fourk_3 = 0x1000;

Имена символов

Без использования двойных кавычек, имена символов могут начинаться с буквы, подчеркивания или точки и могут содержать любые буквы, подчеркивания, точки и тире. Имена символов, не заключенные в двойные кавычки, не должны конфликтовать с названиями команд ЯУЛ. Если Вы хотите использовать в имени пробелы или необычные буквы, Вы должны заключить это имя в двойные кавычки.

 "SECTION" = 9;
 "with a space" = "also with a space" + 10;
Так как имена могут содержать много неалфавитных знаков, рекомендуется разделять имена пробелами. Для примера, 'a-b' - это один символ, а 'a - b' это выражение.

Счетчик позиций

Специальная переменная линкера "." ("точка") всегда содержит текущую позицию вывода. Так как она всегда указывает на позицию выходной секции, она должна всегда прояляться внутри команды SECTIONS. Она может использоваться в любом месте выражения как обычный символ, но присваивания ей значения имеют побочный эффект. Если вы присвоите "точке" значение, это вызовет изменение счетчика позиций. Иногда это используется для создания дыр в выходных секциях. Счетчик позиций никогда не должен уменьшаться.

 SECTION 
 { 
   ouptut; 
   {
   file1(.text) 
   . = . + 1000;
   file2(.text)
   . += 1000;
   file3(.text)
   } = 0x1234;
 }
В предыдущем примере file1 располагается в начале выходной секции, после него идет пустое пространство размером в тысячу байт. Потом идет file2, после которого также пропуск размером 1000 байт перед file3. Строка '=0x1234' определяет, какие данные записывать в дырки (см. 3.4.4).

Операторы

Линкер распознает стандартный арифметический набор языка Cи с обычным приоритетом операций.

        Приоритет                 Ассоциативность         Операторы
         высшый
           1                         левая                 - ~ ! 
           2                         левая                 * / %
           3                         левая                  + -
           4                         левая                 >> << 
           5                         левая              == != > < <= >=   
           6                         левая                    & 
           7                         левая                    | 
           8                         левая                    &&
           9                         левая                    ||
           10                        правая                   ?:      
           11                        правая            &= += -= *= /=    
         низший                                         (См. 3.2.6).

Вычисления

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

Присваивание: определение символов

Вы можете создавать глобальные символы и присваивать им значения (адреса), используя любой оператор присваивания языка Cи:

 символ = выражение ;
 символ &= выражение ;
 символ += выражение ;       
 символ -= выражение ; 
 символ *= выражение ;
 символ /= выражение ;
Две вещи отличают присваивание от других операторов в выражениях ЯУЛ.

Присваивание может появляться:

Первые два случая эквивалентны по производимым действиям, оба определяют символ с абсолютным адресом, последний случай определяет символ, адрес которого зависит от данной секции (см. 3.4).

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

Тип выражения зависит от его позиции в скрипт файле. Символ, значение которому присваивается в описании секции, является относительным. Символ, присваивание которому производится в любом другом месте, создается как абсолютный. Так как символ, создаваемый внутри описания секции, создается относительным к началу секции, он будет перемещаемый, если затребован перемещаемый формат объектного файла. Символ может быть создан абсолютным, даже когда присваивание ему значения происходит внутри описания секции с помощью использования функции ABSOLUTE. Для примера, чтобы создать абсолютный символ, адрес которого указывает на последний байт выходной секции с названием '.data', нужно написать:

 SECTIONS{...
   .data :
     { 
       *(.data)
       _edata = ABSOLUTE(.) ;
     }
 ...}
Линкер пытается отложить вычисление присваивания до того момента, пока все переменные в выражении не станут известны (см. 3.2.5). Для примера, размер секции не может быть известен до ее размещения, так что присваивания, зависящие от этого, не будут совершены до размещения секций. Некоторые выражения, например те, которые зависят от счетчика позиций ('.'), должны быть вычислены во время размещения секций. Если результат выражения необходим, а его вычисление не является возможным, линкер сообщает об ошибке. Для примера следующие команды:

 SECTIONS{...
   text 9+this_isnt_constant :
     { ...
     }
 ...}
Вызовут сообщение об ошибке: "Non constant expression for initial address" (Выражение для вычисления начального адреса не является константой).

Иногда желательно описать символ только в том случае, если он не используется, и если он не определен другим объектом, включенным в линковку. К примеру, традиционные линкеры определяют символ 'etext'. Тем не менее, ANSI-C требует, чтобы пользователь мог использовать 'etext' в качестве имени функции без возникновения ошибки. Ключевое слово PROVIDE может быть использовано для определения такого символа. Оно используется в виде PROVIDE(символ = выражение).

Арифметические функции

ЯУЛ предоставляет большее количество функций для использования внутри скриптов.

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

 ADDR(section)
Возвращает абсолютный адрес указанной секции. Вы должны определить положение этой секции до использования функции ADDR. В следующем примере переменным symbol_1 и symbol_2 присваиваются одинаковые значения.

 SECTION {... 
   .ouptut1 : 
     {
     start_of_output_1 = ABSOLUTE(.) ;
     ...
     }
   .output;
     { 
     symbol_1 = ADDR(.output1);
     symbol_2 = start_of_output_1;
     } 
 ...}
 ALIGN(exp)
Возвращает значение счетчика позиций, выравненное на границу следующего за exp выражения. Значение параметра функции должно быть кратно 2. Это эквивалентно следующему выражению:

   (. + exp - 1) & ~(exp - 1)
Функция ALIGN не изменяет значение счетчика позиций. В качестве примера выравняем текущую секцию на границу следующих 0x2000 байт после предыдущей секции и установим переменную внутри секции на границу следующих 0x2000 байт после входной секции.

 SECTION {... 
   .data ALIGN(0x2000):  {
     *(.data)
     variable = ALIGN(0x8000);
   } 
 ...}
Первое использование функции ALIGN в этом примере указывает положение этой секции, потому что оно используется в качестве атрибута начала секции в описании секции (см. 3.4.4). Второе использование просто определяет значение переменной. Встроенная функция NEXT тесно связана с описываемой функцией.

 DEFINED(<символ>)
Возвращает 1, если символ находится в глобальной таблице символов линкера и определен, в противном случае возвращает 0. Вы можете использовать эту функцию для присваиванию символам значений по умолчанию. Для примера, следующий фрагмент показывает, как установить глобальному символу BEGIN значение первой позиции в секции '.text'; но если символ с таким названием уже существует, его значение не изменяется.

 SECTION {... 
   .text : {
     begin = DEFINED(begin) ? begin : . ;
     ...
   }
 ...}
 NEXT(exp)
Возвращает следующий размещенный адрес, который является числом, кратным exp. Эта функция тесно связана с функцией ALIGN. Если вы не используете команду MEMORY для определения множественных участков памяти в выходном файле, эти две функции эквивалентны.

 SIZEOF(section)
Возвращает размер секции в байтах, если эта секция была размещена. В следующем примере переменным symbol_1 и symbol_2 присваиваются одинаковые значения.

 SECTIONS {...
   .output {
      .start = . ;
      ...
      .end = . ; 
      }
   symbol_1 = .end - .start;
   symbol_2 = SIZEOF(.output);
 ...} 
 SIZEOF_HEADERS
 sizeof_headers
Возвращает размер заголовка выходного файла в байтах. Вы можете использовать это значение, как начальный адрес первой секции, если Вы хотите облегчить постраничность.

3.3 Размещение памяти

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

Скрипт может содержать максимум одну команду MEMORY, тем не менее Вы можете определить любое необходимое Вам число блоков памяти внутри этой команды. Синтаксис этой команды таков:

 MEMORY 
   { 
     <имя> (<аттр>) : ORIGIN=origin, LENGTH =len     
     ...
   }
 <имя> 
Имя, используемое внутри линкера для ссылки на регионы. Вы можете использовать любое имя символа по Вашему желанию. Имена районов располагаются в отдельном хранилище имен и не будут конфликтовать с названиями символов, файлов или секций. Используйте различные имена для описания нескольких районов.

 <аттр> 
Необязательный список атрибутов, разрешенный для совместимости с линкером AT&T, но не используемый LD, кроме проверки на правильность атрибутов. Возможный список атрибутов должен быть создан с использованием набора символов ("LIRWX"). Если Вы не будете использовать список атрибутов, Вы можете также не писать круглые скобки.

 origin 
Начальный адрес региона физической памяти. Это выражение, которое должно быть сосчитано до операции по размещению памяти. Ключевое слово ORIGIN можно сократить до org или o (но не ORG).

 len     
Размер региона в байтах. Ключевое слово LENGHT можно сократить до len или l.

Например, для того, чтобы определить два региона в памяти, доступные для размещения: первый, начинающийся с 0 размером 256 килобайт и второй, начинающийся с адреса 0x40000000 размером 4 мегабайта:

 MEMORY 
   {
   rom : ORIGIN = 0, LENGTH = 256K
   ram : org = 0x40000000, l = 4M
   }
Как только вы определили регион памяти под названием mem, Вы можете напрямую описать специфические выходные секции в нем, используя в окончании команды ключевое слово '>mem' внутри команды SECTION (см. 3.4.4). Если размер объединенных выходных секций в регионе больше размера региона, линкер выводит сообщение об ошибке.

3.4 Описание выходных секций

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

Возможно использование первых двух операций - определения точки входа и описания значений символов вне команды SECTIONS (см. 3.5 и 3.2.6). Их место расположения определяется Вашим выбором для удобства чтения скрипт файла, так что символы и точка входа могут быть описаны в значащих для Вас местах.

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

Описание секций

Наиболее часто используемые выражения в команде SECTIONS - это описания секций, которые определяют их свойства в выходных секциях: расположение, выравнивание, содержание, образец заполнения и целевой регион памяти. Большинство из этих описаний необязательны. Простейшая форма описания секции приведена ниже:

 SECTIONS { ...  
   secname  : { 
     contents 
   } 
 ... }
secname - это имя выходной секции, а contens - это описание того, что туда пойдет, например, описание входных файлов или секций во входных файлах (см. 3.4.2). Как Вы можете догадаться, число пробелов может быть любым по Вашему усмотрению. Название секции должно отвечать ограничениям формата Вашего объектного файла; в форматах, которые поддерживают ограниченное число секций, таких как 'a.out', имя должно быть одним из имен, поддерживаемых форматом файла (a.out, например, разрешает только следующие имена секций: .text, .data, .bss). В форматах объектных файлов, которые поддерживают любое число секций, но с цифрами вместо имен, например 'Oasys', имя должно быть строкой цифр, заключенной в двойные кавычки. Имя секции может состоять из любой последовательности симолов, но любое имя, которое не удовлетворяет стандартому синтаксису имен LD, должно быть заключено в двойные кавычки (см. 3.2.2).

Линкер не будет создавать выходных секций, которые ничего в себе не содержат. Например:

 .foo { *.(foo } 
создаст секцию '.foo' в выходном файле, только в том случае, если секция '.foo' существует хотя бы в одном входном файле.

Расположение секций

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

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

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

Для того чтобы определить список файлов, нужно набрать

    .data : { afile.o bfile.o cfile.o } 
Этот пример также показывает, что множество выражений может быть включено в описание секции, так как каждое имя файла является отдельным выражением.

 <имя-файла>(<секция>)
 <имя-файла>(<секция>, <секция>, ...)
 <имя-файла>(<секция> <секция> ...)
Вы можете назвать одну или более секций из Ваших входных файлов для включения в текущую выходную секцию. Если Вы хотите описать список секций входных файлов внутри скобок, Вы можете разделить имена секций с помощью запятых или пробелов. Вместо точного наименования входных файлов в скрипте Вы можете ссылаться на файлы в командной строке LD, используя '*' вместо имени отдельного файла перед скобками, внутри которых находится список секций.

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

Например, чтобы скопировать секции с номерами от одного до четырех из файла в формате 'Oasys' в секцию '.text' файла в формате 'a.out', и секции 13 и 14 в секцию '.data', необходимо дать следующие команды:

 SECTIONS {
   .text :{
     *("1" "2" "3" "4")
   }
    .data :{ 
     *("13" "14")
   }
 }
'[секция...]' более не используется в качестве альтернативного пути для определения секций из всех неразмещенных входных файлов. Так как некоторые операционные системы (VMS) разрешают квадратные скобки в именах файлов, эта нотация больше не поддерживается.

 <имя-файла>(COMMON)
 * (COMMON) 
Указывает, где в выходном файле помещать неинициализированные данные. * (COMMON) указывает на все неинициализированные данные из всех входных файлов(т.е. на те, которые не были размещены). <имя-файла>(COMMON) указывает на неинициализированные данные из отдельного файла. Оба этих выражения являются специальными случаями общих механизмов определения и размещения секций входных файлов: LD разрешает Вам ссылаться на неинициализированные данные так, как если бы они находились в секции COMMON независимо от формата входных файлов.

Например, следующий скрипт разделяет выходной файл на три секции с названиями: '.text', '.data', '.bss', беря из каждого входного файла соответствующие секции:

 SECTIONS { 
   .text : { *(.text) } 
   .data : { *(.data) }
   .bss :  { *(.bss)  *(COMMON) }
 }
Следующий пример читает все секции из файла 'all.o' и помещает их в начале выходной секции 'outputa', которая начинается с позиции 0x10000. Все секции с названием '.input1' из файла 'foo.o' идут далее в той же выходной секции. Все секции '.input2' из файла 'foo.o' записываются в выходную секцию 'outputb', следующую за секцией '.input1' из файла 'foo1.o'. Все секции называющиеся '.input1' и '.input2' из остальных файлов записываются в выходную секцию 'outputc'.

 SECTIONS { 
   outputa 0x10000 :
     { 
     all.o
     foo.o (.input1) 
     }
   outputb : 
     { 
     foo.o (.input2)
     foo1.o (.input1) 
     } 
   outputc : 
     { 
     *(.input1) 
     *(.input2) 
     } 
 } 

Описание секций данных

Нижеследующий набор команд используется для управления размещением секций данных в выходном файле. Вы можете помещать секции данных как из входных файлов, так и непосредственно описывая их на языке управления линкером в скрипт файлах. Большинство этих дополнительных команд включает в себя выражения (см. 3.2). Все эти команды показаны отдельно для простоты восприятия информации, но такая изоляция не является необходимой в описании секции с помощью команды SECTIONS. Вы можете спокойно смешивать их с любыми командами и выражениями, которые мы уже описали.

 CREATE_OBJECT_SYMBOLS 
Создает символ для каждого входного файла в текущей секции и устанавливает адрес первого байта данных, записанного из того входного файла. Например, с файлом в формате 'a.out' возможно иметь символ для каждого входного файла. Вы можете совершить это, описав выходную секцию 'a.text' как в нижеследующем примере:

 SECTIONS { 
   .text 0x2020 : 
      { 
     CREATE_OBJECT_SYMBOLS 
     *(.text) 
     _etext = ALING(0x2000); 
     } 
   ...
 }

Пусть 'sample.ld' - файл, содержащий этот скрипт, и 'a.o', 'b.o', 'c.o' и 'd.o' - четыре входных файла с содержимым, похожим на нижеследующий пример:

 /* a.c */ 
 
 afunction() { } 
 int adata=1;
 int abss; 
Команда 'ld -M -T sample.ld a.o b.o c.o d.o' создаст файл карты, содержащий символы соответственно именам объектных файлов:

 00000000 A __DYNAMIC 
 00004020 B _abss
 00004000 D _adata 
 00002020 T _afunction 
 00004024 B _bbss 
 00004008 D _bdata 
 00002038 T _bfunction
 00004028 B _cbss 
 00004010 D _cdata 
 00002050 T _cfunction
 0000402c B _dbss
 00004018 D _ddata
 00002068 T _dfunction
 00004020 D _edata
 00004030 B _end 
 00004000 T _etext
 00002020 t a.o
 00002038 t b.o
 00002050 t c.o 
 00002068 t d.o
 
 <символ> = <выражение>
 <символ> f = <выражение>
<символ> - это любое имя символа (см. 3.2.2). "f=" ссылается на любой из операторов '&=', '+=', '-=', '*=', '/=', которые объединяют арифметическую операцию и присваивание. Когда Вы присваиваете значение символу внутри некоторого описания секции, значение зависит от начала секции (см. 3.2.6). Если Вы напишете:

 SECTIONS { 
   abs = 14; 
   ...
   .data : { ... rel = 14; ... } 
   abs2= 14+ ADDR(.data);
   ... 
 } 
abs и rel не равны; rel имеет значение равное abs2.

 BYTE(<выражение>)
 SHORT(<выражение>)
 LONG(<выражение>)
 QUAD(<выражение>)
Путем включения одного из этих четырех выражений в описание секции Вы можете точно разместить один, два, четыре или восемь байт по текущему адресу секции. QUAD поддерживается только на 64-битной архитектуре.

Многобайтовые последовательности размещаются в том порядке, который определен для формата выходного файла (см. 5).

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

Дополнительные атрибуты секции

Перед Вами полный синтаксис описания секции, включающий все дополнительные порции:

 SECTIONS { 
 ...
 secname start BLOCK(align) (NOLOAD) : AT (ldadr) 
   { contents } > region =fill
 ...
 }
Названия секций и содержимое необходимы (см. 3.4.1 и 3.4.2). Остальные элементы start, BLOCK(align), (NOLOAD), AT(ldadr), >region и =fill не обязательны.

Вы можете заставить выходную секцию загружаться по указанному адресу путем определения начала непосредственно после названия секции. 'start' может быть представлен любым выражением. Следующий пример генерирует секцию output по адресу 0x40000000:

 SECTIONS { 
   ... 
   output 0x40000000: { 
     ... 
     } 
   ...
 }
  
 BLOCK(align)
Вы можете включить функцию BLOCK для увеличения счетчика позиций, так что секция будет располагаться с указанным выравниванием. Параметр функции BLOCK может быть выражением.

 (NOLOAD)
Используйте '(NOLOAD)' для запрета загрузки секции в память при каждом обращении. В нижеследующем примере сегмент ROM начинается с абсолютной позиции '0', и его не нужно загружать в каждый объектный файл. Пример:

 SECTIONS { 
   ROM  0  (NOLOAD)  : { ... } 
   ...
 }
 
 AT(ldadr) 
Выражение ldadr, которое следует за ключевым словом AT, определяет адрес загрузки секции. По умолчанию, если Вы не использовали ключевое слово AT, адрес загрузки равен адресу перемещения. Эта возможность создана для облегчения построения образов ROM. Например, в следущем примере команда SECTIONS создает две выходные секции: одна называется '.text' и начинается с адреса 0x1000, а другая называется '.mdata' и загружается в конце секции '.text' несмотря на то, что адрес ее перемещения равен 0x2000. Символ '.data' описан со значением 0x2000:

 SECTIONS
   { 
   .text 0x1000 : { *(.text) _etext = . ; } 
   .mdata 0x2000 : 
     AT ( ADDR(.text) + SIZEOF (.text) )
     { _data = . ; *(.data); _edata= . ; } 
   .bss 0x3000 : 
     { _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
 } 
Инициализационный код времени выполнения (для Cи программ обычно crt0), при использовании его с образами ROM, созданными таким путем, вынужден включать что-то похожее на нижеследующий пример для копирования инициализированных данных из образа ROM на адрес времени выполнения:

 char *scr = _etext;
 char *dst = _data; 
 
 /* Данные находятся в конце секции текст; скопировать их. */
 while (dst < _edata) { 
   *dst++ = *scr++;
 } 
 
 /* Обнулить bss */ 
 for (dst = _bstart; dst< _bend; dst++)
   *dst = 0;
 
 >region
Присвоить текущей секции предыдущий регион памяти (см. 3.3).

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

3.5 Точка входа

ЯУЛ включает команду специально для определения первой запускаемой инструкции в выходном файле (его точку входа); аргумент команды - это имя символа:

   ENTRY(<символ>) 
Как присваивание символов команда ENTRY может быть помещена в качестве независимой команды в скрипт файле или внутри описания секции в команде SECTIONS - как Вам больше нравится.

ENTRY - один из нескольких путей указания точки входа. Вы можете использовать любой из описанных ниже способов (способы расположены в порядке уменьшения приоритета; методы, расположенные в списке выше, переопределяют методы, лежащие в списке ниже):

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

 start = 0x2020; 
В примере переменной start присваивается абсолютное значение, но Вы можете присвоить ей любое выражение. Например, если Ваш входной файл использует какое-либо другое имя символа для точки вхождения, Вы можете присвоить значение этого символа символу start:

 start = other_symbol ;

3.6 Команды опций

ЯУЛ включает в себя несколько команд, которые имеют специальное назначение. Они идентичны опциям командной строки.

 CONSTRUCTORS 
Это команда связывает записи конструкторов и деструкторов в стиле языка C++. Детали представления конструктора отличаются в разных объектных форматах, но обычно список конструкторов и деструкторов располагается в специальных секциях. Команда CONSTRUCTORS определяет, где линкер должен помещать информацию из этих секций относительно остального выхода линкера. Данные конструкторов помечены символом __CTOR_LIST__ в начале и __CTOR_LIST_END в конце. Данные деструктора разделяются аналогично между __DTOR_LIST__ и __DTOR_LIST_END (компилятор должен обработать данные этих секций для правильной работы программы).

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

 FORCE_COMMON_ALLOCATION 
Эта команда имеет эффект, аналогичный опции командной строки '-d'. Она используется для того, чтобы LD присваивал значения общим символам, даже если используется перемещаемый формат объектного файла. (См. '-r')

 INPUT(<файл>, <файл>, ...)
 INPUT(<файл> <файл> ...)
Используйте эту команду для включения двоичных файлов в линковку, без включения их в описание конкретной секции. Необходимо описывать полное имя каждого файла, включая '.a', если оно присутствует.

LD ищет каждый файл по путям, указанным для поиска библиотек, как для файлов, имена которых Вы описали в командной строке (см. 2.1).

Если Вы использовали '-l<файл>', LD трансформирует это имя в 'lib<файл>.a', как и опцию командной строки '-l'.

 GROUP(<файл>, <файл>, ...)
 GROUP(<файл> <файл> ...)
Эта команда похожа на команду INPUT, за исключением того, что указанные файлы должны быть библиотеками, и они будут просматриваться многократно до тех пор, пока не будет создано ни одной новой неопределенной ссылки (см. 2.1).

 OUTPUT(<имя-файла>)
Используйте эту команду для определения имени выходного файла. Действие описываемой команды аналогично действию опции командной строки '-o <имя-файла>', которая может переопределить данную команду. Вы можете использовать эту команду для переопределения имени файла по умолчанию и делания его отличным от 'a.out'.

 OUTPUT_ARCH(<имя-bfd>)
Указывает архитектуру машины, выбирая из одной поддерживаемой BFD (см. 5). В большинсте случаев эта команда не является необходимой; как правило, архитектура определяется во время конфигурации библиотеки BFD.

 OUTPUT_FORMAT(<имя-bfd>) 
Когда LD отконфигурирован для поддержки нескольких объектных форматов, Вы можете использовать эту команду для определения конкретного формата выходного файла. <имя-bfd> - это одна из архитектур, поддерживаемая библиотекой BFD (см. 5). Действие этой команды идентично действиям опции командной строки '-oformat'. Эта команда влияет только на выходной файл. Для изменения формата входных файлов используйте команду TARGET.

 SEARCH_DIR(<путь>)
Действие этой команды идентично действию опции командной строки '-L<путь>'.

 STARTUP(<имя-файла>)
Делает указанный файл первым входным файлом в процессе линковки.

 TARGET(<формат>)
Когда LD отконфигурирован для поддержки нескольких форматов объектного файла, Вы можете использовать эту команду для изменения форматов входных файлов. Действие этой команды аналогично действиям опции командной строки '-b' и '-format'. Если команда TARGET используется, а команда OUTPUT_FORMAT нет, последний аргумент команды TARGET используется в качестве формата выходного файла (см. 5).


Вперед Назад Содержание
  Закладки на сайте
  Проследить за страницей
Created 1996-2018 by Maxim Chirkov  
ДобавитьПоддержатьВебмастеруГИД  
Hosting by Ihor