Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LLEXT full ARM relocation support (adding executable) #70171

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions cmake/modules/llext.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Copyright (c) 2024 Schneider Electric
# SPDX-License-Identifier: Apache-2.0

include_guard(GLOBAL)

# Add a macro that add a llext extension.
# macro is based on zephyr_library template
#
# output an elf file usable by llext
#
# Code can be linked by 2 differents ways:
# 1. using partial link (-r flag in gcc and -fno-pic) (default)
# 2. using shared link (-shared flag in gcc and -fpic)
#
# The code will be compiled with mostly the same C compiler flags used
# in the Zephyr build, but with some important modifications.
# Toolchain flag are automatically inherited and filtered.
#
# NOPIC:
# -mlong-calls flag is always added
# libc is detected from zephyr configuration and libc function code is embedded in extension
# PIC:
# code is compiled and linked with -fpic flag
# libc is detected from zephyr configuration and libc function are called. They are external symbol for extension.
#
# Example usage:
# zephyr_llext(hello_world)
# zephyr_llext_sources(hello_world.c hello_world2.c)
# zephyr_llext_include_directories(.)
# will compile the source file hello_world.c and hello_world2.c to
# ${PROJECT_BINARY_DIR}/hello_world.llext
#


macro(zephyr_llext name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a macro and not a function ?

All variables defined inside a macro will keep living when macro returns, which can give some nasty surprises, so extra care must be taken when deciding to define a macro.

So far I've not seen a reason why this must be implemented as a macro, so would like to understand the reasons.

set(LLEXT_IS_PIC no)
set(ZEPHYR_CURRENT_LLEXT ${name})
set(ZEPHYR_LLEXT_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/linker/llext.ld)

# ARGN is not a variable: assign its value to a variable
set(ExtraMacroArgs ${ARGN})
# Get the length of the list
list(LENGTH ExtraMacroArgs NumExtraMacroArgs)
# Execute the following block only if the length is > 0
if(NumExtraMacroArgs GREATER 0)
foreach(ExtraArg ${ExtraMacroArgs})
if(${ExtraArg} STREQUAL PIC)
set(LLEXT_IS_PIC yes)
endif()
endforeach()
endif()
Comment on lines +43 to +51
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit unusual.

Why not a proper function and then use cmake_parse_arguments() ?


add_executable(${ZEPHYR_CURRENT_LLEXT})

set_property(TARGET ${ZEPHYR_CURRENT_LLEXT} APPEND PROPERTY LINK_DEPENDS ${ZEPHYR_LLEXT_LINKER_SCRIPT})

target_compile_definitions(${ZEPHYR_CURRENT_LLEXT}
PRIVATE $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_DEFINITIONS>
PRIVATE ZEPHYR_LLEXT
)

target_compile_options(${ZEPHYR_CURRENT_LLEXT}
PRIVATE $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS>
)

if(LLEXT_IS_PIC)
target_compile_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -fpic -fpie)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this code in general is GNU centric, but we actually have compiler flags defined for such cases to avoid custom toolchain flags / handling throughout the codebase.

This makes it easier to add better support for more toolchains in Zephyr.

For example:

set_compiler_property(PROPERTY no_position_independent
-fno-pic
-fno-pie

else()
if("${ARCH}" STREQUAL "arm")
target_compile_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -mlong-calls)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment regarding compiler flag handling.

endif()
endif()

target_include_directories(${ZEPHYR_CURRENT_LLEXT}
PUBLIC $<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES>
)
target_include_directories(${ZEPHYR_CURRENT_LLEXT} SYSTEM
PUBLIC $<TARGET_PROPERTY:zephyr_interface,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>
)

# copy toolchain link flag
set(LD_FLAGS ${TOOLCHAIN_LD_FLAGS})
list(REMOVE_ITEM LD_FLAGS NO_SPLIT)

set_target_properties(${ZEPHYR_CURRENT_LLEXT} PROPERTIES LINK_DEPENDS ${ZEPHYR_LLEXT_LINKER_SCRIPT})
get_property(LIBC_LINK_LIBRARIES TARGET zephyr_interface PROPERTY LIBC_LINK_LIBRARIES)

if(LLEXT_IS_PIC)
# to use -shared, we need libc build with -fpic or not used libc
target_link_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -shared)
else()
target_link_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -r)
endif()

target_link_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE
-n # disable section alignment
-e start # no entry point
-T ${ZEPHYR_LLEXT_LINKER_SCRIPT}
-Wl,-Map=$<TARGET_FILE_BASE_NAME:${ZEPHYR_CURRENT_LLEXT}>.map
${LD_FLAGS}
)
if(LLEXT_IS_PIC)
# libc is not linked shared so we cannot link with it
target_link_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -nolibc -nostartfiles -fpic -fpie)
target_link_libraries(${ZEPHYR_CURRENT_LLEXT} PRIVATE gcc)
else()
# TODO add newlib and newlib nano support
if(CONFIG_PICOLIBC)
target_sources(${ZEPHYR_CURRENT_LLEXT} PRIVATE ${ZEPHYR_BASE}/lib/libc/picolibc/libc-hooks.c)
target_link_options(${ZEPHYR_CURRENT_LLEXT} PRIVATE -DPICOLIBC_INTEGER_PRINTF_SCANF)
endif()

target_link_libraries(${ZEPHYR_CURRENT_LLEXT} PRIVATE ${LIBC_LINK_LIBRARIES})
endif()

# strip llext
add_custom_target(${ZEPHYR_CURRENT_LLEXT}.llext ALL
COMMAND ${CMAKE_OBJCOPY} --strip-unneeded $<TARGET_FILE:${ZEPHYR_CURRENT_LLEXT}> ${ZEPHYR_CURRENT_LLEXT}.llext
DEPENDS ${ZEPHYR_CURRENT_LLEXT}
)

add_dependencies(${ZEPHYR_CURRENT_LLEXT}
zephyr_interface
zephyr_generated_headers
)

endmacro()

function(zephyr_llext_sources source)
target_sources(${ZEPHYR_CURRENT_LLEXT} PRIVATE ${source} ${ARGN})
endfunction()

function(zephyr_llext_include_directories)
target_include_directories(${ZEPHYR_CURRENT_LLEXT} PRIVATE ${ARGN})
endfunction()
1 change: 1 addition & 0 deletions cmake/modules/zephyr_default.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ list(APPEND zephyr_cmake_modules kconfig)
list(APPEND zephyr_cmake_modules arch_v2)
list(APPEND zephyr_cmake_modules soc_v1)
list(APPEND zephyr_cmake_modules soc_v2)
list(APPEND zephyr_cmake_modules llext)

foreach(component ${SUB_COMPONENTS})
if(NOT ${component} IN_LIST zephyr_cmake_modules)
Expand Down
36 changes: 33 additions & 3 deletions arch/arm/core/llext.ld → include/zephyr/linker/llext.ld
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SECTIONS
.text :
{
. = ALIGN(4);
*(.literal .literal.*)
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
. = ALIGN(4);
Expand Down Expand Up @@ -43,12 +44,21 @@ SECTIONS
{
. = ALIGN(4);
app_sgot = .;
*(.got)
*(.got)
*(.got.plt)
app_egot = .;
. = ALIGN(4);
} > all

/* .got.loc section (xtensa only) */
.got.loc :
{
. = ALIGN(4);
*(.got.loc)
. = ALIGN(4);
} >all


/* PLT section contains code for accessing the dynamically linked functions
* ie funtions from shared libraries in a postion independent manner */
.plt :
Expand Down Expand Up @@ -76,6 +86,26 @@ SECTIONS
. = ALIGN(4);
} > all

/** This section will be used by the debugger and disassembler to get more information
* about raw data present in the code.
* Indeed, it may be required to add some padding at some points in the code
* in order to align a branch/jump destination on a particular bound.
* Padding these instructions will generate null bytes that shall be
* interpreted as data, and not code by the debugger or disassembler.
* This section will only be present in the ELF file, not in the final binary
* For more details, check GCC-212
*/
.xt.prop 0 :
{
KEEP (*(.xt.prop))
}

.xt.lit 0 :
{
KEEP (*(.xt.lit))
KEEP (*(.xt.lit.*))
}



.dynsym : { *(.dynsym) }
Expand All @@ -97,12 +127,12 @@ SECTIONS

/*
.ARM.extab and .ARM.exidx are related to unwinding.
You can find more information here http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/index.html.
You can find more information here http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/index.html.
You don't need them if you don't care about unwinding (unwinding is useful for C++ exception and for debugging)
*/
/DISCARD/ : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
/DISCARD/ : { *(.ARM.exidx*) }

/* discard .comment section */
/DISCARD/ : { *(.comment) }

Expand Down