learnxinyminutes-docs/ru/linker.md
2024-12-08 20:37:52 -07:00

9.9 KiB
Raw Blame History

category tool contributors translators
tool GNU linker (ld)
Alexander Kovalchuk
https://github.com/Zamuhrishka
Alexander Kovalchuk
https://github.com/Zamuhrishka

Основные понятия и определения

Счетчик позиций - у компоновщика есть специальная переменная "." (точка) всегда содержит текущую позицию вывода.

Функции

ADDR(section) - возвращает абсолютный адрес указанной секции. Однако данная секция должна быть определенна до использования функции ADDR.

ALIGN(exp) - возвращает значение счетчика позиций, выравненное на границу следующего за exp выражения.

SIZEOF(section) - возвращает размер секции в байтах.

FILL(param) - определяет образец заполнения для текущей секции. Все остальные неуказанные регионы внутри секции заполняются значением указанными в аргументе функции.

KEEP(param) - используется чтобы помечать param как неустранимый.

ENTRY(func) - определяет функцию, которая будет являться точкой входа в программу.

# Определяем точку входа в программу
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
}