blob: d8dacfc2123641d58672bb151fc2ed1f75340b0d [file] [log] [blame]
# 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(SEND_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")
else()
set(ld_version_flag "--version")
endif()
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} "-Wl,${ld_version_flag}" ${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}")
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}")
else()
message(STATUS "Linker not found")
endif()
endfunction()