Skip to content

Zephyr toolchain / linking improvements #78320

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

Merged
Show file tree
Hide file tree
Changes from 7 commits
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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2233,3 +2233,5 @@ add_subdirectory_ifdef(
CONFIG_MAKEFILE_EXPORTS
cmake/makefile_exports
)

toolchain_linker_finalize()
2 changes: 2 additions & 0 deletions cmake/compiler/clang/compiler_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,5 @@ set_compiler_property(PROPERTY warning_error_coding_guideline
)

set_compiler_property(PROPERTY no_global_merge "-mno-global-merge")

set_compiler_property(PROPERTY specs)
40 changes: 38 additions & 2 deletions cmake/compiler/clang/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,42 @@ if(NOT "${ARCH}" STREQUAL "posix")
endif()
endif()

# LLVM will use a default sysroot for selection of the C library. The default
# C library sysroot was defined at built time of clang/LLVM.
#
# For example, LLVM for Arm comes pre-built with Picolibc, and thus no flags
# are required for selecting Picolibc.
#
# Other clang/LLVM distributions may come with other pre-built C libraries.
# clang/LLVM supports using an alternative C library, either by direct linking,
# or by specifying '--sysroot <path>'.
#
# LLVM for Arm provides a 'newlib.cfg' file for newlib C selection.
# Let us support this principle by looking for a dedicated 'newlib.cfg' or
# 'picolibc.cfg' and specify '--config <spec>.cfg' if such a file is found.
# If no cfg-file matching the chosen C implementation, then we assume that the
# chosen C implementation is identical to the default C library used be the
# toolchain.
if(CONFIG_NEWLIB_LIBC)
file(GLOB_RECURSE newlib_cfg ${LLVM_TOOLCHAIN_PATH}/newlib.cfg)
if(newlib_cfg)
list(GET newlib_cfg 0 newlib_cfg)
set_linker_property(PROPERTY c_library "--config=${newlib_cfg};-lc")
list(APPEND CMAKE_REQUIRED_FLAGS --config=${newlib_cfg})
list(APPEND TOOLCHAIN_C_FLAGS --config=${newlib_cfg})
endif()
endif()

if(CONFIG_PICOLIBC)
file(GLOB_RECURSE picolibc_cfg ${LLVM_TOOLCHAIN_PATH}/picolibc.cfg)
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe share the --specs mechanism somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see other answer related to this. But good to have in mind for further improvements 👍

if(picolibc_cfg)
list(GET picolibc_cfg 0 picolibc_cfg)
set_linker_property(PROPERTY c_library "--config=${picolibc_cfg};-lc")
list(APPEND CMAKE_REQUIRED_FLAGS --config=${picolibc_cfg})
list(APPEND TOOLCHAIN_C_FLAGS --config=${picolibc_cfg})
endif()
endif()

# This libgcc code is partially duplicated in compiler/*/target.cmake
execute_process(
COMMAND ${CMAKE_C_COMPILER} ${clang_target_flag} ${TOOLCHAIN_C_FLAGS}
Expand All @@ -76,8 +112,8 @@ if(NOT "${ARCH}" STREQUAL "posix")
get_filename_component(RTLIB_NAME_WITH_PREFIX ${RTLIB_FILE_NAME} NAME_WLE)
string(REPLACE lib "" RTLIB_NAME ${RTLIB_NAME_WITH_PREFIX})

list(APPEND LIB_INCLUDE_DIR -L${RTLIB_DIR})
list(APPEND TOOLCHAIN_LIBS ${RTLIB_NAME})
set_property(TARGET linker PROPERTY lib_include_dir "-L${RTLIB_DIR}")
set_property(TARGET linker PROPERTY rt_library "-l${RTLIB_NAME}")

list(APPEND CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib ${isystem_include_flags})
string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
Expand Down
4 changes: 4 additions & 0 deletions cmake/compiler/compiler_flags_template.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,7 @@ set_compiler_property(PROPERTY warning_shadow_variables)
# Compiler flags to avoid recognizing built-in functions
set_compiler_property(PROPERTY no_builtin)
set_compiler_property(PROPERTY no_builtin_malloc)

# Compiler flag for defining specs. Used only by gcc, other compilers may keep
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we eventually plan on supporting clang's --config using this same mechanism?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking of that, and some clang/LLVM releases, like LLVM for Arm, does provide dedicated configs for this, but i'm unsure how well established / standardized this principle is, so for a start it's kept a bit independent in cmake/compiler/clang/target.cmake.
and also because it's needed for the existing compile check functionality.

# this undefined.
set_compiler_property(PROPERTY specs)
2 changes: 2 additions & 0 deletions cmake/compiler/gcc/compiler_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,5 @@ set_compiler_property(PROPERTY warning_shadow_variables -Wshadow)

set_compiler_property(PROPERTY no_builtin -fno-builtin)
set_compiler_property(PROPERTY no_builtin_malloc -fno-builtin-malloc)

set_compiler_property(PROPERTY specs -specs=)
3 changes: 1 addition & 2 deletions cmake/compiler/gcc/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ get_filename_component(LIBGCC_DIR ${LIBGCC_FILE_NAME} DIRECTORY)

assert_exists(LIBGCC_DIR)

LIST(APPEND LIB_INCLUDE_DIR "-L\"${LIBGCC_DIR}\"")
LIST(APPEND TOOLCHAIN_LIBS gcc)
set_linker_property(PROPERTY lib_include_dir "-L\"${LIBGCC_DIR}\"")

# For CMake to be able to test if a compiler flag is supported by the
# toolchain we need to give CMake the necessary flags to compile and
Expand Down
6 changes: 0 additions & 6 deletions cmake/linker/armlink/linker_flags.cmake

This file was deleted.

8 changes: 8 additions & 0 deletions cmake/linker/armlink/linker_libraries.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0

# Per default armclang (Arm Compiler 6) doesn't need explicit C library linking
# so we only need to set link order linking in case a custom C library is linked
# in, such as picolibc.
set_property(TARGET linker APPEND PROPERTY link_order_library "c;rt")
18 changes: 18 additions & 0 deletions cmake/linker/armlink/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,23 @@ function(toolchain_ld_link_elf)
)
endfunction(toolchain_ld_link_elf)

# This function will generate the correct CMAKE_C_LINK_EXECUTABLE / CMAKE_CXX_LINK_EXECUTABLE
# rule to ensure that standard c and runtime libraries are correctly placed
# and the end of link invocation and doesn't appear in the middle of the link
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this explicit ordering working around some gaps in the dependency graph? Would cmake automatically move those libraries to the correct location if the graph was complete?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this explicit ordering working around some gaps in the dependency graph?

yes, what we are ensuring here is that we link the std libs at the exact location where they are required and not being relocated by CMake's internal logic.

This is what you usually do in a CMake toolchain file using CMAKE_<lang>_LINK_EXECUTABLE when building for bare-metal.

At the same time the use of properties on the linker target follows the design of compiler and linker flags and allow toolchains a uniform way of describing how standard libraries are selected and in which order (even allowing multiple times without de-duplication).

The toolchain can describe a default set, while still allowing Zephyr and Zephyr module to adjust this set (and link order) if there is a need, for example based on Kconfig settings or static libs.
Examples are:

  • newlib Toolchain built-in
  • picolibc built as module.

Followup to this work will be some proper documentation regarding Zephyr's toolchain infrastructure so that you should be able to create oot toolchain configurations, and even upstream those.

Would cmake automatically move those libraries to the correct location if the graph was complete?

The problem is that this graph will never be complete.
As you have experienced, then it would require every static library created to know that it wants a specific link, like
target_link_libraries(foo PUBLIC gcc)

but that is not scalable and actually makes it harder to create static libs.
Technically we could wrap this for Zephyr libs, but what about libs created by regular add_library() CMake calls.
Also such solution would make it much harder to integrate new toolchains in future.

From design perspective, then such library link information is not something a library should ever care about.
This is also a reason why you'd normally place this in a CMake Toolchain file.
CMake toolchain files are fine for well-known project / a project with well-defined boundries but really not flexible enough for a project like Zephyr which tries to span so many use-cases and where things are flexible and can be configured through Kconfig.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I managed to construct a complete graph in #65434, but the results were not terribly robust. In any case, I think this change is a good step forward.

# command invocation.
macro(toolchain_linker_finalize)
set(zephyr_std_libs)
get_property(link_order TARGET linker PROPERTY link_order_library)
foreach(lib ${link_order})
get_property(link_flag TARGET linker PROPERTY ${lib}_library)
set(zephyr_std_libs "${zephyr_std_libs} ${link_flag}")
endforeach()

set(common_link "<LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> ${zephyr_std_libs} -o <TARGET>")
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_C_LINK_FLAGS> ${common_link}")
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> ${common_link}")
set(CMAKE_ASM_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_ASM_LINK_FLAGS> ${common_link}")
endmacro()

include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake)
include(${ZEPHYR_BASE}/cmake/linker/ld/target_configure.cmake)
2 changes: 2 additions & 0 deletions cmake/linker/ld/gcc/linker_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ add_link_options(-gdwarf-4)

# Extra warnings options for twister run
set_property(TARGET linker PROPERTY warnings_as_errors -Wl,--fatal-warnings)

set_linker_property(PROPERTY specs -specs=)
4 changes: 0 additions & 4 deletions cmake/linker/ld/linker_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ check_set_linker_property(TARGET linker PROPERTY base
${LINKERFLAGPREFIX},--build-id=none
)

if(NOT CONFIG_MINIMAL_LIBCPP AND NOT CONFIG_NATIVE_LIBRARY AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP)
set_property(TARGET linker PROPERTY cpp_base -lstdc++)
endif()

check_set_linker_property(TARGET linker PROPERTY baremetal
-nostdlib
-static
Expand Down
38 changes: 38 additions & 0 deletions cmake/linker/ld/linker_libraries.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0

# Do not specify default link libraries when targeting host (native build).
if(NOT CONFIG_NATIVE_BUILD)
set_linker_property(NO_CREATE PROPERTY c_library "-lc")
set_linker_property(NO_CREATE PROPERTY rt_library "-lgcc")
set_linker_property(NO_CREATE PROPERTY c++_library "-lstdc++")
set_linker_property(NO_CREATE PROPERTY math_library "-lm")
# Keeping default include dir empty. The linker will then select libraries
# from its default search path. The toolchain may adjust the value to a
# specific location, for example gcc infrastructure will set the value based
# on output from --print-libgcc-file-name.
set_linker_property(NO_CREATE PROPERTY lib_include_dir "")
endif()

if(CONFIG_CPP
AND NOT CONFIG_MINIMAL_LIBCPP
AND NOT CONFIG_NATIVE_LIBRARY
# When new link principle is fully introduced, then the below condition can
# be removed, and instead the external module c++ should use:
# set_property(TARGET linker PROPERTY c++_library "<external_c++_lib>")
AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP
)
set_property(TARGET linker PROPERTY link_order_library "c++")
endif()


if(CONFIG_NEWLIB_LIBC AND CMAKE_C_COMPILER_ID STREQUAL "GNU")
# We are using c;rt;c (expands to '-lc -lgcc -lc') in code below.
# This is needed because when linking with newlib on aarch64, then libgcc has a
# link dependency to libc (strchr), but libc also has dependencies to libgcc.
# Lib C depends on libgcc. e.g. libc.a(lib_a-fvwrite.o) references __aeabi_idiv
set_property(TARGET linker APPEND PROPERTY link_order_library "math;c;rt;c")
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be reasonable to use --start-group -lc -lgcc --end-group instead of listing libc twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally I think that it's good to make it visible that there are indeed circular dependencies, and that we can see the number of loops.
--start/end-group are useful when you really start to have complex circular dependencies, but it easily turn into cargo-cult which no one dares to cleanup, and in future easily ends in a bunch of libs inside those flags.

As side-note, from the ld manual:

--start-group archives --end-group
...
Using this option has a significant performance cost. It is best to use it only when there are unavoidable circular references between two or more archives.

else()
set_property(TARGET linker APPEND PROPERTY link_order_library "c;rt")
endif()
57 changes: 38 additions & 19 deletions cmake/linker/ld/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@ set(CMAKE_LINKER ${GNULD_LINKER})

set_ifndef(LINKERFLAGPREFIX -Wl)

if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host")
if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR)
# When building with C++ Exceptions, it is important that crtbegin and crtend
# are linked at specific locations.
# The location is so important that we cannot let this be controlled by normal
# link libraries, instead we must control the link command specifically as
# part of toolchain.
set(CMAKE_CXX_LINK_EXECUTABLE
"<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${LIBGCC_DIR}/crtend.o")
endif()
if((${CMAKE_LINKER} STREQUAL "${CROSS_COMPILE}ld.bfd") OR
${GNULD_LINKER_IS_BFD})
# ld.bfd was found so let's explicitly use that for linking, see #32237
list(APPEND TOOLCHAIN_LD_FLAGS -fuse-ld=bfd)
list(APPEND CMAKE_REQUIRED_FLAGS -fuse-ld=bfd)
string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
endif()

# Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen}
Expand Down Expand Up @@ -117,16 +113,9 @@ function(toolchain_ld_link_elf)
${ARGN} # input args to parse
)

if((${CMAKE_LINKER} STREQUAL "${CROSS_COMPILE}ld.bfd") OR
${GNULD_LINKER_IS_BFD})
# ld.bfd was found so let's explicitly use that for linking, see #32237
set(use_linker "-fuse-ld=bfd")
endif()

target_link_libraries(
${TOOLCHAIN_LD_LINK_ELF_TARGET_ELF}
${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_PRE_SCRIPT}
${use_linker}
${TOPT}
${TOOLCHAIN_LD_LINK_ELF_LINKER_SCRIPT}
${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_POST_SCRIPT}
Expand All @@ -137,14 +126,44 @@ function(toolchain_ld_link_elf)
${LINKERFLAGPREFIX},--no-whole-archive
${NO_WHOLE_ARCHIVE_LIBS}
$<TARGET_OBJECTS:${OFFSETS_LIB}>
${LIB_INCLUDE_DIR}
-L${PROJECT_BINARY_DIR}
${TOOLCHAIN_LIBS}
Copy link
Contributor

Choose a reason for hiding this comment

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

Now -lhal is missing when using xt-xcc and compilation fails with undefined xthal_window_spill (xthal_window_spill is provided by the xt-xcc toolchain in libhal.a)

If I restore this specific line then xt-xcc compilation succeeds again.

I wonder why was this ${TOOLCHAIN_LIBS} line removed from SOME target.cmake files by commit 2e3873a but as of today's commit 27456ed it is still present in the following files? This seems inconsistent.

cmake/compiler/icx/target.cmake
cmake/linker/arcmwdt/target.cmake
cmake/linker/xt-ld/target.cmake

Indeed, if I remove ${TOOLCHAIN_LIBS} from cmake/linker/xt-ld/target.cmake then xt-clang fails with the same error.

cc: @kv2019i , @andyross, @cujomalainey, @dcpleung

FYI:

cmake/compiler/xcc/target.cmake:list(APPEND TOOLCHAIN_LIBS
cmake/compiler/xcc/target.cmake-  gcc
cmake/compiler/xcc/target.cmake-  hal
cmake/compiler/xcc/target.cmake-  )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

an oversight.

I initially wanted to still allow for the old use of TOOLCHAIN_LIBS for toolchains I didn't have available, but later changes related to picolibc has actually resulted in the situation that all toolchains must be updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @tejlmand !

actually resulted in the situation that all toolchains must be updated.

While @dcpleung is removing the obsolete TOOLCHAIN_LIBS for Xtensa in #79731, as of today I see it still present in the main branch for cmake/compiler/icx/target.cmake and cmake/linker/arcmwdt/target.cmake. I think it would be good to file a bug and use git blame to warn the owners of these files that TOOLCHAIN_LIBS does not work anymore in case they still try and fail to use it downstream somehow. Something I did not mention before: it took me HOURS to debug this Xtensa compilation failure and find that it was down to this TOOLCHAIN_LIBS variable. Funny enough, git grep hal does not work very well! It would be nice to avoid others' a similar... effort.

In case these toolchains have no owner left, then it is a great opportunity to notice that and delete them entirely.

There is also a mysterious TOOLCHAIN_LIBS_OBJECTS in cmake/linker/armlink/target.cmake... My 2 cents.


${TOOLCHAIN_LD_LINK_ELF_DEPENDENCIES}
)
endfunction(toolchain_ld_link_elf)

# Function for finalizing link setup after Zephyr configuration has completed.
#
# This function will generate the correct CMAKE_C_LINK_EXECUTABLE / CMAKE_CXX_LINK_EXECUTABLE
# rule to ensure that standard c and runtime libraries are correctly placed
# and the end of link invocation and doesn't appear in the middle of the link
# command invocation.
macro(toolchain_linker_finalize)
get_property(zephyr_std_libs TARGET linker PROPERTY lib_include_dir)
get_property(link_order TARGET linker PROPERTY link_order_library)
foreach(lib ${link_order})
get_property(link_flag TARGET linker PROPERTY ${lib}_library)
list(APPEND zephyr_std_libs "${link_flag}")
endforeach()
string(REPLACE ";" " " zephyr_std_libs "${zephyr_std_libs}")

set(link_libraries "<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${zephyr_std_libs}")
set(common_link "<LINK_FLAGS> ${link_libraries}")

set(CMAKE_ASM_LINK_EXECUTABLE "<CMAKE_ASM_COMPILER> <FLAGS> <CMAKE_ASM_LINK_FLAGS> ${common_link}")
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> ${common_link}")

set(cpp_link "${common_link}")
if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host")
if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR)
# When building with C++ Exceptions, it is important that crtbegin and crtend
# are linked at specific locations.
set(cpp_link "<LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o ${link_libraries} ${LIBGCC_DIR}/crtend.o")
endif()
endif()
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${cpp_link}")
endmacro()

# Load toolchain_ld-family macros
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_relocation.cmake)
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_configure.cmake)
4 changes: 4 additions & 0 deletions cmake/linker/linker_flags_template.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ set_property(TARGET linker PROPERTY no_relax)

# Linker flag for enabling relaxation of address optimization for jump calls.
set_property(TARGET linker PROPERTY relax)

# Linker flag for defining specs. Defined only by gcc, when gcc is used as
# front-end for ld.
set_compiler_property(PROPERTY specs)
16 changes: 16 additions & 0 deletions cmake/linker/linker_libraries_template.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0

# Linker flags for fixed linking with standard libraries, such as the C and runtime libraries.
# It is the responsibility of the linker infrastructure to use those properties to specify the
# correct placement of those libraries for correct link order.
# For example, GCC usually has the order: -lc -lgcc
# It is also possible to define extra libraries of the form `<name>_library`, and then include
# Fixed library search path can be defined in the `lib_include_dir` property if needed.
# <name> in the link_order_property.
# Usage example:
# set_linker_property(PROPERTY lib_include_dir "-L/path/to/libs")
# set_linker_property(PROPERTY c_library "-lc")
# set_linker_property(PROPERTY rt_library "-lgcc")
# set_linker_property(PROPERTY link_order_library "c;rt")
4 changes: 3 additions & 1 deletion cmake/linker/lld/linker_flags.cmake
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Copyright (c) 2022 Google LLC
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0

# Since lld is a drop in replacement for ld, we can just use ld's flags as a base
# and adjust for lld specifics afterwards.
include(${ZEPHYR_BASE}/cmake/linker/ld/linker_flags.cmake OPTIONAL)

if(NOT CONFIG_MINIMAL_LIBCPP AND NOT CONFIG_NATIVE_LIBRARY AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP)
set_property(TARGET linker PROPERTY cpp_base -lc++ ${LINKERFLAGPREFIX},-z,norelro)
set_property(TARGET linker PROPERTY cpp_base ${LINKERFLAGPREFIX},-z,norelro)
endif()

# Force LLVM to use built-in lld linker
Expand Down
21 changes: 21 additions & 0 deletions cmake/linker/lld/linker_libraries.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0

set_linker_property(NO_CREATE TARGET linker PROPERTY c_library "-lc")
# Default per standard, will be populated by clang/target.cmake based on clang output.
set_linker_property(NO_CREATE TARGET linker PROPERTY rt_library "")
set_linker_property(TARGET linker PROPERTY c++_library "-lc++;-lc++abi")
Copy link
Contributor

Choose a reason for hiding this comment

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

should this mention math_library somehow? Either noting that it's not needed or setting to the empty string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

II don't think so.
The lib names are not fixed in that sense, a linker implementation can define extra libraries to always link in, as described here:

# It is also possible to define extra libraries of the form `<name>_library`, and then include
# Fixed library search path can be defined in the `lib_include_dir` property if needed.

I will be working on a docset to describe the whole toolchain infrastructure and how to implement support for a custom one.
And there the math lib could be a good candidate to show.


if(CONFIG_CPP
AND NOT CONFIG_MINIMAL_LIBCPP
AND NOT CONFIG_NATIVE_LIBRARY
# When new link principle is fully introduced, then the below condition can
# be removed, and instead the external module c++ should use:
# set_property(TARGET linker PROPERTY c++_library "<external_c++_lib>")
AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP
)
set_property(TARGET linker PROPERTY link_order_library "c++")
endif()

set_property(TARGET linker APPEND PROPERTY link_order_library "c;rt")
26 changes: 24 additions & 2 deletions cmake/linker/lld/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ set(CMAKE_LINKER ${LLVMLLD_LINKER})

set_ifndef(LINKERFLAGPREFIX -Wl)

list(APPEND TOOLCHAIN_LD_FLAGS -fuse-ld=lld)
list(APPEND CMAKE_REQUIRED_FLAGS -fuse-ld=lld)
string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")

# Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen}
# NOTE: ${linker_script_gen} will be produced at build-time; not at configure-time
macro(configure_linker_script linker_script_gen linker_pass_define)
Expand Down Expand Up @@ -97,14 +101,32 @@ function(toolchain_ld_link_elf)
${LINKERFLAGPREFIX},--no-whole-archive
${NO_WHOLE_ARCHIVE_LIBS}
$<TARGET_OBJECTS:${OFFSETS_LIB}>
${LIB_INCLUDE_DIR}
-L${PROJECT_BINARY_DIR}
${TOOLCHAIN_LIBS}

${TOOLCHAIN_LD_LINK_ELF_DEPENDENCIES}
)
endfunction(toolchain_ld_link_elf)

# Function for finalizing link setup after Zephyr configuration has completed.
#
# This function will generate the correct CMAKE_C_LINK_EXECUTABLE / CMAKE_CXX_LINK_EXECUTABLE
# signature to ensure that standard c and runtime libraries are correctly placed
# and the end of link invocation and doesn't appear in the middle of the link
# command invocation.
macro(toolchain_linker_finalize)
get_property(zephyr_std_libs TARGET linker PROPERTY lib_include_dir)
get_property(link_order TARGET linker PROPERTY link_order_library)
foreach(lib ${link_order})
get_property(link_flag TARGET linker PROPERTY ${lib}_library)
list(APPEND zephyr_std_libs "${link_flag}")
endforeach()
string(REPLACE ";" " " zephyr_std_libs "${zephyr_std_libs}")

set(common_link "<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${zephyr_std_libs}")
set(CMAKE_ASM_LINK_EXECUTABLE "<CMAKE_ASM_COMPILER> <FLAGS> <CMAKE_ASM_LINK_FLAGS> ${common_link}")
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> ${common_link}")
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${common_link}")
endmacro()

# Load toolchain_ld-family macros
include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake)
Expand Down
13 changes: 13 additions & 0 deletions cmake/linker/target_template.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2024, Nordic Semiconductor ASA

# Template file for optional Zephyr linker macros.
#
# This file will define optional linker macros for toolchains that are not
# defining these macros themselves.

if(NOT COMMAND toolchain_linker_finalize)
macro(toolchain_linker_finalize)
endmacro()
endif()
Loading