mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(ulp): support custom LP-core linker layouts via the LINKER_LAYOUT option
This commit is contained in:
@@ -320,6 +320,16 @@ elseif(ULP_TYPE STREQUAL "lp_core")
|
||||
# via FLAGS.
|
||||
idf_component_get_property(esp_system_dir esp_system COMPONENT_DIR)
|
||||
set(lp_core_ld_flags "${ulp_ld_flags} -I\"${esp_system_dir}/ld\" -I\"${esp_system_dir}/ld/${target}\"")
|
||||
# A user-supplied LINKER layout replaces the default one composed into
|
||||
# lp_core_riscv.ld.in. It reaches this child project as LP_CORE_LINKER_SCRIPT
|
||||
# (passed by __setup_ulp_project, same as the CMake v1 path). Hand it to the
|
||||
# wrapper as the LP_CORE_LINKER_INCLUDE macro it #includes. The escaped quotes
|
||||
# survive separate_arguments() in the linker-script preprocessor so the macro
|
||||
# expands to a quoted header-name token ("/abs/path.ld").
|
||||
if(LP_CORE_LINKER_SCRIPT)
|
||||
message(STATUS "Using custom LP-core linker layout: ${LP_CORE_LINKER_SCRIPT}")
|
||||
string(APPEND lp_core_ld_flags " -DLP_CORE_LINKER_INCLUDE=\\\"${LP_CORE_LINKER_SCRIPT}\\\"")
|
||||
endif()
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/lp_core_riscv.ld.in" MEMORY
|
||||
FLAGS "${lp_core_ld_flags}")
|
||||
|
||||
|
||||
@@ -32,9 +32,14 @@ function(__ulp_create_arg_file arguments output_file)
|
||||
endfunction()
|
||||
|
||||
function(__ulp_add_preprocessed_linker_script ulp_app_name ld_template ld_script ld_script_target)
|
||||
# Use the C preprocessor so sdkconfig and SoC constants can shape the
|
||||
# linker template before the ULP executable is linked.
|
||||
set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -o ${ld_script} ${ARGN} ${ld_template})
|
||||
# Use the C preprocessor so sdkconfig and SoC constants can shape the linker
|
||||
# template before the ULP executable is linked. -MD -MF -MT records every file
|
||||
# the preprocessor reads (sdkconfig.h, SoC headers, the base/layout/checks
|
||||
# parts and a custom LINKER_LAYOUT with its own includes) into a depfile, so
|
||||
# the script regenerates when any of them changes.
|
||||
set(ld_depfile ${CMAKE_CURRENT_BINARY_DIR}/${ld_script}.d)
|
||||
set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -MD -MF ${ld_depfile} -MT ${ld_script}
|
||||
-o ${ld_script} ${ARGN} ${ld_template})
|
||||
set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ld_script}_args.txt)
|
||||
__ulp_create_arg_file("${preprocessor_args}" "${compiler_arguments_file}")
|
||||
|
||||
@@ -42,13 +47,21 @@ function(__ulp_add_preprocessed_linker_script ulp_app_name ld_template ld_script
|
||||
COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
MAIN_DEPENDENCY ${ld_template}
|
||||
DEPENDS ${SDKCONFIG_HEADER}
|
||||
# The response file is a dependency too: a change visible only
|
||||
# in the preprocessor flags (e.g. a different LINKER layout
|
||||
# path) must regenerate the script even though no file recorded
|
||||
# in the depfile changed. __ulp_create_arg_file rewrites it
|
||||
# only when its content actually changes, so this does not
|
||||
# retrigger on every reconfigure.
|
||||
DEPENDS ${compiler_arguments_file}
|
||||
DEPFILE ${ld_depfile}
|
||||
COMMENT "Generating ${ld_script} linker script..."
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(${ld_script_target} DEPENDS ${ld_script})
|
||||
add_dependencies(${ulp_app_name} ${ld_script_target})
|
||||
target_link_options(${ulp_app_name} PRIVATE SHELL:-T ${CMAKE_CURRENT_BINARY_DIR}/${ld_script})
|
||||
set_property(TARGET ${ulp_app_name} APPEND PROPERTY LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ld_script})
|
||||
endfunction()
|
||||
|
||||
function(ulp_apply_default_options ulp_app_name)
|
||||
@@ -85,7 +98,10 @@ function(ulp_apply_default_sources ulp_app_name)
|
||||
|
||||
target_include_directories(${ulp_app_name} PRIVATE ${COMPONENT_INCLUDES} ${sdkconfig_dir})
|
||||
|
||||
# Pre-process the linker script
|
||||
# Pre-process the linker script. The LP-core script is assembled from
|
||||
# base + layout + checks. A user-supplied LINKER_LAYOUT (LP_CORE_LINKER_SCRIPT)
|
||||
# replaces the layout, swapped in by the wrapper. This is supported for the
|
||||
# LP-core type only.
|
||||
if(BUILD_RISCV)
|
||||
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_riscv.ld.in)
|
||||
elseif(BUILD_LP_CORE)
|
||||
@@ -96,6 +112,18 @@ function(ulp_apply_default_sources ulp_app_name)
|
||||
message(FATAL_ERROR "Unable to determine ULP type. ")
|
||||
endif()
|
||||
|
||||
if(LP_CORE_LINKER_SCRIPT)
|
||||
# The LINKER_LAYOUT-is-LP-core-only check is enforced once in
|
||||
# ulp_embed_binary/ulp_add_project, so LP_CORE_LINKER_SCRIPT only ever
|
||||
# reaches an LP-core child.
|
||||
message(STATUS "Using custom LP-core linker layout: ${LP_CORE_LINKER_SCRIPT}")
|
||||
# The inner escaped quotes are required: the wrapper does
|
||||
# `#include LP_CORE_LINKER_INCLUDE`, so the macro must expand to a quoted
|
||||
# header-name token ("/abs/path.ld"). __ulp_create_arg_file() additionally
|
||||
# escapes spaces in the path, keeping it a single preprocessor token.
|
||||
list(APPEND ULP_PREPRO_ARGS "-DLP_CORE_LINKER_INCLUDE=\\\"${LP_CORE_LINKER_SCRIPT}\\\"")
|
||||
endif()
|
||||
|
||||
# Strip the .in suffix so the generated script keeps its .ld name.
|
||||
get_filename_component(ULP_LD_SCRIPT ${ULP_LD_TEMPLATE} NAME_WLE)
|
||||
__ulp_add_preprocessed_linker_script(${ulp_app_name} ${ULP_LD_TEMPLATE} ${ULP_LD_SCRIPT}
|
||||
|
||||
@@ -7,7 +7,7 @@ endif()
|
||||
# Create ULP binary and embed into the application.
|
||||
|
||||
function(__setup_ulp_project app_name project_path prefix prefix_append_bin_name type binary_names
|
||||
s_sources exp_dep_srcs)
|
||||
s_sources exp_dep_srcs linker_script)
|
||||
|
||||
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
set(ulp_cmake_dir "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/cmake")
|
||||
@@ -189,6 +189,9 @@ function(__setup_ulp_project app_name project_path prefix prefix_append_bin_name
|
||||
-DADD_PICOLIBC_SPECS=${CONFIG_LIBC_PICOLIBC}
|
||||
-DULP_VAR_PREFIX=${prefix}
|
||||
-DULP_TYPE=${type}
|
||||
# Read by the LP-core child to swap in a custom layout;
|
||||
# empty and ignored otherwise.
|
||||
-DLP_CORE_LINKER_SCRIPT=${linker_script}
|
||||
${ulp_project_args}
|
||||
-DIDF_TARGET=${idf_target}
|
||||
-DIDF_PATH=${idf_path}
|
||||
@@ -276,26 +279,52 @@ function(__ulp_resolve_type out_var type)
|
||||
set(${out_var} "${resolved_type}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Resolve a user-supplied LINKER_LAYOUT for a ULP of the given (already resolved)
|
||||
# type: reject it for non-LP-core types, since only the LP-core linker script has
|
||||
# a swappable layout part, then resolve the layout to an absolute path and verify
|
||||
# it exists. The result is written back to the variable named by out_var. A
|
||||
# relative path is resolved against CMAKE_CURRENT_LIST_DIR, the same base used for
|
||||
# relative ULP source files in __setup_ulp_project, so that sources and the linker
|
||||
# layout in one call are resolved consistently. Enforcing this here, at the public
|
||||
# API, gives a single check that covers every ULP child build style.
|
||||
function(__resolve_lp_core_linker linker type out_var)
|
||||
if(linker)
|
||||
if(NOT type STREQUAL "lp_core")
|
||||
message(FATAL_ERROR "A custom LINKER_LAYOUT script is only supported for the LP-core ULP type.")
|
||||
endif()
|
||||
get_filename_component(linker "${linker}" ABSOLUTE BASE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
if(NOT EXISTS "${linker}")
|
||||
message(FATAL_ERROR "LINKER_LAYOUT linker script not found: ${linker}")
|
||||
endif()
|
||||
endif()
|
||||
set(${out_var} "${linker}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(ulp_embed_binary app_name s_sources exp_dep_srcs)
|
||||
cmake_parse_arguments(ULP "" "PREFIX;TYPE" "" ${ARGN})
|
||||
cmake_parse_arguments(ULP "" "PREFIX;TYPE;LINKER_LAYOUT" "" ${ARGN})
|
||||
if(NOT ULP_PREFIX)
|
||||
set(ULP_PREFIX "ulp_")
|
||||
endif()
|
||||
|
||||
validate_ulp_type("${ULP_TYPE}")
|
||||
# Resolve the effective ULP type (from TYPE or the project config) so the
|
||||
# LINKER_LAYOUT guard is applied uniformly; the full-subproject child also
|
||||
# consumes the resolved type.
|
||||
__ulp_resolve_type(ulp_resolved_type "${ULP_TYPE}")
|
||||
if(IDF_BUILD_V2)
|
||||
__ulp_resolve_type(ULP_TYPE "${ULP_TYPE}")
|
||||
set(ULP_TYPE "${ulp_resolved_type}")
|
||||
endif()
|
||||
__resolve_lp_core_linker("${ULP_LINKER_LAYOUT}" "${ulp_resolved_type}" LP_CORE_LINKER)
|
||||
set(ulp_cmake_dir "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/cmake")
|
||||
|
||||
__setup_ulp_project("${app_name}" "${ulp_cmake_dir}"
|
||||
"${ULP_PREFIX}" FALSE "${ULP_TYPE}"
|
||||
"${app_name}"
|
||||
"${s_sources}" "${exp_dep_srcs}")
|
||||
"${s_sources}" "${exp_dep_srcs}" "${LP_CORE_LINKER}")
|
||||
endfunction()
|
||||
|
||||
function(ulp_add_project app_name project_path)
|
||||
cmake_parse_arguments(ULP "" "PREFIX;TYPE" "BINARIES" ${ARGN})
|
||||
cmake_parse_arguments(ULP "" "PREFIX;TYPE;LINKER_LAYOUT" "BINARIES" ${ARGN})
|
||||
if(NOT ULP_BINARIES)
|
||||
set(ULP_BINARIES "${app_name}")
|
||||
endif()
|
||||
@@ -311,10 +340,15 @@ function(ulp_add_project app_name project_path)
|
||||
endif()
|
||||
|
||||
validate_ulp_type("${ULP_TYPE}")
|
||||
# Resolve the effective ULP type (from TYPE or the project config) so the
|
||||
# LINKER_LAYOUT guard is applied uniformly; the full-subproject child also
|
||||
# consumes the resolved type.
|
||||
__ulp_resolve_type(ulp_resolved_type "${ULP_TYPE}")
|
||||
if(IDF_BUILD_V2)
|
||||
__ulp_resolve_type(ULP_TYPE "${ULP_TYPE}")
|
||||
set(ULP_TYPE "${ulp_resolved_type}")
|
||||
endif()
|
||||
__resolve_lp_core_linker("${ULP_LINKER_LAYOUT}" "${ulp_resolved_type}" LP_CORE_LINKER)
|
||||
|
||||
__setup_ulp_project("${app_name}" "${project_path}" "${ULP_PREFIX}"
|
||||
"${ULP_PREFIX_APPEND_BIN_NAME}" "${ULP_TYPE}" "${ULP_BINARIES}" "" "")
|
||||
"${ULP_PREFIX_APPEND_BIN_NAME}" "${ULP_TYPE}" "${ULP_BINARIES}" "" "" "${LP_CORE_LINKER}")
|
||||
endfunction()
|
||||
|
||||
@@ -1279,16 +1279,25 @@ function(__preprocess_linker_script script_in script_out flags component_include
|
||||
|
||||
set(linker_script_generator "${idf_path}/tools/cmake/linker_script_preprocessor.cmake")
|
||||
|
||||
# -MD -MF -MT makes the C preprocessor record every file it reads (the
|
||||
# template plus everything it #includes, transitively) into a depfile, so
|
||||
# the output is regenerated when any of them changes. MAIN_DEPENDENCY and
|
||||
# DEPENDS only cover the top-level script and sdkconfig; the depfile closes
|
||||
# the gap for includes the build system cannot enumerate up front.
|
||||
set(depfile "${script_out}.d")
|
||||
set(depfile_flags "-MD -MF \"${depfile}\" -MT \"${script_out}\"")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${script_out}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DCC=${CMAKE_C_COMPILER}"
|
||||
"-DSOURCE=${script_in}"
|
||||
"-DTARGET=${script_out}"
|
||||
"-DCFLAGS=-I\"${config_dir}\" ${base_flags} ${component_includes}"
|
||||
"-DCFLAGS=-I\"${config_dir}\" ${depfile_flags} ${base_flags} ${component_includes}"
|
||||
-P "${linker_script_generator}"
|
||||
MAIN_DEPENDENCY "${script_in}"
|
||||
DEPENDS "${sdkconfig_header}"
|
||||
DEPFILE "${depfile}"
|
||||
COMMENT "Preprocessing linker script ${script_in} -> ${script_out}"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
Reference in New Issue
Block a user