1# ToDo:
2# - Ensure LMA / VMA sections are correctly grouped similar to scatter file creation.
3cmake_minimum_required(VERSION 3.20.0)
4
5set(SORT_TYPE_NAME SORT_BY_NAME)
6
7function(system_to_string)
8  cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN})
9
10  get_property(name    GLOBAL PROPERTY ${STRING_OBJECT}_NAME)
11  get_property(regions GLOBAL PROPERTY ${STRING_OBJECT}_REGIONS)
12  get_property(format  GLOBAL PROPERTY ${STRING_OBJECT}_FORMAT)
13  get_property(entry   GLOBAL PROPERTY ${STRING_OBJECT}_ENTRY)
14  get_property(symbols GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOLS)
15
16  set(${STRING_STRING} "OUTPUT_FORMAT(\"${format}\")\n\n")
17
18  set(${STRING_STRING} "${${STRING_STRING}}MEMORY\n{\n")
19  foreach(region ${regions})
20    get_property(name    GLOBAL PROPERTY ${region}_NAME)
21    get_property(address GLOBAL PROPERTY ${region}_ADDRESS)
22    get_property(flags   GLOBAL PROPERTY ${region}_FLAGS)
23    get_property(size    GLOBAL PROPERTY ${region}_SIZE)
24
25    if(DEFINED flags)
26      set(flags "(${flags})")
27    endif()
28
29    if(DEFINED address)
30      set(start ": ORIGIN = (${address})")
31    endif()
32
33    if(DEFINED size)
34      set(size ", LENGTH = (${size})")
35    endif()
36    set(memory_region "  ${name} ${flags} ${start}${size}")
37
38    set(${STRING_STRING} "${${STRING_STRING}}${memory_region}\n")
39  endforeach()
40
41  set(${STRING_STRING} "${${STRING_STRING}}}\n\n")
42
43  set(${STRING_STRING} "${${STRING_STRING}}ENTRY(\"${entry}\")\n\n")
44
45  set(${STRING_STRING} "${${STRING_STRING}}SECTIONS\n{")
46  foreach(region ${regions})
47    to_string(OBJECT ${region} STRING ${STRING_STRING})
48  endforeach()
49
50  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS_FIXED)
51  foreach(section ${sections})
52    to_string(OBJECT ${section} STRING ${STRING_STRING})
53  endforeach()
54
55  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS)
56  foreach(section ${sections})
57    to_string(OBJECT ${section} STRING ${STRING_STRING})
58  endforeach()
59
60  foreach(symbol ${symbols})
61    to_string(OBJECT ${symbol} STRING ${STRING_STRING})
62  endforeach()
63
64  set(${STRING_STRING} "${${STRING_STRING}}\n}\n" PARENT_SCOPE)
65endfunction()
66
67function(symbol_to_string)
68  cmake_parse_arguments(STRING "" "SYMBOL;STRING" "" ${ARGN})
69
70  get_property(name     GLOBAL PROPERTY ${STRING_SYMBOL}_NAME)
71  get_property(expr     GLOBAL PROPERTY ${STRING_SYMBOL}_EXPR)
72  get_property(size     GLOBAL PROPERTY ${STRING_SYMBOL}_SIZE)
73  get_property(symbol   GLOBAL PROPERTY ${STRING_SYMBOL}_SYMBOL)
74  get_property(subalign GLOBAL PROPERTY ${STRING_SYMBOL}_SUBALIGN)
75
76  string(REPLACE "\\" "" expr "${expr}")
77  string(REGEX MATCHALL "@([^@]*)@" match_res ${expr})
78
79  foreach(match ${match_res})
80    string(REPLACE "@" "" match ${match})
81    string(REPLACE "@${match}@" "${match}" expr ${expr})
82  endforeach()
83
84  set(${STRING_STRING} "${${STRING_STRING}}\n${symbol} = ${expr};\n" PARENT_SCOPE)
85endfunction()
86
87function(group_to_string)
88  cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN})
89
90  get_property(type GLOBAL PROPERTY ${STRING_OBJECT}_OBJ_TYPE)
91
92  # Output a comment for the section start
93  set(${STRING_STRING} "${${STRING_STRING}}\n  /* ${type} : ${STRING_OBJECT} */")
94
95  if(${type} STREQUAL REGION)
96    get_property(empty GLOBAL PROPERTY ${STRING_OBJECT}_EMPTY)
97    if(empty)
98      return()
99    endif()
100
101    get_property(address GLOBAL PROPERTY ${STRING_OBJECT}_ADDRESS)
102    set(${STRING_STRING} "${${STRING_STRING}}\n  . = ${address};\n\n")
103  else()
104    get_property(name GLOBAL PROPERTY ${STRING_OBJECT}_NAME)
105    get_property(symbol GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOL)
106    string(TOLOWER ${name} name)
107
108    get_objects(LIST sections OBJECT ${STRING_OBJECT} TYPE SECTION)
109    list(GET sections 0 section)
110    get_property(first_section_name GLOBAL PROPERTY ${section}_NAME)
111
112    if(DEFINED first_section_name AND "${symbol}" STREQUAL "SECTION")
113      set_property(GLOBAL APPEND PROPERTY ${section}_START_SYMBOLS __${name}_start)
114    else()
115      set(${STRING_STRING} "${${STRING_STRING}}\n  __${name}_start = .;\n")
116    endif()
117
118    set(${STRING_STRING} "${${STRING_STRING}}\n  __${name}_size = __${name}_end - __${name}_start;\n")
119    set(${STRING_STRING} "${${STRING_STRING}}\n  __${name}_load_start = LOADADDR(${first_section_name});\n")
120  endif()
121
122  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS_FIXED)
123  foreach(section ${sections})
124    to_string(OBJECT ${section} STRING ${STRING_STRING})
125  endforeach()
126
127  get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_GROUPS)
128  foreach(group ${groups})
129    to_string(OBJECT ${group} STRING ${STRING_STRING})
130  endforeach()
131
132  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS)
133  foreach(section ${sections})
134    to_string(OBJECT ${section} STRING ${STRING_STRING})
135  endforeach()
136
137  get_parent(OBJECT ${STRING_OBJECT} PARENT parent TYPE SYSTEM)
138  get_property(regions GLOBAL PROPERTY ${parent}_REGIONS)
139  list(REMOVE_ITEM regions ${STRING_OBJECT})
140  foreach(region ${regions})
141    if(${type} STREQUAL REGION)
142      get_property(address GLOBAL PROPERTY ${region}_ADDRESS)
143      set(${STRING_STRING} "${${STRING_STRING}}\n  . = ${address};\n\n")
144    endif()
145
146    get_property(vma GLOBAL PROPERTY ${region}_NAME)
147    get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS_FIXED)
148    foreach(section ${sections})
149      to_string(OBJECT ${section} STRING ${STRING_STRING})
150    endforeach()
151
152    get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_GROUPS)
153    foreach(group ${groups})
154      to_string(OBJECT ${group} STRING ${STRING_STRING})
155    endforeach()
156
157    get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS)
158    foreach(section ${sections})
159      to_string(OBJECT ${section} STRING ${STRING_STRING})
160    endforeach()
161  endforeach()
162
163  if(NOT ${type} STREQUAL REGION)
164    set(${STRING_STRING} "${${STRING_STRING}}\n  __${name}_end = .;\n")
165  endif()
166
167  get_property(symbols GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOLS)
168  foreach(symbol ${symbols})
169    to_string(OBJECT ${symbol} STRING ${STRING_STRING})
170  endforeach()
171
172  set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE)
173endfunction()
174
175# Helper to add size-checks and padding to handle MIN_SIZE and MAX_SIZE
176macro(add_min_max_size TEMP min_size max_size symbol_start symbol_end)
177  if(DEFINED ${min_size})
178    set(${TEMP} "${${TEMP}}\n  . = MAX(${${symbol_start}} + ${${min_size}}, .); /*MIN_SIZE*/")
179  endif()
180  if(DEFINED ${max_size})
181    set(${TEMP} "${${TEMP}}\n  ASSERT( ${${symbol_end}} <= ( ${${symbol_start}} + ${${max_size}}), \"MAX_SIZE\");\n . = MIN( ., ${${symbol_start}} + ${${max_size}}); /*MAX_SIZE*/")
182  endif()
183endmacro()
184
185function(section_to_string)
186  cmake_parse_arguments(STRING "" "SECTION;STRING" "" ${ARGN})
187
188  get_property(name       GLOBAL PROPERTY ${STRING_SECTION}_NAME)
189  get_property(name_clean GLOBAL PROPERTY ${STRING_SECTION}_NAME_CLEAN)
190  get_property(address    GLOBAL PROPERTY ${STRING_SECTION}_ADDRESS)
191  get_property(type       GLOBAL PROPERTY ${STRING_SECTION}_TYPE)
192  get_property(align_in   GLOBAL PROPERTY ${STRING_SECTION}_ALIGN_WITH_INPUT)
193  get_property(align      GLOBAL PROPERTY ${STRING_SECTION}_ALIGN)
194  get_property(subalign   GLOBAL PROPERTY ${STRING_SECTION}_SUBALIGN)
195  get_property(vma        GLOBAL PROPERTY ${STRING_SECTION}_VMA)
196  get_property(lma        GLOBAL PROPERTY ${STRING_SECTION}_LMA)
197  get_property(noinput    GLOBAL PROPERTY ${STRING_SECTION}_NOINPUT)
198  get_property(noinit     GLOBAL PROPERTY ${STRING_SECTION}_NOINIT)
199  get_property(nosymbols  GLOBAL PROPERTY ${STRING_SECTION}_NOSYMBOLS)
200  get_property(parent     GLOBAL PROPERTY ${STRING_SECTION}_PARENT)
201  get_property(start_syms GLOBAL PROPERTY ${STRING_SECTION}_START_SYMBOLS)
202  get_property(sect_min_size GLOBAL PROPERTY ${STRING_SECTION}_MIN_SIZE)
203  get_property(sect_max_size GLOBAL PROPERTY ${STRING_SECTION}_MAX_SIZE)
204
205  string(REGEX REPLACE "^[\.]" "" name_clean "${name}")
206  string(REPLACE "." "_" name_clean "${name_clean}")
207
208  set(SECTION_TYPE_NOLOAD NOLOAD)
209  set(SECTION_TYPE_BSS    NOLOAD)
210  if(DEFINED type)
211    set(type " (${SECTION_TYPE_${type}})")
212  endif()
213
214  set(TEMP "${TEMP} :")
215  set(secalign "")
216
217  if(align_in)
218    set(secalign " ALIGN_WITH_INPUT")
219  endif()
220
221  if(DEFINED align)
222    set(secalign "${secalign} ALIGN(${align})")
223  endif()
224
225  if(DEFINED subalign)
226    set(secalign "${secalign} SUBALIGN(${subalign})")
227  endif()
228
229  set(TEMP "${name} ${address}${type} :${secalign}\n{")
230
231  # Symbol names for size_check
232  set(sect_symbol_start)
233  set(sect_symbol_end)
234
235  foreach(start_symbol ${start_syms})
236    set(TEMP "${TEMP}\n  ${start_symbol} = .;")
237    set(sect_symbol_start "${start_symbol}")
238  endforeach()
239
240  if(NOT nosymbols)
241    set(TEMP "${TEMP}\n  __${name_clean}_start = .;")
242    set(sect_symbol_start "__${name_clean}_start")
243  endif()
244
245  # Min and Max size may need symbols too, even if the user did not want them
246  if((DEFINED sect_min_size OR DEFINED sect_max_size)
247    AND NOT DEFINED sect_symbol_start)
248      set(sect_symbol_start "__${name_clean}_size_check_start")
249  endif()
250
251  if(NOT noinput)
252    set(TEMP "${TEMP}\n  *(${name})")
253    set(TEMP "${TEMP}\n  *(\"${name}.*\")")
254  endif()
255
256  get_property(indicies GLOBAL PROPERTY ${STRING_SECTION}_SETTINGS_INDICIES)
257  foreach(idx ${indicies})
258    get_property(align    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ALIGN)
259    get_property(any      GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ANY)
260    get_property(first    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FIRST)
261    get_property(keep     GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_KEEP)
262    get_property(sort     GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SORT)
263    get_property(flags    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FLAGS)
264    get_property(input    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_INPUT)
265    get_property(symbols  GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SYMBOLS)
266    get_property(offset   GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_OFFSET)
267    get_property(min_size   GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_MIN_SIZE)
268    get_property(max_size   GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_MAX_SIZE)
269
270    if(DEFINED align)
271      set(TEMP "${TEMP}\n  . = ALIGN(${align});")
272    endif()
273
274    if(DEFINED symbols)
275      list(LENGTH symbols symbols_count)
276      if(${symbols_count} GREATER 0)
277        list(GET symbols 0 symbol_start)
278      endif()
279      if(${symbols_count} GREATER 1)
280        list(GET symbols 1 symbol_end)
281      endif()
282    endif()
283    # Min and Max size may need symbols too, even if the user did not want them
284    if(DEFINED min_size OR DEFINED max_size)
285      foreach(se "start" "end")
286        if(NOT DEFINED symbol_${se})
287          set(symbol_${se} "__${name_clean}_${idx}_size_check_${se}")
288        endif()
289      endforeach()
290    endif()
291
292    if(DEFINED offset AND NOT ("${offset}" STREQUAL "${current_offset}"))
293      set(TEMP "${TEMP}\n  . = ${offset}; /* OFFSET */")
294      set(current_offset ${offset})
295    endif()
296
297    if(DEFINED symbol_start)
298      set(TEMP "${TEMP}\n  ${symbol_start} = .;")
299    endif()
300
301    foreach(setting ${input})
302      if(DEFINED offset AND NOT ("${offset}" STREQUAL "${current_offset}"))
303        set(TEMP "${TEMP}\n  . = ${offset};")
304        set(current_offset ${offset})
305      endif()
306
307      #setting may have file-pattern or not.
308      if(setting MATCHES "(.+)\\((.+)\\)")
309        set(file_pattern "${CMAKE_MATCH_1}")
310        set(section_pattern "${CMAKE_MATCH_2}")
311      else()
312        set(file_pattern "*")
313        set(section_pattern "${setting}")
314      endif()
315
316      if(sort)
317        set(section_pattern "${SORT_TYPE_${sort}}(${section_pattern})")
318      endif()
319      set(pattern "${file_pattern}(${section_pattern})")
320      if(keep)
321        set(pattern "KEEP(${pattern})")
322      endif()
323      set(TEMP "${TEMP}\n  ${pattern};")
324    endforeach()
325
326    if(DEFINED symbol_end)
327      set(TEMP "${TEMP}\n  ${symbol_end} = .;")
328    endif()
329
330    add_min_max_size(TEMP min_size max_size symbol_start symbol_end)
331
332    set(symbol_start)
333    set(symbol_end)
334  endforeach()
335
336  if(NOT nosymbols)
337    set(TEMP "${TEMP}\n  __${name_clean}_end = .;")
338    set(sect_symbol_end "__${name_clean}_end")
339  endif()
340
341  if(DEFINED extra_symbol_end)
342    set(TEMP "${TEMP}\n  ${extra_symbol_end} = .;")
343    set(sect_symbol_end "${extra_symbol_end}")
344  endif()
345
346  # Min and Max size may need symbols too, even if the user did not want them
347  if((DEFINED sect_min_size OR DEFINED sect_max_size)
348    AND NOT DEFINED sect_symbol_end)
349      set(sect_symbol_end "__${name_clean}_size_check_end")
350  endif()
351
352  add_min_max_size(TEMP sect_min_size sect_max_size sect_symbol_start sect_symbol_end)
353
354  set(TEMP "${TEMP}\n}")
355
356  get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
357  if(${parent_type} STREQUAL GROUP)
358    get_property(vma GLOBAL PROPERTY ${parent}_VMA)
359    get_property(lma GLOBAL PROPERTY ${parent}_LMA)
360  endif()
361
362  if(DEFINED vma)
363    set(TEMP "${TEMP} > ${vma}")
364  endif()
365
366  if(DEFINED vma AND DEFINED lma)
367    set(TEMP "${TEMP} AT")
368  endif()
369
370  if(DEFINED lma)
371    set(TEMP "${TEMP} > ${lma}")
372  endif()
373
374  if(NOT nosymbols)
375    set(TEMP "${TEMP}\n__${name_clean}_size = __${name_clean}_end - __${name_clean}_start;")
376    set(TEMP "${TEMP}\nPROVIDE(__${name_clean}_align = ALIGNOF(${name}));")
377    set(TEMP "${TEMP}\n__${name_clean}_load_start = LOADADDR(${name});")
378  endif()
379
380  set(${STRING_STRING} "${${STRING_STRING}}\n${TEMP}\n" PARENT_SCOPE)
381endfunction()
382
383# /DISCARD/ is an ld specific section, so let's append it here before processing.
384list(APPEND SECTIONS "{NAME\;/DISCARD/\;NOINPUT\;TRUE\;NOSYMBOLS\;TRUE}")
385
386function(process_region)
387  cmake_parse_arguments(REGION "" "OBJECT" "" ${ARGN})
388
389  process_region_common(${ARGN})
390
391  set(groups)
392  get_objects(LIST groups OBJECT ${REGION_OBJECT} TYPE GROUP)
393  foreach(group ${groups})
394    get_property(parent GLOBAL PROPERTY ${group}_PARENT)
395    get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
396
397    if(${parent_type} STREQUAL GROUP)
398      get_property(vma GLOBAL PROPERTY ${parent}_VMA)
399      get_property(lma GLOBAL PROPERTY ${parent}_LMA)
400
401      set_property(GLOBAL PROPERTY ${group}_VMA ${vma})
402      set_property(GLOBAL PROPERTY ${group}_LMA ${lma})
403    endif()
404  endforeach()
405endfunction()
406
407include(${CMAKE_CURRENT_LIST_DIR}/../linker_script_common.cmake)
408