cmake: ld: adding ld linker script generator

Adding intial version of ld_script.cmake.

This script can generate ld linker script from the Zephyr CMake linker
functions.

This script is called by the build system and all linker settings, such
as memory regions, groups, output sections, linker symbols defined with
Zephyr CMake linker functions are passed to this script in order to
generate an ld linker script.

Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
Torsten Rasmussen 2021-06-10 14:12:38 +02:00 committed by Anas Nashif
commit e9e47a45dd

View file

@ -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)