glibc/conform/linknamespace.pl
Joseph Myers d709042a6e Fix lgamma setting signgam for ISO C (bug 15421).
The lgamma (and likewise lgammaf, lgammal) function wrongly sets the
signgam variable even when building for strict ISO C conformance
(-std=c99 / -std=c11), although the user may define such a variable
and it's only in the implementation namespace for POSIX with XSI
extensions enabled.

Following discussions starting at
<https://sourceware.org/ml/libc-alpha/2013-04/msg00767.html> and
<https://sourceware.org/ml/libc-alpha/2015-10/msg00844.html>, it seems
that the safest approach for fixing this particular issue is for
signgam to become a weak alias for a newly exported symbol __signgam,
with the library functions only setting __signgam, at which point
static linker magic will preserve the alias for newly linked binaries
that refer to the library's signgam rather than defining their own,
while breaking the alias for programs that define their own signgam,
with new symbol versions for lgamma functions and with compat symbols
for existing binaries that set both signgam and __signgam.

This patch implements that approach for the fix.  signgam is made into
a weak alias.  The four symbols __signgam, lgamma, lgammaf, lgammal
get new symbol versions at version GLIBC_2.23, with the existing
versions of lgamma, lgammaf and lgammal becoming compat symbols.

When the compat versions are built, gamma, gammaf and gammal are
aliases for the compat versions (i.e. always set signgam); this is OK
as they are not ISO C functions, and avoids adding new symbol versions
for them unnecessarily.  When the compat versions are not built
(i.e. for static linking and for future glibc ports), gamma, gammaf
and gammal are aliases for the new versions that set __signgam.  The
ldbl-opt versions are updated accordingly.

The lgamma wrappers are adjusted so that the same source files,
included from different files with different definitions of
USE_AS_COMPAT, can build either the new versions or the compat
versions.  Similar changes are made to the ia64 versions (untested).

Tests are added that the lgamma functions do not interfere with a user
variable called signgam for ISO C, with various choices for the size
of that variable, whether it is initialized, and for static and
dynamic linking.  The conformtest whitelist entry is removed as well.

Tested for x86_64, x86, mips64 and powerpc, including looking at
objdump --dynamic-syms output to make sure the expected sets of
symbols were aliases.  Also spot-tested that a binary built with old
glibc works properly (i.e. gets signgam set) when run with new glibc.

	[BZ #15421]
	* sysdeps/ieee754/s_signgam.c (signgam): Rename to __signgam,
	initialize with 0 and define as weak alias of __signgam.
	* include/math.h [!_ISOMAC] (__signgam): Declare.
	* math/Makefile (libm-calls): Add w_lgamma_compat.
	(tests): Add test-signgam-uchar, test-signgam-uchar-init,
	test-signgam-uint, test-signgam-uint-init, test-signgam-ullong and
	test-signgam-ullong-init.
	(tests-static): Add test-signgam-uchar-static,
	test-signgam-uchar-init-static, test-signgam-uint-static,
	test-signgam-uint-init-static, test-signgam-ullong-static and
	test-signgam-ullong-init-static.
	(CFLAGS-test-signgam-uchar.c): New variable.
	(CFLAGS-test-signgam-uchar-init.c): Likewise.
	(CFLAGS-test-signgam-uchar-static.c): Likewise.
	(CFLAGS-test-signgam-uchar-init-static.c): Likewise.
	(CFLAGS-test-signgam-uint.c): Likewise.
	(CFLAGS-test-signgam-uint-init.c): Likewise.
	(CFLAGS-test-signgam-uint-static.c): Likewise.
	(CFLAGS-test-signgam-uint-init-static.c): Likewise.
	(CFLAGS-test-signgam-ullong.c): Likewise.
	(CFLAGS-test-signgam-ullong-init.c): Likewise.
	(CFLAGS-test-signgam-ullong-static.c): Likewise.
	(CFLAGS-test-signgam-ullong-init-static.c): Likewise.
	* math/Versions (libm): Add GLIBC_2.23.
	* math/lgamma-compat.h: New file.
	* math/test-signgam-main.c: Likewise.
	* math/test-signgam-uchar-init-static.c: Likewise.
	* math/test-signgam-uchar-init.c: Likewise.
	* math/test-signgam-uchar-static.c: Likewise.
	* math/test-signgam-uchar.c: Likewise.
	* math/test-signgam-uint-init-static.c: Likewise.
	* math/test-signgam-uint-init.c: Likewise.
	* math/test-signgam-uint-static.c: Likewise.
	* math/test-signgam-uint.c: Likewise.
	* math/test-signgam-ullong-init-static.c: Likewise.
	* math/test-signgam-ullong-init.c: Likewise.
	* math/test-signgam-ullong-static.c: Likewise.
	* math/test-signgam-ullong.c: Likewise.
	* math/w_lgamma.c: Rename to w_lgamma_main.c and replace by
	wrapper of w_lgamma_main.c.
	* math/w_lgamma_compat.c: New file.
	* math/w_lgamma_compatf.c: Likewise.
	* math/w_lgamma_compatl.c: Likewise.
	* math/w_lgamma_main.c: New file.  Based on w_lgamma.c.  Include
	<lgamma-compat.h>.  Condition contents on [BUILD_LGAMMA].  Support
	defining compatibility symbols.
	(__lgamma): Change to LGFUNC (__lgamma).  Use CALL_LGAMMA.
	* math/w_lgammaf.c: Rename to w_lgammaf_main.c and replace by
	wrapper of w_lgammaf_main.c.
	* math/w_lgammaf_main.c: New file.  Based on w_lgammaf.c.  Include
	<lgamma-compat.h>.  Condition contents on [BUILD_LGAMMA].  Support
	defining compatibility symbols.
	(__lgammaf): Change to LGFUNC (__lgammaf).  Use CALL_LGAMMA.
	* math/w_lgammal.c: Rename to w_lgammal_main.c and replace by
	wrapper of w_lgammal_main.c.
	* math/w_lgammal_main.c: New file.  Based on w_lgammal.c.  Include
	<lgamma-compat.h>.  Condition contents on [BUILD_LGAMMA].  Support
	defining compatibility symbols.
	(__lgammal): Change to LGFUNC (__lgammal).  Use CALL_LGAMMA.
	* sysdeps/ia64/fpu/lgamma-compat.h: New file.
	* sysdeps/ia64/fpu/w_lgamma.c: Move to ....
	* sysdeps/ia64/fpu/w_lgamma_main.c: ...here.  Include
	<lgamma-compat.h>.
	(__ieee754_lgamma): Change to LGFUNC (lgamma).  Use CALL_LGAMMA.
	(__ieee754_gamma): Define as alias.
	* sysdeps/ia64/fpu/w_lgammaf.c: Move to ....
	* sysdeps/ia64/fpu/w_lgammaf_main.c: ...here.  Include
	<lgamma-compat.h>.
	(__ieee754_lgammaf): Change to LGFUNC (lgammaf).  Use CALL_LGAMMA.
	(__ieee754_gammaf): Define as alias.
	* sysdeps/ia64/fpu/w_lgammal.c: Move to ....
	* sysdeps/ia64/fpu/w_lgammal_main.c: ...here.  Include
	<lgamma-compat.h>.
	(__ieee754_lgammal): Change to LGFUNC (lgammal).  Use CALL_LGAMMA.
	(__ieee754_gammal): Define as alias.
	* sysdeps/ieee754/ldbl-opt/w_lgamma.c: Move to ....
	* sysdeps/ieee754/ldbl-opt/w_lgamma_compat.c: ...here.  Include
	<math/w_lgamma_compat.c>.
	[LONG_DOUBLE_COMPAT(libm, GLIBC_2_0)] (__lgammal_dbl_compat):
	Define as alias of __lgamma_compat and use in defining lgammal.
	* sysdeps/ieee754/ldbl-opt/w_lgammal.c: Move to ....
	* sysdeps/ieee754/ldbl-opt/w_lgamma_compatl.c: ...here.  Include
	<math/lgamma-compat.h> and <math/w_lgamma_compatl.c>.
	(USE_AS_COMPAT): New macro.
	(LGAMMA_OLD_VER): Undefine and redefine.
	(lgammal): Do not define here.
	(gammal): Only define here if [GAMMA_ALIAS].
	* conform/linknamespace.pl (@whitelist): Remove signgam.
	* sysdeps/nacl/libm.abilist: Update.
	* sysdeps/unix/sysv/linux/aarch64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/alpha/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/arm/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/hppa/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/i386/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/ia64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/nios2/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libm.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libm.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/libm-le.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/libm.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/sh/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libm.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libm.abilist:
	Likewise.
	* sysdeps/unix/sysv/linux/tile/tilepro/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libm.abilist: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libm.abilist: Likewise.
2015-11-20 22:49:59 +00:00

238 lines
7.6 KiB
Raku

#! /usr/bin/perl
# Check that use of symbols declared in a given header does not result
# in any symbols being brought in that are not reserved with external
# linkage for the given standard.
# Copyright (C) 2014-2015 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
# <http://www.gnu.org/licenses/>.
use GlibcConform;
use Getopt::Long;
GetOptions ('header=s' => \$header, 'standard=s' => \$standard,
'flags=s' => \$flags, 'cc=s' => \$CC, 'tmpdir=s' => \$tmpdir,
'stdsyms=s' => \$stdsyms_file, 'libsyms=s' => \$libsyms_file,
'readelf=s' => \$READELF);
# Load the list of symbols that are OK.
%stdsyms = ();
open (STDSYMS, "<$stdsyms_file") || die ("open $stdsyms_file: $!\n");
while (<STDSYMS>) {
chomp;
$stdsyms{$_} = 1;
}
close (STDSYMS) || die ("close $stdsyms_file: $!\n");
# The following whitelisted symbols are also allowed for now.
#
# * Bug 17576: stdin, stdout, stderr only reserved with external
# linkage when stdio.h included (and possibly not then), not
# generally.
#
# * Bug 18442: re_syntax_options wrongly brought in by regcomp and
# used by re_comp.
#
# * False positive: matherr only used conditionally. matherrf/matherrl are used
# by IA64 too for the same reason.
#
@whitelist = qw(stdin stdout stderr re_syntax_options matherr matherrf
matherrl);
foreach my $sym (@whitelist) {
$stdsyms{$sym} = 1;
}
# Return information about GLOBAL and WEAK symbols listed in readelf
# -s output.
sub list_syms {
my ($syms_file) = @_;
open (SYMS, "<$syms_file") || die ("open $syms_file: $!\n");
my ($file) = $syms_file;
my (@ret) = ();
while (<SYMS>) {
chomp;
if (/^File: (.*)/) {
$file = $1;
$file =~ s|^.*/||;
next;
}
s/^\s*//;
# Architecture-specific st_other bits appear inside [] and disrupt
# the format of readelf output.
s/\[.*?\]//;
my (@fields) = split (/\s+/, $_);
if (@fields < 8) {
next;
}
my ($bind) = $fields[4];
my ($ndx) = $fields[6];
my ($sym) = $fields[7];
if ($bind ne "GLOBAL" && $bind ne "WEAK") {
next;
}
if ($sym !~ /^\w+$/) {
next;
}
push (@ret, [$file, $sym, $bind, $ndx ne "UND"]);
}
close (SYMS) || die ("close $syms_file: $!\n");
return @ret;
}
# Load information about GLOBAL and WEAK symbols defined or used in
# the standard libraries.
# Symbols from a given object, except for weak defined symbols.
%seen_syms = ();
# Strong undefined symbols from a given object.
%strong_undef_syms = ();
# Objects defining a given symbol (strongly or weakly).
%sym_objs = ();
@sym_data = list_syms ($libsyms_file);
foreach my $sym (@sym_data) {
my ($file, $name, $bind, $defined) = @$sym;
if ($defined) {
if (!defined ($sym_objs{$name})) {
$sym_objs{$name} = [];
}
push (@{$sym_objs{$name}}, $file);
}
if ($bind eq "GLOBAL" || !$defined) {
if (!defined ($seen_syms{$file})) {
$seen_syms{$file} = [];
}
push (@{$seen_syms{$file}}, $name);
}
if ($bind eq "GLOBAL" && !$defined) {
if (!defined ($strong_undef_syms{$file})) {
$strong_undef_syms{$file} = [];
}
push (@{$strong_undef_syms{$file}}, $name);
}
}
# Determine what ELF-level symbols are brought in by use of C-level
# symbols declared in the given header.
#
# The rules followed are heuristic and so may produce false positives
# and false negatives.
#
# * All undefined symbols are considered of signficance, but it is
# possible that (a) any standard library definition is weak, so can be
# overridden by the user's definition, and (b) the symbol is only used
# conditionally and not if the program is limited to standard
# functionality. (matherr is an example of such a false positive.)
#
# * If a symbol reference is only brought in by the user using a data
# symbol rather than a function from the standard library, this will
# not be detected.
#
# * If a symbol reference is only brought in by crt*.o or libgcc, this
# will not be detected.
#
# * If a symbol reference is only brought in through __builtin_foo in
# a standard macro being compiled to call foo, this will not be
# detected.
#
# * Header inclusions should be compiled several times with different
# options such as -O2, -D_FORTIFY_SOURCE and -D_FILE_OFFSET_BITS=64 to
# find out what symbols are undefined from such a compilation; this is
# not yet implemented.
#
# * This script finds symbols referenced through use of macros on the
# basis that if a macro calls an internal function, that function must
# also be declared in the header. However, the header might also
# declare implementation-namespace functions that are not called by
# any standard macro in the header, resulting in false positives for
# any symbols brought in only through use of those
# implementation-namespace functions.
#
# * Namespace issues can apply for dynamic linking as well as static
# linking, when a call is from one shared library to another or uses a
# PLT entry for a call within a shared library; such issues are only
# detected by this script if the same namespace issue applies for
# static linking.
@c_syms = list_exported_functions ("$CC $flags", $standard, $header, $tmpdir);
$cincfile = "$tmpdir/undef-$$.c";
$cincfile_o = "$tmpdir/undef-$$.o";
$cincfile_sym = "$tmpdir/undef-$$.sym";
open (CINCFILE, ">$cincfile") || die ("open $cincfile: $!\n");
print CINCFILE "#include <$header>\n";
foreach my $sym (sort @c_syms) {
print CINCFILE "void *__glibc_test_$sym = (void *) &$sym;\n";
}
close CINCFILE || die ("close $cincfile: $!\n");
system ("$CC $flags -D_ISOMAC $CFLAGS{$standard} -c $cincfile -o $cincfile_o")
&& die ("compiling failed\n");
system ("LC_ALL=C $READELF -W -s $cincfile_o > $cincfile_sym")
&& die ("readelf failed\n");
@elf_syms = list_syms ($cincfile_sym);
unlink ($cincfile) || die ("unlink $cincfile: $!\n");
unlink ($cincfile_o) || die ("unlink $cincfile_o: $!\n");
unlink ($cincfile_sym) || die ("unlink $cincfile_sym: $!\n");
%seen_where = ();
%files_seen = ();
%all_undef = ();
%current_undef = ();
foreach my $sym (@elf_syms) {
my ($file, $name, $bind, $defined) = @$sym;
if ($bind eq "GLOBAL" && !$defined) {
$seen_where{$name} = "[initial] $name";
$all_undef{$name} = "[initial] $name";
$current_undef{$name} = "[initial] $name";
}
}
while (%current_undef) {
%new_undef = ();
foreach my $sym (sort keys %current_undef) {
foreach my $file (@{$sym_objs{$sym}}) {
if (defined ($files_seen{$file})) {
next;
}
$files_seen{$file} = 1;
foreach my $ssym (@{$seen_syms{$file}}) {
if (!defined ($seen_where{$ssym})) {
$seen_where{$ssym} = "$current_undef{$sym} -> [$file] $ssym";
}
}
foreach my $usym (@{$strong_undef_syms{$file}}) {
if (!defined ($all_undef{$usym})) {
$all_undef{$usym} = "$current_undef{$sym} -> [$file] $usym";
$new_undef{$usym} = "$current_undef{$sym} -> [$file] $usym";
}
}
}
}
%current_undef = %new_undef;
}
$ret = 0;
foreach my $sym (sort keys %seen_where) {
if ($sym =~ /^_/) {
next;
}
if (defined ($stdsyms{$sym})) {
next;
}
print "$seen_where{$sym}\n";
$ret = 1;
}
exit $ret;