diff --git a/cmake/linker/ld/ld_script.cmake b/cmake/linker/ld/ld_script.cmake new file mode 100644 index 00000000000..c2586e43ab2 --- /dev/null +++ b/cmake/linker/ld/ld_script.cmake @@ -0,0 +1,328 @@ +# ToDo: +# - Ensure LMA / VMA sections are correctly grouped similar to scatter file creation. +cmake_minimum_required(VERSION 3.18) + +set(SORT_TYPE_NAME SORT_BY_NAME) + +function(system_to_string) + cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN}) + + get_property(name GLOBAL PROPERTY ${STRING_OBJECT}_NAME) + get_property(regions GLOBAL PROPERTY ${STRING_OBJECT}_REGIONS) + get_property(format GLOBAL PROPERTY ${STRING_OBJECT}_FORMAT) + get_property(entry GLOBAL PROPERTY ${STRING_OBJECT}_ENTRY) + get_property(symbols GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOLS) + + set(${STRING_STRING} "OUTPUT_FORMAT(\"${format}\")\n\n") + + set(${STRING_STRING} "${${STRING_STRING}}MEMORY\n{\n") + foreach(region ${regions}) + get_property(name GLOBAL PROPERTY ${region}_NAME) + get_property(address GLOBAL PROPERTY ${region}_ADDRESS) + get_property(flags GLOBAL PROPERTY ${region}_FLAGS) + get_property(size GLOBAL PROPERTY ${region}_SIZE) + + if(DEFINED flags) + set(flags "(${flags})") + endif() + + if(DEFINED address) + set(start ": ORIGIN = (${address})") + endif() + + if(DEFINED size) + set(size ", LENGTH = (${size})") + endif() + set(memory_region " ${name} ${flags} ${start}${size}") + + set(${STRING_STRING} "${${STRING_STRING}}${memory_region}\n") + endforeach() + + set(${STRING_STRING} "${${STRING_STRING}}}\n\n") + + set(${STRING_STRING} "${${STRING_STRING}}ENTRY(\"${entry}\")\n\n") + + set(${STRING_STRING} "${${STRING_STRING}}SECTIONS\n{") + foreach(region ${regions}) + to_string(OBJECT ${region} STRING ${STRING_STRING}) + endforeach() + + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS_FIXED) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + + foreach(symbol ${symbols}) + to_string(OBJECT ${symbol} STRING ${STRING_STRING}) + endforeach() + + set(${STRING_STRING} "${${STRING_STRING}}\n}\n" PARENT_SCOPE) +endfunction() + +function(symbol_to_string) + cmake_parse_arguments(STRING "" "SYMBOL;STRING" "" ${ARGN}) + + get_property(name GLOBAL PROPERTY ${STRING_SYMBOL}_NAME) + get_property(expr GLOBAL PROPERTY ${STRING_SYMBOL}_EXPR) + get_property(size GLOBAL PROPERTY ${STRING_SYMBOL}_SIZE) + get_property(symbol GLOBAL PROPERTY ${STRING_SYMBOL}_SYMBOL) + get_property(subalign GLOBAL PROPERTY ${STRING_SYMBOL}_SUBALIGN) + + string(REPLACE "\\" "" expr "${expr}") + string(REGEX MATCHALL "%([^%]*)%" match_res ${expr}) + + foreach(match ${match_res}) + string(REPLACE "%" "" match ${match}) + string(REPLACE "%${match}%" "${match}" expr ${expr}) + endforeach() + + set(${STRING_STRING} "${${STRING_STRING}}\n${symbol} = ${expr};\n" PARENT_SCOPE) +endfunction() + +function(group_to_string) + cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN}) + + get_property(type GLOBAL PROPERTY ${STRING_OBJECT}_OBJ_TYPE) + if(${type} STREQUAL REGION) + get_property(empty GLOBAL PROPERTY ${STRING_OBJECT}_EMPTY) + if(empty) + return() + endif() + + get_property(address GLOBAL PROPERTY ${STRING_OBJECT}_ADDRESS) + set(${STRING_STRING} "${${STRING_STRING}}\n . = ${address};\n\n") + else() + get_property(name GLOBAL PROPERTY ${STRING_OBJECT}_NAME) + string(TOLOWER ${name} name) + set(${STRING_STRING} "${${STRING_STRING}}\n __${name}_start = .;\n") + + get_objects(LIST sections OBJECT ${STRING_OBJECT} TYPE SECTION) + list(GET sections 0 section) + get_property(first_section_name GLOBAL PROPERTY ${section}_NAME) + + set(${STRING_STRING} "${${STRING_STRING}}\n __${name}_size = __${name}_end - __${name}_start;\n") + set(${STRING_STRING} "${${STRING_STRING}}\n __${name}_load_start = LOADADDR(${first_section_name});\n") + endif() + + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS_FIXED) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + + get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_GROUPS) + foreach(group ${groups}) + to_string(OBJECT ${group} STRING ${STRING_STRING}) + endforeach() + + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_SECTIONS) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + + get_parent(OBJECT ${STRING_OBJECT} PARENT parent TYPE SYSTEM) + get_property(regions GLOBAL PROPERTY ${parent}_REGIONS) + list(REMOVE_ITEM regions ${STRING_OBJECT}) + foreach(region ${regions}) + if(${type} STREQUAL REGION) + get_property(address GLOBAL PROPERTY ${region}_ADDRESS) + set(${STRING_STRING} "${${STRING_STRING}}\n . = ${address};\n\n") + endif() + + get_property(vma GLOBAL PROPERTY ${region}_NAME) + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS_FIXED) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + + get_property(groups GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_GROUPS) + foreach(group ${groups}) + to_string(OBJECT ${group} STRING ${STRING_STRING}) + endforeach() + + get_property(sections GLOBAL PROPERTY ${STRING_OBJECT}_${vma}_SECTIONS) + foreach(section ${sections}) + to_string(OBJECT ${section} STRING ${STRING_STRING}) + endforeach() + endforeach() + + if(NOT ${type} STREQUAL REGION) + set(${STRING_STRING} "${${STRING_STRING}}\n __${name}_end = .;\n") + endif() + + get_property(symbols GLOBAL PROPERTY ${STRING_OBJECT}_SYMBOLS) + foreach(symbol ${symbols}) + to_string(OBJECT ${symbol} STRING ${STRING_STRING}) + endforeach() + + set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE) +endfunction() + +function(section_to_string) + cmake_parse_arguments(STRING "" "SECTION;STRING" "" ${ARGN}) + + get_property(name GLOBAL PROPERTY ${STRING_SECTION}_NAME) + get_property(name_clean GLOBAL PROPERTY ${STRING_SECTION}_NAME_CLEAN) + get_property(address GLOBAL PROPERTY ${STRING_SECTION}_ADDRESS) + get_property(type GLOBAL PROPERTY ${STRING_SECTION}_TYPE) + get_property(align_in GLOBAL PROPERTY ${STRING_SECTION}_ALIGN_WITH_INPUT) + get_property(align GLOBAL PROPERTY ${STRING_SECTION}_ALIGN) + get_property(subalign GLOBAL PROPERTY ${STRING_SECTION}_SUBALIGN) + get_property(vma GLOBAL PROPERTY ${STRING_SECTION}_VMA) + get_property(lma GLOBAL PROPERTY ${STRING_SECTION}_LMA) + get_property(noinput GLOBAL PROPERTY ${STRING_SECTION}_NOINPUT) + get_property(noinit GLOBAL PROPERTY ${STRING_SECTION}_NOINIT) + get_property(nosymbols GLOBAL PROPERTY ${STRING_SECTION}_NOSYMBOLS) + get_property(parent GLOBAL PROPERTY ${STRING_SECTION}_PARENT) + + string(REGEX REPLACE "^[\.]" "" name_clean "${name}") + string(REPLACE "." "_" name_clean "${name_clean}") + + set(SECTION_TYPE_NOLOAD NOLOAD) + set(SECTION_TYPE_BSS NOLOAD) + if(DEFINED type) + set(type " (${SECTION_TYPE_${type}})") + endif() + + set(TEMP "${TEMP} :") + set(secalign "") + + if(align_in) + set(secalign " ALIGN_WITH_INPUT") + endif() + + if(DEFINED align) + set(secalign "${secalign} ALIGN(${align})") + endif() + + if(DEFINED subalign) + set(secalign "${secalign} SUBALIGN(${subalign})") + endif() + + set(TEMP "${name} ${address}${type} :${secalign}\n{") + if(NOT nosymbols) + set(TEMP "${TEMP}\n __${name_clean}_start = .;") + endif() + + if(NOT noinput) + set(TEMP "${TEMP}\n *(${name})") + set(TEMP "${TEMP}\n *(\"${name}.*\")") + endif() + + get_property(indicies GLOBAL PROPERTY ${STRING_SECTION}_SETTINGS_INDICIES) + foreach(idx ${indicies}) + get_property(align GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ALIGN) + get_property(any GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_ANY) + get_property(first GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FIRST) + get_property(keep GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_KEEP) + get_property(sort GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SORT) + get_property(flags GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_FLAGS) + get_property(input GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_INPUT) + get_property(symbols GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_SYMBOLS) + get_property(offset GLOBAL PROPERTY ${STRING_SECTION}_SETTING_${idx}_OFFSET) + + if(DEFINED SETTINGS_ALIGN) + set(TEMP "${TEMP}\n . = ALIGN(${align});") + endif() + + if(DEFINED symbols) + list(LENGTH symbols symbols_count) + if(${symbols_count} GREATER 0) + list(GET symbols 0 symbol_start) + endif() + if(${symbols_count} GREATER 1) + list(GET symbols 1 symbol_end) + endif() + endif() + + if(DEFINED symbol_start) + set(TEMP "${TEMP}\n ${symbol_start} = .;") + endif() + + foreach(setting ${input}) + if(DEFINED offset AND NOT ("${offset}" STREQUAL "${current_offset}")) + set(TEMP "${TEMP}\n . = ${offset};") + set(current_offset ${offset}) + endif() + + if(keep AND sort) + set(TEMP "${TEMP}\n KEEP(*(${SORT_TYPE_${sort}}(${setting})));") + elseif(SETTINGS_SORT) + message(WARNING "Not tested") + set(TEMP "${TEMP}\n *(${SORT_TYPE_${sort}}(${setting}));") + elseif(keep) + set(TEMP "${TEMP}\n KEEP(*(${setting}));") + else() + set(TEMP "${TEMP}\n *(${setting})") + endif() + endforeach() + + if(DEFINED symbol_end) + set(TEMP "${TEMP}\n ${symbol_end} = .;") + endif() + + set(symbol_start) + set(symbol_end) + endforeach() + + if(NOT nosymbols) + set(TEMP "${TEMP}\n __${name_clean}_end = .;") + endif() + + set(TEMP "${TEMP}\n}") + + get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE) + if(${parent_type} STREQUAL GROUP) + get_property(vma GLOBAL PROPERTY ${parent}_VMA) + get_property(lma GLOBAL PROPERTY ${parent}_LMA) + endif() + + if(DEFINED vma) + set(TEMP "${TEMP} > ${vma}") + endif() + + if(DEFINED vma AND DEFINED lma) + set(TEMP "${TEMP} AT") + endif() + + if(DEFINED lma) + set(TEMP "${TEMP} > ${lma}") + endif() + + if(NOT nosymbols) + set(TEMP "${TEMP}\n__${name_clean}_size = __${name_clean}_end - __${name_clean}_start;") + set(TEMP "${TEMP}\n__${name_clean}_load_start = LOADADDR(${name});") + endif() + + set(${STRING_STRING} "${${STRING_STRING}}\n${TEMP}\n" PARENT_SCOPE) +endfunction() + +# /DISCARD/ is an ld specific section, so let's append it here before processing. +list(APPEND SECTIONS "{NAME\;/DISCARD/\;NOINPUT\;TRUE\;NOSYMBOLS\;TRUE}") + +function(process_region) + cmake_parse_arguments(REGION "" "OBJECT" "" ${ARGN}) + + process_region_common(${ARGN}) + + set(groups) + get_objects(LIST groups OBJECT ${REGION_OBJECT} TYPE GROUP) + foreach(group ${groups}) + get_property(parent GLOBAL PROPERTY ${group}_PARENT) + get_property(parent_type GLOBAL PROPERTY ${parent}_OBJ_TYPE) + + if(${parent_type} STREQUAL GROUP) + get_property(vma GLOBAL PROPERTY ${parent}_VMA) + get_property(lma GLOBAL PROPERTY ${parent}_LMA) + + set_property(GLOBAL PROPERTY ${group}_VMA ${vma}) + set_property(GLOBAL PROPERTY ${group}_LMA ${lma}) + endif() + endforeach() +endfunction() + +include(${CMAKE_CURRENT_LIST_DIR}/../linker_script_common.cmake)