Support for multiple versions in versioned_symbol, compat_symbol

This essentially folds compat_symbol_unique functionality into
compat_symbol.

This change eliminates the need for intermediate aliases for defining
multiple symbol versions, for both compat_symbol and versioned_symbol.
Some binutils versions do not suport multiple versions per symbol on
some targets, so aliases are automatically introduced, similar to what
compat_symbol_unique did.  To reduce symbol table sizes, a configure
check is added to avoid these aliases if they are not needed.

The new mechanism works with data symbols as well as function symbols,
due to the way an assembler-level redirect is used.  It is not
compatible with weak symbols for old binutils versions, which is why
the definition of __malloc_initialize_hook had to be changed.  This
is not a loss of functionality because weak symbols do not matter
to dynamic linking.

The placeholder symbol needs repeating in nptl/libpthread-compat.c
now that compat_symbol is used, but that seems more obvious than
introducing yet another macro.

A subtle difference was that compat_symbol_unique made the symbol
global automatically.  compat_symbol does not do this, so static
had to be removed from the definition of
__libpthread_version_placeholder.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
This commit is contained in:
Florian Weimer 2021-03-25 11:05:37 +01:00
parent 6d8fcee694
commit 0923f74ada
14 changed files with 197 additions and 67 deletions

View File

@ -190,6 +190,10 @@
/* Define if the linker defines __ehdr_start. */
#undef HAVE_EHDR_START
/* Define to 1 if the assembler needs intermediate aliases to define
multiple symbol versions for one symbol. */
#define SYMVER_NEEDS_ALIAS 0
/*
*/

28
configure vendored
View File

@ -6590,6 +6590,34 @@ elif test "$libc_cv_ehdr_start" = broken; then
$as_echo "$as_me: WARNING: linker is broken -- you should upgrade" >&2;}
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the assembler requires one version per symbol" >&5
$as_echo_n "checking whether the assembler requires one version per symbol... " >&6; }
if ${libc_cv_symver_needs_alias+:} false; then :
$as_echo_n "(cached) " >&6
else
cat > conftest.s <<EOF
.text
testfunc:
.globl testfunc
.symver testfunc, testfunc1@VERSION1
.symver testfunc, testfunc1@VERSION2
EOF
libc_cv_symver_needs_alias=no
if ${CC-cc} $ASFLAGS -c conftest.s 2>&5; then
libc_cv_symver_needs_alias=no
else
libc_cv_symver_needs_alias=yes
fi
rm conftest.*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_symver_needs_alias" >&5
$as_echo "$libc_cv_symver_needs_alias" >&6; }
if test "$libc_cv_symver_needs_alias" = yes; then
$as_echo "#define SYMVER_NEEDS_ALIAS 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_trap with no external dependencies" >&5
$as_echo_n "checking for __builtin_trap with no external dependencies... " >&6; }
if ${libc_cv_builtin_trap+:} false; then :

View File

@ -1673,6 +1673,29 @@ elif test "$libc_cv_ehdr_start" = broken; then
AC_MSG_WARN([linker is broken -- you should upgrade])
fi
dnl Starting with binutils 2.35, GAS can attach multiple symbol versions
dnl to one symbol (PR 23840).
AC_CACHE_CHECK(whether the assembler requires one version per symbol,
libc_cv_symver_needs_alias, [dnl
cat > conftest.s <<EOF
.text
testfunc:
.globl testfunc
.symver testfunc, testfunc1@VERSION1
.symver testfunc, testfunc1@VERSION2
EOF
libc_cv_symver_needs_alias=no
if ${CC-cc} $ASFLAGS -c conftest.s 2>&AS_MESSAGE_LOG_FD; then
libc_cv_symver_needs_alias=no
else
libc_cv_symver_needs_alias=yes
fi
rm conftest.*
])
if test "$libc_cv_symver_needs_alias" = yes; then
AC_DEFINE(SYMVER_NEEDS_ALIAS)
fi
AC_CACHE_CHECK(for __builtin_trap with no external dependencies,
libc_cv_builtin_trap, [dnl
libc_cv_builtin_trap=no

View File

@ -404,12 +404,13 @@ for linking")
symbol_version_reference(real, name, version)
# define default_symbol_version(real, name, version) \
_default_symbol_version(real, name, version)
/* See <libc-symver.h>. */
# ifdef __ASSEMBLER__
# define _default_symbol_version(real, name, version) \
.symver real, name##@##@##version
_set_symbol_version (real, name@@version)
# else
# define _default_symbol_version(real, name, version) \
__asm__ (".symver " #real "," #name "@@" #version)
_set_symbol_version (real, #name "@@" #version)
# endif
/* Evalutes to a string literal for VERSION in LIB. */

View File

@ -62,6 +62,20 @@
i.e. either GLIBC_2.1 or the "earliest version" specified in
shlib-versions if that is newer. */
/* versioned_symbol (LIB, LOCAL, SYMBOL, VERSION) emits a definition
of SYMBOL with a default (@@) VERSION appropriate for LIB. (The
actually emitted symbol version is adjusted according to the
baseline symbol version for LIB.) The address of the symbol is
taken from LOCAL. Properties of LOCAL are copied to the exported
symbol. In particular, LOCAL itself should be global. It is
unspecified whether SYMBOL@VERSION is associated with LOCAL, or if
an intermediate alias is created. If LOCAL and SYMBOL are
distinct, and LOCAL is also intended for export, its version should
be specified explicitly with versioned_symbol, too.
If LOCAL is a data symbol and does not have a non-zero initializer,
it should be defined with __attribute__ ((nocommon)) for
compatibility with GCC versions that default to -fcommon. */
# define versioned_symbol(lib, local, symbol, version) \
versioned_symbol_1 (lib, local, symbol, version)
# define versioned_symbol_1(lib, local, symbol, version) \
@ -69,44 +83,25 @@
# define versioned_symbol_2(local, symbol, name) \
default_symbol_version (local, symbol, name)
/* compat_symbol is like versioned_symbol, but emits a compatibility
version (with @ instead of @@). The same issue related to
intermediate aliases applies, so LOCAL should not be listed in the
Versions file, or otherwise it can be exported with an undesired
default symbol version. */
# define compat_symbol(lib, local, symbol, version) \
compat_symbol_reference (lib, local, symbol, version)
/* This is similar to compat_symbol, but allows versioning the same symbol
to multiple version without having multiple symbol definitions. For
instance:
#if (SHLIB_COMPAT (libpthread, GLIBC_2_1_2, GLIBC_2_2))
compat_symbol_unique (libc, old_foo, GLIBC_2_1_2)
#endif
#if (SHLIB_COMPAT (libpthread, GLIBC_2_2_6, GLIBC_2_3))
compat_symbol_unique (libc, old_foo, GLIBC_2_2_6)
#endif
Internally it creates a unique strong alias to the input symbol and
creates one compat_symbol on the alias. Using the above example,
it is similar to:
#if (SHLIB_COMPAT (libpthread, GLIBC_2_1_2, GLIBC_2_2))
strong_alias (old_foo, old_foo__COUNTER__)
compat_symbol (libc, old_foo__COUNTER__, foo, GLIBC_2_2)
#endif.
With __COUNTER__ being a monotonic number generated by the compiler. */
# define __compat_symbol_unique_concat(x, y) x ## y
# define _compat_symbol_unique_concat(x, y) \
__compat_symbol_unique_concat (x, y)
# define _compat_symbol_unique_alias(name) \
_compat_symbol_unique_concat (name, __COUNTER__)
# define _compat_symbol_unique(lib, orig_name, name, version) \
strong_alias (orig_name, name) \
compat_symbol (lib, name, orig_name, version)
# define compat_symbol_unique(lib, name, version) \
_compat_symbol_unique (lib, name, _compat_symbol_unique_alias (name), \
version)
compat_symbol_1 (lib, local, symbol, version)
# define compat_symbol_1(lib, local, symbol, version) \
compat_symbol_2 (local, symbol, VERSION_##lib##_##version)
/* See <libc-symver.h>. */
# ifdef __ASSEMBLER__
#define compat_symbol_2(local, symbol, name) \
_set_symbol_version (local, symbol@name)
# else
# define compat_symbol_2(local, symbol, name) \
compat_symbol_3 (local, symbol, name)
# define compat_symbol_3(local, symbol, name) \
_set_symbol_version (local, #symbol "@" #name)
# endif
#else
/* Not compiling ELF shared libraries at all, so never any old versions. */
@ -118,16 +113,17 @@
/* This should not appear outside `#if SHLIB_COMPAT (...)'. */
# define compat_symbol(lib, local, symbol, version) ...
# define compat_symbol_unique(lib, name, version) ...
#endif
/* Use compat_symbol_reference for a reference *or* definition of a
specific version of a symbol. Definitions are primarily used to
ensure tests reference the exact compat symbol required, or define an
interposing symbol of the right version e.g. __malloc_initialize_hook
in mcheck-init.c. Use compat_symbol to define such a symbol within
the shared libraries that are built for users. */
specific version of a symbol. compat_symbol_reference does not
create intermediate aliases. Definitions are primarily used to
ensure tests reference the exact compat symbol required, or define
an interposing symbol of the right version e.g.,
__malloc_initialize_hook in mcheck-init.c. Use compat_symbol to
define such a symbol within the shared libraries that are built for
users. */
#define compat_symbol_reference(lib, local, symbol, version) \
compat_symbol_reference_1 (lib, local, symbol, version)
#define compat_symbol_reference_1(lib, local, symbol, version) \

View File

@ -1991,7 +1991,7 @@ static void *memalign_hook_ini (size_t alignment, size_t sz,
const void *caller) __THROW;
#if HAVE_MALLOC_INIT_HOOK
void weak_variable (*__malloc_initialize_hook) (void) = NULL;
void (*__malloc_initialize_hook) (void) __attribute__ ((nocommon));
compat_symbol (libc, __malloc_initialize_hook,
__malloc_initialize_hook, GLIBC_2_0);
#endif

View File

@ -20,10 +20,10 @@
#include <shlib-compat.h>
#ifdef SHARED
static void
void
attribute_compat_text_section
__attribute_used__
__libpthread_version_placeholder (void)
__libpthread_version_placeholder_1 (void)
{
}
#endif
@ -37,16 +37,16 @@ __libpthread_version_placeholder (void)
there are plenty of other symbols which populate those later
versions. */
#if (SHLIB_COMPAT (libpthread, GLIBC_2_1_2, GLIBC_2_2))
compat_symbol_unique (libpthread,
__libpthread_version_placeholder, GLIBC_2_1_2);
compat_symbol (libpthread, __libpthread_version_placeholder_1,
__libpthread_version_placeholder, GLIBC_2_1_2);
#endif
#if (SHLIB_COMPAT (libpthread, GLIBC_2_2_3, GLIBC_2_2_4))
compat_symbol_unique (libpthread,
__libpthread_version_placeholder, GLIBC_2_2_3);
compat_symbol (libpthread, __libpthread_version_placeholder_1,
__libpthread_version_placeholder, GLIBC_2_2_3);
#endif
#if (SHLIB_COMPAT (libpthread, GLIBC_2_2_6, GLIBC_2_3))
compat_symbol_unique (libpthread,
__libpthread_version_placeholder, GLIBC_2_2_6);
compat_symbol (libpthread, __libpthread_version_placeholder_1,
__libpthread_version_placeholder, GLIBC_2_2_6);
#endif

View File

@ -22,17 +22,67 @@
#ifndef _LIBC_SYMVER_H
#define _LIBC_SYMVER_H 1
#include <config.h>
/* Use symbol_version_reference to specify the version a symbol
reference should link to. Use symbol_version or
default_symbol_version for the definition of a versioned symbol.
The difference is that the latter is a no-op in non-shared
builds. */
builds.
_set_symbol_version is similar to symbol_version_reference, except
that this macro expects the name and symbol version as a single
string or token sequence, with an @ or @@ separator. (A string is
used in C mode and a token sequence in assembler mode.)
_set_symbol_version only be used for definitions because it may
introduce an alias symbol that would not be globally unique for
mere references. The _set_symbol_version macro is used to define
default_symbol_version and compat_symbol. */
#ifdef __ASSEMBLER__
# define symbol_version_reference(real, name, version) \
.symver real, name##@##version
#else /* !__ASSEMBLER__ */
#else
# define symbol_version_reference(real, name, version) \
__asm__ (".symver " #real "," #name "@" #version)
#endif
#endif /* !__ASSEMBLER__ */
#if SYMVER_NEEDS_ALIAS
/* If the assembler cannot support multiple versions for the same
symbol, introduce __SInnn_ aliases to which the symbol version is
attached. */
# define __symbol_version_unique_concat(x, y) __SI ## x ## _ ## y
# define _symbol_version_unique_concat(x, y) \
__symbol_version_unique_concat (x, y)
# define _symbol_version_unique_alias(name) \
_symbol_version_unique_concat (name, __COUNTER__)
# ifdef __ASSEMBLER__
# define _set_symbol_version_2(real, alias, name_version) \
.globl alias ASM_LINE_SEP \
.equiv alias, real ASM_LINE_SEP \
.symver alias, name_version
# else
# define _set_symbol_version_2(real, alias, name_version) \
__asm__ (".globl " #alias "\n\t" \
".equiv " #alias ", " #real "\n\t" \
".symver " #alias "," name_version)
# endif
# define _set_symbol_version_1(real, alias, name_version) \
_set_symbol_version_2 (real, alias, name_version)
/* REAL must be globally unique, so that the counter also produces
globally unique symbols. */
# define _set_symbol_version(real, name_version) \
_set_symbol_version_1 (real, _symbol_version_unique_alias (real), \
name_version)
# else /* !SYMVER_NEEDS_ALIAS */
# ifdef __ASSEMBLER__
# define _set_symbol_version(real, name_version) \
.symver real, name_version
# else
# define _set_symbol_version(real, name_version) \
__asm__ (".symver " #real "," name_version)
# endif
#endif /* !SYMVER_NEEDS_ALIAS */
#endif /* _LIBC_SYMVER_H */

View File

@ -0,0 +1,33 @@
/* Symbol version management. ia64 version.
Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _LIBC_SYMVER_H
#include <sysdeps/generic/libc-symver.h>
/* ia64 recognizes loc1 as a register name. Add the # suffix to all
symbol references. */
#if !defined (__ASSEMBLER__) && SYMVER_NEEDS_ALIAS
#undef _set_symbol_version_2
# define _set_symbol_version_2(real, alias, name_version) \
__asm__ (".globl " #alias "#\n\t" \
".equiv " #alias ", " #real "#\n\t" \
".symver " #alias "#," name_version)
#endif
#endif /* _LIBC_SYMVER_H */

View File

@ -42,6 +42,5 @@ versioned_symbol (libc, __clock_getcpuclockid, clock_getcpuclockid, GLIBC_2_17);
/* clock_getcpuclockid moved to libc in version 2.17;
old binaries may expect the symbol version it had in librt. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_17)
strong_alias (__clock_getcpuclockid, __clock_getcpuclockid_2);
compat_symbol (libc, __clock_getcpuclockid_2, clock_getcpuclockid, GLIBC_2_2);
compat_symbol (libc, __clock_getcpuclockid, clock_getcpuclockid, GLIBC_2_2);
#endif

View File

@ -32,8 +32,7 @@ versioned_symbol (libc, __clock_getres, clock_getres, GLIBC_2_17);
/* clock_getres moved to libc in version 2.17;
old binaries may expect the symbol version it had in librt. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_17)
strong_alias (__clock_getres, __clock_getres_2);
compat_symbol (libc, __clock_getres_2, clock_getres, GLIBC_2_2);
compat_symbol (libc, __clock_getres, clock_getres, GLIBC_2_2);
#endif
stub_warning (clock_getres)

View File

@ -33,8 +33,7 @@ versioned_symbol (libc, __clock_gettime, clock_gettime, GLIBC_2_17);
/* clock_gettime moved to libc in version 2.17;
old binaries may expect the symbol version it had in librt. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_17)
strong_alias (__clock_gettime, __clock_gettime_2);
compat_symbol (libc, __clock_gettime_2, clock_gettime, GLIBC_2_2);
compat_symbol (libc, __clock_gettime, clock_gettime, GLIBC_2_2);
#endif
stub_warning (clock_gettime)

View File

@ -38,8 +38,7 @@ versioned_symbol (libc, __clock_nanosleep, clock_nanosleep, GLIBC_2_17);
/* clock_nanosleep moved to libc in version 2.17;
old binaries may expect the symbol version it had in librt. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_17)
strong_alias (__clock_nanosleep, __clock_nanosleep_2);
compat_symbol (libc, __clock_nanosleep_2, clock_nanosleep, GLIBC_2_2);
compat_symbol (libc, __clock_nanosleep, clock_nanosleep, GLIBC_2_2);
#endif
stub_warning (clock_nanosleep)

View File

@ -33,8 +33,7 @@ versioned_symbol (libc, __clock_settime, clock_settime, GLIBC_2_17);
/* clock_settime moved to libc in version 2.17;
old binaries may expect the symbol version it had in librt. */
#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_17)
strong_alias (__clock_settime, __clock_settime_2);
compat_symbol (libc, __clock_settime_2, clock_settime, GLIBC_2_2);
compat_symbol (libc, __clock_settime, clock_settime, GLIBC_2_2);
#endif
stub_warning (clock_settime)