1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (c) 2022, Nordic Semiconductor ASA
4# Copyright (c) 2023, Intel Corporation
5
6# FindGnuLd module for locating GNU ld (linker from binutils).
7#
8# The module defines the following variables:
9#
10# 'GNULD_LINKER'
11# Path to GNU ld linker
12# Set to 'GNULD_LINKER-NOTFOUND' if ld was not found.
13#
14# 'GnuLd_FOUND', 'GNULD_FOUND'
15# True if GNU ld was found.
16#
17# 'GNULD_VERSION_STRING'
18# The version of GNU ld.
19#
20# 'GNULD_LINKER_IS_BFD'
21# True if linker is ld.bfd (or compatible)
22#
23# Note that this will use CROSS_COMPILE, if defined,
24# as a prefix to the linker executable.
25
26include(FindPackageHandleStandardArgs)
27
28# GNULD_LINKER exists on repeated builds or defined manually...
29if(EXISTS "${GNULD_LINKER}")
30  if(NOT DEFINED GNULD_LINKER_IS_BFD)
31    # ... issue warning if GNULD_LINKER_IS_BFD is not already set.
32    message(
33      WARNING
34      "GNULD_LINKER specified directly in cache, but GNULD_LINKER_IS_BFD is not "
35      "defined. Assuming GNULD_LINKER_IS_BFD as OFF, please set GNULD_LINKER_IS_BFD "
36      "to correct value in cache to silence this warning"
37    )
38    set(GNULD_LINKER_IS_BFD OFF)
39  endif()
40
41  # Since GNULD_LINKER already exists, there is no need to find it again (below).
42  return()
43endif()
44
45# See if the compiler has a preferred linker
46execute_process(COMMAND ${CMAKE_C_COMPILER} --print-prog-name=ld.bfd
47                OUTPUT_VARIABLE GNULD_LINKER
48                OUTPUT_STRIP_TRAILING_WHITESPACE)
49
50if(EXISTS "${GNULD_LINKER}")
51  cmake_path(NORMAL_PATH GNULD_LINKER)
52  set(GNULD_LINKER_IS_BFD ON CACHE BOOL "Linker BFD compatibility (compiler reported)" FORCE)
53else()
54  # Need to clear it or else find_program() won't replace the value.
55  set(GNULD_LINKER)
56
57  if(DEFINED TOOLCHAIN_HOME)
58    # Search for linker under TOOLCHAIN_HOME if it is defined
59    # to limit which linker to use, or else we would be using
60    # host tools.
61    set(LD_SEARCH_PATH PATHS ${TOOLCHAIN_HOME} NO_DEFAULT_PATH)
62  endif()
63
64  find_program(GNULD_LINKER ${CROSS_COMPILE}ld.bfd ${LD_SEARCH_PATH})
65  if(GNULD_LINKER)
66    set(GNULD_LINKER_IS_BFD ON CACHE BOOL "Linker BFD compatibility (inferred from binary)" FORCE)
67  else()
68    find_program(GNULD_LINKER ${CROSS_COMPILE}ld ${LD_SEARCH_PATH})
69    set(GNULD_LINKER_IS_BFD OFF CACHE BOOL "Linker BFD compatibility (inferred from binary)" FORCE)
70  endif()
71endif()
72
73if(GNULD_LINKER)
74  # Parse the 'ld.bfd --version' output to find the installed version.
75  execute_process(
76    COMMAND
77    ${GNULD_LINKER} --version
78    OUTPUT_VARIABLE gnuld_version_output
79    ERROR_VARIABLE  gnuld_error_output
80    RESULT_VARIABLE gnuld_status
81    )
82
83  if(${gnuld_status} EQUAL 0)
84    # Extract GNU ld version. Different distros have their
85    # own version scheme so we need to account for that.
86    # Examples:
87    # - "GNU ld (GNU Binutils for Ubuntu) 2.34"
88    # - "GNU ld (Zephyr SDK 0.15.2) 2.38"
89    # - "GNU ld (Gentoo 2.39 p5) 2.39.0"
90    string(REGEX MATCH
91           "GNU ld \\(.+\\) ([0-9]+[.][0-9]+[.]?[0-9]*).*"
92           out_var ${gnuld_version_output})
93    set(GNULD_VERSION_STRING ${CMAKE_MATCH_1} CACHE STRING "GNU ld version" FORCE)
94  endif()
95endif()
96
97find_package_handle_standard_args(GnuLd
98                                  REQUIRED_VARS GNULD_LINKER
99                                  VERSION_VAR GNULD_VERSION_STRING
100)
101