Каждый DSP-проект требует один файл команд линкера (Linker Description File, файл с расширением .ldf). Файл .ldf задает все исходные данные о том, как линковать проекты. Глава 2 "Linker" даташита [1] (перевод см. в статье [2]), описывает процесс линковки, и как файл .ldf участвует в этом процессе.
Когда генерируется новый файл .ldf, используйте Expert Linker. Подробности см. в Главе 4 "Expert Linker" руководства [1].
Файл .ldf позволяет разрабатывать код для любой процессорной системы. Он описывает Вашу систему для линкера, и указывает, как линкер будет создавать исполняемый код программы для Вашей системы. В этой главе документации [1] описывается синтаксис файла .ldf, его структура и отдельные составляющие. Обратитесь к Приложению C "LDF Programming Examples for TigerSHARC Processors", Приложению D, "LDF Programming Examples for SHARC Processors" и Приложению E "LDF Programming Examples for Blackfin Processors" для получения примеров файла .ldf для типичных систем.
В этой статье (перевод Главы 3 "Linker Description File" даташита [1]) рассматриваются следующие вопросы:
• Обзор файла LDF • Структура файла LDF • Выражения LDF • Ключевые слова, команды и операторы LDF • Макросы LDF • Команды LDF
Линкер запускает препроцессор файла .ldf, поэтому Вы можете использовать команды препроцессора (такие как определения #define), записанные в этом файле. Для получения информации по командам препроцессора см. руководство "VisualDSP++ 5.0 Assembler and Preprocessor Manual".
Декларации секции ассемблера в этом документе относятся к директиве ассемблера .SECTION.
Обращайтесь к примерам программ DSP, поставляемым вместе с VisualDSP++ для получения рабочих примеров файлов .ldf, поддерживающих типичные модели систем (вместо того, чтобы создавать файлы LDF с нуля).
[Обзор файла LDF]
Файл .ldf указывает линкеру отображать код или данные на определенные сегменты памяти. Линкер отображает кода программы (и данные) в системной памяти и памяти процессора (процессоров), и присваивает абсолютный адрес каждому символу (symbol) программы, где:
symbol = label symbol = function_name symbol = variable_name
Здесь "label" это метка в программе пользователя, "function_name" и "variable_name" это соответственно имя функции и имя переменной программы пользователя.
Если Вы не создали свой файл .ldf для проекта (или вручную с нуля, или с помощью инструментария VisualDSP++), и не импортировали готовый файл .ldf в свой проект, то среда VisualDSP++ для линковки кода будет использовать файл .ldf по умолчанию. Выбор файла .ldf по умолчанию зависит от типа процессора, указанного в окне диалога настройки свойств проекта VisualDSP++ (окно Project Options).
Примечание: файлы .ldf по умолчанию поставляются вместе со средой разработки VisualDSP++ в папке $ADI_DSP\семейство\ldf. Здесь "$ADI_DSP" означает корневой каталог установки VisualDSP++. Каждый подкаталог "семейство" соответствует определенной разновидности процессора (подробнее см. "Default LDF" ниже).
Файл .ldf комбинирует информацию, указывающую линкеру размещать входные секции в исполняемом файле в память, доступную в системе DSP (подробнее про входные, выходные секции и разновидности областей памяти см. [2]).
Линкер может выводить предупреждающие сообщения и сообщения об ошибках. Разница между ними в том, что предупреждающие сообщения просто сообщают о потенциальной проблеме, не останавливая процесс линковки и генерации исполняемого файла, а сообщения об ошибке завершают работу линкера. Вы должны устранить причины сообщений об ошибке, чтобы линкер мог сгенерировать требуемый результат (обычно это исполняемый файл .dxe). Для получения дополнительной информации см. раздел "Сообщения об ошибках и предупреждения линкера" статьи [2].
Сгенерированный LDF для Blackfin. На платформе Blackfin имеется инструментарий создания нового проекта (VisualDSP++ New Project Wizard) и диалог настройки свойств проекта (Project Options), которые позволяют Вам генерировать и конфигурировать пользовательский файл настроек линкера (Linker Description File, .ldf). Файл .ldf можно добавить в разделе Add Startup Code/LDF диалога свойств проекта Project Options. Это самый простой и быстрый способ получить для проекта требуемым образом настроенный файл .ldf. Для получения дополнительной информации про возможности Project Wizard и Project Options см. VisualDSP++ Help.
Default LDF (LDF по умолчанию). Имя каждого файла .ldf показывает процессор, для которого файл .ldf предназначен (например ADSP-BF531.ldf). Если файл .ldf не имеет суффикса, то это файл LDF по умолчанию (default .ldf). Таким образом, когда для проекта не указан явно файл .ldf, то будет использоваться файл LDF по умолчанию, когда собирается приложения для определенного процессора. Например, в этом случае для процессора ADSP-BF531 будет использоваться файл ADSP-BF531.ldf.
Если файл .ldf не указан явно ключом командной строки –T, то драйвер компилятора выбирает для целевого процессора файл .ldf по умолчанию. Например, первая из следующих команд использует файл .ldf по умолчанию, и вторая использует пользовательский файл:
На платформах SHARC и TigerSHARC для каждого процессора имеется 3 файла .ldf с суффиксами _C, _CPP и _ASM (например, ADSP-21363_C.ldf). На платформах SHARC и TigerSHARC эти файлы .ldf являются шаблонами для Expert Linker. Если используете Expert Linker для создания пользовательского файла .ldf для своего проекта, то Expert Linker запрашивает тип этого файла LDF, какой Вам нужен (для C, C++ или ассемблера), и затем делает копию из одного из вышеперечисленных шаблонов (после чего настраивается именно копия). Суффиксы показывают вид поддерживаемого файла .ldf.
Шаблон CPP является надстройкой (superset) над шаблоном C, и шаблон C является надстройкой над шаблоном ASM. Отличия между ними следующие:
• Шаблон CPP линкует библиотеки C++ run-time, библиотеки C++ exception, и заголовки run-time, собранные для инициализации конструкторов C++. Это отображает секции данных, которые содержат информацию, управляющую обработкой выброшенных исключений (thrown exceptions). • Шаблон C в настоящее время идентичен шаблону CPP, поскольку проект C может линковать локальные или системные библиотеки, которые реализованы на C++. В будущих релизах VisualDSP++ могут быть отличия. • Шаблон ASM не подключает заголовок run-time, и не позволяет использовать для встраиваемых приложений аргументы командной строки (argc, argv). Поэтому шаблон ASM не подходит для использования оптимизации PGO [3]. Поскольку у шаблона ASM нет заголовка run-time, то не передается символ "start" для распознавания адреса сброса (Reset address). Шаблон ASM не отображает на память секции исключений C++ (C++ exception). sections into memory.
Каждый файл .ldf обрабатывает различные требования, позволяя осуществить сборку приложений в нескольких конфигурациях, что делается простым предоставлением нескольких опций командной строки. Эта гибкость достигается экстенсивным использованием в файле .ldf макросов препроцессора. Макросы работают как флаги для того или иного выбора, и также как переменные в файле .ldf для хранения имени выбранного файла или другого параметра времени линковки.
Если коротко, то различные конфигурации LDF выбираются определением макросов препроцессора через опции командной строки линкера. Эти опции могут быть указаны в разделе Link диалога настройки свойств проекта среды разработки VisualDSP++ IDDE (окно Project Options), либо непосредственно в командной строке при ручном (или из командного файла) запуске линкера.
В начале файлов .ldf по умолчанию для процессоров Blackfin Вы найдете документацию по макросам, которые можете использовать для конфигурирования файлов .ldf по умолчанию.
Вы можете использовать файл .ldf, который напишете с нуля. Однако модифицирование готового файла .ldf (взятого из проектов примеров или из файла .ldf по умолчанию) часто будет самой простой альтернативой, когда не требуется ввод больших изменений в аппаратуре и программном обеспечении Вашей системы.
Листинги 3-1, 3-2 и 3-3 по врезках ниже показывают базовые файлы .ldf для поддерживаемых процессоров. См. "Общие замечания по примерам базовых файлов LDF" для получения основной информации о структуре LDF.
Для примеров кода к процессорам TigerSHARC, SHARC и Blackfin см. в руководстве [1] соответственно приложения "LDF Programming Examples for TigerSHARC Processors” на странице E-1, "LDF Programming Examples for SHARC Processors" на странице D-1, и "LDF Programming Examples for Blackfin Processors" на странице C-1.
Листинг 3-1 дает пример базового файла .ldf для процессоров ADSP-BF535 (отформатированный для улучшения читаемости). Обратите внимание, что команды MEMORY{} и SECTIONS{} ссылаются на информацию из подраздела "Общие замечания по примерам базовых файлов LDF". Другие примеры файлов LDF предоставляются в Приложении C "LDF Programming Examples for Blackfin Processors" документа [1].
Листинг 3-1. Пример LDF для процессора ADSP-BF535.
argv
{ /* Выделение места под аргументы командной строки приложения */
ldf_argv_space = .;
ldf_argv_end = ldf_argv_space + MEMORY_SIZEOF(MEM_ARGV) - 1;
ldf_argv_length = ldf_argv_end - ldf_argv_space;
} >MEM_ARGV
} /* Конец SECTIONS */
} /* Конец PROCESSOR p0 */
Использование памяти в процессорах Blackfin. Файлы .ldf по умолчанию определяют обрасти памяти для всех доступных областей процессора (за исключением MMR, служебных регистров процессора и его периферийных устройств которые находятся в недоступной для линкера области адресного пространства). Не все из этих областей используются в файлах .ldf. Вместо этого файлы .ldf предоставляют 3 базовые конфигурации памяти:
• В конфигурации по умолчанию задано, что доступна только внутренняя память (обычно это L1, находящаяся на кристалле чипа процессора), и кэширование запрещено (кэширование не требуется, так как L1 самая быстрая память). Таким образом, в память SDRAM (это внешняя, более медленная память L3) не отображается никакой код и никакие данные (если это явно не задано в программе пользователя), и вся необходимая информация (код, данные) размещается во внутренней памяти L1. • Определение макроса USE_CACHE выбирается альтернативная конфигурация, когда разрешено кэширование кода и данных, и используется внешняя память SDRAM (динамическая память L3, которая работает медленнее L1, и поэтому кэширование для неё может быть полезным). Код и данные отображаются в L1, пока это возможно, но области кэша/SRAM оставляются пустыми; все, что не поместится в L1, будет отображено в SDRAM автоматически (либо это может быть сделано программистом вручную с помощью директив section для управления размещением). • Определение макроса USE_SDRAM дает тот же эффект, как и определение макроса USE_CACHE, с исключением, что код и данные отображаются в области L1 Cache/SRAM.
Если используется макрос USE_CACHE, то кэширование может быть безопасно включено, потому что это не приведет к повреждению кода или данных. Выбор опции USE_CACHE не делает реального разрешения кэширования, это должно быть сделано отдельно (например, через конфигурационную переменную ___cplb_ctrl). Вместо этого опция USE_CACHE гарантирует, что карта памяти обеспечит резервирование места для кэша, который будет разрешен в приложении позже.
Общей пользовательской ошибкой бывает ситуация, когда работа кэша разрешена, несмотря на отсутствие в проекте определения макроса USE_CACHE. Это приведет к повреждению кода или данных, так как кэш перезапишет содержимое SRAM с полезными данными. Таким образом, файлы LDF используют следующие символы защиты ("guard symbols"):
Эти символы определяются файлами .ldf, и им даются указанные значения (0 или 1, т. е. разрешены ли они на адреса), в зависимости от того, определен ли макрос USE_CACHE, или нет. Библиотека run-time проверяет эти символы, когда запрашивается конфигурация кэша, и отменяет разрешение кэша, если соответствующий защитный символ равен 0 (если символ защиты равен 0, то это показывает, что в соответствующей области памяти может находиться полезная информация, и её нельзя использовать для кэша).
Для дополнительной информации см. руководство "VisualDSP++ 5.0 C/C++ Compiler and Library Manual", раздел "Caching and Memory Protection" (кэширование из защита памяти).
Листинг 3-2 дает базовый пример файла .ldf для процессора ADSP-TS101 (отформатированный для улучшения читаемости). Обратите внимание, что команды MEMORY{} и SECTIONS{} ссылаются на информацию из подраздела "Общие замечания по примерам базовых файлов LDF". Другие примеры файлов LDF предоставляются в Приложении E "LDF Programming Examples for TigerSHARC Processors".
Листинг 3-2. Пример файла LDF для процессора ADSP-TS201.
Листинг 3-3 дает пример базового файла .ldf для процессора ADSP-21161 (форматированный для удобства чтения). Обратите внимание, что команды MEMORY{} и SECTIONS{} ссылаются на информацию из подраздела "Общие замечания по примерам базовых файлов LDF". Другие примеры файлов LDF для ассемблера и C предоставляются в Приложении D "LDF Programming Examples for SHARC Processors".
Листинг 3-3. Пример файла LDF для процессора ADSP-21161.
// $ADI_DSP это предопределенный макрос линкера, который расширяется
// до директории инсталляции VisualDSP++. Поиск объектов в директории
// 21k/lib относителен к директории инсталляции lib161.dlb осуществляется
// как библиотеки, специфичной для ADSP-2116x, и должна линковаться
// C-библиотека libc.dlb с поддержкой библиотек 2116x.
$LIBS = lib161.dlb, libc.dlb;
// single.doj это файл, сгенерированный из кода пользователя.
// Линкер будет запускаться следующим образом:
// linker -T single-processor.ldf single.doj.
// $COMMAND_LINE_OBJECTS это предопределенный макрос линкера.
// Линкер расширяет этот макрос в имя (имена) объекта (объектов,
// файлов .doj) и библиотек (файлов .dlb), которые появятся
// в командной строек линкера. Для этого примера:
// $COMMAND_LINE_OBJECTS = single.doj
// 161_hdr.doj это стандартный файл инициализации для 2116x
$OBJS = $COMMAND_LINE_OBJECTS, 161_hdr.doj;
// Линкер генерирует исполняемый файл .dxe проекта
PROCESSOR P0
{
OUTPUT ( ./SINGLE.dxe ) // Имя выходного файла
MEMORY // Команда памяти, зависящая от процессора
{ INCLUDE("21161_memory.h"}
SECTIONS // Здесь указаны выходные секции
{
INCLUDE("21161_sections.h" )
} // конец секций P0
} // конец установок процессора P0
Общие замечания по примерам базовых файлов LDF. В следующем описании LDF-команды MEMORY{} и SECTIONS{} подключают программу к целевому процессору. Для информации по синтаксису команд LDF см. далее раздел "Команды LDF".
Эти заметки описывают типовой файл .ldf (как он представлен в листингах 3-1, 3-2, 3-3 врезок выше).
• ARCHITECTURE(ADSP-xxxxx) указывает целевую архитектуру (target architecture) процессора. Например, ARCHITECTURE(ADSP-BF533). Архитектура диктует возможные значения ширины данных (1 байт, 2 байта, 3 байта, 40, 48 бит, и т. п.), набор регистров и другую структурную информацию, которую использует отладчик (debugger), линкер (linker) и загрузчик кода (loader). Целевая архитектура должна быть установлена (поддерживаться) в среде разработки VisualDSP++.
• SEARCH_DIR() указывает директорию для пути поиска библиотек и объектных файлов. Например, аргумент этой команды $ADI_DSP/Blackfin/lib задает одну директорию поиска для библиотек Blackfin и объектных файлов.
Линкер поддерживает последовательность из нескольких указанных директорий поиска, представленных в виде списка в аргументе этой команды (directory1, directory2, ...). Линкер следует этой последовательности и останавливает поиск при первом подходящем совпадении.
• $LIBRARIES это список библиотек и объектных файлов, по которым в требуемом порядке осуществляется поиск для разрешения ссылок на внешние объекты (подпрограммы, функции, переменные). Некоторые из опций задает выбор одной библиотеки из нескольких.
• $OBJECTS это пример макроса, определенного пользователем, который расширяется на список имен файлов, отделенных друг от друга запятой. Макросы улучшают читаемость кода путем замены больших строк текста на более короткие. Строковые макросы концептуально подобны поддержке макросов препроцессора (#define), которые также доступны в файле .ldf, однако строковые макросы независимы. В этом примере $OBJECTS расширяется до списка входных файлов (файлы списка отделяются друг от друга запятой), используемых для линковки.
Примечание: в этом примере, и в файлах .ldf по умолчанию (которые сопровождают VisualDSP++), $OBJECTS к команде SECTIONS() задает объектные файлы, которые ищутся для отображения в определенной входной секции.
В качестве другого примера, $ADI_DSP расширяется до домашнего каталога установки VisualDSP++.
• $COMMAND_LINE_OBJECTS это макрос командной строки LDF, который расширяется в файле .ldf до списка входных файлов, которые указаны в командной строке линкера.
Примечание: порядок, в котором линкер обрабатывает объектные файлы (это влияет на порядок, в каком адреса в сегментах памяти назначаются на входные секции и символы) определяется порядком файлов, как они перечислены в командах INPUT_SECTIONS(). Как это было указано ранее, этот порядок обычно соответствует порядку в $OBJECTS ($COMMAND_LINE_OBJECTS).
VisualDSP++ генерирует командную строку линкера, которая перечисляет объекты в алфавитном порядке. Этот порядок передается через макрос $OBJECTS. Вы можете настроить .ldf для линковки объектов в любом желаемом порядке. Вместо использования макросов по умолчанию, таких как $OBJECTS, каждая команда INPUT_SECTION может иметь одно имя или большее количество явно указанных имен объектов.
• Команда MEMORY{} определяет физическую память целевой системы, и подключает программу к целевой системе (отображает объекты программы на физические адреса памяти системы). Аргументы команды MEMORY делят память на сегменты. Каждому сегменту памяти назначается отдельное имя, тип памяти, начальный и конечный адрес (или длина сегмента), и ширина ячеек в памяти (memory width). Эти имена занимают отдельное пространство имен, отличающееся от пространства имен входных секций и выходных секций. Таким образом, имена сегментов памяти и имена входных (и/или) выходных секций могут получать одинаковые имена (что, кстати, иногда вносит немалую путаницу).
• Каждая команда PROCESSOR{} генерирует один исполняемый файл.
• Команда OUTPUT() задает имя для генерируемого выходного исполняемого файла (.dxe).
В базовом примере аргументом команды OUTPUT() является макрос $COMMAND_LINE_OUTPUT_FILE. Линкер даст имя исполняемому файлу в соответствии с текстом, который идет в опции командной строки -o outputfilename (этот текст соответствует имени, которое задано в диалоге настройки свойств проекта Project Options, когда линкер запускается средой разработки VisualDSP++ IDDE).
linker ... -o outputfilename
• SECTIONS{} задает размещение кода и данных в физической памяти. Линкер отображает входные секции (в объектных файлах) на выходные секции (в исполняемых файлах), и отображает выходные секции на сегменты памяти, указанные командой MEMORY{}.
• Оператор INPUT_SECTIONS() задает объектный файл, который линкер использует в качестве входной информации для реализации отображения на подходящий сегмент памяти, декларированный в файле .ldf.
Например, в процессорах TigerSHARC следующий оператор INPUT_SECTIONS() указывает линкеру разместить входную секцию program в выходной секции code, чтобы отобразить это на сегмент памяти M0Code.
Для процессоров SHARC следующий оператор INPUT_SECTIONS() указывает линкеру разместить входную секцию isr_tbl в выходной секции dxe_isr, для отображения этого на сегмент памяти mem_isr.
Вторая строка этого примера указывает линкеру разместить объектный код, собранный из исходного кода во входной секции program (это задается директивой ".section program" в исходном коде ассемблера), в выходную секцию dxe_L2, и отобразить это в сегмент памяти MEM_L2. Четвертая строка делает то же самое для входной секции data1 и выходной секции dxe_L2, отображая их в тот же сегмент памяти MEM_L2.
Эти два куска кода будут идти друг за другом в сегменте памяти program.
Команды INPUT_SECTIONS() обрабатываются в том же порядке, в каком объектные файлы появляются в макросе $OBJECTS. Вы можете вставлять операторы INPUT_SECTIONS() в выходную секцию вместе с другим директивами, включая информацию размещения счетчика (location counter information).
[Структура файла LDF]
Один из способов создать простой файл .ldf, удобный для дальнейшей поддержки - задействовать параллельную структуру Вашей DSP-системы. Следуйте указаниям ниже, используя Вашу систему в качестве модели.
• Поделите файл на набор команд PROCESSOR{} по одной на каждый процессор DSP, присутствующий в Вашей системе. • Поместите команду MEMORY{} в области, которая соответствует Вашей системе, и определите память, уникальную для процессора, в соответствующей области команды PROCESSOR{}. • Если это применимо, разместите команду SHARED_MEMORY{} в глобальной области файла .ldf. Эта команда задает ресурсы системы, доступные в качестве общей памяти для многопроцессорной рабочей среды.
Декларируйте общие определения памяти (shared) в глобальной области перед командами PROCESSOR{}. Для дополнительной информации см. далее "Область действия команды".
Комментарии в LDF. Допустимы комментарии в стиле языка C, начинающиеся на /* и заканчивающиеся на */, и которые могут содержать символы перевода строки.
Комментарии в стиле C++ начинаются на // и заканчиваются в конце строки. Для дополнительной информации по структуре файла .ldf см. в [2] следующие разделы:
• "Линковка описания целевого процессора (Link Target Description)" • "Размещение кода в целевом процессоре"
Также см. примеры LDF для различных процессоров в приложениях C, D, E документации [1].
Область действия команды. Область действия и область видимости это одно и тоже (см. врезку ниже с объяснением термина). В контексте файла LDF имеется в виду область действия операторов и имен. Всего существует 2 области действия операторов в файле LDF: глобальная область и область команды (см. рис. 3-1).
"В программировании область видимости (англ. scope) обозначает область программы, в пределах которой идентификатор (имя) некоторой переменной продолжает быть связанным с этой переменной и возвращать её значение. За пределами области видимости тот же самый идентификатор может быть связан с другой переменной, либо быть свободным (не связанным ни с какой из них).
В большинстве языков программирования область видимости переменной определяется местом её объявления. Кроме того, область видимости может задаваться явно с помощью классов памяти или пространств имён."
Глобальная область находится вне команд. Команды и выражения в глобальной области всегда доступны и видны в любых последующих областях действия. Макросы LDF доступны глобально, независимо от области, в которой макрос определен (см. раздел "Макросы LDF").
Область команды (command scope) относится ко всем командам, которые появляются между скобками ({ }) другой команды, такой как PROCESSOR{} или PLIT{}. Команды и выражения, которые появляются в областях команды, имеют ограничение по действию - они работают только в этой области.
Рис. 3-1 показывает некоторые проблемы областей действия. Например, команда MEMORY{}, которая появляется глобальной области LDF, доступна во всех областях команд, но команда MEMORY{}, которая появляется в области команды, ограничена действием в этой области.
[Выражения LDF]
Команды LDF могут содержать в себе арифметические выражения, которые следуют правилам синтаксиса выражений C/C++. Линкер:
• Вычисляет все выражения как тип unsigned long, и обрабатывает константы как тип unsigned long. • Поддерживает все арифметические операторы языка C/C++. • Позволяет применять в LDF определения (definitions) и ссылки (references) на символические константы. • Позволяет делать ссылки на глобальные переменные в линкуемой программе. • Распознает метки, которые удовлетворяют следующим ограничениям:
- Должны начинаться с буквы, подчеркивания или точки. - Могут содержать буквы, подчеркивания, цифры или точки. - Используют в качестве разделителя пробел. - Не конфликтуют ни с каким из ключевых слов. - Являются уникальными.
Таблица 3-1 перечисляет элементы, допустимые в выражении.
Таблица 3-1. Допустимые элементы в выражениях.
Соглашение
Описание
.
Текущее значение счетчика размещения, location counter (символ точки в выражении адреса). Подробнее см. врезку "Location Counter (.), текущая позиция".
0xnumber
Шестнадцатиричное число (с префиксом 0x).
number
Десятичное число (число без префикса).
numberk или numberK
Десятичное число, обозначающее количество в единицах 1024.
B#number или b#number
Двоичное число.
[Ключевые слова, команды и операторы LDF]
Описания ключевых слов, операторов, макросов и команд предоставлены в следующих секциях.
Ключевые слова чувствительны к регистру символов (case sensitive); линкер распознает только те ключевые слова, которые целиком указаны в верхнем регистре (UPPERCASE).
Ключевые слова LDF. Таблица 3-2 перечисляет все основные ключевые слова LDF (используются в семействах процессоров Blackfin, SHARC и TigerSHARC).
Таблица 3-2. Общий список ключевых слов LDF.
Ключевое слово
Описание
ABSOLUTE
Возвращает абсолютное значение выражения, применяется для назначения символу абсолютного адреса.
ADDR
Вернет начальный адрес именованной выходной секции.
ALGORITHM
Указывает линкеру использовать определенный алгоритм линковки оверлея.
ALIGN
Выравнивает адрес текущей позиции размещения.
ALL_FIT
Пока что единственный алгоритм для ALGORITHM.
ARCHITECTURE
Задает целевой процессор системы.
BEST_FIT
Пока что не реализованный алгоритм для ALGORITHM.
BOOT
?
COMMON_MEMORY
Используется для отображения памяти, которая является общей (shared) в MP-системе.
DATA64
Квалификатор типа, используемый в команде SECTIONS{}. Соответствует 8 байтам на слово.
DEFINED
Вернет 1, если указанный символ находится в глобальной таблице символов.
DM
Квалификатор типа, используемый в команде SECTIONS{}. Обозначает память данных, соответствует 4 байтам на слово.
DYNAMIC
?
ELIMINATE
Разрешает работу функции удаления не используемых объектов.
ELIMINATE_SECTIONS
Разрешает работу функции удаления не используемых объектов в указанных секциях.
ENTRY
Задает адрес входа в программу.
END
Обозначает конец сегмента.
FILL
Используется в команде определения секции для заполнения указанным значением пустых мест секции, которые образуются из-за выравнивания.
FIRST_FIT
Пока что не реализованный алгоритм для ALGORITHM.
INCLUDE
Используется для подключения файла LDF в код файла LDF (работает аналогично #include языка C).
INPUT_SECTION_ALIGN
Директива для выравнивания входной секции.
INPUT_SECTIONS
Часть команды секции, идентифицирует части программы, размещаемые в исполняемом файле.
INPUT_SECTIONS_PIN
Используется для отображения входной секции в одну из нескольких выходных секций.
INPUT_SECTIONS_PIN_EXCLUSIVE
KEEP
Применяется для сохранения некоторых символов от удаления функцией eliminate.
KEEP_SECTIONS
Применяется для отмены функции eliminate для указанных секций.
LENGTH
Идентифицирует длину сегмента памяти (в словах).
LINK_AGAINST
Проверяет указанные исполняемые файлы для разрешения в адреса имен переменных и меток, которые не разрешаются локально.
MAP
Команда задает вывод файла отображения.
MEMORY
Задает карту памяти целевой системы.
MEMORY_SIZEOF
Вернет размер в словах именованного сегмента памяти.
MPMEMORY
Задает смещение каждого процессора MP-системы в физической памяти.
NO_INIT
Квалификатор памяти, который обозначает не инициализируемую память.
NUMBER_OF_OVERLAYS
Пока недоступная команда. Вернет количество оверлеев, генерируемых указанным текущим алгоритмом.
OUTPUT
Задает имя выходного исполняемого файла.
OVERLAY_GROUP
Устаревшая команда. Предоставляет поддержку для определения набора оверлеев, которые совместно используют блок памяти run-time.
OVERLAY_ID
Вернет идентификатор оверлея.
OVERLAY_INPUT
Идентифицирует в команде секции части программы, которые должны быть размещены в исполняемом файле оверлея (*.ovl).
OVERLAY_OUTPUT
Выводит файл оверлея (*.ovl) с указанным именем.
PACKING
Используется только для процессоров SHARC. Применяется для структурирования выходного исполняемого файла в соответствии с организацией памяти.
PLIT
Обслуживает создание таблиц линковки процедур (procedure linkage table).
PLIT_SYMBOL_ADDRESS
Вернет абсолютный адрес размещенного символа в памяти run-time.
PLIT_SYMBOL_OVERLAYID
Вернет идентификатор оверлея.
PM
Квалификатор типа, используемый в команде SECTIONS{}. Обозначает память программы, соответствует 6 байтам на слово.
PROCESSOR
Декларирует процессор и связанную с ним информацию для линковки.
RAM
Задает функциональность памяти для TYPE.
RESERVE
Выделяет место в памяти и определяет для него символы начала и конца.
RESOLVE
Игнорирует команду LINK_AGAINST для указанного символа.
RESERVE_EXPAND
Может идти за командой RESERVE, и может использоваться для определения тех же символов, что и RESERVE. Обычно используется последней для задействования не используемой памяти после отображения входных секций.
ROM
Задает функциональность памяти для TYPE.
RUNTIME_INIT
Квалификатор памяти, который задает инициализацию памяти библиотекой C run-time.
SEARCH_DIR
Задает одну или большее количество директорий поиска входных файлов.
SECTIONS
Использует сегменты памяти (определенные командой MEMORY{}) для размещения выходных секций.
SHARED_MEMORY
Используется для генерации файлов общей памяти (*.sm).
SHT_NOBITS
Квалификатор секции, эквивалент NO_INIT.
SIZE
Задает верхний лимит памяти, которую может занимать оверлей.
SIZEOF
Вернет размер (в байтах) указанной выходной секции.
START
Задает начальный абсолютный адрес сегмента.
SW
Квалификатор типа, используемый в команде SECTIONS{}. Соответствует 2 байтам на слово.
TYPE
Задает тип памяти, зависящий от архитектуры, в внутри сегмента памяти.
VERBOSE
?
WIDTH
Задает физическую разрядность в битах интерфейса памяти.
ZERO_INIT
Квалификатор памяти, который обозначает память, инициализируемую нулями.
Дополнительные ключевые слова LDF. Следующие ключевые слова линкера не являются операторами, макросами или командами.
Таблица 3-3. Дополнительные ключевые слова LDF.
Ключевое слово
Описание
FALSE
Константа со значением 0.
TRUE
Константа со значением 1.
XREF
Установка опции перекрестной ссылки (cross-reference). См. описание опции -xref.
Для дополнительной информации о ключевых словах LDF см. разделы "Операторы LDF", "Макросы LDF" и "Команды LDF".
Операторы LDF. Это выражения, поддерживающие операции с адресами памяти. Выражения, которые содержат эти операторы, завершаются точкой с запятой, за исключением случаев, когда оператор работает как переменная для получения адреса. Линкер отвечает на несколько операторов LDF, включая счетчик размещения (location counter).
Каждый оператор LDF описывается ниже в отдельных врезках.
Линкер вернет значение выражения expression. Используйте этот оператор для назначения символу абсолютного адреса. В качестве выражения может быть:
• Символическое выражение, заключенное в круглые скобки, например:
ldf_start_expr = ABSOLUTE(start +8);
В этом примере ldf_start_expr назначается значение соответствующее адресу символа start, к которому прибавлено 8, таким образом:
ldf_start_expr = start +8;
• Целочисленная константа в одной из следующих форм: шестнадцатеричная, десятичная, или десятичная, за которой следует "K" (обозначает кило [*1024]) или "M" (обозначает мега [*1024*1024])
• Точка, показывающая текущее место в памяти (см. врезку "Location Counter (.), текущая позиция").
Следующий оператор, который определяет нижнюю границу пространства стека в LDF:
Этот оператор вернет начальный адрес именованной выходной секции, которая определена в файле .ldf. Используйте этот оператор для назначения символу абсолютного адреса секции.
Пример кода Blackfin. Если в файле .ldf определены выходные секции:
Линкер вернет 1 из этого оператора, когда символ symbol появляется в глобальной таблице символов, и вернет 0, когда symbol не определен. Используйте этот оператор для назначения символам значений по умолчанию.
Например, если объектный файл ассемблера, линкуемый файлом .ldf, определяет глобальный символ test, то следующий оператор установит константу test_present в значение 1. Иначе (если символ test не определен), константа получит значение 0.
Этот оператор вернет размер (в словах) именованного сегмента памяти. Используйте этот оператор, когда требуется размер сегмента для перемещения текущего location counter в подходящую ячейку памяти.
В примере ниже (взят из файла .ldf по умолчанию) устанавливается сгенерированная линкером константа, основанная на значении location counter плюс оператор MEMORY_SIZEOF.
Этот оператор вернет размер (в байтах) именованной выходной секции. Используйте этот оператор, когда нужно узнать размер секции для перемещения текущей позиции location counter в нужное место памяти.
Пример кода SHARC. Фрагмент кода ниже определяет константу _sizeofdata1 в размер секции seg_dmda.
Линкер обрабатывает символ точки, окруженной пробелами, как символ текущей позиции размещения (current location counter). Location counter это указатель на место в памяти в конце предыдущей команды линкера. Из-за того, что точка относится к месту в выходной секции, этот оператор может появляться только в выходной секции, в команде SECTIONS{}.
Соблюдайте следующие правила:
• Используйте точку в любом месте, где в выражении разрешено использование символа. • Присваивайте значение оператору точки для перемещения текущей позиции location counter, чтобы оставлять пустоты (не заполненные промежутки) в памяти. • Не допускайте декремент для location counter.
[Макросы LDF]
Макросы LDF (или макросы линкера) являются встроенными макросами. Они заранее определены зависящими от системы процедурами или значениями. Другие макросы, которые называются user macros, определяются пользователем.
Макросы LDF идентифицируются начальным символом доллара ($). Каждый макрос LDF это имя для строки текста. Вы можете назначать макросы LDF текстовыми или процедурными значениями, или просто декларируете их, чтобы они были доступны.
Линкер:
• Заменяет имя макроса строковым значением. Обычно строковое значение длиннее, чем имя макроса, так что макрос позволяет сократить текст кода LDF и улучшить его читаемость. • Выполняет действия в зависимости от существования макроса (или в зависимости от его значения). • Назначает значение макросу, что возможно как результат процедуры, и использует это значение в дальнейшей обработке.
Макросы LDF направляют входную информацию из командной строки линкера в предварительно определенные макросы, и предоставляют поддержку для макро-замен, определенных пользователем. Макросы линкера глобальный в файле .ldf, независимо от места их определения. Для дополнительной информации см. разделы "Область действия команды" и "Макросы LDF и взаимодействие с командной строкой".
Макросы LDF не зависят от поддержки макросов препроцессором, которая также доступна в файле .ldf. Препроцессор размещает макросы препроцессора (или другие команды препроцессора) в файлах исходного кода. Макросы препроцессора (см. "Встроенные макросы препроцессора") повторяют последовательности инструкций в Вашем исходном коде, или определяют символические константы. Эти макросы упрощают замену текста, подключение файла, условное ассемблирование и условную компиляцию (или наоборот все усложняют при фанатичном использовании). Например, препроцессор ассемблера использует команду #define, чтобы определить макрос или символическую константу.
Для дополнительной информации обращайтесь к руководству "VisualDSP++ 5.0 Compiler and Library Manual" для подходящего целевого процессора, и к руководству "VisualDSP++ 5.0 Assembler and Preprocessor Manual".
Встроенные макросы LDF. Линкер предоставляет следующие встроенные макросы LDF.
$COMMAND_LINE_OBJECTS Этот макрос раскрывается в список объектных файлов (.doj) и файлов библиотек (.dlb), которые поступают на обработку линкером из командной строки. Используйте этот макрос в синтаксисе INPUT_SECTIONS() команды линкера SECTIONS{}. Этот макрос предоставляет полный список входных объектных файлов, который линкер ищет для входных секций.
$COMMAND_LINE_LINK_AGAINST Этот макрос раскрывается в список исполняемых файлов (исполняемый файл .dxe или файл общей памяти .sm), который поступает к линкеру из его командной строки. Этот макрос предоставляет полный список исполняемых файлов, который линкер использует для разрешения внешних символов.
$COMMAND_LINE_OUTPUT_FILE Этот макрос раскрывается в имя выходного исполняемого файле, который установлен в командной строке линкера с помощью ключа -o. Это имя файла соответствует < projectname.dxe >, установленному в диалоге настройки свойств проекта VisualDSP++ (окно Project Options). Используйте этот макрос в файле LDF только 1 раз, для замены в команде OUTPUT().
$COMMAND_LINE_OUTPUT_DIRECTORY Этот макрос раскрывается в путь до выходной директории, которая устанавливается ключом -od командной строки линкера (или ключом -o, когда ключ -od не указан). Например, следующий оператор позволяет изменить конфигурацию (Release против Debug) без модификации файла .ldf.
$ADI_DSP Этот макрос раскрывается в путь до каталога инсталляции VisualDSP++. Используйте этот макрос для управления поиском файлов линкера.
Макросы, определяемые пользователем. Линкер поддерживает макросы, декларируемые пользователем, для списков файлов. Следующий синтаксис определяет макрос $macroname как список файлов, каждый элемент в котором отделяется друг от друга запятой.
$macroname = file1, file2, file3, ... ;
После того, как макрос $macroname был определен, линкер подставляет список файлов из него в тех местах файла .ldf, где появляется имя $macroname. Завершите декларацию $macroname точкой с запятой. Линкер обработает файлы в указанном порядке.
Макросы LDF и взаимодействие с командной строкой. Линкер принимает команды через интерфейс командной строки, независимо от того, как было запущен линкер - автоматически из среды разработки VisualDSP++, или из окна командной строки. Многие операции линкера, такие как ввод и вывод, управляются через элементы командной строки. Используйте макросы LDF, чтобы использовать исходные данные из командной строки в коде файла .ldf.
Принимайте решение - использовать или нет в файле .ldf входные данные командной строки, или управлять линкером через код LDF - на основе следующих соображений:
• Файл .ldf, который использует информацию о входных файлах из командной строки, является обычным, который удобно использовать во многих проектах. Из-за того, что командная строка может задать только один выходной файл, файл .ldf, который полагается на входные файлы из командной строки, лучше всего подходит для однопроцессорных систем. • Файл .ldf, который не использует информацию о входных файлах из командной строки, приводит к более специфичному файлу .ldf, который может управлять более сложными функциями линкера.
Встроенные макросы препроцессора. Препроцессор линкера определяет некоторое количество макросов, которые предоставляют информацию о линкере. Эти макросы могут проверяться с помощью #ifdef и подобных директив, чтобы поддерживать Ваши нужды программирования.
Эта секция предоставляет информацию о следующих встроенных макросах препроцессора.
Это предопределенный макрос, который предоставляет информацию о версии VisualDSP++. Макрос __VISUALDSPVERSION__ позволяет разместить в файле .ldf проверку препроцессором. Это может использоваться для дифференциации между релизами и обновлениями VisualDSP++. Этот макрос применим для всех процессоров компании Analog Devices.
Синтаксис:
__VISUALDSPVERSION__=0xMMmmUUxx
Таблица 3-4 объясняет значение параметров этого макроса.
VersionMajor, старшая часть номера релиза. Например, это будет 4 для номера версии 4.5.
mm
VersionMinor, младшая часть номера релиза. Например, это будет 5 для номера версии 4.5.
UU
VersionPatch, номер обновления релиза (release update), например version 4.5, update 6.
xx
Зарезервировано для будущего использования (изначально всегда 00).
Информация 0xMMmmUUxx берется из файла < каталог инсталляции >\System\VisualDSP.ini file. Параметр xx изначально установлен в 00.
Если Вы встречаетесь с неожиданной проблемой в попытке найти файл VisualDSP.ini или при извлечении информации из файла VisualDSP.ini, то макрос __VISUALDSPVERSION__ может быть не закодирован в версию продукта VisualDSP++. Строка __VISUALDSPVERSION__ 0xffffffff отобразится как часть сообщения об ошибке, когда не получается закодировать информацию о версии.
Пример кода (Legacy):
#if !defined(__VISUALDSPVERSION__)
#warning Building with VisualDSP++ 4.5 Update 5 or prior.
#warning No __VISUALDSPVERSION__ available.
#endif
Пример кода (VisualDSP++ 4.5 Update 6, или более поздний релиз VisualDSP++):
#if __VISUALDSPVERSION__ >= 0x04050600
#warning Building with VisualDSP++ 4.5 Update 6 or later
#endif
Этот предварительно определенный макрос предоставляет информацию о версии линкера VisualDSP++ в шестнадцатеричной форме. Этот макрос позволяет разместить в файле .ldf проверку препроцессора. Это можно использовать для дифференциации между версиями линкера VisualDSP++. Этот макрос применим для всех процессоров компании Analog Devices.
Другими словами, этот макрос определяет __VERSIONNUM__ как числовой вариант __VERSION__, сконструированный из номера версии линкера. 8 бит используется для каждой составляющей номера версии, и самый значащий байт значения представляет наиболее значимый компонент версии.
Для примера, линкер версии 3.6.0.0 определит __VERSIONNUM__ как 0x03060000, и линкер версии 3.6.2.10 определит __VERSIONNUM__ как 0x0306020A.
Этот предварительно определенный макрос предоставляет информацию о версии линкера VisualDSP++ в строковой форме. Этот макрос позволяет разместить в файле .ldf проверку препроцессора. Это можно использовать для дифференциации между версиями линкера VisualDSP++. Этот макрос применим для всех процессоров компании Analog Devices.
Например, для линкера версии 3.9.1.1, значение макроса будет 3.9.1.1.
Значение этого предварительно определенного макроса задает ключ командной строки линкера -si-revision.
Например, если ключ ревизии кремния (silicon revision, опция -si-revision) установлен в "any", то макрос __SILICON_REVISION__ установится в значение 0xffff. Если ключ -si-revision установлен в значение "none", то линкер не устанавливает макрос __SILICON_REVISION__.
Этот предварительно определенный макрос определяется на основе ключа -meminit командной строки линкера.
[Команды LDF]
Команды в файле .ldf это специальные слова, определяющие целевую систему, и указывающие, в каком порядке линкер обрабатывает данные для получения выходного файла (файлов) этой системы. Команды LDF работают в определенной области действия (видимости, scope), влияя не действие других команд, которые появляются в этой области действия. Для дополнительной информации см. раздел "Область действия команды".
Линкер поддерживает символы групповой подстановки (wildcards) при указании имен секций в файле .ldf. Символы * и ? предоставляются в именах входных секций.
Линкер поддерживает следующие команды LDF (не все команды используются с определенными процессорами):
Команда ALIGN(number) выравнивает адрес текущего location counter на следующий адрес, который нацело делится на указанное число number, где number является результатом степени числа 2 (т. е. 1, 2, 4, 8). Значение number это граница слова (адрес), которая зависит от размера слова того сегмента памяти, в котором действует ALIGN().
Команда ARCHITECTURE() задает целевой процессор системы. Файл .ldf может содержать только одну команду ARCHITECTURE(). Команда ARCHITECTURE() должна появляться в глобальной области LDF, чтобы её действие распространялось на весь файл .ldf.
Синтаксис команды следующий:
ARCHITECTURE(processor)
Команда ARCHITECTURE() чувствительна к регистру параметра. Например, допустимым параметром будет значение ADSP-BF533, и значение adsp-BF533 будет недопустимым.
Если ARCHITECTURE() не задает целевой процессор, то Вы должны определить целевой процессор через командную строку линкера (linker -proc processor ...). Иначе линкер не сможет собрать программу.
Если команда MEMORY{}, специфическая для процессора, конфликтует с типом процессора, то линкер выведет сообщение об ошибке и остановит работу.
Следующая команда позволяет проверить, что Ваша инсталляция VisualDSP++ поддерживает указанный процессор:
linker -proc processor
Если указанная архитектура processor не установлена, то линкер напечатает соответствующее сообщение. Например:
c:\Program Files (x86)\Analog Devices\VisualDSP 5.0>linker -proc ADSP-BF5zz
[Error li1010] The target processor 'ADSP-BF5zz' is unknown or unsupported.
Команда COMMON_MEMORY{} используется для отображения объектов на память, которая является общей для использования между разными процессорами системы (shared memory). Отображение осуществляется в контексте процессоров, которые будут использовать общую память (кусок их адресного пространства); эти процессоры идентифицируются как "master" для общей памяти.
Для подробного описание команды см. "COMMON_MEMORY{}" в Главе 5 руководства [1].
Команда ELIMINATE() разрешает работу функции object elimination, которая удалит те символы из исполняемого файла, на которые нет ссылок (т. е. они нигде не используются). Путем добавления ключевого слова VERBOSE команда ELIMINATE(VERBOSE) будет сообщать об объектах, которые были удалены. У этой команды действие точно такое же, как у ключа -e командной строки линкера (см. [2], где приведено описание ключей командной строки линкера).
При использовании функции elimination (заданной в файле LDF командой ELIMINATE, либо с помощью Expert Linker, либо из командной строки) важно иметь в виду, что определенные объекты можно сохранить, избавив их от удаления с помощью команды KEEP(), что позволит правильно работать библиотекам C/C++ run-time. Самый безопасный путь для этого - сделать копию команды KEEP() в свой файл LDF из файла .ldf по умолчанию.
Чтобы правильно работали библиотеки C и C++ run-time, с помощью команды KEEP() сохраните от удаления следующие символы: ___ctor_NULL_marker и ___lib_end_of_heap_descriptions.
Чтобы функция elimination работала эффективно, структурируйте исходный код ассемблера таким образом, чтобы линкер мог однозначно идентифицировать каждый "source object" во входной секции ("source object", исходный объект это функция в коде или элемент данных). В частности вся входная секция должна быть полностью покрыта не перекрывающимися объектами с явно указанными границами. Граница элемента-функции указывается меткой в начале функции и соответствующей ей директивой окончания действия метки ".end". Если принцип размещения во входной секции не удовлетворяет этому правилу, то в этой секции не может сработать функция elimination. Для получения дополнительной информации по использованию меток и ключевых слов ".end" см. руководство "VisualDSP++ 5.0 Assembler and Preprocessor Manual".
Команда ELIMINATE_SECTIONS(sectionList) инструктирует линкер удалять не используемый код и данные только из перечисленных секций sectionList.
Список sectionList состоит из имен секций, отделенных друг от друга запятой. И эта команда LDF, и ключ -es командной строки (см. [2]), оба этих варианта могут использоваться для указания секций, где должна работать функция удаления не используемого кода и данных.
Команда ENTRY(symbol) указывает адрес входа в программу. Адрес обычно заполняется из глобального символа "start" (без символов нижнего подчеркивания), если он присутствует. Подробнее см. раздел "Адрес элемента (Entry Address)" в статье [2].
Оба этих варианта, и команда LDF, и ключ -entry командной строки, одинаково могут использоваться для адреса входа в программу.
Команда INCLUDE() указывает дополнительные файлы .ldf, которые линкер обработает перед обработкой остальной части файла .ldf. Можно указать любое количество дополнительных файлов .ldf. Для одной команды INCLUDE() предоставляется одно имя файла.
Только один из обрабатываемых файлов .ldf может задавать целевую архитектуру. Обычно файлы .ldf верхнего уровня подключают другие файлы .ldf.
Команда INPUT_SECTION_ALIGN(number) выравнивает каждую входную секцию (данных или инструкций кода) в выходной секции на адрес, удовлетворяющий указанному числу number. Аргумент number должен равняться числу - степени двойки, что задает границу слова (байтовый адрес границы слова в памяти). Допустимые значения для number зависят от размера слова сегмента памяти, который принимает выравниваемую выходную секцию.
Линкер по умолчанию заполняет нулями пустые пространства, созданные командами INPUT_SECTION_ALIGN(), или значение для заполнения может быть задано предшествующей командой FILL, допустимой для текущей области действия LDF. Подробнее см. описание FILL во врезке "SECTIONS{}".
Команда INPUT_SECTION_ALIGN() допустима только в области действия выходной секции. Для дополнительной информации см. раздел "Область действия команды". Для дополнительной информации по выходным секциям см. врезку с описанием команды "SECTIONS{}".
В следующем примере для Blackfin входные секции из a.doj, b.doj и c.doj выравниваются на четные адреса. Входные секции из d.doj и e.doj не выравниваются по счетверенному слову, потому что INPUT_SECTION_ALIGN(1) показывает, что последующие секции не являются субъектом выравнивания входной секции.
SECTIONS
{
program
{
INPUT_SECTION_ALIGN(2)
INPUT_SECTIONS ( a.doj(program) )
INPUT_SECTIONS ( b.doj(program) )
INPUT_SECTIONS ( c.doj(program) )
// конец директивы выравнивания для входных секций
INPUT_SECTION_ALIGN(1)
// следующие секции не будут выровнены:
INPUT_SECTIONS ( d.doj(data1) )
INPUT_SECTIONS ( e.doj(data1) )
} >MEM_PROGRAM
}
Когда разрешена функция elimination в секции, линкер использует команду KEEP(keepList), чтобы сохранить перечисленные в списке keepList объекты, даже если они не используются. Список keepList содержит объекты, отделенные друг от друга запятой.
Когда используется функция линкера data elimination, важно продолжить использование определенных объектов с помощью команды KEEP(), чтобы библиотеки C/C++ run-time работали правильно. Самый безопасный способ сделать это - копировать команду KEEP() из файла .ldf по умолчанию в свой собственный файл .ldf.
Для корректной работы библиотек C и C++ run-time сохраните следующие символы с помощью команды KEEP: ___ctor_NULL_marker и ___lib_end_of_heap_descriptions. Символ, указанный в списке сохранения keepList, должен быть глобальным символом.
Линкер использует команду KEEP_SECTIONS(), чтобы указать имя секций, где функция elimination не будет применена. Эта команда может появиться в любом месте, где появляется команда ELIMINATE_SECTION. Вы можете использовать либо команду KEEP_SECTIONS(), либо ключ -ek (см. описание этого ключа в [2]).
Команда LINK_AGAINST() проверяет специфические исполняемые файлы, чтобы разрешить переменные и метки, которые нельзя разрешить локально.
Чтобы линковать программы для мультипроцессорных систем (MP), команда LINK_AGAINST() должна присутствовать в файле .ldf.
Эта команда является опциональной (не обязательной) частью команд PROCESSOR{} и SHARE_MEMORY{}. Синтаксис команды LINK_AGAINST() (используемой как часть команды PROCESSOR{}) следующий:
• Pn это имя процессора; например P0 или P1. • executable_file_names это список имен одного или большего количества исполняемых (.dxe) файлов или файлов общей памяти (shared memory, .sm). Отдельные имена в списке отделяются друг от друга запятыми. Однако утилита Expert Linker позволяет использовать пробелы для отделения друг от друга нескольких имен.
Линкер при поиске символов просматривает исполняемые файлы в порядке, указанном командой LINK_AGAINST(). Когда найдено определение символа, линкер останавливает поиск. Изменить порядок просмотра для определенной переменной или метки можно с использованием команды RESOLVE() (см. врезку с описанием этой команды), которая указывает линкеру использовать указанный resolver, что игнорирует действие LINK_AGAINST() для специфичного символа. При этом команда LINK_AGAINST() все еще будет действовать для других символов.
Команда MAP(filename) выводит информацию отображения (MAP-файл в формате .xml) в файл с указанным именем filename. Разместите эту команду в любом месте файла .ldf.
Команда MAP(filename) соответствует ключу командной строки -Map < filename > (и может быть переназначена им), подробнее про этот ключ см. [2]. В VisualDSP++, если опции проекта (раздел Link диалога Project Options) указывают генерацию карты символов, то линкер запустится с опцией -Map < projectname >.xml, и установленная в файле .ldf команда MAP() приведет к генерации предупреждения.
Команда MEMORY{} задает карту памяти (memory map) для целевой системы. После декларирования имен сегментов и памяти этой командой, используйте эти имена сегментов памяти для размещения секций программы, с помощью команды SECTIONS{}.
Файл .ldf должен содержать команду MEMORY{} для глобальной памяти целевой системы, может содержать команду MEMORY{}, которая применяется для каждой области действия процессора. Нет ограничения на количество сегментов памяти, которое Вы можете декларировать командой MEMORY{}. Для дополнительной информации см. раздел "Область действия команды".
В каждом сценарии области действия следуйте команде MEMORY{} с командой SECTIONS{}. Используйте сегменты памяти для размещения секций программ. В команде MEMORY{} могут появляться только декларации сегмента. Нет ограничения на длины имен секций.
Если Вы не определили карту памяти целевого процессора командой MEMORY{}, то линкер не сможет выполнить сборку Вашей программы. Если комбинированные секции, направленные на сегмент памяти, требуют больше памяти, чем имеется в этом сегменте, то линкер выдаст сообщение об ошибке и остановит свою работу.
Синтаксис команды MEMORY{} показан на рис. 3-2, и далее идет описание каждой части декларации сегмента.
Декларации сегмента. Декларация сегмента определяет сегмент памяти целевого процессора. Хотя файл .ldf может содержать только одну команду MEMORY{}, которая прикладывается на все области действия, нет ограничения на количество сегментов памяти, декларированных в команде MEMORY{}.
Каждая декларация сегмента должна содержать составляющие segment_name, TYPE(), START(), LENGTH() или END(), и WIDTH(). Ниже описаны отдельные части декларации сегмента.
Имя сегмента segment_name идентифицирует регион памяти. Имя segment_name должно начинаться с буквы, нижнего подчеркивания или точки, и может содержать в себе любые буквы, подчеркивания, цифры и точки, и это указанное имя не должно конфликтовать (совпадать) с ключевыми словами LDF.
Команда TYPE() идентифицирует тип памяти внутри сегмента (тип памяти специфичен для определенной архитектуры).
Не все целевые процессоры поддерживают все типы памяти. Линкер хранит эту информацию в исполняемом файле для использования другими инструментами разработки.
Для процессоров Blackfin и TigerSHARC используйте TYPE(), чтобы указать функциональное или аппаратное место памяти (RAM или ROM). Декларатор RAM задает сегменты, в которые должны быть загружены данные при старте системы. В сегменты ROM данные не загружаются; из них код исполняется непосредственно, либо оттуда берутся данные для загрузки как из PROM, находящегося вне чипа.
Для процессоров SHARC (ADSP-21xxx) используйте TYPE(), чтобы указать 2 параметра: использование памяти (PM для памяти программ program memory или DM для памяти данных data memory), и функциональное или аппаратное свойство памяти (RAM или ROM, как описано выше).
На процессорах ADSP-21261, ADSP-21262, ADSP-21266, ADSP-21267 и ADSP-21362, ADSP-21363, ADSP-21364, ADSP-21365, ADSP-21366 невозможно напрямую получить доступ к внешней памяти, это можно осуществить только через DMA. Чтобы сделать достоверной размещение кода во внешней памяти, доступного через DMA, используйте квалификатор сегмента DMAONLY, чтобы пометить сегмент памяти в файле .ldf как внешнюю память. Пример:
Линкер будет идентифицировать эту секцию как секцию только для DMA. Во время линковки линкер проверяет, что эта секция должна размещаться во внешней памяти, которая идентифицирована квалификатором DMAONLY. Более важно, что линкер проверяет только секции, помеченные "только для DMA", размещенные во внешней памяти. Линкер выдаст ошибку, если обнаружится любое несоответствие между памятью секции и квалификатором секции:
[Error el2017] Invalid/missing memory qualifier for memory 'section name.
Команда LENGTH/END() идентифицирует длину сегмента памяти (в словах) или задает конечный адрес сегмента. Когда Вы устанавливаете длину, length_number равно количеству адресуемых слов в этом регионе. Когда Вы устанавливаете конечный адрес, значение address_number является абсолютным адресом.
Команда WIDTH() задает физическую разрядность (ширину в битах) интерфейса с внутренней или внешней памятью. Параметр width_number должен быть целым числом. Параметр следующий:
• Для процессоров Blackfin ширина должна быть 8 (бит) • Для процессоров TigerSHARC ширина должна быть 32 (бита) • Для процессоров SHARC ширина может быть 8, 16, 32, 48 или 64 (бита)
Команда MPMEMORY{} задает смещение физической памяти каждого процессора для мультипроцессорной (MP) целевой системы. После декларирование имен процессоров и смещений сегмента памяти командой MPMEMORY{}, линкер использует смещения во время MP-линковки.
Команда MPMEMORY{} не используется для процессоров Blackfin.
Подробное описание MPMEMORY{} см. в Главе 5 руководства [1].
Команда OVERLAY_GROUP{} является устаревшей. Она предоставляет поддержку для определения набора оверлеев, которые совместно используют блок памяти run-time.
Подробное описание этой команды см. в Главе 5 документа [1]. Там же см. разделы "Memory Management Using Overlays" и "Memory Overlay Support" для детального описания функционала оверлеев.
В VisualDSP++ 5.0 команда PACKING() используется только для процессоров ADSP-21xxx (SHARC), как это показано в описании команды PACKING() (см. "Упаковка в процессорах SHARC").
Процессоры обмениваются данными через свое окружение (встроенное в кристалл или находящееся вне кристалла) через несколько шин. Конфигурация, размещение и размеры памяти для этого определяются приложением. Задайте значения ширины памяти и порядок следования байт при перемещении данных в соответствии с Вашими требованиями.
Линкер размещает в памяти данные в соответствии с ограничениями, накладываемыми архитектурой Вашей системы. LDF-команда PACKING() задает порядок, в котором линкер использует размещение байт в памяти. Этот порядок размещения данных в памяти соответствует последовательности, которую использует процессор для перемещения данных.
Команда PACKING() позволяет линкеру структурировать свою генерацию исполняемого файла, чтобы она соответствовала организации Вашей инсталляции памяти системы. Эта команда может быть применена в файле .ldf (находится в области действия) на базе сегментов, с адекватной гранулярностью, чтобы поддерживать гетерогенные конфигурации памяти. Любой сегмент памяти, требующий более одной команды упаковки, может быть поделен на гомогенные сегменты.
Синтаксис команды PACKING() следующий:
PACKING (number_of_bytes byte_order_list)
Здесь:
number_of_bytes это целое число, которое задает количество байт для упаковки (переупорядочивания) перед повторением.
byte_order_list это правило следования байт – как линкер записывает байты в память. Каждый элемент списка состоит из символа "B", за которым идет номер байта (в группе) на носителе хранилища (памяти).
Список следует правилам:
• Параметры списка отделены друг от друга пробелами. • Общее количество не нулевых байт равно number_of_bytes. • Если добавлено null байт, то это помечается как B0.
Например, для процессоров SHARC первый байт B1 (не B0). Второй байт B2, и так далее.
Использование команды PACKING() не по умолчанию изменяет порядок следования байт в исполняемых файлах (.dxe, .sm или .ovl), чтобы они достигали цели в правильном количестве, выравнивании и последовательности. Чтобы выполнить эту задачу, команда задает размер заново организованной группы, порядок следования байт в группе, и должны ли быть вставлены, где вставлены байты "null" для сохранения выравнивания на цели. Термин "null" относится к использованию – цель игнорирует байт null; линкер устанавливает эти байты в нули.
Порядок размещения байт в памяти коррелирует с порядком, который процессор может использовать при распаковке данных, когда данные перемещаются из внешней памяти во внутреннюю. Порядок распаковки процессора может быть связан с методом перемещения.
Среда VisualDSP++ поставляется с файлом packing.h, который находится в папке .../include. Этот файл содержит макросы, которые определяют команды упаковки для использования в LDF. Макросы поддерживают различные типы упаковки для прямого доступа к памяти (используется в оверлеях) и для прямого исполнения из внешней памяти. Для использования этих макросов поместите их в команду SECTIONS{} файла .ldf, когда нужна команда PACKING().
Упаковка в процессорах SHARC. На процессорах SHARC команда PACKING() применяется для внешнего порта процессора (external port). Каждый буфер внешнего порта содержит логику упаковки данных, которая позволяет осуществить упаковку 8-, 16- или 32-битных слов внешней шины в 32- или 48-битные внутренние слова. Эта логика полностью реверсивна.
Ниже приведено описание, как команда PACKING() может применяться в файле .ldf для Вашего процессора ADSP-21xxx.
В некоторых режимах прямого доступа к памяти (direct memory access, DMA) процессоры SHARC распаковывают три 32-битных слова в два 48-битных слова инструкции, когда процессор принимает данные из 32-битной памяти. Например, порядок распаковки и порядок сохранения (см. таблицу 3-5) должен применяться для режима DMA.
Таблица 3-5. Порядок упаковки DMA.
Порядок перемещения из хранилища в 32-битную внешнюю память
Порядок распаковки, два 48-битных внутренних слова (после третьего перемещения)
B1 и B2 (слово 1, биты 47-32) B3 и B4 (слово 1, биты 31-16)
B11 и B12 (слово 2, биты 15-0) B5 и B6 (слово 1, биты 15-0)
B7 и B8 (слово 2, биты 47-32) B9 и B10 (слово 2, биты 31-16)
B1, B2, B3, B4, B5, B6 (слово 1, биты 47-0)
B7, B8, B9, B10, B11, B12 (слово 2, биты 47-0)
Порядок распаковки байт не соответствует порядку переноса (сохраненных) данных. Из-за того, что процессор использует 2 байта на короткое слово, вышеуказанные перемещения транслируются в формат, указанный в таблице 3-6.
Таблица 3-6. Порядок сохранения против порядка распаковки.
Порядок сохранения в 32-битную внешнюю память
Порядок распаковки, два 48-битных внутренних слова
B1, B2, B3, B4, B11, B12 B5, B6, B7, B8, B9, B10
B1, B2, B3, B4, B5, B6 B7, B8, B9, B10, B11, B12
Вы указываете линкеру, как совместить упаковку, специфичную для процессора (например, не последовательный порядок следования байт) с синтаксисом PACKING() в команде OVERLAY_INPUT{}. Порядок следования байт из вышеуказанного примера переводится в следующий синтаксис команды PACKING(), которая поддерживает порядок упаковки 48 бит в 32 бита через внешний порт процессора.
Вышеуказанный синтаксис PACKING() размещает инструкции в оверлей, сохраненный в 32-битной внешней памяти, однако они распаковываются и выполняются из внутренней 48-битной памяти.
В качестве примера обратитесь к fft_ovly.fft, где используется макрос для определения упаковки. Этот файл включен в пример overlay3, поставляемый вместе с VisualDSP++.
Форматы упаковки оверлеев в процессорах SHARC. Используйте команду PACKING() в следующих случаях:
• Данные и инструкции для оверлеев исполняются из внешней памяти (путем определения этих оверлеев во внешней памяти как "live"). • Ширина или порядок следования сохраненных данных отличаются от их организации во время выполнения (run-time).
При необходимости линкер выравнивает на слово инструкцию упаковки.
Таблица 3-7 показывает комбинации формата упаковки для оверлеев SHARC DMA, доступных для каждой из двух операций.
Таблица 3-8 показывает комбинации формата упаковки для оверлеев ADSP-21161N, доступных для хранения в 8-битной памяти; 8-битная упаковка доступна на процессорах ADSP-2106x и ADSP-21160 только при загрузке из EPROM.
Таблица 3-8. Дополнительные форматы упаковки для оверлеев DMA.
Упаковка команд выполнения во внешней памяти для процессоров SHARC. Есть только 2 процессора, которые требуют упаковку памяти для внешнего выполнения - чипы ADSP-21161N и ADSP-21065L. Процессор ADSP-21161N поддерживает 48-, 32-, 16- и 8-битную внешнюю память. Процессор ADSP-21065L поддерживает только 32-битную внешнюю память.
В версиях до VisualDSP++ 3.5 требовалось использование packing-команд в файле .ldf, чтобы код размещался правильно. В VisualDSP++ 3.5 и последующих релизах инструменты VisualDSP++ были расширены для выполнения автоматической упаковки.
Чтобы инструменты VisualDSP++ выполняли упаковку напрямую из внешней памяти процессоров ADSP-21065L и ADSP-21161N, инструменты "упаковывают" код во внешнюю память, обеспечивая выполнение следующих условий:
1. Гарантируется, что тип внешней памяти будет PM (Program Memory, память для кода программы). 2. Гарантируется, что ширина данных соответствует реальной/актуальной разрядности памяти: для процессоров ADSP-21065L это 32 бита; для процессоров ADSP-21161N это 48, 32, 16 и 8 бит. 3. Если файл .ldf имеет команду PACKING() для определенной секции, удалите эту команду.
Когда определяются сегменты памяти (требуется для внешней памяти), то рекомендуется, чтобы "тип" секции памяти был следующий:
• PM – код или 40-разрядные данные (данные требуют для доступа к ним регистр PX) • DM – все другие секции
Ширина должна соответствовать действительной/физической разрядности внешней памяти.
Команда PLIT{} (procedure linkage table, линкуемая таблица процедур) в файле .ldf вставляет инструкции ассемблера, которые обрабатывают вызовы функций в оверлеях. Команды PLIT{} предоставляют шаблон из генерируемого линкером кода ассемблера, когда символ разрешается в функцию, находящуюся в памяти оверлея. Подробное описание команды PLIT{} см. в Главе 5 документа [1]. В том же документе см. раздел "Memory Management Using Overlays", где дано детальное описание функциональности оверлея и PLIT.
Команда PROCESSOR{} декларирует процессор и связанную с ним информацию. Команда PROCESSOR{} содержит команды MEMORY{}, SECTIONS{}, RESOLVE{} и другие команды линкера, которые относятся к специфическому процессору.
Линкер генерирует по одному исполняемому файлу из каждой команды PROCESSOR{}. Если Вы не зададите тип линковки командой PROCESSOR{}, то линкер не сможет выполнить сборку программы.
Синтаксис команды PROCESSOR{} показан на рис. 3-3.
processor_name Назначает имя для процессора. Процессор именуется по тем же правилам, как и метки линкера. Для дополнительной информации см. раздел "Выражения LDF".
OUTPUT(file_name.dxe) Задает имя выходного исполняемого файла (.dxe). Команда OUTPUT() в определенной области действия должна появиться перед командой SECTIONS{} в той же области действия.
MEMORY{segment_commands} Определяет сегменты памяти, которые относятся только к этому процессору. Используйте область действия команды (command scoping), чтобы определить эти сегменты памяти вне команды PROCESSOR{}. Для дополнительной информации см. раздел "Область действия команды" и врезку с описанием команды MEMORY{}.
PLIT{plit_commands} Определяет линкуемую таблицу команд процедуры (procedure linkage table, PLIT), которая применяется только к этому процессору. Для дополнительной информации см. врезку с описанием команды PLIT{}.
SECTIONS{section_commands} Определяет секции для размещения в исполняемом файле (.dxe). Для дополнительной информации см. врезку с описанием команды SECTIONS{}.
RESOLVE{symbol, resolver} Игнорирует любую команду LINK_AGAINST(). Для дополнительной информации см. врезку с описанием команды RESOLVE().
Мультипроцессорные/мультиядерные приложения (MP/MC). Команда PROCESSOR{} может использоваться для линковки проектов на архитектурах MP/MC Blackfin, таких как процессор ADSP-BF561. Например, синтаксис команды для 2-процессорной системы будет следующий:
PROCESSOR p0 {
...
}
PROCESSOR p1 {
}
См. также врезки с описанием команд LINK_AGAINST(), MPMEMORY{}, COMMON_MEMORY{} и SHARED_MEMORY{} (последние три команды см. в Главе 5 [1]).
Команда RESERVE (start_symbol, length_symbol, min_size [,align]) выделяет адресное пространство и определяет символы start_symbol и length_symbol. Команда выделяет самый большой доступный блок памяти, который больше или равен min_size. Может быть указан не обязательный параметр align, при этом RESERVE выделяет выровненное адресное пространство.
На входе:
• Параметр min_size определяет минимальное требуемое для выделение пространство в памяти. • Параметр align является необязательным, и он определяет выравнивание выделяемого адресного пространства.
На выходе:
• Символу start_symbol назначается начальный адрес выделенного адресного пространства. • Символу length_symbol назначается размер выделенного адресного пространства.
Пользователь может ограничить команду путем определения символов начала и длины совместно или индивидуально. Пример:
Команда RESERVE() допустима только в области действия выходной секции. Для дополнительной информации по выходным секциям см. раздел "Область действия команды" и врезку с описанием команды SECTIONS{}. Также см. RESERVE_EXPAND() для дополнительной информации от том, как задействовать не используемую память после того, как отображены входные секции.
Ошибки линкера (Linker Error Resolutions). Ниже объясняются некоторые ошибочные ситуации, встречающиеся с командой RESERVE().
Ошибка линкера li1224: когда пользователь определил символ длины length_symbol, параметр минимального размера min_size избыточен, и не включается в команду. Когда пользователь определяет символ начала start_symbol, параметр выравнивания align избыточен, и не включается в команду.
Ошибки линкера li1221, li1222 и li1223: когда пользователь определяет символ начала start_symbol = address, параметр выравнивания align избыточен, и не включается в команду. Когда пользователь определяет параметр выравнивания align, то символ длины length_symbol или параметр min_size должны нацело делиться на значение align; параметр align должен быть числом, которое можно получить возведением в степень числа 2.
Когда start_symbol не ограничен (не определен), команда RESERVE выделяет адресное пространство, начиная с конечного адреса сегмента.
Пример: предположим, что имеется сегмент памяти [0 - 8]. Диапазон [0 - 2] используется входной секцией. Чтобы выделить пространство адресов минимального размера 4 с выравниванием 2, команда RESERVE должна иметь минимальную требуемую длину 4 и выравнивание 2.
Команда RESERVE_EXPAND(start_symbol, length_symbol, min_size) может идти за командой RESERVE, и она используется для определения тех же символов, что и определяет команда RESERVE. Обычно RESERVE_EXPAND указывается последней в выходной секции, чтобы занять любую не используемую память после отображенных входных секций. RESERVE_EXPAND пытается выделить память, смежную с диапазоном, выделенным командой RESERVE. Соответственно start_symbol и length_symbol переопределяются для включения расширенного адресного диапазона. Для дополнительной информации см. описание команды RESERVE() в предыдущей врезке.
Используйте команду RESOLVE(symbol_name, resolver), чтобы игнорировать команду LINK_AGAINST() для определенного символа. Эта команда отменяет порядок просмотра объектных файлов для указанной переменной или метки. Дополнительную информацию см. во врезке с описанием команды LINK_AGAINST().
Команда RESOLVE(symbol_name, resolver) использует resolver, чтобы указать адрес определенного символа (переменной или метки). Параметр resolver является абсолютным адресом или файлом (.dxe или .sm), который содержит определение символа. Пример:
RESOLVE(start, 0xFFA00000)
Если символ не находится в обозначенном файле, то произойдет ошибка линковки. Для команды RESOLVE(symbol_name, resolver):
• Когда символ не определен в текущей области процессора, < resolver > предоставляет имя файла, отменяя действие любой команды LINK_AGAINST(). • Когда символ определен в текущей области процессора, < resolver > предоставляет линкеру адрес размещения символа.
Разрешение имени (resolve) переменной C в команде RESOLVE() осуществляется с префиксным символом нижнего подчеркивания (например, _symbol_name).
Потенциальная проблема с определением символа. Предположим, что символ, используемый в команде RESOLVE(), определен в проекте линковки. Линкер будет использовать это определение из проекта линковки вместо того, чтобы использовать одно из (symbol_name, resolver) (что также известно как "resolve-against") проекта линковки, что было указано в команде RESOLVE(). Например,
Команда SEARCH_DIR() указывает одну или большее количество директорий, которые линкер просматривает при поиске входных файлов. К команде SEARCH_DIR можно указать несколько директорий, отделив их друг от друга символом точки с запятой (;). На операционной системе Windows, заключите в прямые двойные кавычки длинные имена директорий, которые имеют внутри пути пробелы.
Порядок просмотра каталогов поиска соответствует порядку, в котором директории перечислены. Эта команда добавляет директории поиска к директории, выбранной ключом командной строки -L (см. [2]). Поместите эту команду в начало файла .ldf, чтобы гарантировать, что линкер будет использовать эту команду для всех путей поиска.
SEARCH_DIR( $ADI_DSP/Blackfin/lib; ABC/XYZ )
// $ADI_DSP является предопределенным макросом линкера, который
// разворачивается в директорию инсталляции VisualDSP++. Поиск
// объектов в директории Blackfin/lib будет осуществляться
// относительноrelative директории установки и относительно
// директории ABC/XYZ.
Команда SECTIONS{} использует сегменты памяти (определенные командой MEMORY{}), чтобы указать размещение выходных секций в памяти. Рис. 3-4 показывает синтаксис команды SECTIONS{}.
Файл .ldf может содержать команду SECTIONS{} внутри каждой команды PROCESSOR{}. Команде SECTIONS{} должна предшествовать команда MEMORY{}, которая определяет сегменты памяти, в которых линкер размещает выходные секции. Хотя файл .ldf может содержать только одну команду SECTIONS{} в пределах каждой области процессора, в каждой команде SECTIONS{} может быть декларировано несколько выходных секций.
Синтаксис команды SECTIONS{} включает некоторые аргументы, это выражения expressions или декларации секций section_declarations. Используйте expressions для манипуляции символами или для позиционирования текущего положения location counter (см. раздел "Выражения LDF"). Используйте section_declaration, чтобы декларировать выходную секцию. Каждая section_declaration имеет имя секции section_name, не обязательный тип секции section_type или init_qualifier, команды секции section_commands и сегмент памяти memory_segment.
Декларация SECTION имеет следующие части:
section_name Начинается с буквы, символа нижнего подчеркивания или точки, и может включать в себе любые буквы, нижние подчеркивания, цифры и точки. Значение section_name не должно конфликтовать с любыми ключевыми словами LDF.
Специальное имя секции .PLIT показывает секцию таблицы линковки процедуры (procedure linkage table, PLIT), которую генерирует линкер для разрешения символов, находящихся в памяти оверлея. Поместите эту секцию в памяти, не относящейся к оверлею, чтобы обрабатывать ссылки на элементы в памяти оверлея.
type_qualifier Задает адресное пространство, в которое должна быть отображена секция, и логическую организацию данных. Имейте в виду, что эти квалификаторы применяются только для процессоров SHARC ADSP-2146x, ADSP-2147x, ADSP-2148x.
Квалификаторы следующие:
PM – память программ (Program Memory), содержит 6 байт на слово. DM – память данных (Data Memory), содержит 4 байта на слово. DATA64 – содержит 8 байт на слово. SW – содержит 2 байта на слово.
Тип памяти выходной секции заменяет тип памяти секции, которая туда отображается. Если тип памяти выходной секции отличается от типа сегмента, то в выводе создается дополнительная ELF-секция. Эта ELF-секция содержит выходную секцию и определяет её содержимое.
Использование квалификатора выходной секции также инструктирует линкер игнорировать входные секции, у которых тип памяти отличается от указанной квалификатором. Все игнорированные входные секции от определенной команды отображения перечисляются в лог-файле линкера.
init_qualifier Задает тип run-time инициализации (опционально). Квалификаторы следующие:
NO_INIT – содержит не инициализируемые данные. Для этой секции файл .dxe не содержит в себе данных (эквивалентно традиционному квалификатору SHT_NOBITS). ZERO_INIT – содержит только данные, инициализированные нулями. Если применен ключ командной строки -meminit (см. [2]), то "обнуление" секции осуществляется во время работы программы с помощью библиотеки C run-time. Если ключ -meminit не указан, то "обнуление" осуществляется в момент загрузки (load time), это делает отладчик JTAG или загрузчик bootROM. RUNTIME_INIT – если линкер запускается с ключом -meminit, то эта секция заполняется во время работы программы (run time). Если ключ -meminit не указан в командной строке линкера, то эта секция заполняется в момент загрузки (load time), эту функцию берет на себя отладчик или загрузчик кода.
section_commands Может состоять из любой комбинации команд и/или выражений, таких как (см. соответствующие врезки и разделы): команда INPUT_SECTIONS(), выражение expression, команды FILL(hex number), PLIT{plit_commands}, OVERLAY_INPUT{overlay_commands}, FORCE_CONTIGUITY/NOFORCE_CONTIGUITY.
memory_segment Декларирует, что эта выходная секция помещается в указанный сегмент памяти.
Порция INPUT_SECTIONS() команды секции идентифицирует части программы, которые помещаются в исполняемый файл. Когда размещается входная секция, Вы должны указать file_source (файл источника). Опционально Вы можете указать выражение фильтра. Когда file_source является библиотекой, укажите входные секции archive_member и input_labels.
Обратите внимание, что в этом синтаксисе важны пробелы.
В LDF-команде INPUT_SECTIONS():
• file_source может быть списком файлов или макросом LDF, который раскрывается в список файлов, такой как $COMMAND_LINE_OBJECTS. Разделителем в списке объектных файлов или библиотечных файлов служит запятая. • archive_member именует исходный объектный файл в библиотеке. Параметр archive_member и левые/правые скобки [ ] требуются, когда file_source для input_label является библиотекой. • input_labels берутся из run-time имен .SECTION в программах ассемблера (например, program). Разделителем в списке имен служит пробел. Могут использоваться символы групповой подстановки * и ? (wildcard characters), чтобы разместить несколько имен секций из объекта в библиотеке. Для дополнительной информации по символам wildcard см. раздел "Символы группового замещения" в статье [2].
Пример: для размещения секции program объектного файла foo.doj в библиотеке myLib.dlb:
INPUT_SECTIONS(myLib.dlb [ foo.doj (program) ])
Для использования символа wildcard, который размещает все секции с префиксом data объектного файла foo.doj в библиотеке myLib.dlb:
INPUT_SECTIONS(myLib.dlb [ foo.doj (data*) ])
Использование опциональных выражений фильтра (Filter Expression). Операция фильтра осуществляется через фигурные скобки, и может использоваться для определения sub-списков и sub-библиотек. Это может использоваться для линковки с атрибутами.
INPUT_SECTIONS( $FILES { expr } (program) )
Опциональное выражение фильтра expr является двоичным выражением, и может содержать:
• Операторы атрибута:
name Вернет true, если объект имеет один или большее количество атрибутов с именем name, независимо от значения; иначе вернет false.
name("string") Вернет true, если имя атрибута совпадает со строкой string. Сравнение осуществляется с учетом регистра (case-sensitive). Этот оператор может использоваться для атрибутов с несколькими значениями (multi-valued attributes). Обратите внимание, что строка должна быть заключена в кавычки.
name cmp-op "string" Вернет true, если атрибут с именем name имеет одно значение, которое совпадает со строкой, в соответствии с оператором сравнения cmp-op. Иначе будет возвращено false. Оператор cmp-op может быть == или != для эквивалентности или не эквивалентности, сравнение осуществляется с учетом регистра (case-sensitive). Обратите внимание, что строка должна быть заключена в кавычки. Этот оператор может использоваться только для атрибутов с одиночным значением (single-valued attributes). Если атрибут не имеет точно только одно значение, линкер генерирует ошибку.
name cmp-op number Вернет true, если атрибут с именем name имеет одно значение, которое в виде числа соответствует целому числу number (которое может быть отрицательным). Иначе будет возвращено false. Оператор сравнения cmp-op может быть ==, !=, < , < =, > или >=. Этот оператор может использоваться только для атрибутов с одиночным значением (single-valued attributes). Если атрибут не имеет точно только одно значение, линкер генерирует ошибку.
• Логические операторы: &&, || и !, которые имеют тот же смысл и приоритет, что и в языке C.
Команды INPUT_SECTIONS_PIN и INPUT_SECTIONS_PIN_EXCLUSIVE используются, чтобы позволить осуществить отображение входной секции в одну из некоторых выходных секций, что дает функция ликера "одна входная секция к нескольким выходным". Пример:
В этом примере, если входные секции, подключаемые в $OBJECTS(program), не помещаются в os_mem1, то линкер попробует отобразить их в os_mem2.
Входная секция, перечисленная в команде INPUT_SECTIONS_PIN(), не будет отображена любой командой INPUT_SECTIONS, которая появится позже в файле .ldf, и входная секция, перечисленная в команде (командах) INPUT_SECTIONS_PIN_EXCLUSIVE, не будет отображена любой другой командой INPUT_SECTIONS.
Каждый раз, когда входные секции упомянуты в команде INPUT_SECTIONS, линкер инструктируется "дать еще один шанс" для входной секции, пытаясь отобразить её в другую выходную секцию (если секция уже не отображена), что дает эффект отображения "одного-ко-многим".
Команды INPUT_SECTIONS_PIN() и INPUT_SECTIONS_PIN_EXCLUSIVE() ограничивают эффект отображения "одного-ко-многим" – как только входная секция упомянута в INPUT_SECTIONS_PIN(), линкер не будет отображать её в любой другой из следующих выходных секций; входная секция, упомянутая внутри команды INPUT_SECTIONS_PIN_EXCLUSIVE(), не может быть отображена в любую другую выходную секцию.
Эти команды помогают избежать разбиения существующих макросов LDF. Чтобы достичь того же эффекта без использования команд INPUT_SECTIONS_PIN и INPUT_SECTIONS_PIN_EXCLUSIVE, определение выходных секций должно быть следующим:
• Входные секции program из входных файлов, помеченные атрибутом высокого приоритета (high_priority), которые могут отображаться только на внутреннюю память mem_internal. • Входные секции program из входных файлов, помеченные атрибутом низкого приоритета (low_priority), могут быть размещены только во внешнюю память mem_external. • Все другие входные секции program могут быть отображены в во внутреннюю память mem_internal, или во внешнюю память mem_external.
В команде секции section_command выражение expression манипулирует символами или позициями текущего location counter. Для подробной информации см. "Выражения LDF" в статье [2].
В команде секции section_command команда FILL() заполняет пропуски (которые получаются из-за выравнивания или при увеличении текущего location counter) шестнадцатеричными числами.
Команда FILL() используется только в декларации секции. По умолчанию линкер заполняет пропуски нулями. Укажите только одну команду FILL() на выходную секцию. Примеры:
В команде секции section_command команда PLIT{plit_commands} декларирует линкуемую таблицу процедуры локальной области действия (procedure linkage table, PLIT). Она содержит собственные метки и выражения. Для дополнительной информации см. описание команды PLIT{} в Главе 5 документа [1].
В команде секции section_command команда OVERLAY_INPUT{overlay_commands} идентифицирует части программы для размещения в исполняемом файле оверлея (overlay executable, .ovl). Для дополнительной информации по оверлеям см. раздел "Memory Management Using Overlays" в Главе 5 документа [1]. Для примеров кода с оверлеями см. примеры, поставляемые с программным обеспечением разработки.
Элемент overlay_commands состоит из как минимум одной из следующих команд: INPUT_SECTIONS(), OVERLAY_ID(), NUMBER_OF_OVERLAYS(), OVERLAY_OUTPUT(), ALGORITHM() или SIZE().
Элемент overlay_memory_segment (опционально) определяет, размещается ли секция оверлея в сегменте памяти оверлея. Некоторые секции оверлея, такие как секции, загружаемые с хоста, не нуждаются в подключении к образу памяти оверлея исполняемого файла, но требуют других инструментов, которые читают исполняемый файл. Пропуск назначения сегмента памяти оверлея из секции сохраняет секцию в исполняемом файле, но помечает эту секцию для исключения из образа памяти оверлея исполняемого файла.
Порция overlay_commands команды OVERLAY_INPUT{} следует правилам:
DEFAULT_OVERLAY Когда используется команда DEFAULT_OVERLAY, то линкер изначально размещает оверлей в области run-time (т. е. без запуска менеджера оверлея).
OVERLAY_OUTPUT() Выводит файл оверлея (.OVL) для оверлея с указанным именем. Команда OVERLAY_OUTPUT() в команде OVERLAY_INPUT{} должна появиться до любой команды INPUT_SECTIONS() для этого оверлея.
INPUT_SECTIONS() Это имеет тот же синтаксис, что и в команде OVERLAY_INPUT{}, когда она появляется в команде output_section_command, кроме случая, когда секция .PLIT может быть размещена в памяти оверлея. Для дополнительной информации см. врезку INPUT_SECTIONS().
NUMBER_OF_OVERLAYS() Вернет количество оверлеев, которое генерирует текущая линковка при использовании размещения оверлея FIRST_FIT или BEST_FIT для команды ALGORITHM().
Замечание: в настоящий момент эта функция недоступна.
ALGORITHM() Указывает линкеру использовать специальный алгоритм линковки оверлея. В настоящее время доступен только лишь вариант алгоритма ALL_FIT. Для ALL_FIT линкер пытается поместить все OVERLAY_INPUT{} в один оверлей, который может быть наложен на выходные секции сегмента памяти run-time.
(FIRST_FIT – в настоящее время этот вариант недоступен). Для FIRST_FIT линкер делит входные секции, перечисленные в OVERLAY_INPUT{}, на набор оверлеев, каждый из которых может быть наложен на выходные секции сегмента памяти run-time, в соответствии с принципом следования First-In-First-Out (FIFO, первым вошел первым вышел).
(BEST_FIT – в настоящее время этот вариант недоступен). Для BEST_FIT линкер делит входные секции, перечисленные в OVERLAY_INPUT{}, на набор оверлеев, каждый из которых может быть наложен на выходные секции сегмента памяти run-time, но делит эти оверлеи для оптимизации использования памяти.
SIZE() Устанавливает верхний лимит на размер памяти, которую может занимать оверлей.
В команде section_command команда FORCE_CONTIGUITY задает принудительное последовательное размещение в выходной секции. Команда NOFORCE_CONTIGUITY подавляет предупреждение линкера по поводу не последовательного размещения в выходной секции.
Линкер может генерировать 2 типа выходных исполняемых файла - файлы .dxe и файлы .sm. Файлы .dxe запускаются адресном пространстве однопроцессорной системы. Исполняемые файлы общей памяти (shared memory executable, .sm) находятся в общей для процессоров памяти в системе с несколькими процессорами/ядрами (multiprocessor/multi-core, MP/MC). Команда SHARED_MEMORY{} используется для генерации файлов .sm.
Для дополнительной информации см. описание команды SHARED_MEMORY{} в Главе 5 документа [1].