# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # The Gold linker must be manually enabled. function(APPEND_LINKER_FLAGS) # Search candidate linkers in the order of decreasing speed and functionality. # In particular, lld is the best choice since it's quite fast and supports # ThinLTO, etc. # # On macOS, LLD from third-party ${THIRDPARTY_TOOLCHAIN_DIR}/bin/ld64.lld # isn't fully functional yet: it doesn't support -U option, etc. if (NOT APPLE) set(linkers "lld" "${THIRDPARTY_TOOLCHAIN_DIR}/bin/ld.lld" "gold") endif() list(APPEND linkers "default") foreach(candidate_linker ${linkers}) if(candidate_linker STREQUAL "default") set(candidate_flags "") else() set(candidate_flags "-fuse-ld=${candidate_linker}") endif() GET_LINKER_VERSION("${candidate_flags}") if(NOT LINKER_FOUND) continue() endif() # Older versions of the gold linker are vulnerable to a bug [1] which # prevents weak symbols from being overridden properly. This leads to # omitting of Kudu's tcmalloc dependency if using dynamic linking. # # We'll skip gold in our list of candidate linkers if this bug is relevant. # # 1. https://sourceware.org/bugzilla/show_bug.cgi?id=16979. if ("${LINKER_FAMILY}" STREQUAL "gold") if("${LINKER_VERSION}" VERSION_LESS "1.12" AND "${KUDU_LINK}" STREQUAL "d") message(WARNING "Skipping gold <1.12 with dynamic linking.") continue() endif() # We can't use the gold linker if it's inside devtoolset because the compiler # won't find it when invoked directly from make/ninja (which is typically # done outside devtoolset). This seems to be fixed in devtoolset-6 or later # by having the gcc/clang binary search the devtoolset bin directory even # if it's not on $PATH. execute_process(COMMAND which ld.gold OUTPUT_VARIABLE GOLD_LOCATION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if ("${GOLD_LOCATION}" MATCHES "^/opt/rh/devtoolset-[3-5]/") message(WARNING "Cannot use gold with devtoolset < 6, skipping...") continue() endif() endif() # LLD < 10.0.0 has a bug[1] which causes the __tsan_default_options (and similar) # symbols to be defined as WEAK instead of DEFINED in the resulting binary. This # causes our suppressions to not work properly, resulting in failed tests. # # [1] https://reviews.llvm.org/D63974 if ("${LINKER_FAMILY}" STREQUAL "lld" AND "${LINKER_VERSION}" VERSION_LESS "10.0.0" AND ("${KUDU_USE_TSAN}" OR "${KUDU_USE_ASAN}" OR "${KUDU_USE_UBSAN}")) message(WARNING "Cannot use lld < 10.0.0 with TSAN/ASAN/UBSAN. Skipping...") continue() endif() set(linker_flags "${candidate_flags}") break() endforeach() if(NOT DEFINED linker_flags) message(FATAL_ERROR "Could not find a suitable linker") endif() message(STATUS "Using linker flags: ${linker_flags}") foreach(var CMAKE_SHARED_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) set(${var} "${${var}} ${linker_flags}" PARENT_SCOPE) endforeach() endfunction() # Interrogates the linker version via the C++ compiler to determine which linker # we're using, along with its version. # # Sets the following variables: # LINKER_FOUND: true/false # When LINKER_FOUND is true, additionally sets the following variables: # LINKER_FAMILY: lld/ld/gold on Linux, ld64 on macOS # LINKER_VERSION: version number of the linker # Any additional arguments are passed verbatim into the C++ compiler invocation. function(GET_LINKER_VERSION) if(ARGN) message(STATUS "Checking linker version with flags: ${ARGN}") else() message(STATUS "Checking linker version when not specifying any flags") endif() if (APPLE) set(ld_version_flag "-v,-bundle") set(osx_sysroot --sysroot ${CMAKE_OSX_SYSROOT}) else() set(ld_version_flag "--version") set(osx_sysroot "") endif() execute_process( COMMAND ${CMAKE_CXX_COMPILER} ${osx_sysroot} "-Wl,${ld_version_flag}" -o /dev/null ${ARGN} ERROR_VARIABLE LINKER_STDERR OUTPUT_VARIABLE LINKER_STDOUT RESULT_VARIABLE LINKER_EXITCODE) if (NOT LINKER_EXITCODE EQUAL 0) set(LINKER_FOUND FALSE) elseif (LINKER_STDOUT MATCHES "GNU gold") # We're expecting LINKER_STDOUT to look like one of these: # GNU gold (version 2.24) 1.11 # GNU gold (GNU Binutils for Ubuntu 2.30) 1.15 if (NOT "${LINKER_STDOUT}" MATCHES "GNU gold \\([^\\)]*\\) (([0-9]+\\.?)+)") message(SEND_ERROR "Could not extract GNU gold version. " "Linker version output: ${LINKER_STDOUT}") endif() set(LINKER_FOUND TRUE) set(LINKER_FAMILY "gold") set(LINKER_VERSION "${CMAKE_MATCH_1}") elseif (LINKER_STDOUT MATCHES "GNU ld") # We're expecting LINKER_STDOUT to look like one of these: # GNU ld (GNU Binutils for Ubuntu) 2.30 # GNU ld version 2.20.51.0.2-5.42.el6 20100205 # GNU ld version 2.25.1-22.base.el7 if (NOT "${LINKER_STDOUT}" MATCHES "GNU ld version (([0-9]+\\.?)+)" AND NOT "${LINKER_STDOUT}" MATCHES "GNU ld \\([^\\)]*\\) (([0-9]+\\.?)+)") message(SEND_ERROR "Could not extract GNU ld version. " "Linker version output: ${LINKER_STDOUT}") endif() set(LINKER_FOUND TRUE) set(LINKER_FAMILY "ld") set(LINKER_VERSION "${CMAKE_MATCH_1}") elseif (LINKER_STDOUT MATCHES "LLD") # Sample: # LLD 9.0.0 (example.com:kudu.git a5848a4c3c8c72a1ac823182e87cd1f6c31ddc15) (compatible with GNU linkers) if (NOT "${LINKER_STDOUT}" MATCHES "LLD (([0-9]+\\.?)+)") message(SEND_ERROR "Could not extract lld version. " "Linker version output: ${LINKER_STDOUT}") endif() set(LINKER_FOUND TRUE) set(LINKER_FAMILY "lld") set(LINKER_VERSION "${CMAKE_MATCH_1}") elseif (LINKER_STDERR MATCHES "PROJECT:ld64") # ld64 outputs the versioning information into stderr. # Sample: # @(#)PROGRAM:ld PROJECT:ld64-409.12 # @(#)PROGRAM:ld PROJECT:ld64-530 if (NOT "${LINKER_STDERR}" MATCHES "PROJECT:ld64-([0-9]+(\\.[0-9]+)?)") message(SEND_ERROR "Could not extract ld64 version. " "Linker version output: ${LINKER_STDOUT}") endif() set(LINKER_FOUND TRUE) set(LINKER_FAMILY "ld64") set(LINKER_VERSION "${CMAKE_MATCH_1}") elseif (LINKER_STDERR MATCHES "PROJECT:ld") # ld outputs the versioning information into stderr. # This is an example of output from newer versions of the LLVM's linker: # e.g. the linker which comes along with CLANG 15.0.0 (clang-1500.3.9.4) # as a part of Xcode 15.3 on macOS. # # Sample: # @(#)PROGRAM:ld PROJECT:ld-1053.12 if (NOT "${LINKER_STDERR}" MATCHES "PROJECT:ld-([0-9]+(\\.[0-9]+)?)") message(SEND_ERROR "Could not extract ld version. " "Linker version output: ${LINKER_STDOUT}") endif() set(LINKER_FOUND TRUE) set(LINKER_FAMILY "ld64") set(LINKER_VERSION "${CMAKE_MATCH_1}") else() set(LINKER_FOUND FALSE) endif() # Propagate the results to the caller. set(LINKER_FOUND "${LINKER_FOUND}" PARENT_SCOPE) set(LINKER_FAMILY "${LINKER_FAMILY}" PARENT_SCOPE) set(LINKER_VERSION "${LINKER_VERSION}" PARENT_SCOPE) if (LINKER_FOUND) message(STATUS "Found linker: ${LINKER_FAMILY} version ${LINKER_VERSION}") endif() endfunction()