diff --git a/cmake/bintools/gnu/target_bintools.cmake b/cmake/bintools/gnu/target_bintools.cmake index e8cb6e181d55af..8aac55b0400cdc 100644 --- a/cmake/bintools/gnu/target_bintools.cmake +++ b/cmake/bintools/gnu/target_bintools.cmake @@ -93,6 +93,7 @@ set_property(TARGET bintools PROPERTY strip_flag_final "") set_property(TARGET bintools PROPERTY strip_flag_all --strip-all) set_property(TARGET bintools PROPERTY strip_flag_debug --strip-debug) set_property(TARGET bintools PROPERTY strip_flag_dwo --strip-dwo) +set_property(TARGET bintools PROPERTY strip_flag_remove_section -R ) set_property(TARGET bintools PROPERTY strip_flag_infile "") set_property(TARGET bintools PROPERTY strip_flag_outfile -o ) diff --git a/cmake/compiler/gcc/target.cmake b/cmake/compiler/gcc/target.cmake index 86807bd8a4f69d..5b1e5db12180a3 100644 --- a/cmake/compiler/gcc/target.cmake +++ b/cmake/compiler/gcc/target.cmake @@ -76,6 +76,8 @@ elseif("${ARCH}" STREQUAL "sparc") include(${CMAKE_CURRENT_LIST_DIR}/target_sparc.cmake) elseif("${ARCH}" STREQUAL "mips") include(${CMAKE_CURRENT_LIST_DIR}/target_mips.cmake) +elseif("${ARCH}" STREQUAL "xtensa") + include(${CMAKE_CURRENT_LIST_DIR}/target_xtensa.cmake) endif() if(SYSROOT_DIR) diff --git a/cmake/compiler/gcc/target_arm.cmake b/cmake/compiler/gcc/target_arm.cmake index a813c2563a3b0b..6659c7bf417018 100644 --- a/cmake/compiler/gcc/target_arm.cmake +++ b/cmake/compiler/gcc/target_arm.cmake @@ -41,3 +41,22 @@ endif() list(APPEND TOOLCHAIN_C_FLAGS ${ARM_C_FLAGS}) list(APPEND TOOLCHAIN_LD_FLAGS NO_SPLIT ${ARM_C_FLAGS}) + +# Flags not supported by llext linker +# (regexps are supported and match whole word) +set(LLEXT_REMOVE_FLAGS + -fno-pic + -fno-pie + -ffunction-sections + -fdata-sections + -g.* + -Os + -mcpu=.* +) + +# Flags to be added to llext code compilation +set(LLEXT_APPEND_FLAGS + -mlong-calls + -mthumb + -mcpu=cortex-m33+nodsp +) diff --git a/cmake/compiler/gcc/target_xtensa.cmake b/cmake/compiler/gcc/target_xtensa.cmake new file mode 100644 index 00000000000000..177830427dbdda --- /dev/null +++ b/cmake/compiler/gcc/target_xtensa.cmake @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Flags not supported by llext linker +# (regexps are supported and match whole word) +set(LLEXT_REMOVE_FLAGS + -fno-pic + -fno-pie + -ffunction-sections + -fdata-sections + -g.* + -Os + -mcpu=.* +) + +# Flags to be added to llext code compilation +set(LLEXT_APPEND_FLAGS + -fPIC + -nostdlib + -nodefaultlibs + -shared +) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index 5a216fc1a79948..b51ff49d2fd227 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -34,6 +34,7 @@ include(CheckCXXCompilerFlag) # 5. Zephyr linker functions # 5.1. zephyr_linker* # 6 Function helper macros +# 7 Linkable loadable extensions (llext) ######################################################## # 1. Zephyr-aware extensions @@ -4786,3 +4787,138 @@ macro(zephyr_check_flags_exclusive function prefix) ) endif() endmacro() + +######################################################## +# 7. Linkable loadable extensions (llext) +######################################################## +# +# These functions simplify the creation and management of linkable +# loadable extensions (llexts). +# + +# Add a custom target that compiles a single source file to a .llext file. +# +# Output and source files must be specified using the OUTPUT and SOURCES +# arguments. Only one source file is currently supported. +# +# The llext code will be compiled with mostly the same C compiler flags used +# in the Zephyr build, but with some important modifications. The list of +# flags to remove and flags to append is controlled respectively by the +# LLEXT_REMOVE_FLAGS and LLEXT_APPEND_FLAGS global variables. + +# The C_FLAGS argument can be used to pass additional compiler flags to the +# compilation of this particular llext. +# +# Example usage: +# add_llext_target(hello_world +# OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext +# SOURCES ${PROJECT_SOURCE_DIR}/src/llext/hello_world.c +# C_FLAGS -Werror +# ) +# will compile the source file src/llext/hello_world.c to a file +# ${PROJECT_BINARY_DIR}/hello_world.llext, adding -Werror to the compilation. +# +function(add_llext_target target_name) + set(single_args OUTPUT) + set(multi_args SOURCES;C_FLAGS) + cmake_parse_arguments(PARSE_ARGV 1 LLEXT "${options}" "${single_args}" "${multi_args}") + + # Check that the llext subsystem is enabled for this build + if (NOT CONFIG_LLEXT) + message(FATAL_ERROR "add_llext_target: CONFIG_LLEXT must be enabled") + endif() + + # Output file must be provided + if(NOT LLEXT_OUTPUT) + message(FATAL_ERROR "add_llext_target: OUTPUT argument must be provided") + endif() + + # Source list length must currently be 1 + list(LENGTH LLEXT_SOURCES source_count) + if(NOT source_count EQUAL 1) + message(FATAL_ERROR "add_llext_target: only one source file is supported") + endif() + + set(output_file ${LLEXT_OUTPUT}) + set(source_file ${LLEXT_SOURCES}) + get_filename_component(output_name ${output_file} NAME) + + # Add user-visible target and dependency + add_custom_target(${target_name} + COMMENT "Compiling ${output_name}" + DEPENDS ${output_file} + ) + + # Convert the LLEXT_REMOVE_FLAGS list to a regular expression, and use it to + # filter out these flags from the Zephyr target settings + list(TRANSFORM LLEXT_REMOVE_FLAGS + REPLACE "(.+)" "^\\1$" + OUTPUT_VARIABLE llext_remove_flags_regexp + ) + string(REPLACE ";" "|" llext_remove_flags_regexp "${llext_remove_flags_regexp}") + set(zephyr_flags + "$" + ) + set(zephyr_filtered_flags + "$" + ) + + # Compile the source file to an object file using current Zephyr settings + # but a different set of flags + add_library(${target_name}_lib OBJECT ${source_file}) + target_compile_definitions(${target_name}_lib PRIVATE + $ + ) + target_compile_options(${target_name}_lib PRIVATE + ${zephyr_filtered_flags} + ${LLEXT_APPEND_FLAGS} + ${LLEXT_C_FLAGS} + ) + target_include_directories(${target_name}_lib PRIVATE + $ + ) + target_include_directories(${target_name}_lib SYSTEM PUBLIC + $ + ) + add_dependencies(${target_name}_lib + zephyr_interface + zephyr_generated_headers + ) + + # Arch-specific conversion of the object file to an llext + if(CONFIG_ARM) + + # No conversion required, simply copy the object file + add_custom_command( + OUTPUT ${output_file} + COMMAND ${CMAKE_COMMAND} -E copy $ ${output_file} + DEPENDS ${target_name}_lib $ + ) + + elseif(CONFIG_XTENSA) + + # Generate an intermediate file name + get_filename_component(output_dir ${output_file} DIRECTORY) + get_filename_component(output_name_we ${output_file} NAME_WE) + set(pre_output_file ${output_dir}/${output_name_we}.pre.llext) + + # Need to convert the object file to a shared library, then strip some sections + add_custom_command( + OUTPUT ${output_file} + BYPRODUCTS ${pre_output_file} + COMMAND ${CMAKE_C_COMPILER} ${LLEXT_APPEND_FLAGS} + -o ${pre_output_file} + $ + COMMAND $ + $ + $.xt.* + $${pre_output_file} + $${output_file} + $ + DEPENDS ${target_name}_lib $ + ) + + else() + message(FATAL_ERROR "add_llext_target: unsupported architecture") + endif() +endfunction() diff --git a/tests/subsys/llext/hello_world/CMakeLists.txt b/tests/subsys/llext/hello_world/CMakeLists.txt index 980f0e97d0056c..45e0fa0b9a3151 100644 --- a/tests/subsys/llext/hello_world/CMakeLists.txt +++ b/tests/subsys/llext/hello_world/CMakeLists.txt @@ -7,34 +7,14 @@ project(hello_world) if(NOT CONFIG_MODULES OR CONFIG_LLEXT_TEST_HELLO STREQUAL "m") -# TODO check which architecture is being used -if(CONFIG_ARM) - set(CMAKE_C_FLAGS "-mlong-calls" "-mthumb") + set(llext_src_file ${PROJECT_SOURCE_DIR}/hello_world.c) + set(llext_bin_file ${PROJECT_BINARY_DIR}/hello_world.llext) - add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext - COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} -c - -I ${PROJECT_SOURCE_DIR}/../../../../include - -imacros${PROJECT_BINARY_DIR}/../zephyr/include/generated/autoconf.h - -o ${PROJECT_BINARY_DIR}/hello_world.llext - ${PROJECT_SOURCE_DIR}/hello_world.c - ) -elseif(CONFIG_XTENSA) - set(CMAKE_C_FLAGS "-shared" "-fPIC" "-nostdlib" "-nodefaultlibs") + add_llext_target(hello_world + OUTPUT ${llext_bin_file} + SOURCES ${llext_src_file} + ) - add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext - COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} - -I ${PROJECT_SOURCE_DIR}/../../../../include - -imacros${PROJECT_BINARY_DIR}/../zephyr/include/generated/autoconf.h - -o ${PROJECT_BINARY_DIR}/hello_world.pre.llext - ${PROJECT_SOURCE_DIR}/hello_world.c - COMMAND ${CROSS_COMPILE}strip -R .xt.* - -o ${PROJECT_BINARY_DIR}/hello_world.llext - ${PROJECT_BINARY_DIR}/hello_world.pre.llext - ) -endif() - -set(HELLO_WORLD_LLEXT ${PROJECT_BINARY_DIR}/hello_world.llext PARENT_SCOPE) - -add_custom_target(hello_world DEPENDS ${PROJECT_BINARY_DIR}/hello_world.llext) + set(HELLO_WORLD_LLEXT ${llext_bin_file} PARENT_SCOPE) endif()