Skip to content

Commit

Permalink
configure: Workaround for ppc64 TLS optimization bug with -fno-plt code
Browse files Browse the repository at this point in the history
On ppc64 architectures we need to turn off the __tls_get_addr
runtime optimization for -fno-plt code in dynamic shared objects
by passing a certain flag (-Wl,--no-tls-get-addr-optimize) to
the linker, when compiling with gcc and using ld.bfd < 2.44.

See: https://sourceware.org/PR32387
  • Loading branch information
besser82 committed Jan 5, 2025
1 parent 1209291 commit 19ad950
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ APPLY_SYMVERS = no
endif

libcrypt_la_LDFLAGS += $(UNDEF_FLAG) $(TEXT_RELOC_FLAG) $(AM_LDFLAGS)
libcrypt_la_LDFLAGS += $(NO_TLS_GET_ADDR_OPT_FLAG)

libcrypt_la_CPPFLAGS = $(AM_CPPFLAGS) -DIN_LIBCRYPT

Expand Down
177 changes: 177 additions & 0 deletions build-aux/m4/ax_compare_version.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compare_version.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
#
# DESCRIPTION
#
# This macro compares two version strings. Due to the various number of
# minor-version numbers that can exist, and the fact that string
# comparisons are not compatible with numeric comparisons, this is not
# necessarily trivial to do in a autoconf script. This macro makes doing
# these comparisons easy.
#
# The six basic comparisons are available, as well as checking equality
# limited to a certain number of minor-version levels.
#
# The operator OP determines what type of comparison to do, and can be one
# of:
#
# eq - equal (test A == B)
# ne - not equal (test A != B)
# le - less than or equal (test A <= B)
# ge - greater than or equal (test A >= B)
# lt - less than (test A < B)
# gt - greater than (test A > B)
#
# Additionally, the eq and ne operator can have a number after it to limit
# the test to that number of minor versions.
#
# eq0 - equal up to the length of the shorter version
# ne0 - not equal up to the length of the shorter version
# eqN - equal up to N sub-version levels
# neN - not equal up to N sub-version levels
#
# When the condition is true, shell commands ACTION-IF-TRUE are run,
# otherwise shell commands ACTION-IF-FALSE are run. The environment
# variable 'ax_compare_version' is always set to either 'true' or 'false'
# as well.
#
# Examples:
#
# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
# AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
#
# would both be true.
#
# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
# AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
#
# would both be false.
#
# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
#
# would be true because it is only comparing two minor versions.
#
# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
#
# would be true because it is only comparing the lesser number of minor
# versions of the two values.
#
# Note: The characters that separate the version numbers do not matter. An
# empty string is the same as version 0. OP is evaluated by autoconf, not
# configure, so must be a string, not a variable.
#
# The author would like to acknowledge Guido Draheim whose advice about
# the m4_case and m4_ifvaln functions make this macro only include the
# portions necessary to perform the specific comparison specified by the
# OP argument in the final configure script.
#
# LICENSE
#
# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.

#serial 13

dnl #########################################################################
AC_DEFUN([AX_COMPARE_VERSION], [
AC_REQUIRE([AC_PROG_AWK])
# Used to indicate true or false condition
ax_compare_version=false
# Convert the two version strings to be compared into a format that
# allows a simple string comparison. The end result is that a version
# string of the form 1.12.5-r617 will be converted to the form
# 0001001200050617. In other words, each number is zero padded to four
# digits, and non digits are removed.
AS_VAR_PUSHDEF([A],[ax_compare_version_A])
A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
AS_VAR_PUSHDEF([B],[ax_compare_version_B])
B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
dnl # then the first line is used to determine if the condition is true.
dnl # The sed right after the echo is to remove any indented white space.
m4_case(m4_tolower($2),
[lt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[gt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[le],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],
[ge],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],[
dnl Split the operator from the subversion count if present.
m4_bmatch(m4_substr($2,2),
[0],[
# A count of zero means use the length of the shorter version.
# Determine the number of characters in A and B.
ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
# Set A to no more than B's length and B to no more than A's length.
A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
],
[[0-9]+],[
# A count greater than zero means use only that many subversions
A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
],
[.+],[
AC_WARNING(
[invalid OP numeric parameter: $2])
],[])
# Pad zeros at end of numbers to make same length.
ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
B="$B`echo $A | sed 's/./0/g'`"
A="$ax_compare_version_tmp_A"
# Check for equality or inequality as necessary.
m4_case(m4_tolower(m4_substr($2,0,2)),
[eq],[
test "x$A" = "x$B" && ax_compare_version=true
],
[ne],[
test "x$A" != "x$B" && ax_compare_version=true
],[
AC_WARNING([invalid OP parameter: $2])
])
])
AS_VAR_POPDEF([A])dnl
AS_VAR_POPDEF([B])dnl
dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
if test "$ax_compare_version" = "true" ; then
m4_ifvaln([$4],[$4],[:])dnl
m4_ifvaln([$5],[else $5])dnl
fi
]) dnl AX_COMPARE_VERSION
119 changes: 119 additions & 0 deletions build-aux/m4/ax_compiler_vendor.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPILER_VENDOR
#
# DESCRIPTION
#
# Determine the vendor of the C, C++ or Fortran compiler. The vendor is
# returned in the cache variable $ax_cv_c_compiler_vendor for C,
# $ax_cv_cxx_compiler_vendor for C++ or $ax_cv_fc_compiler_vendor for
# (modern) Fortran. The value is one of "intel", "ibm", "pathscale",
# "clang" (LLVM), "cray", "fujitsu", "sdcc", "sx", "nvhpc" (NVIDIA HPC
# Compiler), "portland" (PGI), "gnu" (GCC), "sun" (Oracle Developer
# Studio), "hp", "dec", "borland", "comeau", "kai", "lcc", "sgi",
# "microsoft", "metrowerks", "watcom", "tcc" (Tiny CC) or "unknown" (if
# the compiler cannot be determined).
#
# To check for a Fortran compiler, you must first call AC_FC_PP_SRCEXT
# with an appropriate preprocessor-enabled extension. For example:
#
# AC_LANG_PUSH([Fortran])
# AC_PROG_FC
# AC_FC_PP_SRCEXT([F])
# AX_COMPILER_VENDOR
# AC_LANG_POP([Fortran])
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2008 Matteo Frigo
# Copyright (c) 2018-19 John Zaitseff <J.Zaitseff@zap.org.au>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.

#serial 32

AC_DEFUN([AX_COMPILER_VENDOR], [dnl
AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [dnl
dnl If you modify this list of vendors, please add similar support
dnl to ax_compiler_version.m4 if at all possible.
dnl
dnl Note: Do NOT check for GCC first since some other compilers
dnl define __GNUC__ to remain compatible with it. Compilers that
dnl are very slow to start (such as Intel) are listed first.
vendors="
intel: __ICC,__ECC,__INTEL_COMPILER
ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__,__ibmxl__
pathscale: __PATHCC__,__PATHSCALE__
clang: __clang__
cray: _CRAYC
fujitsu: __FUJITSU
sdcc: SDCC,__SDCC
sx: _SX
nvhpc: __NVCOMPILER
portland: __PGI
gnu: __GNUC__
sun: __SUNPRO_C,__SUNPRO_CC,__SUNPRO_F90,__SUNPRO_F95
hp: __HP_cc,__HP_aCC
dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER
borland: __BORLANDC__,__CODEGEARC__,__TURBOC__
comeau: __COMO__
kai: __KCC
lcc: __LCC__
sgi: __sgi,sgi
microsoft: _MSC_VER
metrowerks: __MWERKS__
watcom: __WATCOMC__
tcc: __TINYC__
unknown: UNKNOWN
"
for ventest in $vendors; do
case $ventest in
*:)
vendor=$ventest
continue
;;
*)
vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")"
;;
esac
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[
#if !($vencpp)
thisisanerror;
#endif
]])], [break])
done
ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1`
])
])dnl
83 changes: 83 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,89 @@ AX_APPEND_COMPILE_FLAGS([-fno-plt], [OPTI_FLAGS])
# Export compiler flags for optimization.
AC_SUBST([OPTI_FLAGS])

# The following tests apply for ppc64 architectures, only.
AC_CACHE_CHECK([if the host cpu is any ppc64 architecture],
[ac_cv_host_cpu_is_ppc64], [
case "$host_cpu" in
powerpc64*) ac_cv_host_cpu_is_ppc64=yes;;
*) ac_cv_host_cpu_is_ppc64=no;;
esac])

# On ppc64 architectures we need to turn off the __tls_get_addr
# runtime optimization for -fno-plt code in dynamic shared objects
# by passing a certain flag (-Wl,--no-tls-get-addr-optimize) to
# the linker, when compiling with gcc and using ld.bfd < 2.43.2.
# See: https://sourceware.org/PR32387
AS_IF([test "x$ac_cv_host_cpu_is_ppc64" = xyes &&
test "x$ac_cv_tls" != xnone &&
test "x$ax_cv_check_cflags___fno_plt" = xyes], [
# Get compiler vendor (gcc = gnu).
AX_COMPILER_VENDOR
# Check if compiler is gcc that uses ld.bfd.
AC_CACHE_CHECK([if $ax_cv_c_compiler_vendor compiler uses GNU bfd as linker],
[ac_cv_cc_ld_is_gnu_bfd], [
AS_IF([test "x$ax_cv_c_compiler_vendor" = xgnu], [
AS_IF([`$CC $CFLAGS -Wl,--version 2>/dev/null | \
$GREP -q "GNU ld version"`], [
ac_cv_cc_ld_is_gnu_bfd=yes], [
ac_cv_cc_ld_is_gnu_bfd=no])], [
ac_cv_cc_ld_is_gnu_bfd="$ax_cv_c_compiler_vendor compiler not affected by https://sourceware.org/PR32387"])])])

# Determine if ld.bfd has bugfix.
AS_IF([test "x$ac_cv_cc_ld_is_gnu_bfd" = xyes], [
AC_CACHE_CHECK([GNU ld.bfd version used by $ax_cv_c_compiler_vendor compiler],
[ac_cv_ld_version], [
ac_cv_ld_version=`$CC $CFLAGS -Wl,--version 2>/dev/null | \
$SED -e "s/GNU gold /GNU ld /; \
s/GNU ld version /GNU ld /; \
s/GNU ld ([^)]*) /GNU ld /; \
s/GNU ld \([0-9.][0-9.]*\).*/\1/; q"`])
AC_CACHE_CHECK([if the GNU bfd linker is version <= 2.30],
[ac_cv_gnu_bfd_le_2_30], [
AX_COMPARE_VERSION([$ac_cv_ld_version], [le], [2.31], [
ac_cv_gnu_bfd_le_2_30=yes], [
ac_cv_gnu_bfd_le_2_30=no])])
AS_IF([test "x$ac_cv_gnu_bfd_le_2_30" = xno], [
AC_CACHE_CHECK([if the GNU bfd linker is version >= 2.42.1 and < 2.43],
[ac_cv_gnu_bfd_ge_2_42_1], [
AX_COMPARE_VERSION([$ac_cv_ld_version], [ge], [2.42.1], [
AX_COMPARE_VERSION([$ac_cv_ld_version], [lt], [2.43.0], [
ac_cv_gnu_bfd_ge_2_42_1=yes], [
ac_cv_gnu_bfd_ge_2_42_1=no])], [
ac_cv_gnu_bfd_ge_2_42_1=no])])])
AS_IF([test "x$ac_cv_gnu_bfd_ge_2_42_1" = xno], [
AC_CACHE_CHECK([if the GNU bfd linker is version >= 2.43.2],
[ac_cv_gnu_bfd_ge_2_43_2], [
AX_COMPARE_VERSION([$ac_cv_ld_version], [ge], [2.43.2], [
ac_cv_gnu_bfd_ge_2_43_2=yes], [
ac_cv_gnu_bfd_ge_2_43_2=no])])])
AC_CACHE_CHECK([if the GNU bfd linker is affected by ppc64 TLS optimization bug with -fno-plt],
[ac_cv_need_tls_opt_bugfix], [
AS_IF([test "x$ac_cv_gnu_bfd_le_2_30" = xyes ||
test "x$ac_cv_gnu_bfd_ge_2_42_1" = xyes ||
test "x$ac_cv_gnu_bfd_ge_2_43_2" = xyes], [
ac_cv_need_tls_opt_bugfix=no], [
ac_cv_need_tls_opt_bugfix=yes])])])

AS_IF([test "x$ac_cv_need_tls_opt_bugfix" = xyes], [
# FIXME: This only checks whether the linker accepts
# -Wl,--no-tls-get-addr-optimize. It doesn't check that the switch
# actually does what we want it to do.
AC_CACHE_CHECK([how to explicitly disable __tls_get_addr_opt runtime optimization],
[ac_cv_no_tls_get_addr_opt], [
ac_cv_no_tls_get_addr_opt=unknown
SAVED_LDFLAGS="$LDFLAGS"
LDFLAGS="$SAVED_LDFLAGS -Wl,--no-tls-get-addr-optimize"
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [int i = 1;])],
[ac_cv_no_tls_get_addr_opt=-Wl,--no-tls-get-addr-optimize])
LDFLAGS="$SAVED_LDFLAGS"])
NO_TLS_GET_ADDR_OPT_FLAG=
AS_IF([test "x$ac_cv_no_tls_get_addr_opt" != xunknown], [
NO_TLS_GET_ADDR_OPT_FLAG="$ac_cv_no_tls_get_addr_opt"])
AC_SUBST([NO_TLS_GET_ADDR_OPT_FLAG])])

# Checks for library functions.
AC_CHECK_FUNCS_ONCE([
arc4random_buf
Expand Down

0 comments on commit 19ad950

Please sign in to comment.