Skip to content

Commit 2e3873a

Browse files
tejlmandfabiobaltieri
authored andcommitted
cmake: improve Zephyr link phase
Zephyr is a bare metal build where standard libs are disabled. This means that c and runtime libraries must manually be linked in. This has generally been handled by using CMake's link libraries handling but the issue with that is both de-duplication but also library link order. Standard libraries must be linked at last location to ensure symbols are always available, however this is not optimal with target_link_libraries() because this would ultimately require every library to know the c library to link with, which is not desired. Therefore, setup standard C and runtime library linking in linker CMake files for toolchains where this is required. This commit expands the principle introduced with toolchain abstraction, see PR#24851. This means that a toolchain implementation may specify standard C, runtime, C++, etc libraries, as well as their link order. Because a property approach is used, then Zephyr modules, such as the Picolibc module can adjust such properties. An optional `zephyr_linker_finalize()` macro is called at the end of Zephyr's CMakeList process and can be used by the toolchain implementation to define the final linker invocation. This aligns the linker handling flow to the principle introduced in PR#24851 and improves the flexibility and robustness of Zephyr build system. Signed-off-by: Torsten Rasmussen <[email protected]>
1 parent 9fe6c5e commit 2e3873a

21 files changed

+234
-62
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,3 +2233,5 @@ add_subdirectory_ifdef(
22332233
CONFIG_MAKEFILE_EXPORTS
22342234
cmake/makefile_exports
22352235
)
2236+
2237+
toolchain_linker_finalize()

cmake/compiler/clang/target.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ if(NOT "${ARCH}" STREQUAL "posix")
7676
get_filename_component(RTLIB_NAME_WITH_PREFIX ${RTLIB_FILE_NAME} NAME_WLE)
7777
string(REPLACE lib "" RTLIB_NAME ${RTLIB_NAME_WITH_PREFIX})
7878

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

8282
list(APPEND CMAKE_REQUIRED_FLAGS -nostartfiles -nostdlib ${isystem_include_flags})
8383
string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")

cmake/compiler/gcc/target.cmake

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ get_filename_component(LIBGCC_DIR ${LIBGCC_FILE_NAME} DIRECTORY)
109109

110110
assert_exists(LIBGCC_DIR)
111111

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

115114
# For CMake to be able to test if a compiler flag is supported by the
116115
# toolchain we need to give CMake the necessary flags to compile and

cmake/linker/armlink/linker_flags.cmake

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2024 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Per default armclang (Arm Compiler 6) doesn't need explicit C library linking
6+
# so we only need to set link order linking in case a custom C library is linked
7+
# in, such as picolibc.
8+
set_property(TARGET linker APPEND PROPERTY link_order_library "c;rt")

cmake/linker/armlink/target.cmake

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,23 @@ function(toolchain_ld_link_elf)
108108
)
109109
endfunction(toolchain_ld_link_elf)
110110

111+
# This function will generate the correct CMAKE_C_LINK_EXECUTABLE / CMAKE_CXX_LINK_EXECUTABLE
112+
# rule to ensure that standard c and runtime libraries are correctly placed
113+
# and the end of link invocation and doesn't appear in the middle of the link
114+
# command invocation.
115+
macro(toolchain_linker_finalize)
116+
set(zephyr_std_libs)
117+
get_property(link_order TARGET linker PROPERTY link_order_library)
118+
foreach(lib ${link_order})
119+
get_property(link_flag TARGET linker PROPERTY ${lib}_library)
120+
set(zephyr_std_libs "${zephyr_std_libs} ${link_flag}")
121+
endforeach()
122+
123+
set(common_link "<LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> ${zephyr_std_libs} -o <TARGET>")
124+
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_C_LINK_FLAGS> ${common_link}")
125+
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> ${common_link}")
126+
set(CMAKE_ASM_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_ASM_LINK_FLAGS> ${common_link}")
127+
endmacro()
128+
111129
include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake)
112130
include(${ZEPHYR_BASE}/cmake/linker/ld/target_configure.cmake)

cmake/linker/ld/linker_flags.cmake

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ check_set_linker_property(TARGET linker PROPERTY base
77
${LINKERFLAGPREFIX},--build-id=none
88
)
99

10-
if(NOT CONFIG_MINIMAL_LIBCPP AND NOT CONFIG_NATIVE_LIBRARY AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP)
11-
set_property(TARGET linker PROPERTY cpp_base -lstdc++)
12-
endif()
13-
1410
check_set_linker_property(TARGET linker PROPERTY baremetal
1511
-nostdlib
1612
-static
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright (c) 2024 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Do not specify default link libraries when targeting host (native build).
6+
if(NOT CONFIG_NATIVE_BUILD)
7+
set_linker_property(NO_CREATE PROPERTY c_library "-lc")
8+
set_linker_property(NO_CREATE PROPERTY rt_library "-lgcc")
9+
set_linker_property(NO_CREATE PROPERTY c++_library "-lstdc++")
10+
set_linker_property(NO_CREATE PROPERTY math_library "-lm")
11+
# Keeping default include dir empty. The linker will then select libraries
12+
# from its default search path. The toolchain may adjust the value to a
13+
# specific location, for example gcc infrastructure will set the value based
14+
# on output from --print-libgcc-file-name.
15+
set_linker_property(NO_CREATE PROPERTY lib_include_dir "")
16+
endif()
17+
18+
if(CONFIG_CPP
19+
AND NOT CONFIG_MINIMAL_LIBCPP
20+
AND NOT CONFIG_NATIVE_LIBRARY
21+
# When new link principle is fully introduced, then the below condition can
22+
# be removed, and instead the external module c++ should use:
23+
# set_property(TARGET linker PROPERTY c++_library "<external_c++_lib>")
24+
AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP
25+
)
26+
set_property(TARGET linker PROPERTY link_order_library "c++")
27+
endif()
28+
29+
30+
if(CONFIG_NEWLIB_LIBC AND CMAKE_C_COMPILER_ID STREQUAL "GNU")
31+
# We are using c;rt;c (expands to '-lc -lgcc -lc') in code below.
32+
# This is needed because when linking with newlib on aarch64, then libgcc has a
33+
# link dependency to libc (strchr), but libc also has dependencies to libgcc.
34+
# Lib C depends on libgcc. e.g. libc.a(lib_a-fvwrite.o) references __aeabi_idiv
35+
set_property(TARGET linker APPEND PROPERTY link_order_library "math;c;rt;c")
36+
else()
37+
set_property(TARGET linker APPEND PROPERTY link_order_library "c;rt")
38+
endif()

cmake/linker/ld/target.cmake

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ if((${CMAKE_LINKER} STREQUAL "${CROSS_COMPILE}ld.bfd") OR
1414
string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
1515
endif()
1616

17-
if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host")
18-
if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR)
19-
# When building with C++ Exceptions, it is important that crtbegin and crtend
20-
# are linked at specific locations.
21-
# The location is so important that we cannot let this be controlled by normal
22-
# link libraries, instead we must control the link command specifically as
23-
# part of toolchain.
24-
set(CMAKE_CXX_LINK_EXECUTABLE
25-
"<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${LIBGCC_DIR}/crtend.o")
26-
endif()
27-
endif()
28-
2917
# Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen}
3018
# NOTE: ${linker_script_gen} will be produced at build-time; not at configure-time
3119
macro(configure_linker_script linker_script_gen linker_pass_define)
@@ -138,14 +126,44 @@ function(toolchain_ld_link_elf)
138126
${LINKERFLAGPREFIX},--no-whole-archive
139127
${NO_WHOLE_ARCHIVE_LIBS}
140128
$<TARGET_OBJECTS:${OFFSETS_LIB}>
141-
${LIB_INCLUDE_DIR}
142129
-L${PROJECT_BINARY_DIR}
143-
${TOOLCHAIN_LIBS}
144130

145131
${TOOLCHAIN_LD_LINK_ELF_DEPENDENCIES}
146132
)
147133
endfunction(toolchain_ld_link_elf)
148134

135+
# Function for finalizing link setup after Zephyr configuration has completed.
136+
#
137+
# This function will generate the correct CMAKE_C_LINK_EXECUTABLE / CMAKE_CXX_LINK_EXECUTABLE
138+
# rule to ensure that standard c and runtime libraries are correctly placed
139+
# and the end of link invocation and doesn't appear in the middle of the link
140+
# command invocation.
141+
macro(toolchain_linker_finalize)
142+
get_property(zephyr_std_libs TARGET linker PROPERTY lib_include_dir)
143+
get_property(link_order TARGET linker PROPERTY link_order_library)
144+
foreach(lib ${link_order})
145+
get_property(link_flag TARGET linker PROPERTY ${lib}_library)
146+
list(APPEND zephyr_std_libs "${link_flag}")
147+
endforeach()
148+
string(REPLACE ";" " " zephyr_std_libs "${zephyr_std_libs}")
149+
150+
set(link_libraries "<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${zephyr_std_libs}")
151+
set(common_link "<LINK_FLAGS> ${link_libraries}")
152+
153+
set(CMAKE_ASM_LINK_EXECUTABLE "<CMAKE_ASM_COMPILER> <FLAGS> <CMAKE_ASM_LINK_FLAGS> ${common_link}")
154+
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> ${common_link}")
155+
156+
set(cpp_link "${common_link}")
157+
if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host")
158+
if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR)
159+
# When building with C++ Exceptions, it is important that crtbegin and crtend
160+
# are linked at specific locations.
161+
set(cpp_link "<LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o ${link_libraries} ${LIBGCC_DIR}/crtend.o")
162+
endif()
163+
endif()
164+
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${cpp_link}")
165+
endmacro()
166+
149167
# Load toolchain_ld-family macros
150168
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_relocation.cmake)
151169
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_configure.cmake)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) 2024 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Linker flags for fixed linking with standard libraries, such as the C and runtime libraries.
6+
# It is the responsibility of the linker infrastructure to use those properties to specify the
7+
# correct placement of those libraries for correct link order.
8+
# For example, GCC usually has the order: -lc -lgcc
9+
# It is also possible to define extra libraries of the form `<name>_library`, and then include
10+
# Fixed library search path can be defined in the `lib_include_dir` property if needed.
11+
# <name> in the link_order_property.
12+
# Usage example:
13+
# set_linker_property(PROPERTY lib_include_dir "-L/path/to/libs")
14+
# set_linker_property(PROPERTY c_library "-lc")
15+
# set_linker_property(PROPERTY rt_library "-lgcc")
16+
# set_linker_property(PROPERTY link_order_library "c;rt")

0 commit comments

Comments
 (0)