1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
---
category: tool
tool: linker
contributors:
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
translators:
- ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"]
lang: ru-ru
---
# Основные понятия и определения
**Счетчик позиций** - у компоновщика есть специальная переменная
"." (точка) всегда содержит текущую позицию вывода.
# Функции
**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
}
```
|