Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cmake/linker/eld/linker_flags.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
#
# SPDX-License-Identifier: Apache-2.0

# eld is mostly flag-compatible with ld, so use ld's flags as a base.
include(${ZEPHYR_BASE}/cmake/linker/ld/linker_flags.cmake)

set_property(TARGET linker PROPERTY no_position_independent "${LINKERFLAGPREFIX},-no-pie")

# ld sets gcc-specific flags, so clear this out.
set_property(TARGET linker PROPERTY lto_arguments)
set_property(TARGET linker PROPERTY lto_arguments_st)
7 changes: 7 additions & 0 deletions cmake/linker/eld/linker_libraries.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
#
# SPDX-License-Identifier: Apache-2.0

# Currently there isn't any need for eld-specific changes here, so just use
# lld's linker_libraries.
include(${ZEPHYR_BASE}/cmake/linker/lld/linker_libraries.cmake)
142 changes: 142 additions & 0 deletions cmake/linker/eld/target.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# SPDX-License-Identifier: Apache-2.0
set_property(TARGET linker PROPERTY devices_start_symbol "_device_list_start")

find_package(Eld 22.0 REQUIRED)
set(CMAKE_LINKER ${ELD_LINKER})

set_ifndef(LINKERFLAGPREFIX -Wl)

list(APPEND TOOLCHAIN_LD_FLAGS -fuse-ld=eld)
list(APPEND CMAKE_REQUIRED_FLAGS -fuse-ld=eld)
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)
set(extra_dependencies ${ARGN})
set(template_script_defines ${linker_pass_define})
list(TRANSFORM template_script_defines PREPEND "-D")

# Different generators deal with depfiles differently.
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
# Note that the IMPLICIT_DEPENDS option is currently supported only
# for Makefile generators and will be ignored by other generators.
set(linker_script_dep IMPLICIT_DEPENDS C ${LINKER_SCRIPT})
elseif(CMAKE_GENERATOR STREQUAL "Ninja")
# Using DEPFILE with other generators than Ninja is an error.
set(linker_script_dep DEPFILE ${PROJECT_BINARY_DIR}/${linker_script_gen}.dep)
else()
# TODO: How would the linker script dependencies work for non-linker
# script generators.
message(WARNING "This generator is not well supported. The
Linker script may not be regenerated when it should.")
set(linker_script_dep "")
endif()

zephyr_get_include_directories_for_lang(C current_includes)

add_custom_command(
OUTPUT ${linker_script_gen}
DEPENDS
${LINKER_SCRIPT}
${extra_dependencies}
# NB: 'linker_script_dep' will use a keyword that ends 'DEPENDS'
${linker_script_dep}
COMMAND ${CMAKE_C_COMPILER}
-x assembler-with-cpp
${NOSYSDEF_CFLAG}
-MD -MF ${linker_script_gen}.dep -MT ${linker_script_gen}
-D_LINKER
-D_ASMLANGUAGE
-D__ELD_LINKER_CMD__
-imacros ${AUTOCONF_H}
${current_includes}
${template_script_defines}
-E ${LINKER_SCRIPT}
-P # Prevent generation of debug `#line' directives.
-o ${linker_script_gen}
VERBATIM
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMAND_EXPAND_LISTS
)
endmacro()


# Force symbols to be entered in the output file as undefined symbols
function(toolchain_ld_force_undefined_symbols)
foreach(symbol ${ARGN})
zephyr_link_libraries(${LINKERFLAGPREFIX},-u,${symbol})
endforeach()
endfunction()

# Link a target to given libraries with toolchain-specific argument order
#
# Usage:
# toolchain_ld_link_elf(
# TARGET_ELF <target_elf>
# OUTPUT_MAP <output_map_file_of_target>
# LIBRARIES_PRE_SCRIPT [libraries_pre_script]
# LINKER_SCRIPT <linker_script>
# LIBRARIES_POST_SCRIPT [libraries_post_script]
# DEPENDENCIES [dependencies]
# )
function(toolchain_ld_link_elf)
cmake_parse_arguments(
TOOLCHAIN_LD_LINK_ELF # prefix of output variables
"" # list of names of the boolean arguments
"TARGET_ELF;OUTPUT_MAP;LINKER_SCRIPT" # list of names of scalar arguments
"LIBRARIES_PRE_SCRIPT;LIBRARIES_POST_SCRIPT;DEPENDENCIES" # list of names of list arguments
${ARGN} # input args to parse
)

target_link_libraries(
${TOOLCHAIN_LD_LINK_ELF_TARGET_ELF}
${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_PRE_SCRIPT}
${TOPT}
${TOOLCHAIN_LD_LINK_ELF_LINKER_SCRIPT}
${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_POST_SCRIPT}

${LINKERFLAGPREFIX},-Map=${TOOLCHAIN_LD_LINK_ELF_OUTPUT_MAP}
${LINKERFLAGPREFIX},--whole-archive
${WHOLE_ARCHIVE_LIBS}
${LINKERFLAGPREFIX},--no-whole-archive
${NO_WHOLE_ARCHIVE_LIBS}
$<TARGET_OBJECTS:${OFFSETS_LIB}>
-L${PROJECT_BINARY_DIR}

${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()

# Function to map compiler flags into suitable linker flags
# When using the compiler driver to run the linker, just pass
# them all through

function(toolchain_linker_add_compiler_options)
add_link_options(${ARGV})
endfunction()

# Load toolchain_ld-family macros
include(${ZEPHYR_BASE}/cmake/linker/ld/target_relocation.cmake)
include(${ZEPHYR_BASE}/cmake/linker/ld/target_configure.cmake)
64 changes: 64 additions & 0 deletions cmake/modules/FindEld.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2022, Nordic Semiconductor ASA
# Copyright (c) 2023, Intel Corporation
# Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.

# FindEld module for locating the eld linker.
#
# The module defines the following variables:
#
# 'ELD_LINKER'
# Path to eld linker
# Set to 'ELD_LINKER-NOTFOUND' if eld was not found.
#
# 'Eld_FOUND', 'ELD_FOUND'
# True if eld was found.
#
# 'ELD_VERSION_STRING'
# The version of eld.

include(FindPackageHandleStandardArgs)

# See if the compiler has a preferred linker
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-prog-name=ld.eld
OUTPUT_VARIABLE ELD_LINKER
OUTPUT_STRIP_TRAILING_WHITESPACE)

if(NOT EXISTS "${ELD_LINKER}")
# Need to clear it or else find_program() won't replace the value.
set(ELD_LINKER)

if(DEFINED TOOLCHAIN_HOME)
# Search for linker under TOOLCHAIN_HOME if it is defined
# to limit which linker to use, or else we would be using
# host tools.
set(ELD_SEARCH_PATH PATHS ${TOOLCHAIN_HOME} NO_DEFAULT_PATH)
endif()

find_program(ELD_LINKER ld.eld ${ELD_SEARCH_PATH})
endif()

if(ELD_LINKER)
# Parse the 'ld.eld --version' output to find the installed version.
execute_process(
COMMAND ${ELD_LINKER} --version
OUTPUT_VARIABLE eld_version_output
RESULT_VARIABLE eld_status
)

set(ELD_VERSION_STRING)
if(${eld_status} EQUAL 0)
string(REGEX
MATCH "eld ([0-9]+[.][0-9]+[.]?[0-9]*).*"
out_var ${eld_version_output}
)
set(ELD_VERSION_STRING ${CMAKE_MATCH_1})
endif()
endif()


find_package_handle_standard_args(Eld
REQUIRED_VARS ELD_LINKER
VERSION_VAR ELD_VERSION_STRING
)
5 changes: 5 additions & 0 deletions cmake/toolchain/llvm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ config LLVM_USE_LLD
help
Use LLVM built-in lld linker with llvm/clang.

config LLVM_USE_ELD
bool "ELD ld.eld"
help
Use the ELD linker (ld.eld) with llvm/clang.

endchoice

config TOOLCHAIN_LLVM_SUPPORTS_THREAD_LOCAL_STORAGE
Expand Down
2 changes: 2 additions & 0 deletions cmake/toolchain/llvm/target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ if(CONFIG_LLVM_USE_LD)
set(LINKER ld)
elseif(CONFIG_LLVM_USE_LLD)
set(LINKER lld)
elseif(CONFIG_LLVM_USE_ELD)
set(LINKER eld)
endif()

if("${ARCH}" STREQUAL "arm")
Expand Down
2 changes: 1 addition & 1 deletion include/zephyr/arch/arm/cortex_m/scripts/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ GROUP_END(ITCM)
* section overlap.
*/
__exidx_start = .;
#if defined (__GCC_LINKER_CMD__) || defined (__LLD_LINKER_CMD__)
#if defined (__GCC_LINKER_CMD__) || defined (__LLD_LINKER_CMD__) || defined(__ELD_LINKER_CMD__)
*(.ARM.exidx* gnu.linkonce.armexidx.*)
#endif
__exidx_end = .;
Expand Down
2 changes: 2 additions & 0 deletions include/zephyr/linker/linker-tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <zephyr/linker/linker-tool-mwdt.h>
#elif defined(__LLD_LINKER_CMD__)
#include <zephyr/linker/linker-tool-lld.h>
#elif defined(__ELD_LINKER_CMD__)
#include <zephyr/linker/linker-tool-gcc.h>
#else
#error "Unknown toolchain"
#endif
Expand Down
3 changes: 2 additions & 1 deletion include/zephyr/toolchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
#include <zephyr/toolchain/armclang.h>
#elif defined(__IAR_SYSTEMS_ICC__)
#include <zephyr/toolchain/iar.h>
#elif defined(__llvm__) || (defined(_LINKER) && defined(__LLD_LINKER_CMD__))
#elif defined(__llvm__) || \
(defined(_LINKER) && (defined(__LLD_LINKER_CMD__) || defined(__ELD_LINKER_CMD__)))
#include <zephyr/toolchain/llvm.h>
#elif defined(__GNUC__) || (defined(_LINKER) && defined(__GCC_LINKER_CMD__))
#include <zephyr/toolchain/gcc.h>
Expand Down
3 changes: 2 additions & 1 deletion scripts/ci/check_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -1558,10 +1558,11 @@ def check_no_undef_outside_kconfig(self, kconf):
"LIBGCC_RTLIB",
"LLEXT_EXPORT_SYMBOL_GROUP_", # Used in regexp by
# scripts/build/llext_inspect_discarded_groups.py
"LLVM_USE_LD", # Both LLVM_USE_* are in cmake/toolchain/llvm/Kconfig
"LLVM_USE_ELD", # All LLVM_USE_* are in cmake/toolchain/llvm/Kconfig
# which are only included if LLVM is selected but
# not other toolchains. Compliance check would complain,
# for example, if you are using GCC.
"LLVM_USE_LD",
"LLVM_USE_LLD",
"LOG_BACKEND_MOCK_OUTPUT_DEFAULT", # Referenced in tests/subsys/logging/log_syst
"LOG_BACKEND_MOCK_OUTPUT_SYST", # Referenced in testcase.yaml of log_syst test
Expand Down
3 changes: 2 additions & 1 deletion subsys/testsuite/include/zephyr/test_toolchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

#include <zephyr/toolchain.h>

#if defined(__llvm__) || (defined(_LINKER) && defined(__LLD_LINKER_CMD__))
#if defined(__llvm__) || \
(defined(_LINKER) && (defined(__LLD_LINKER_CMD__) || defined(__ELD_LINKER_CMD__)))
#include <zephyr/test_toolchain/llvm.h>
#elif defined(__GNUC__) || (defined(_LINKER) && defined(__GCC_LINKER_CMD__))
#include <zephyr/test_toolchain/gcc.h>
Expand Down