Merge branch 'lld_orphan' into 'master'

esp_system: enable linking with LLD on RISC-V targets

Closes LLVM-216

See merge request espressif/esp-idf!48712
This commit is contained in:
morris
2026-08-17 14:40:36 +08:00
17 changed files with 147 additions and 11 deletions
+36 -1
View File
@@ -1053,8 +1053,42 @@ macro(project project_name)
target_link_options(${project_elf} PRIVATE "-Wl,--defsym=IDF_TARGET_${idf_target}=0")
# Enable map file output
target_link_options(${project_elf} PRIVATE "-Wl,--Map=${mapfile}")
if(CONFIG_COMPILER_USE_LLD)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
# The last --ld-path on the link line takes precedence over the
# one set in the toolchain file, selecting LLD for the link.
# The bare name is looked up in PATH.
target_link_options(${project_elf} PRIVATE "--ld-path=ld.lld")
else()
# GCC has no --ld-path; -fuse-ld=lld makes the driver search
# its search paths and PATH for a binary named "ld.lld".
target_link_options(${project_elf} PRIVATE "-fuse-ld=lld")
endif()
# Bare-metal images have no dynamic loader, so PT_GNU_RELRO is
# meaningless. LLD enables relro by default and errors out when
# relro sections (e.g. TLS .flash.tdata) are not contiguous in the
# IDF linker scripts, so disable it.
target_link_options(${project_elf} PRIVATE "-Wl,-z,norelro")
# Some targets (e.g. esp32s3) use NOLOAD dummy sections that
# deliberately overlap other sections' address ranges, because two
# memory regions alias the same bus address space. GNU ld skips
# NOBITS sections in its overlap check; LLD does not, so disable
# the check there.
target_link_options(${project_elf} PRIVATE "-Wl,--no-check-sections")
set(linker_binary "ld.lld")
else()
set(linker_binary "${CMAKE_LINKER}")
endif()
# Report which linker the link will use, with its version banner
execute_process(COMMAND ${linker_binary} "--version"
OUTPUT_VARIABLE linker_version
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX REPLACE "\n.*" "" linker_version "${linker_version}")
message(STATUS "Linker: ${linker_binary} (${linker_version})")
unset(linker_version)
# Check if linker supports --no-warn-rwx-segments
execute_process(COMMAND ${CMAKE_LINKER} "--no-warn-rwx-segments" "--version"
execute_process(COMMAND ${linker_binary} "--no-warn-rwx-segments" "--version"
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_QUIET)
@@ -1069,6 +1103,7 @@ macro(project project_name)
# Throw error if orphan sections are found
target_link_options(${project_elf} PRIVATE "-Wl,--orphan-handling=error")
endif()
unset(linker_binary)
unset(idf_target)
endif()
+4 -1
View File
@@ -225,7 +225,10 @@ function(preprocess_linker_file cmake_target script_in output_var preserve_suffi
if(script_parent_name STREQUAL idf_target)
# Add "../.." directory to include path (e.g. for esp_system component)
get_filename_component(dir_to_include "${script_parent_dir}" DIRECTORY)
set(extra_cflags "-I\"${dir_to_include}\"")
# Add esp_system/ld directory to include path for the shared linker
# script fragments (e.g. ld.elf.internal.sections)
idf_component_get_property(esp_system_dir esp_system COMPONENT_DIR)
set(extra_cflags "-I\"${dir_to_include}\" -I\"${esp_system_dir}/ld\"")
endif()
# Keep comments (-C): historical behavior for cmakev1 linker scripts. It was
+35 -1
View File
@@ -512,8 +512,42 @@ function(__init_project_configuration)
string(TOUPPER ${target_upper} target_upper)
# Add this symbol as a hint for esp_idf_size to guess the target name
list(APPEND link_options "-Wl,--defsym=IDF_TARGET_${target_upper}=0")
if(CONFIG_COMPILER_USE_LLD)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
# The last --ld-path on the link line takes precedence over the
# one set in the toolchain file, selecting LLD for the link.
# The bare name is looked up in PATH.
list(APPEND link_options "--ld-path=ld.lld")
else()
# GCC has no --ld-path; -fuse-ld=lld makes the driver search
# its search paths and PATH for a binary named "ld.lld".
list(APPEND link_options "-fuse-ld=lld")
endif()
# Bare-metal images have no dynamic loader, so PT_GNU_RELRO is
# meaningless. LLD enables relro by default and errors out when
# relro sections (e.g. TLS .flash.tdata) are not contiguous in the
# IDF linker scripts, so disable it.
list(APPEND link_options "-Wl,-z,norelro")
# Some targets (e.g. esp32s3) use NOLOAD dummy sections that
# deliberately overlap other sections' address ranges, because two
# memory regions alias the same bus address space. GNU ld skips
# NOBITS sections in its overlap check; LLD does not, so disable
# the check there.
list(APPEND link_options "-Wl,--no-check-sections")
set(linker_binary "ld.lld")
else()
set(linker_binary "${CMAKE_LINKER}")
endif()
# Report which linker the link will use, with its version banner
execute_process(COMMAND ${linker_binary} "-v"
OUTPUT_VARIABLE linker_version
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX REPLACE "\n.*" "" linker_version "${linker_version}")
message(STATUS "Linker: ${linker_binary} (${linker_version})")
unset(linker_version)
# Check if linker supports --no-warn-rwx-segments
execute_process(COMMAND ${CMAKE_LINKER} "--no-warn-rwx-segments" "--version"
execute_process(COMMAND ${linker_binary} "--no-warn-rwx-segments" "--version"
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_QUIET)
+6 -1
View File
@@ -1268,12 +1268,17 @@ function(__preprocess_linker_script script_in script_out flags component_include
# ld.common file, so add the parent directory to the C preprocessor
# search path. Works for that layout; a component with a different
# layout, or one that must drop -C, should pass FLAGS instead.
# esp_system/ld is added as well so that scripts of other components
# can include the shared fragments living there (e.g.
# ld.elf.internal.sections).
set(base_flags "-C")
get_filename_component(script_parent_dir "${script_in}" DIRECTORY)
get_filename_component(script_parent_name "${script_parent_dir}" NAME)
if(script_parent_name STREQUAL idf_target)
get_filename_component(dir_to_include "${script_parent_dir}" DIRECTORY)
string(APPEND base_flags " -I\"${dir_to_include}\"")
idf_component_get_property(esp_system_dir esp_system COMPONENT_DIR)
string(APPEND base_flags
" -I\"${dir_to_include}\" -I\"${esp_system_dir}/ld\"")
endif()
else()
# Explicit FLAGS replace the whole default set, including -C. config_dir
+2 -2
View File
@@ -51,9 +51,9 @@ tools/test_apps/system/build_tests/trax_esp32s2:
tools/test_apps/system/clang_build_test:
enable:
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2"]
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"]
temporary: true
reason: the other targets are not supported yet, esp32p4 # TODO: IDF-14355
reason: the other targets are not supported yet
tools/test_apps/system/cxx_no_except:
enable:
@@ -1,4 +1,4 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
This project is for testing if the application can be built with Clang toolchain.
@@ -0,0 +1,3 @@
# Build test: link with LLD instead of GNU ld (esp32p4 only)
CONFIG_IDF_TARGET="esp32p4"
CONFIG_COMPILER_USE_LLD=y