--- category: tool tool: GNU linker (ld) contributors: - ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"] translators: - ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"] --- # Основные понятия и определения **Счетчик позиций** - у компоновщика есть специальная переменная "." (точка) всегда содержит текущую позицию вывода. # Функции **ADDR(section)** - возвращает абсолютный адрес указанной секции. Однако данная секция должна быть определенна до использования функции ADDR. **ALIGN(exp)** - возвращает значение счетчика позиций, выравненное на границу следующего за exp выражения. **SIZEOF(section)** - возвращает размер секции в байтах. **FILL(param)** - определяет образец заполнения для текущей секции. Все остальные неуказанные регионы внутри секции заполняются значением указанными в аргументе функции. **KEEP(param)** - используется чтобы помечать param как неустранимый. **ENTRY(func)** - определяет функцию, которая будет являться точкой входа в программу. ```bash # Определяем точку входа в программу ENTRY(Reset_Handler) # Определяем переменную которая содержит адрес вершины стека _estack = 0x20020000; # Определяем переменную которая содержит значение размера кучи _Min_Heap_Size = 0x200; # Определяем переменную которая содержит значение размера стека _Min_Stack_Size = 0x400; # Описание карты памяти доступной для данного процессора # MEMORY # { # ИМЯ_ОБЛАСТИ_ПАМЯТИ (права доступа) : ORIGIN = АДРЕС_НАЧАЛА, LENGTH = РАЗМЕР # } # В нашем примере контроллер содержит три области памяти: # RAM - начинается с адреса 0x20000000 и занимает 128 Кбайт; # CCMRAM - начинается с адреса 0x10000000и занимает 64 Кбайт; # FLASH - начинается с адреса 0x8000000 занимает 1024 Кбайт; # Причем RAM память доступна для чтения, записи и исполнения. # 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, которая позволяет собирать мусор из неиспользуемых # входных разделов. И если есть разделы, которые сборщик мусора не должен трогать, # то их необходимо указать в качестве аргумента функции KEEP() (аналог ключевого слова # volatile). # Запись (*(.isr_vector)) означает разделы .isr_vector во всех объектных файлах. Т.к. # обращение к разделу в общем виде выглядит так: (ИМЯ_ФАЙЛА(ИМЯ_РАЗДЕЛА)) KEEP(*(.isr_vector)) # Выравниваем текущую позицию на границу 4-х байт. . = ALIGN(4); # Выражение ">ОБЛАСТЬ_ПАМЯТИ" указывает в какую именно область памяти будет помещена # данная секция. В нашем случае секция .isr_vector будет размещена во FLASH памяти. } >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); # Указываем, что в данной секции будут хранится области .rodata всех # объектных файлов *(.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 указывает на то, что данный сектор хранится в одной области памяти # (в нашем случае FLASH), а исполняться будет из другой области памяти (в нашем случае RAM). # Есть два типа адресов: # * VMA (Virtual memory address) - это run-time адрес по которому компилятор ожидает # видеть данные. # * LMA (Load memory address) - это адрес по которому линкер хранит данные. #Startup должен код скопировать секцию .data из адресов LMA в адреса VMA. } >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 } ```