zephyr/cmake/modules/FindGnuLd.cmake
Daniel Leung 2d78cb74bd cmake: modules/FindGnuLd: cache variable GNULD_VERSION_STRING
This changes to cache variable GNULD_VERSION_STRING across cmake
runs. This variable is used to determine whether -Wl,-no-pie is
being passed to linker. However, if cmake is run multiple times
without clearing the build directory, GNULD_VERSION_STRING was
lost and the script falsely assumed the linker could not take
this argument, and thus omitting it during linking. Depending on
the host, it would warn on something like this:

  /usr/bin/ld.bfd: app/libapp.a(main.c.obj):
      warning: relocation in read-only section
      `.text._posix_zephyr_main'
  /usr/bin/ld.bfd: warning: creating DT_TEXTREL in a PIE

To fix this, simply caches GNULD_VERSION_STRING so it can be
used during subsequent cmake runs.

Fixes #61725

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2023-09-01 09:02:38 -04:00

100 lines
3.2 KiB
CMake

# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2022, Nordic Semiconductor ASA
# Copyright (c) 2023, Intel Corporation
# FindGnuLd module for locating GNU ld (linker from binutils).
#
# The module defines the following variables:
#
# 'GNULD_LINKER'
# Path to GNU ld linker
# Set to 'GNULD_LINKER-NOTFOUND' if ld was not found.
#
# 'GnuLd_FOUND', 'GNULD_FOUND'
# True if GNU ld was found.
#
# 'GNULD_VERSION_STRING'
# The version of GNU ld.
#
# 'GNULD_LINKER_IS_BFD'
# True if linker is ld.bfd (or compatible)
#
# Note that this will use CROSS_COMPILE, if defined,
# as a prefix to the linker executable.
include(FindPackageHandleStandardArgs)
# GNULD_LINKER exists on repeated builds or defined manually...
if(EXISTS "${GNULD_LINKER}")
if(NOT DEFINED GNULD_LINKER_IS_BFD)
# ... issue warning if GNULD_LINKER_IS_BFD is not already set.
message(
WARNING
"GNULD_LINKER specified directly in cache, but GNULD_LINKER_IS_BFD is not "
"defined. Assuming GNULD_LINKER_IS_BFD as OFF, please set GNULD_LINKER_IS_BFD "
"to correct value in cache to silence this warning"
)
set(GNULD_LINKER_IS_BFD OFF)
endif()
# Since GNULD_LINKER already exists, there is no need to find it again (below).
return()
endif()
# See if the compiler has a preferred linker
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-prog-name=ld.bfd
OUTPUT_VARIABLE GNULD_LINKER
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(EXISTS "${GNULD_LINKER}")
set(GNULD_LINKER_IS_BFD ON CACHE BOOL "Linker BFD compatibility (compiler reported)" FORCE)
else()
# Need to clear it or else find_program() won't replace the value.
set(GNULD_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(LD_SEARCH_PATH PATHS ${TOOLCHAIN_HOME} NO_DEFAULT_PATH)
endif()
find_program(GNULD_LINKER ${CROSS_COMPILE}ld.bfd ${LD_SEARCH_PATH})
if(GNULD_LINKER)
set(GNULD_LINKER_IS_BFD ON CACHE BOOL "Linker BFD compatibility (inferred from binary)" FORCE)
else()
find_program(GNULD_LINKER ${CROSS_COMPILE}ld ${LD_SEARCH_PATH})
set(GNULD_LINKER_IS_BFD OFF CACHE BOOL "Linker BFD compatibility (inferred from binary)" FORCE)
endif()
endif()
if(GNULD_LINKER)
# Parse the 'ld.bfd --version' output to find the installed version.
execute_process(
COMMAND
${GNULD_LINKER} --version
OUTPUT_VARIABLE gnuld_version_output
ERROR_VARIABLE gnuld_error_output
RESULT_VARIABLE gnuld_status
)
if(${gnuld_status} EQUAL 0)
# Extract GNU ld version. Different distros have their
# own version scheme so we need to account for that.
# Examples:
# - "GNU ld (GNU Binutils for Ubuntu) 2.34"
# - "GNU ld (Zephyr SDK 0.15.2) 2.38"
# - "GNU ld (Gentoo 2.39 p5) 2.39.0"
string(REGEX MATCH
"GNU ld \\(.+\\) ([0-9]+[.][0-9]+[.]?[0-9]*).*"
out_var ${gnuld_version_output})
set(GNULD_VERSION_STRING ${CMAKE_MATCH_1} CACHE STRING "GNU ld version" FORCE)
endif()
endif()
find_package_handle_standard_args(GnuLd
REQUIRED_VARS GNULD_LINKER
VERSION_VAR GNULD_VERSION_STRING
)