2019-06-23 15:41:25 +00:00
|
|
|
|
---
|
|
|
|
|
category: tool
|
|
|
|
|
tool: linker
|
|
|
|
|
contributors:
|
|
|
|
|
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
|
|
|
|
|
translators:
|
2019-08-02 18:55:27 +00:00
|
|
|
|
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
|
2019-06-23 15:41:25 +00:00
|
|
|
|
lang: ru-ru
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Основные понятия и определения
|
|
|
|
|
**Счетчик позиций** - у компоновщика есть специальная переменная
|
|
|
|
|
"." (точка) всегда содержит текущую позицию вывода.
|
|
|
|
|
|
|
|
|
|
# Функции
|
|
|
|
|
**ADDR(section)** - возвращает абсолютный адрес указанной секции. Однако
|
|
|
|
|
данная секция должна быть определенна до использования функции ADDR.
|
|
|
|
|
|
|
|
|
|
**ALIGN(exp)** - возвращает значение счетчика позиций, выравненное на границу
|
|
|
|
|
следующего за exp выражения.
|
|
|
|
|
|
|
|
|
|
**SIZEOF(section)** - возвращает размер секции в байтах.
|
|
|
|
|
|
|
|
|
|
**FILL(param)** - определяет образец заполнения для текущей секции. Все
|
|
|
|
|
остальные неуказанные регионы внутри секции заполняются значением указанными
|
|
|
|
|
в аргументе функции.
|
|
|
|
|
|
|
|
|
|
**KEEP(param)** - используется чтобы помечать param как неустранимый.
|
|
|
|
|
|
|
|
|
|
**ENTRY(func)** - определяет функцию, которая будет являться точкой входа
|
|
|
|
|
в программу.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Определяем точку входа в программу
|
|
|
|
|
ENTRY(Reset_Handler)
|
|
|
|
|
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Определяем переменную которая содержит адрес вершины стека
|
2019-06-23 15:41:25 +00:00
|
|
|
|
_estack = 0x20020000;
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Определяем переменную которая содержит значение размера кучи
|
2019-06-23 15:41:25 +00:00
|
|
|
|
_Min_Heap_Size = 0x200;
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Определяем переменную которая содержит значение размера стека
|
2019-06-23 15:41:25 +00:00
|
|
|
|
_Min_Stack_Size = 0x400;
|
|
|
|
|
|
|
|
|
|
# Описание карты памяти доступной для данного процессора
|
|
|
|
|
# MEMORY
|
|
|
|
|
# {
|
|
|
|
|
# ИМЯ_ОБЛАСТИ_ПАМЯТИ (права доступа) : ORIGIN = АДРЕС_НАЧАЛА, LENGTH = РАЗМЕР
|
|
|
|
|
# }
|
|
|
|
|
# В нашем примере контроллер содержит три области памяти:
|
|
|
|
|
# RAM - начинается с адреса 0x20000000 и занимает 128 Кбайт;
|
|
|
|
|
# CCMRAM - начинается с адреса 0x10000000и занимает 64 Кбайт;
|
|
|
|
|
# FLASH - начинается с адреса 0x8000000 занимает 1024 Кбайт;
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Причем RAM память доступна для чтения, записи и исполнения.
|
2019-06-23 15:41:25 +00:00
|
|
|
|
# CCMRAM память доступна только на чтение и запись.
|
|
|
|
|
# FLASH память доступна на чтение и исполнение.
|
|
|
|
|
MEMORY
|
|
|
|
|
{
|
|
|
|
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
|
|
|
|
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
|
|
|
|
|
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Описываем выходные секции
|
|
|
|
|
SECTIONS
|
|
|
|
|
{
|
|
|
|
|
# Первая секция содержит таблицу векторов прерываний
|
|
|
|
|
.isr_vector :
|
|
|
|
|
{
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Существует опция --gc-sections, которая позволяет собирать мусор из неиспользуемых
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# входных разделов. И если есть разделы, которые сборщик мусора не должен трогать,
|
2019-06-23 15:41:25 +00:00
|
|
|
|
# то их необходимо указать в качестве аргумента функции KEEP() (аналог ключевого слова
|
|
|
|
|
# volatile).
|
|
|
|
|
# Запись (*(.isr_vector)) означает разделы .isr_vector во всех объектных файлах. Т.к.
|
|
|
|
|
# обращение к разделу в общем виде выглядит так: (ИМЯ_ФАЙЛА(ИМЯ_РАЗДЕЛА))
|
|
|
|
|
KEEP(*(.isr_vector))
|
|
|
|
|
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Выражение ">ОБЛАСТЬ_ПАМЯТИ" указывает в какую именно область памяти будет помещена
|
|
|
|
|
# данная секция. В нашем случае секция .isr_vector будет размещена во FLASH памяти.
|
2019-06-23 15:41:25 +00:00
|
|
|
|
} >FLASH
|
|
|
|
|
|
|
|
|
|
# ИТОГО: Секция .isr_vector, которая содержит таблицу векторов прерываний выравнивается
|
|
|
|
|
# по границе 4-х байт, помечается как недоступная для сборщика мусора и размещается в начале
|
|
|
|
|
# FLASH памяти микроконтроллера.
|
|
|
|
|
|
|
|
|
|
# Вторая секция содержит код программы.
|
|
|
|
|
.text :
|
|
|
|
|
{
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Указываем, что в данной секции будут хранится области .text всех
|
|
|
|
|
# объектных файлов
|
|
|
|
|
*(.text)
|
|
|
|
|
*(.text*)
|
|
|
|
|
|
|
|
|
|
# Защищаем от сборщика мусора секции .init и .fini
|
|
|
|
|
KEEP (*(.init))
|
|
|
|
|
KEEP (*(.fini))
|
|
|
|
|
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Определяется переменная _etext, которая хранит в себе адрес конца секции .text и которая
|
|
|
|
|
# может быть доступна в исходном тексте программы через объявление
|
|
|
|
|
# volaile unsigned int extern _etext;
|
|
|
|
|
_etext = .;
|
|
|
|
|
} >FLASH
|
|
|
|
|
|
|
|
|
|
# ИТОГО: Секция .text, которая содержит код программы выравнивается по границе 4-х байт,
|
|
|
|
|
# включает в себя: все секции с кодом программы во всех объектных файлах и защищенные
|
|
|
|
|
от сборщика муссора секции .init и .fini во всех объектных файлах, распологается во FLASH
|
|
|
|
|
памяти микроконтроллера сразу за таблицей векторов.
|
|
|
|
|
Секции text, .init и .fini. располагаются в памяти в той последовательности в которой они
|
|
|
|
|
объявлены в скрипте.
|
|
|
|
|
|
|
|
|
|
# Третья секция содержит константные данные.
|
|
|
|
|
.rodata :
|
|
|
|
|
{
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# Указываем, что в данной секции будут хранится области .rodata всех
|
2019-06-23 15:41:25 +00:00
|
|
|
|
# объектных файлов
|
|
|
|
|
*(.rodata)
|
|
|
|
|
*(.rodata*)
|
|
|
|
|
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
} >FLASH
|
|
|
|
|
|
|
|
|
|
# Сохраняем в переменной _sidata абсолютный адрес секции .data
|
|
|
|
|
_sidata = LOADADDR(.data);
|
|
|
|
|
|
|
|
|
|
# Четвертая секция содержит инициализированные переменные.
|
|
|
|
|
.data :
|
|
|
|
|
{
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Сохраняем в переменной _sdata адрес текущей позиции (начала секции)
|
|
|
|
|
_sdata = .;
|
|
|
|
|
|
|
|
|
|
# Указываем, что в данной секции будут хранится области .data всех
|
|
|
|
|
# объектных файлов
|
|
|
|
|
*(.data)
|
|
|
|
|
*(.data*)
|
|
|
|
|
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Сохраняем в переменной _sdata адрес текущей позиции (конец секции)
|
|
|
|
|
_edata = .;
|
|
|
|
|
|
|
|
|
|
# Функция AT указывает на то, что данный сектор хранится в одной области памяти
|
2022-06-15 09:17:20 +00:00
|
|
|
|
# (в нашем случае FLASH), а исполняться будет из другой области памяти (в нашем случае RAM).
|
|
|
|
|
# Есть два типа адресов:
|
|
|
|
|
# * VMA (Virtual memory address) - это run-time адрес по которому компилятор ожидает
|
2019-06-23 15:41:25 +00:00
|
|
|
|
# видеть данные.
|
|
|
|
|
# * LMA (Load memory address) - это адрес по которому линкер хранит данные.
|
|
|
|
|
|
2022-06-15 09:17:20 +00:00
|
|
|
|
#Startup должен код скопировать секцию .data из адресов LMA в адреса VMA.
|
2019-06-23 15:41:25 +00:00
|
|
|
|
|
|
|
|
|
} >RAM AT> FLASH
|
|
|
|
|
|
|
|
|
|
# Пятая секция содержит инициализированные нулем переменные.
|
|
|
|
|
.bss :
|
|
|
|
|
{
|
|
|
|
|
# Сохраняем в переменной _sbss и __bss_start__ адрес текущей позиции (начала секции)
|
|
|
|
|
_sbss = .;
|
|
|
|
|
__bss_start__ = _sbss;
|
|
|
|
|
|
|
|
|
|
# Указываем, что в данной секции будут хранится области .bss всех
|
|
|
|
|
# объектных файлов
|
|
|
|
|
*(.bss)
|
|
|
|
|
*(.bss*)
|
|
|
|
|
|
|
|
|
|
# Выравниваем текущую позицию на границу 4-х байт.
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
|
|
|
|
|
# Сохраняем в переменной _ebss и __bss_end__ адрес текущей позиции (начала секции)
|
|
|
|
|
_ebss = .;
|
|
|
|
|
__bss_end__ = _ebss;
|
|
|
|
|
} >RAM
|
|
|
|
|
|
|
|
|
|
# Шестая секция содержит кучу и стек. Размещается в самом конце RAM.
|
|
|
|
|
._user_heap_stack :
|
|
|
|
|
{
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
PROVIDE ( end = . );
|
|
|
|
|
PROVIDE ( _end = . );
|
|
|
|
|
. = . + _Min_Heap_Size;
|
|
|
|
|
. = . + _Min_Stack_Size;
|
|
|
|
|
. = ALIGN(4);
|
|
|
|
|
} >RAM
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|