1# Copyright (c) 2025 IAR Systems AB
2#
3# SPDX-License-Identifier: Apache-2.0
4
5cmake_minimum_required(VERSION 3.17)
6
7set(SORT_TYPE_NAME Lexical)
8
9set_property(GLOBAL PROPERTY ILINK_REGION_SYMBOL_ICF)
10
11# This function post process the region for easier use.
12#
13# Tasks:
14# - Symbol translation using a steering file is configured.
15function(process_region)
16  cmake_parse_arguments(REGION "" "OBJECT" "" ${ARGN})
17
18  process_region_common(${ARGN})
19
20  get_property(empty GLOBAL PROPERTY ${REGION_OBJECT}_EMPTY)
21  if(NOT empty)
22    # For scatter files we move any system symbols into first non-empty load section.
23    get_parent(OBJECT ${REGION_OBJECT} PARENT parent TYPE SYSTEM)
24    get_property(symbols GLOBAL PROPERTY ${parent}_SYMBOLS)
25    set_property(GLOBAL APPEND PROPERTY ${REGION_OBJECT}_SYMBOLS ${symbols})
26    set_property(GLOBAL PROPERTY ${parent}_SYMBOLS)
27  endif()
28
29  get_property(sections GLOBAL PROPERTY ${REGION_OBJECT}_SECTION_LIST_ORDERED)
30  foreach(section ${sections})
31
32    get_property(name       GLOBAL PROPERTY ${section}_NAME)
33    get_property(name_clean GLOBAL PROPERTY ${section}_NAME_CLEAN)
34    get_property(noinput    GLOBAL PROPERTY ${section}_NOINPUT)
35    get_property(type       GLOBAL PROPERTY ${section}_TYPE)
36    get_property(nosymbols  GLOBAL PROPERTY ${section}_NOSYMBOLS)
37
38    if(NOT nosymbols)
39      if(${name} STREQUAL .ramfunc)
40        create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name_clean}_load_start
41          EXPR "@ADDR(.ramfunc_init)@"
42          )
43      else()
44        create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name_clean}_load_start
45          EXPR "@LOADADDR(${name_clean})@"
46          )
47      endif()
48    endif()
49
50    get_property(indicies GLOBAL PROPERTY ${section}_SETTINGS_INDICIES)
51    list(LENGTH indicies length)
52    foreach(idx ${indicies})
53      set(steering_postfixes Base Limit)
54      get_property(symbols GLOBAL PROPERTY ${section}_SETTING_${idx}_SYMBOLS)
55      get_property(sort    GLOBAL PROPERTY ${section}_SETTING_${idx}_SORT)
56      get_property(offset  GLOBAL PROPERTY ${section}_SETTING_${idx}_OFFSET)
57      if(DEFINED offset AND NOT offset EQUAL 0 )
58        # Same behavior as in section_to_string
59      elseif(DEFINED offset AND offset STREQUAL 0 )
60        # Same behavior as in section_to_string
61      elseif(sort)
62        # Treated by labels in the icf or image symbols.
63      elseif(DEFINED symbols AND ${length} EQUAL 1 AND noinput)
64      endif()
65    endforeach()
66
67    # Symbols translation here.
68
69    get_property(symbol_val GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_end)
70
71    if("${symbol_val}" STREQUAL "${name_clean}")
72      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name_clean}_size
73        EXPR "@SIZE(${name_clean})@"
74        )
75    else()
76      # These seem to be thing that can't be transformed to $$Length
77      set_property(GLOBAL APPEND PROPERTY ILINK_REGION_SYMBOL_ICF
78        "define image symbol __${name_clean}_size = (__${symbol_val} - ADDR(${name_clean}))")
79    endif()
80    set(ZI)
81
82    if(${name_clean} STREQUAL last_ram_section)
83      # A trick to add the symbol for the nxp devices
84      # _flash_used = LOADADDR(.last_section) + SIZEOF(.last_section) - __rom_region_start;
85      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL _flash_used
86        EXPR "(@LOADADDR(last_section)@ + @SIZE(last_section)@ - @__rom_region_start@)"
87        )
88    endif()
89
90    if(${name_clean} STREQUAL rom_start)
91      # The below two symbols is meant to make aliases to the _vector_table symbol.
92      list(GET symbols 0 symbol_start)
93      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __Vectors
94        EXPR "@ADDR(${symbol_start})@"
95        )
96      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __vector_table
97        EXPR "@ADDR(${symbol_start})@"
98        )
99    endif()
100    # Treat BSS to be noinit
101    if(CONFIG_IAR_ZEPHYR_INIT AND type STREQUAL BSS)
102      set_property(GLOBAL PROPERTY ${section}_NOINIT TRUE)
103    endif()
104  endforeach() # all sections
105
106  #Add houseeeping symbols for sektion start, end, size, load start.
107  get_property(groups GLOBAL PROPERTY ${REGION_OBJECT}_GROUP_LIST_ORDERED)
108  foreach(group ${groups})
109    get_property(name GLOBAL PROPERTY ${group}_NAME)
110    string(TOLOWER ${name} name)
111
112    get_property(group_type  GLOBAL PROPERTY ${group}_OBJ_TYPE)
113    get_property(parent      GLOBAL PROPERTY ${group}_PARENT)
114    get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
115    # Need to find the init manually group or parent
116    if(${parent_type} STREQUAL GROUP)
117      get_property(vma GLOBAL PROPERTY ${parent}_VMA)
118      get_property(lma GLOBAL PROPERTY ${parent}_LMA)
119    else()
120      get_property(vma GLOBAL PROPERTY ${group}_VMA)
121      get_property(lma GLOBAL PROPERTY ${group}_LMA)
122    endif()
123
124    get_objects(LIST sections OBJECT ${group} TYPE SECTION)
125    list(GET sections 0 section)
126    get_property(first_section_name GLOBAL PROPERTY ${section}_NAME_CLEAN)
127    list(POP_BACK sections section)
128    get_property(last_section_name GLOBAL PROPERTY ${section}_NAME_CLEAN)
129
130    if(DEFINED vma AND DEFINED lma)
131      # Something to init
132      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_load_start
133        EXPR "@ADDR(${first_section_name}_init)@"
134        )
135    else()
136      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_load_start
137        EXPR "@LOADADDR(${first_section_name})@"
138        )
139    endif()
140
141    create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_start
142      EXPR "@ADDR(${first_section_name})@"
143      )
144    create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_end
145      EXPR "@END(${last_section_name})@"
146      )
147    create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_size
148      EXPR "(@(__${name}_end)@ - @(__${name}_start)@)"
149      )
150
151  endforeach()
152
153  # create_symbol() for region-symbols that dont have an expression ?
154  get_property(symbols GLOBAL PROPERTY ${REGION_OBJECT}_SYMBOLS)
155  foreach(symbol ${symbols})
156    get_property(name GLOBAL PROPERTY ${symbol}_NAME)
157    get_property(expr GLOBAL PROPERTY ${symbol}_EXPR)
158    if(NOT DEFINED expr)
159      create_symbol(OBJECT ${REGION_OBJECT} SYMBOL __${name}_size
160        EXPR "@(ADDR(${name})@"
161        )
162    endif()
163  endforeach()
164
165  # This is only a trick to get the memories
166  set(groups)
167  get_objects(LIST groups OBJECT ${REGION_OBJECT} TYPE GROUP)
168  foreach(group ${groups})
169    get_property(group_type  GLOBAL PROPERTY ${group}_OBJ_TYPE)
170    get_property(parent      GLOBAL PROPERTY ${group}_PARENT)
171    get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
172
173    if(${group_type} STREQUAL GROUP)
174      get_property(group_name GLOBAL PROPERTY ${group}_NAME)
175      get_property(group_lma  GLOBAL PROPERTY ${group}_LMA)
176      if(${group_name} STREQUAL ROM_REGION)
177        set_property(GLOBAL PROPERTY ILINK_ROM_REGION_NAME ${group_lma})
178      endif()
179    endif()
180
181    #Short circuit our vma and lma to the parent's vma and lma
182    if(${parent_type} STREQUAL GROUP)
183      get_property(vma GLOBAL PROPERTY ${parent}_VMA)
184      get_property(lma GLOBAL PROPERTY ${parent}_LMA)
185
186      set_property(GLOBAL PROPERTY ${group}_VMA ${vma})
187      set_property(GLOBAL PROPERTY ${group}_LMA ${lma})
188    endif()
189  endforeach()
190
191endfunction()
192
193#
194# String functions - start
195#
196
197function(system_to_string)
198  cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN})
199
200  get_property(name    GLOBAL PROPERTY ${STRING_OBJECT}_NAME)
201  get_property(regions GLOBAL PROPERTY ${STRING_OBJECT}_REGIONS)
202  get_property(format  GLOBAL PROPERTY ${STRING_OBJECT}_FORMAT)
203
204  # Ilink specials
205  # set(${STRING_STRING} "build for rom;\n")
206  set(${STRING_STRING} "build for ram;\n")
207  if("${format}" MATCHES "aarch64")
208    set(${STRING_STRING} "${${STRING_STRING}}define memory mem with size = 16E;\n")
209  else()
210    set(${STRING_STRING} "${${STRING_STRING}}define memory mem with size = 4G;\n")
211  endif()
212
213  foreach(region ${regions})
214    get_property(name    GLOBAL PROPERTY ${region}_NAME)
215    get_property(address GLOBAL PROPERTY ${region}_ADDRESS)
216    get_property(flags   GLOBAL PROPERTY ${region}_FLAGS)
217    get_property(size    GLOBAL PROPERTY ${region}_SIZE)
218
219    if(DEFINED flags)
220      if(${flags} STREQUAL rx)
221        set(flags " rom")
222      elseif(${flags} STREQUAL ro)
223        set(flags " rom")
224      elseif(${flags} STREQUAL wx)
225        set(flags " ram")
226      elseif(${flags} STREQUAL rw)
227        set(flags " ram")
228      endif()
229    endif()
230
231    if(${name} STREQUAL IDT_LIST)
232      # Need to use a untyped region for IDT_LIST
233      set(flags "")
234    endif()
235
236    if(DEFINED address)
237      set(start "${address}")
238    endif()
239
240    if(DEFINED size)
241      set(size "${size}")
242    endif()
243    # define rom region FLASH    = mem:[from 0x0 size 0x40000];
244    set(memory_region "define${flags} region ${name} = mem:[from ${start} size ${size}];")
245
246    set(${STRING_STRING} "${${STRING_STRING}}${memory_region}\n")
247    set(flags)
248  endforeach()
249
250  set(${STRING_STRING} "${${STRING_STRING}}\n\n")
251  set_property(GLOBAL PROPERTY ILINK_SYMBOL_ICF)
252
253  #Generate all regions
254  foreach(region ${regions})
255    get_property(empty GLOBAL PROPERTY ${region}_EMPTY)
256    if(NOT empty)
257      get_property(name    GLOBAL PROPERTY ${region}_NAME)
258      set(ILINK_CURRENT_NAME ${name})
259      to_string(OBJECT ${region} STRING ${STRING_STRING})
260      set(ILINK_CURRENT_NAME)
261    endif()
262  endforeach()
263  set(${STRING_STRING} "${${STRING_STRING}}\n/*SYSTEM_SECTIONS*/\n")
264
265  # Sections that sit directly under the system are fishy characters.
266  # Currently there are two classes of them:
267  # 1 - .rel.iplt & friends - these are not used by iar tools currently.
268  #     These do not have any parents, so get no placement. Ignore them for
269  #     now, since the get Error[Lc041]: "foo" defined but not referenced
270  # 2 - TYPE LINKER_SCRIPT_FOOTER - these have vma and lma settings, and so
271  #     are easy to handle
272  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS)
273  foreach(section ${sections})
274    get_property(vma GLOBAL PROPERTY ${section}_VMA)
275    get_property(lma GLOBAL PROPERTY ${section}_LMA)
276    if(DEFINED lma OR DEFINED vma)
277      to_string(OBJECT ${section} STRING ${STRING_STRING})
278      place_in_region(STRING place OBJECT ${section})
279      string(APPEND ${STRING_STRING} "${place}")
280    endif()
281  endforeach()
282
283  #Generate all image symbols we have collected
284  get_property(symbols_icf GLOBAL PROPERTY ILINK_SYMBOL_ICF)
285  foreach(image_symbol ${symbols_icf})
286    set(${STRING_STRING} "${${STRING_STRING}}define image symbol ${image_symbol};\n")
287  endforeach()
288
289  get_property(symbols_icf GLOBAL PROPERTY ILINK_REGION_SYMBOL_ICF)
290  set(${STRING_STRING} "${${STRING_STRING}}\n")
291  foreach(image_symbol ${symbols_icf})
292    set(${STRING_STRING} "${${STRING_STRING}}${image_symbol};\n")
293  endforeach()
294
295  if(IAR_LIBC)
296    set(${STRING_STRING} "${${STRING_STRING}}if (K_HEAP_MEM_POOL_SIZE>0)\n{\n")
297    set(${STRING_STRING} "${${STRING_STRING}}  define block HEAP with alignment=8 { symbol kheap__system_heap };\n")
298    set(${STRING_STRING} "${${STRING_STRING}}}\nelse\n{\n")
299    set(${STRING_STRING} "${${STRING_STRING}}  define block HEAP with alignment=8, expanding size { };\n")
300    set(${STRING_STRING} "${${STRING_STRING}}}\n")
301    set(${STRING_STRING} "${${STRING_STRING}}\"DLib heap\": place in RAM { block HEAP };\n")
302#    set(${STRING_STRING} "${${STRING_STRING}}define exported symbol HEAP$$Base=kheap__system_heap;\n")
303#    set(${STRING_STRING} "${${STRING_STRING}}define exported symbol HEAP$$Limit=END(kheap__system_heap);\n")
304  endif()
305
306  set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE)
307endfunction()
308
309#A helper to output "place in <Region>"
310function(place_in_region)
311  cmake_parse_arguments(PLACE "" "OBJECT;STRING" "" ${ARGN})
312  set(section ${PLACE_OBJECT})
313  get_property(name     GLOBAL PROPERTY ${section}_NAME)
314
315  get_property(name_clean GLOBAL PROPERTY ${section}_NAME_CLEAN)
316
317  get_property(parent   GLOBAL PROPERTY ${section}_PARENT)
318  get_property(noinit   GLOBAL PROPERTY ${section}_NOINIT)
319  # This is only a trick to get the memories
320  get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
321  if(${parent_type} STREQUAL GROUP)
322    get_property(vma GLOBAL PROPERTY ${parent}_VMA)
323    get_property(lma GLOBAL PROPERTY ${parent}_LMA)
324  endif()
325
326  if(DEFINED vma)
327    set(ILINK_CURRENT_NAME ${vma})
328  elseif(DEFINED lma)
329    set(ILINK_CURRENT_NAME ${lma})
330  else()
331    # message(FATAL_ERROR "Need either vma or lma")
332  endif()
333
334  set(result "\"${name}\": place in ${ILINK_CURRENT_NAME} { block ${name_clean} };\n")
335  if(CONFIG_IAR_ZEPHYR_INIT AND DEFINED vma AND DEFINED lma AND (NOT ${noinit}) AND NOT ("${vma}" STREQUAL "${lma}") )
336    string(APPEND result "\"${name}_init\": place in ${lma} { block ${name_clean}_init };\n")
337  endif()
338  set(${PLACE_STRING} "${result}" PARENT_SCOPE)
339endfunction()
340
341function(group_to_string)
342  cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN})
343
344  get_property(type GLOBAL PROPERTY ${STRING_OBJECT}_OBJ_TYPE)
345  if(${type} STREQUAL REGION)
346    get_property(empty GLOBAL PROPERTY ${STRING_OBJECT}_EMPTY)
347    if(empty)
348      return()
349    endif()
350  endif()
351
352  #_SECTIONS_FIXED need a place at address statement:
353  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS_FIXED)
354  foreach(section ${sections})
355    to_string(OBJECT ${section} STRING ${STRING_STRING})
356    get_property(name       GLOBAL PROPERTY ${section}_NAME)
357    get_property(name_clean GLOBAL PROPERTY ${section}_NAME_CLEAN)
358    get_property(section_address GLOBAL PROPERTY ${section}_ADDRESS)
359    set(${STRING_STRING} "${${STRING_STRING}}\"${name}\": place at address mem:${section_address} { block ${name_clean} };\n")
360  endforeach()
361
362  #Generate sub-groups
363  get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_GROUPS)
364  foreach(group ${groups})
365    to_string(OBJECT ${group} STRING ${STRING_STRING})
366  endforeach()
367
368  #Generate sections
369  get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS)
370  foreach(section ${sections})
371    to_string(OBJECT ${section} STRING ${STRING_STRING})
372
373    place_in_region(STRING place OBJECT ${section})
374    string(APPEND ${STRING_STRING} "${place}")
375  endforeach()
376
377  get_parent(OBJECT ${STRING_OBJECT} PARENT parent TYPE SYSTEM)
378  get_property(regions GLOBAL PROPERTY ${parent}_REGIONS)
379  list(REMOVE_ITEM regions ${STRING_OBJECT})
380
381  #Go over REGIONS
382  foreach(region ${regions})
383    get_property(vma GLOBAL PROPERTY ${region}_NAME)
384    get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS_FIXED)
385
386    #Generate our fixed-sections that has vma in this region
387    foreach(section ${sections})
388      to_string(OBJECT ${section} STRING ${STRING_STRING})
389    endforeach()
390
391    #generate our groups with vma in region
392    get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_GROUPS)
393    foreach(group ${groups})
394      to_string(OBJECT ${group} STRING ${STRING_STRING})
395    endforeach()
396
397    get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS)
398    foreach(section ${sections})
399      to_string(OBJECT ${section} STRING ${STRING_STRING})
400      get_property(name     GLOBAL PROPERTY ${section}_NAME)
401      string(REGEX REPLACE "^[\.]" "" name_clean "${name}")
402      string(REPLACE "." "_" name_clean "${name_clean}")
403      set(${STRING_STRING} "${${STRING_STRING}}\"${name}\": place in ${vma} { block ${name_clean} };\n")
404
405      # Insert 'do not initialize' here
406      get_property(current_sections GLOBAL PROPERTY ILINK_CURRENT_SECTIONS)
407      if(${name} STREQUAL .bss)
408        if(DEFINED current_sections)
409          set(${STRING_STRING} "${${STRING_STRING}}do not initialize\n")
410          set(${STRING_STRING} "${${STRING_STRING}}{\n")
411          foreach(section ${current_sections})
412            set(${STRING_STRING} "${${STRING_STRING}}  ${section},\n")
413          endforeach()
414          set(${STRING_STRING} "${${STRING_STRING}}};\n")
415          set(current_sections)
416          set_property(GLOBAL PROPERTY ILINK_CURRENT_SECTIONS)
417        endif()
418      endif()
419    endforeach()
420  endforeach()
421
422  get_property(symbols GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOLS)
423  set(${STRING_STRING} "${${STRING_STRING}}\n")
424  foreach(symbol ${symbols})
425    to_string(OBJECT ${symbol} STRING ${STRING_STRING})
426  endforeach()
427
428  set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE)
429endfunction()
430
431
432function(section_to_string)
433  cmake_parse_arguments(STRING "" "SECTION;STRING" "" ${ARGN})
434
435  get_property(name     GLOBAL PROPERTY ${STRING_SECTION}_NAME)
436  get_property(address  GLOBAL PROPERTY ${STRING_SECTION}_ADDRESS)
437  get_property(type     GLOBAL PROPERTY ${STRING_SECTION}_TYPE)
438  get_property(align    GLOBAL PROPERTY ${STRING_SECTION}_ALIGN)
439  get_property(subalign GLOBAL PROPERTY ${STRING_SECTION}_SUBALIGN)
440  get_property(endalign GLOBAL PROPERTY ${STRING_SECTION}_ENDALIGN)
441  get_property(vma      GLOBAL PROPERTY ${STRING_SECTION}_VMA)
442  get_property(lma      GLOBAL PROPERTY ${STRING_SECTION}_LMA)
443  get_property(min_size GLOBAL PROPERTY ${STRING_SECTION}_MIN_SIZE)
444  get_property(max_size GLOBAL PROPERTY ${STRING_SECTION}_MAX_SIZE)
445  get_property(noinput  GLOBAL PROPERTY ${STRING_SECTION}_NOINPUT)
446  get_property(noinit   GLOBAL PROPERTY ${STRING_SECTION}_NOINIT)
447
448  get_property(nosymbols  GLOBAL PROPERTY ${STRING_SECTION}_NOSYMBOLS)
449  get_property(start_syms GLOBAL PROPERTY ${STRING_SECTION}_START_SYMBOLS)
450  get_property(end_syms   GLOBAL PROPERTY ${STRING_SECTION}_END_SYMBOLS)
451
452  get_property(parent   GLOBAL PROPERTY ${STRING_SECTION}_PARENT)
453
454  get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE)
455  if(${parent_type} STREQUAL GROUP)
456    get_property(group_parent_vma GLOBAL PROPERTY ${parent}_VMA)
457    get_property(group_parent_lma GLOBAL PROPERTY ${parent}_LMA)
458    if(NOT DEFINED vma)
459      get_property(vma GLOBAL PROPERTY ${parent}_VMA)
460    endif()
461    if(NOT DEFINED lma)
462      get_property(lma GLOBAL PROPERTY ${parent}_LMA)
463    endif()
464  endif()
465
466  if(DEFINED group_parent_vma AND DEFINED group_parent_lma)
467    # Something to init
468    set(part "rw ")
469  else()
470    set(part)
471  endif()
472
473
474  set_property(GLOBAL PROPERTY ILINK_CURRENT_SECTIONS)
475
476  string(REGEX REPLACE "^[\.]" "" name_clean "${name}")
477  string(REPLACE "." "_" name_clean "${name_clean}")
478
479  # WA for 'Error[Lc036]: no block or place matches the pattern "ro data section .tdata_init"'
480  if("${name_clean}" STREQUAL "tdata")
481    set(TEMP "${TEMP}define block ${name_clean}_init { ro section .tdata_init };\n")
482    set(TEMP "${TEMP}\"${name_clean}_init\": place in ${ILINK_CURRENT_NAME} { block ${name_clean}_init };\n\n")
483  endif()
484
485  get_property(indicies GLOBAL PROPERTY ${STRING_SECTION}_SETTINGS_INDICIES)
486  # ZIP_LISTS partner
487  get_property(next_indicies GLOBAL PROPERTY ${STRING_SECTION}_SETTINGS_INDICIES)
488  list(POP_FRONT next_indicies first_index)
489
490  set(first_index_section)
491  set(first_index_section_name)
492  if(DEFINED first_index)
493    # Handle case where the first section has an offset
494    get_property(first_index_offset
495      GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${first_index}_OFFSET)
496    get_property(keep   GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${first_index}_KEEP)
497    if(DEFINED keep)
498      set(root "root ")
499    else()
500      set(root)
501    endif()
502    if(DEFINED first_index_offset AND NOT first_index_offset EQUAL 0 )
503      set(first_index_section_name "${name_clean}_${first_index}_offset")
504      set(first_index_section
505        "define ${root}section ${first_index_section_name} {};")
506    else()
507      set(first_index)
508    endif()
509  endif()
510
511  foreach(start_symbol ${start_syms})
512    set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "${start_symbol} = ADDR(${name_clean})")
513  endforeach()
514  foreach(end_symbol ${end_syms})
515    set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "${end_symbol} = END(${name_clean})")
516  endforeach()
517
518  if(NOT nosymbols)
519    if("${name_clean}" STREQUAL "tdata")
520      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "__${name_clean}_start = (__iar_tls$$INIT_DATA$$Base)")
521      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "__${name_clean}_end = (__iar_tls$$INIT_DATA$$Limit)")
522    else()
523      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "__${name_clean}_start = ADDR(${name_clean})")
524      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "__${name_clean}_end = END(${name_clean})")
525    endif()
526  endif()
527  # section patterns and blocks to keep { }
528  set(to_be_kept "")
529
530  if(DEFINED first_index_section)
531    set(TEMP "${TEMP}${first_index_section}\n")
532  endif()
533
534  set(TEMP "${TEMP}define block ${name_clean} with fixed order")
535
536  if(align)
537    set(TEMP "${TEMP}, alignment=${align}")
538  elseif(subalign)
539    set(TEMP "${TEMP}, alignment = ${subalign}")
540  elseif(part)
541    set(TEMP "${TEMP}, alignment = input")
542  else()
543    set(TEMP "${TEMP}, alignment=4")
544  endif()
545  if(endalign)
546    set(TEMP "${TEMP}, end alignment=${endalign}")
547  endif()
548  if(DEFINED min_size)
549    set(TEMP "${TEMP}, minimum size=${min_size}")
550  endif()
551  if(DEFINED max_size)
552    set(TEMP "${TEMP}, maximum size=${max_size}")
553  endif()
554
555  set(TEMP "${TEMP}\n{")
556
557  # foreach(start_symbol ${start_syms})
558  #   set(TEMP "${TEMP}\n  section ${start_symbol},")
559  #   set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${start_symbol}")
560  # endforeach()
561
562  # if(NOT nosymbols)
563  #   set(TEMP "${TEMP}\n  section __${name_clean}_start,")
564  #   set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section __${name_clean}_start")
565  # endif()
566
567  list(GET indicies -1 last_index)
568  list(LENGTH indicies length)
569
570  if(NOT noinput)
571
572    set(TEMP "${TEMP}\n  block ${name_clean}_winput")
573    if(align)
574      list(APPEND block_attr "alignment = ${align}")
575    elseif(subalign)
576      list(APPEND block_attr "alignment = ${subalign}")
577    elseif(part)
578      # list(APPEND block_attr "alignment = input")
579    else()
580      list(APPEND block_attr "alignment=4")
581    endif()
582    list(APPEND block_attr "fixed order")
583
584    list(JOIN block_attr ", " block_attr_str)
585    if(block_attr_str)
586      set(TEMP "${TEMP} with ${block_attr_str}")
587    endif()
588    set(block_attr)
589    set(block_attr_str)
590
591    set(TEMP "${TEMP} { ${part}section ${name}, ${part}section ${name}.* }")
592    if(${length} GREATER 0)
593      set(TEMP "${TEMP},")
594    endif()
595    set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${name}")
596    set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${name}.*")
597  endif()
598
599  foreach(idx idx_next IN ZIP_LISTS indicies next_indicies)
600    get_property(align    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ALIGN)
601    get_property(any      GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ANY)
602    get_property(first    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FIRST)
603    get_property(keep     GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_KEEP)
604    get_property(sort     GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SORT)
605    get_property(flags    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FLAGS)
606    get_property(input    GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_INPUT)
607    get_property(symbols  GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SYMBOLS)
608    get_property(i_min_size GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_MIN_SIZE)
609    get_property(i_max_size GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_MAX_SIZE)
610
611    # Get the next offset and use that as this ones size!
612    get_property(offset   GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx_next}_OFFSET)
613
614    if(keep)
615      list(APPEND to_be_kept "block ${name_clean}_${idx}")
616      foreach(setting ${input})
617        list(APPEND to_be_kept "section ${setting}")
618      endforeach()
619    endif()
620    # In ilink if a block with min_size=X  does not match any input sections,
621    # its _init block may be discarded despite being needed for spacing with
622    # other _init blocks. To get around tihs, lets tag min_size blocks as keep.
623    if(CONFIG_IAR_ZEPHYR_INIT
624       AND DEFINED group_parent_vma AND DEFINED group_parent_lma
625       AND DEFINED i_min_size
626       AND NOT ${noinit})
627      list(APPEND to_be_kept "block ${name_clean}_${idx}_init")
628    endif()
629    if(DEFINED symbols)
630      list(LENGTH symbols symbols_count)
631      if(${symbols_count} GREATER 0)
632        list(GET symbols 0 symbol_start)
633      endif()
634      if(${symbols_count} GREATER 1)
635        list(GET symbols 1 symbol_end)
636      endif()
637    endif()
638
639    if(DEFINED symbol_start)
640      # set(TEMP "${TEMP}\n  section ${symbol_start},")
641      # set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${symbol_start}")
642      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "${symbol_start} = ADDR(${name_clean}_${idx})")
643    endif()
644
645    if(DEFINED first_index AND first_index EQUAL ${idx})
646      # Create the offset
647      set(TEMP "${TEMP}\n  block ${first_index_section_name}")
648      list(APPEND block_attr "size = ${first_index_offset}")
649      if(sort)
650        if(${sort} STREQUAL NAME)
651          list(APPEND block_attr "alphabetical order")
652        endif()
653      endif()
654      if(align)
655        list(APPEND block_attr "alignment = ${align}")
656      elseif(subalign)
657        list(APPEND block_attr "alignment = ${subalign}")
658      elseif(part)
659        # list(APPEND block_attr "alignment = input")
660      else()
661        list(APPEND block_attr "alignment=4")
662      endif()
663      list(APPEND block_attr "fixed order")
664
665      list(JOIN block_attr ", " block_attr_str)
666      if(block_attr_str)
667        set(TEMP "${TEMP} with ${block_attr_str}")
668      endif()
669      set(block_attr)
670      set(block_attr_str)
671
672      set(TEMP "${TEMP} { section ${first_index_section_name} },\n")
673    endif()
674
675    # block init_100 with alphabetical order { section .z_init_EARLY_?_}
676    set(TEMP "${TEMP}\n  block ${name_clean}_${idx}")
677    if(DEFINED offset AND NOT offset EQUAL 0 )
678      list(APPEND block_attr "size = ${offset}")
679    elseif(DEFINED offset AND offset STREQUAL 0 )
680      # Do nothing
681    endif()
682    if(sort)
683      if(${sort} STREQUAL NAME)
684        list(APPEND block_attr "alphabetical order")
685      endif()
686    endif()
687    if(align)
688      list(APPEND block_attr "alignment = ${align}")
689    elseif(subalign)
690      list(APPEND block_attr "alignment = ${subalign}")
691    elseif(part)
692      # list(APPEND block_attr "alignment = input")
693    else()
694      list(APPEND block_attr "alignment=4")
695    endif()
696    if(DEFINED i_min_size AND NOT i_min_size EQUAL 0)
697      list(APPEND block_attr "minimum size = ${i_min_size}")
698    endif()
699    if(DEFINED i_max_size )
700      list(APPEND block_attr "maximum size = ${i_max_size}")
701    endif()
702
703    # LD
704    # There are two ways to include more than one section:
705    #
706    # *(.text .rdata)
707    # *(.text) *(.rdata)
708    #
709    # The difference between these is the order in which
710    # the `.text' and `.rdata' input sections will appear in the output section.
711    # In the first example, they will be intermingled,
712    # appearing in the same order as they are found in the linker input.
713    # In the second example, all `.text' input sections will appear first,
714    # followed by all `.rdata' input sections.
715    #
716    # ILINK solved by adding 'fixed order'
717    if(NOT sort AND NOT first)
718      list(APPEND block_attr "fixed order")
719    endif()
720
721    list(JOIN block_attr ", " block_attr_str)
722    if(block_attr_str)
723      set(TEMP "${TEMP} with ${block_attr_str}")
724    endif()
725    set(block_attr)
726    set(block_attr_str)
727
728    list(GET input -1 last_input)
729
730    set(TEMP "${TEMP} {")
731    if(NOT DEFINED input AND NOT any)
732      set(TEMP "${TEMP} }")
733    endif()
734
735    foreach(setting ${input})
736      if(first)
737        set(TEMP "${TEMP} first")
738        set(first "")
739      endif()
740
741      set(section_type "")
742
743      set(TEMP "${TEMP}${section_type} ${part}section ${setting}")
744      set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${setting}")
745      set(section_type "")
746
747      if("${setting}" STREQUAL "${last_input}")
748        set(TEMP "${TEMP} }")
749      else()
750        set(TEMP "${TEMP}, ")
751      endif()
752
753      # set(TEMP "${TEMP}\n    *.o(${setting})")
754    endforeach()
755
756    if(any)
757      if(NOT flags)
758        message(FATAL_ERROR ".ANY requires flags to be set.")
759      endif()
760      set(ANY_FLAG "")
761      foreach(flag ${flags})
762        # if("${flag}" STREQUAL +RO OR "${flag}" STREQUAL +XO)
763        #   set(ANY_FLAG "readonly")
764        # # elseif("${flag}" STREQUAL +RW)
765        # #   set(ANY_FLAG "readwrite")
766        # else
767        if("${flag}" STREQUAL +ZI)
768          set(ANY_FLAG "zeroinit")
769          set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "${ANY_FLAG}")
770        endif()
771      endforeach()
772      set(TEMP "${TEMP} ${ANY_FLAG} }")
773    endif()
774
775    if(DEFINED symbol_end)
776      # set(TEMP "${TEMP},\n  section ${symbol_end}")
777      # set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${symbol_end}")
778      set_property(GLOBAL APPEND PROPERTY ILINK_SYMBOL_ICF "${symbol_end} = END(${name_clean}_${idx})")
779    endif()
780    if(${length} GREATER 0)
781      if(NOT "${idx}" STREQUAL "${last_index}")
782        set(TEMP "${TEMP},")
783      elseif()
784      endif()
785    endif()
786
787    set(symbol_start)
788    set(symbol_end)
789  endforeach()
790  set(next_indicies)
791
792  set(last_index)
793  set(last_input)
794  set(TEMP "${TEMP}")
795
796  # if(NOT nosymbols)
797  #   set(TEMP "${TEMP},\n  section __${name_clean}_end")
798  #   set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section __${name_clean}_end")
799  # endif()
800
801  # foreach(end_symbol ${end_syms})
802  #   set(TEMP "${TEMP},\n  section ${end_symbol}")
803  #   set_property(GLOBAL APPEND PROPERTY ILINK_CURRENT_SECTIONS "section ${end_symbol}")
804  # endforeach()
805
806  set(TEMP "${TEMP}\n};")
807
808  get_property(current_sections GLOBAL PROPERTY ILINK_CURRENT_SECTIONS)
809  if(${noinit})
810    list(JOIN current_sections ", " SELECTORS)
811    set(TEMP "${TEMP}\ndo not initialize {\n${SELECTORS}\n};")
812  elseif(DEFINED group_parent_vma AND DEFINED group_parent_lma)
813    if(CONFIG_IAR_DATA_INIT AND DEFINED current_sections)
814      set(TEMP "${TEMP}\ninitialize by copy\n")
815      set(TEMP "${TEMP}{\n")
816      foreach(section ${current_sections})
817        set(TEMP "${TEMP}  ${section},\n")
818      endforeach()
819      set(TEMP "${TEMP}};")
820
821      set(TEMP "${TEMP}\n\"${name}_init\": place in ${group_parent_lma} {\n")
822      foreach(section ${current_sections})
823        set(TEMP "${TEMP}  ${section}_init,\n")
824      endforeach()
825      set(TEMP "${TEMP}};")
826    elseif(CONFIG_IAR_ZEPHYR_INIT)
827      # Generate the _init block and the initialize manually statement.
828      # Note that we need to have the X_init block defined even if we have
829      # no sections, since there will come a "place in XXX" statement later.
830
831      # "${TEMP}" is there too keep the ';' else it will be a list
832      string(REGEX REPLACE "(block[ \t\r\n]+)([^ \t\r\n]+)" "\\1\\2_init" INIT_TEMP "${TEMP}")
833      string(REGEX REPLACE "(rw)([ \t\r\n]+)(section[ \t\r\n]+)([^ \t\r\n,]+)" "\\1\\2\\3\\4_init" INIT_TEMP "${INIT_TEMP}")
834      string(REGEX REPLACE "(rw)([ \t\r\n]+)(section[ \t\r\n]+)" "ro\\2\\3" INIT_TEMP "${INIT_TEMP}")
835
836      # No alphabetical orders on initializers
837      # Only alphabetical attribute.
838      string(REGEX REPLACE "with alphabetical order {" " {" INIT_TEMP "${INIT_TEMP}")
839      # Respect other attributes.
840      string(REGEX REPLACE "(, alphabetical order|alphabetical order, )" "" INIT_TEMP "${INIT_TEMP}")
841      string(REGEX REPLACE "{ readwrite }" "{ }" INIT_TEMP "${INIT_TEMP}")
842      set(TEMP "${TEMP}\n${INIT_TEMP}\n")
843
844      # If any content is marked as keep, is has to be applied to the init block
845      # too, esp. for blocks that are not referenced (e.g. empty blocks with min_size)
846      if(to_be_kept)
847        list(APPEND to_be_kept "block ${name_clean}_init")
848      endif()
849
850      if(DEFINED current_sections)
851        set(TEMP "${TEMP}\ninitialize manually with copy friendly\n")
852        set(TEMP "${TEMP}{\n")
853        foreach(section ${current_sections})
854          set(TEMP "${TEMP}  ${section},\n")
855        endforeach()
856        set(TEMP "${TEMP}};")
857      endif()
858    endif()
859    set(current_sections)
860  endif()
861
862  # Finally, add the keeps.
863  if(to_be_kept)
864    list(JOIN to_be_kept ", " K)
865    set(TEMP "${TEMP}\nkeep { ${K} };\n")
866  endif()
867
868  set(${STRING_STRING} "${${STRING_STRING}}\n${TEMP}\n" PARENT_SCOPE)
869endfunction()
870
871function(symbol_to_string)
872  cmake_parse_arguments(STRING "" "SYMBOL;STRING" "" ${ARGN})
873
874  get_property(name     GLOBAL PROPERTY ${STRING_SYMBOL}_NAME)
875  get_property(expr     GLOBAL PROPERTY ${STRING_SYMBOL}_EXPR)
876  get_property(size     GLOBAL PROPERTY ${STRING_SYMBOL}_SIZE)
877  get_property(symbol   GLOBAL PROPERTY ${STRING_SYMBOL}_SYMBOL)
878  get_property(subalign GLOBAL PROPERTY ${STRING_SYMBOL}_SUBALIGN)
879
880  string(REPLACE "\\" "" expr "${expr}")
881  string(REGEX MATCHALL "@([^@]*)@" match_res ${expr})
882
883  foreach(match ${match_res})
884    string(REPLACE "@" "" match ${match})
885    get_property(symbol_val GLOBAL PROPERTY SYMBOL_TABLE_${match})
886    string(REPLACE "@${match}@" "${match}" expr ${expr})
887  endforeach()
888
889  list(LENGTH match_res match_res_count)
890
891  if(match_res_count)
892    if(${match_res_count} GREATER 1)
893      set(${STRING_STRING}
894        "${${STRING_STRING}}define image symbol ${symbol} = ${expr};\n"
895        )
896    else()
897      if((expr MATCHES "Base|Limit|Length") OR (expr MATCHES "ADDR\\(|END\\(|SIZE\\("))
898        # Anything like $$Base/$$Limit/$$Length should be an image symbol
899        if( "${symbol}" STREQUAL "__tdata_size")
900          # This will handle the alignment of the TBSS block
901          set(${STRING_STRING}
902            "${${STRING_STRING}}define image symbol ${symbol}=(__iar_tls$$INIT_DATA$$Limit-__iar_tls$$INIT_DATA$$Base);\n"
903            )
904        elseif( "${symbol}" STREQUAL "__tbss_size")
905          # This will handle the alignment of the TBSS block by
906          # pre-padding bytes
907          set(${STRING_STRING}
908            "${${STRING_STRING}}define image symbol ${symbol}=((tbss$$Limit-__iar_tls$$DATA$$Base)-(__iar_tls$$INIT_DATA$$Limit-__iar_tls$$INIT_DATA$$Base));\n"
909            )
910        else()
911          set(${STRING_STRING}
912            "${${STRING_STRING}}define image symbol ${symbol} = ${expr};\n"
913            )
914        endif()
915      else()
916        list(GET match_res 0 match)
917        string(REPLACE "@" "" match ${match})
918        get_property(symbol_val GLOBAL PROPERTY SYMBOL_TABLE_${match})
919        if(symbol_val)
920          set(${STRING_STRING}
921            "${${STRING_STRING}}define image symbol ${symbol} = ${expr};\n"
922            )
923        else()
924          # Treatmen of "zephyr_linker_symbol(SYMBOL z_arm_platform_init EXPR "@SystemInit@")"
925          set_property(GLOBAL APPEND PROPERTY SYMBOL_STEERING_FILE
926            "--redirect ${symbol}=${expr}\n"
927            )
928        endif()
929      endif()
930    endif()
931  else()
932    # Handle things like ADDR(.ramfunc)
933    if(${expr} MATCHES "^[A-Za-z]?ADDR\\(.+\\)")
934      # string(REGEX REPLACE "^[A-Za-z]?ADDR\\((.+)\\)" "(\\1$$Base)" expr ${expr})
935      set(${STRING_STRING}
936        "${${STRING_STRING}}define image symbol ${symbol} = ${expr};\n"
937        )
938    else()
939      set(${STRING_STRING}
940        "${${STRING_STRING}}define exported symbol ${symbol} = ${expr};\n"
941        )
942    endif()
943  endif()
944
945  set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE)
946endfunction()
947
948include(${CMAKE_CURRENT_LIST_DIR}/../linker_script_common.cmake)
949
950if(DEFINED STEERING_FILE)
951  get_property(steering_content GLOBAL PROPERTY SYMBOL_STEERING_FILE)
952  file(WRITE ${STEERING_FILE}  "/* AUTO-GENERATED - Do not modify\n")
953  file(APPEND ${STEERING_FILE} " * AUTO-GENERATED - All changes will be lost\n")
954  file(APPEND ${STEERING_FILE} " */\n")
955
956  file(APPEND ${STEERING_FILE} ${steering_content})
957endif()
958