test/recipes/01-test_symbol_presence.t: check for duplicate symbols in static libs
This checks that all symbols are unique across all public static libraries. This includes a bit of refacftoring to avoid repeating code too much. Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20331)
This commit is contained in:
parent
2c1ec72a7a
commit
1817dcaf55
@ -9,6 +9,7 @@
|
||||
|
||||
use strict;
|
||||
use File::Spec::Functions qw(devnull);
|
||||
use IPC::Cmd;
|
||||
use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file);
|
||||
use OpenSSL::Test::Utils;
|
||||
|
||||
@ -23,54 +24,97 @@ use platform;
|
||||
plan skip_all => "Test is disabled on NonStop" if config('target') =~ m|^nonstop|;
|
||||
# MacOS arranges symbol names differently
|
||||
plan skip_all => "Test is disabled on MacOS" if config('target') =~ m|^darwin|;
|
||||
plan skip_all => "This is unsupported on MSYS, MinGW or MSWin32"
|
||||
if $^O eq 'msys' or $^O eq 'MSWin32' or config('target') =~ m|^mingw|;
|
||||
plan skip_all => "Only useful when building shared libraries"
|
||||
if disabled("shared");
|
||||
|
||||
my @libnames = ("crypto", "ssl");
|
||||
my $testcount = scalar @libnames;
|
||||
|
||||
plan tests => $testcount * 2;
|
||||
plan skip_all => "This is unsupported on platforms that don't have 'nm'"
|
||||
unless IPC::Cmd::can_run('nm');
|
||||
|
||||
note
|
||||
"NOTE: developer test! It's possible that it won't run on your\n",
|
||||
"platform, and that's perfectly fine. This is mainly for developers\n",
|
||||
"on Unix to check that our shared libraries are consistent with the\n",
|
||||
"ordinals (util/*.num in the source tree), something that should be\n",
|
||||
"good enough a check for the other platforms as well.\n";
|
||||
"ordinals (util/*.num in the source tree), and that our static libraries\n",
|
||||
"don't share symbols, something that should be a good enough check for\n",
|
||||
"the other platforms as well.\n";
|
||||
|
||||
foreach my $libname (@libnames) {
|
||||
SKIP:
|
||||
{
|
||||
my $shlibname = platform->sharedlib("lib$libname");
|
||||
my $shlibpath = bldtop_file($shlibname);
|
||||
*OSTDERR = *STDERR;
|
||||
*OSTDOUT = *STDOUT;
|
||||
open STDERR, ">", devnull();
|
||||
open STDOUT, ">", devnull();
|
||||
my @nm_lines = map { s|\R$||; $_ } `nm -DPg $shlibpath 2> /dev/null`;
|
||||
close STDERR;
|
||||
close STDOUT;
|
||||
*STDERR = *OSTDERR;
|
||||
*STDOUT = *OSTDOUT;
|
||||
skip "Can't run 'nm -DPg $shlibpath' => $?... ignoring", 2
|
||||
unless $? == 0;
|
||||
my %stlibname;
|
||||
my %shlibname;
|
||||
my %stlibpath;
|
||||
my %shlibpath;
|
||||
my %defpath;
|
||||
foreach (qw(crypto ssl)) {
|
||||
$stlibname{$_} = platform->staticlib("lib$_");
|
||||
$stlibpath{$_} = bldtop_file($stlibname{$_});
|
||||
$shlibname{$_} = platform->sharedlib("lib$_") unless disabled('shared');
|
||||
$shlibpath{$_} = bldtop_file($shlibname{$_}) unless disabled('shared');
|
||||
}
|
||||
|
||||
my $bldtop = bldtop_dir();
|
||||
my @def_lines;
|
||||
my $testcount
|
||||
= 1 # Check for static library symbols duplicates
|
||||
;
|
||||
$testcount
|
||||
+= (scalar keys %shlibpath) # Check for missing symbols in shared lib
|
||||
unless disabled('shared');
|
||||
|
||||
plan tests => $testcount;
|
||||
|
||||
######################################################################
|
||||
# Collect symbols
|
||||
# [3 tests per library]
|
||||
|
||||
my %stsymbols; # Static library symbols
|
||||
my %shsymbols; # Shared library symbols
|
||||
my %defsymbols; # Symbols taken from ordinals
|
||||
foreach (sort keys %stlibname) {
|
||||
my $stlib_cmd = "nm -Pg $stlibpath{$_} 2> /dev/null";
|
||||
my $shlib_cmd = "nm -DPg $shlibpath{$_} 2> /dev/null";
|
||||
my @stlib_lines;
|
||||
my @shlib_lines;
|
||||
*OSTDERR = *STDERR;
|
||||
*OSTDOUT = *STDOUT;
|
||||
open STDERR, ">", devnull();
|
||||
open STDOUT, ">", devnull();
|
||||
@stlib_lines = map { s|\R$||; $_ } `$stlib_cmd`;
|
||||
if ($? != 0) {
|
||||
note "running '$stlib_cmd' => $?";
|
||||
@stlib_lines = ();
|
||||
}
|
||||
unless (disabled('shared')) {
|
||||
@shlib_lines = map { s|\R$||; $_ } `$shlib_cmd`;
|
||||
if ($? != 0) {
|
||||
note "running '$shlib_cmd' => $?";
|
||||
@shlib_lines = ();
|
||||
}
|
||||
}
|
||||
close STDERR;
|
||||
close STDOUT;
|
||||
*STDERR = *OSTDERR;
|
||||
*STDOUT = *OSTDOUT;
|
||||
|
||||
my $bldtop = bldtop_dir();
|
||||
my @def_lines;
|
||||
unless (disabled('shared')) {
|
||||
indir $bldtop => sub {
|
||||
my $mkdefpath = srctop_file("util", "mkdef.pl");
|
||||
my $libnumpath = srctop_file("util", "lib$libname.num");
|
||||
@def_lines = map { s|\R$||; $_ } `$^X $mkdefpath --ordinals $libnumpath --name $libname --OS linux 2> /dev/null`;
|
||||
ok($? == 0, "running 'cd $bldtop; $^X $mkdefpath --ordinals $libnumpath --name $libname --OS linux' => $?");
|
||||
my $def_path = srctop_file("util", "lib$_.num");
|
||||
my $def_cmd = "$^X $mkdefpath --ordinals $def_path --name $_ --OS linux 2> /dev/null";
|
||||
@def_lines = map { s|\R$||; $_ } `$def_cmd`;
|
||||
if ($? != 0) {
|
||||
note "running 'cd $bldtop; $def_cmd' => $?";
|
||||
@def_lines = ();
|
||||
}
|
||||
}, create => 0, cleanup => 0;
|
||||
}
|
||||
|
||||
note "Number of lines in \@nm_lines before massaging: ", scalar @nm_lines;
|
||||
note "Number of lines in \@stlib_lines before massaging: ", scalar @stlib_lines;
|
||||
unless (disabled('shared')) {
|
||||
note "Number of lines in \@shlib_lines before massaging: ", scalar @shlib_lines;
|
||||
note "Number of lines in \@def_lines before massaging: ", scalar @def_lines;
|
||||
}
|
||||
|
||||
# Massage the nm output to only contain defined symbols
|
||||
@nm_lines =
|
||||
# Massage the nm output to only contain defined symbols
|
||||
my @arrays = ( \@stlib_lines );
|
||||
push @arrays, \@shlib_lines unless disabled('shared');
|
||||
foreach (@arrays) {
|
||||
@$_ =
|
||||
sort
|
||||
map {
|
||||
# Drop the first space and everything following it
|
||||
@ -80,13 +124,15 @@ foreach my $libname (@libnames) {
|
||||
# Return the result
|
||||
$_
|
||||
}
|
||||
grep(m|.* [BCDST] .*|, @nm_lines);
|
||||
grep(m|.* [BCDST] .*|, @$_);
|
||||
}
|
||||
|
||||
# Massage the mkdef.pl output to only contain global symbols
|
||||
# The output we got is in Unix .map format, which has a global
|
||||
# and a local section. We're only interested in the global
|
||||
# section.
|
||||
my $in_global = 0;
|
||||
# Massage the mkdef.pl output to only contain global symbols
|
||||
# The output we got is in Unix .map format, which has a global
|
||||
# and a local section. We're only interested in the global
|
||||
# section.
|
||||
my $in_global = 0;
|
||||
unless (disabled('shared')) {
|
||||
@def_lines =
|
||||
sort
|
||||
map { s|;||; s|\s+||g; $_ }
|
||||
@ -94,46 +140,82 @@ foreach my $libname (@libnames) {
|
||||
$in_global = 0 if m|local:|;
|
||||
$in_global = 0 if m|\}|;
|
||||
$in_global && m|;|; } @def_lines;
|
||||
}
|
||||
|
||||
note "Number of lines in \@nm_lines after massaging: ", scalar @nm_lines;
|
||||
note "Number of lines in \@stlib_lines after massaging: ", scalar @stlib_lines;
|
||||
unless (disabled('shared')) {
|
||||
|
||||
note "Number of lines in \@shlib_lines after massaging: ", scalar @shlib_lines;
|
||||
note "Number of lines in \@def_lines after massaging: ", scalar @def_lines;
|
||||
}
|
||||
|
||||
$stsymbols{$_} = [ @stlib_lines ];
|
||||
unless (disabled('shared')) {
|
||||
$shsymbols{$_} = [ @shlib_lines ];
|
||||
$defsymbols{$_} = [ @def_lines ];
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Check that there are no duplicate symbols in all our static libraries
|
||||
# combined
|
||||
# [1 test]
|
||||
|
||||
my %symbols;
|
||||
foreach (sort keys %stlibname) {
|
||||
foreach (@{$stsymbols{$_}}) {
|
||||
$symbols{$_}++;
|
||||
}
|
||||
}
|
||||
my @duplicates = sort grep { $symbols{$_} > 1 } keys %symbols;
|
||||
ok(scalar @duplicates == 0, "checking no duplicate symbols in static libraries");
|
||||
|
||||
######################################################################
|
||||
# Check that the exported symbols in our shared libraries are consistent
|
||||
# with our ordinals files.
|
||||
# [1 test per library]
|
||||
|
||||
unless (disabled('shared')) {
|
||||
foreach (sort keys %stlibname) {
|
||||
# Maintain lists of symbols that are missing in the shared library,
|
||||
# or that are extra.
|
||||
my @missing = ();
|
||||
my @extra = ();
|
||||
|
||||
while (scalar @nm_lines || scalar @def_lines) {
|
||||
my $nm_first = $nm_lines[0];
|
||||
my $def_first = $def_lines[0];
|
||||
my @sh_symbols = ( @{$shsymbols{$_}} );
|
||||
my @def_symbols = ( @{$defsymbols{$_}} );
|
||||
|
||||
if (!defined($nm_first)) {
|
||||
push @missing, shift @def_lines;
|
||||
while (scalar @sh_symbols || scalar @def_symbols) {
|
||||
my $sh_first = $sh_symbols[0];
|
||||
my $def_first = $def_symbols[0];
|
||||
|
||||
if (!defined($sh_first)) {
|
||||
push @missing, shift @def_symbols;
|
||||
} elsif (!defined($def_first)) {
|
||||
push @extra, shift @nm_lines;
|
||||
} elsif ($nm_first gt $def_first) {
|
||||
push @missing, shift @def_lines;
|
||||
} elsif ($nm_first lt $def_first) {
|
||||
push @extra, shift @nm_lines;
|
||||
push @extra, shift @sh_symbols;
|
||||
} elsif ($sh_first gt $def_first) {
|
||||
push @missing, shift @def_symbols;
|
||||
} elsif ($sh_first lt $def_first) {
|
||||
push @extra, shift @sh_symbols;
|
||||
} else {
|
||||
shift @def_lines;
|
||||
shift @nm_lines;
|
||||
shift @def_symbols;
|
||||
shift @sh_symbols;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar @missing) {
|
||||
note "The following symbols are missing in ${shlibname}:";
|
||||
note "The following symbols are missing in $_:";
|
||||
foreach (@missing) {
|
||||
note " $_";
|
||||
}
|
||||
}
|
||||
if (scalar @extra) {
|
||||
note "The following symbols are extra in ${shlibname}:";
|
||||
note "The following symbols are extra in $_:";
|
||||
foreach (@extra) {
|
||||
note " $_";
|
||||
}
|
||||
}
|
||||
ok(scalar @missing == 0,
|
||||
"check that there are no missing symbols in ${shlibname}");
|
||||
"check that there are no missing symbols in $_");
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user