3 # Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
5 # Licensed under the Apache License 2.0 (the "License"). You may not use
6 # this file except in compliance with the License. You can obtain a copy
7 # in the file LICENSE in the source distribution or at
8 # https://www.openssl.org/source/license.html
11 use File::Spec::Functions qw(devnull);
13 use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file);
14 use OpenSSL::Test::Utils;
17 setup("test_symbol_presence");
20 use lib srctop_dir('Configurations');
21 use lib bldtop_dir('.');
24 plan skip_all => "Test is disabled on NonStop" if config('target') =~ m|^nonstop|;
25 # MacOS arranges symbol names differently
26 plan skip_all => "Test is disabled on MacOS" if config('target') =~ m|^darwin|;
27 plan skip_all => "This is unsupported on platforms that don't have 'nm'"
28 unless IPC::Cmd::can_run('nm');
31 "NOTE: developer test! It's possible that it won't run on your\n",
32 "platform, and that's perfectly fine. This is mainly for developers\n",
33 "on Unix to check that our shared libraries are consistent with the\n",
34 "ordinals (util/*.num in the source tree), and that our static libraries\n",
35 "don't share symbols, something that should be a good enough check for\n",
36 "the other platforms as well.\n";
43 foreach (qw(crypto ssl)) {
44 $stlibname{$_} = platform->staticlib("lib$_");
45 $stlibpath{$_} = bldtop_file($stlibname{$_});
46 $shlibname{$_} = platform->sharedlib("lib$_") unless disabled('shared');
47 $shlibpath{$_} = bldtop_file($shlibname{$_}) unless disabled('shared');
51 = 1 # Check for static library symbols duplicates
54 += (scalar keys %shlibpath) # Check for missing symbols in shared lib
55 unless disabled('shared');
57 plan tests => $testcount;
59 ######################################################################
61 # [3 tests per library]
63 my %stsymbols; # Static library symbols
64 my %shsymbols; # Shared library symbols
65 my %defsymbols; # Symbols taken from ordinals
66 foreach (sort keys %stlibname) {
67 my $stlib_cmd = "nm -Pg $stlibpath{$_} 2> /dev/null";
68 my $shlib_cmd = "nm -DPg $shlibpath{$_} 2> /dev/null";
73 open STDERR, ">", devnull();
74 open STDOUT, ">", devnull();
75 @stlib_lines = map { s|\R$||; $_ } `$stlib_cmd`;
77 note "running '$stlib_cmd' => $?";
80 unless (disabled('shared')) {
81 @shlib_lines = map { s|\R$||; $_ } `$shlib_cmd`;
83 note "running '$shlib_cmd' => $?";
92 my $bldtop = bldtop_dir();
94 unless (disabled('shared')) {
95 indir $bldtop => sub {
96 my $mkdefpath = srctop_file("util", "mkdef.pl");
97 my $def_path = srctop_file("util", "lib$_.num");
98 my $def_cmd = "$^X $mkdefpath --ordinals $def_path --name $_ --OS linux 2> /dev/null";
99 @def_lines = map { s|\R$||; $_ } `$def_cmd`;
101 note "running 'cd $bldtop; $def_cmd' => $?";
104 }, create => 0, cleanup => 0;
107 note "Number of lines in \@stlib_lines before massaging: ", scalar @stlib_lines;
108 unless (disabled('shared')) {
109 note "Number of lines in \@shlib_lines before massaging: ", scalar @shlib_lines;
110 note "Number of lines in \@def_lines before massaging: ", scalar @def_lines;
113 # Massage the nm output to only contain defined symbols
114 my @arrays = ( \@stlib_lines );
115 push @arrays, \@shlib_lines unless disabled('shared');
123 foreach (sort keys %commons) {
124 note "Common symbol: $_";
130 # Drop the first space and everything following it
132 # Drop OpenSSL dynamic version information if there is any
137 # Drop any symbol starting with a double underscore, they
138 # are reserved for the compiler / system ABI and are none
141 # Only look at external definitions
142 grep m|.* [BDST] .*|,
147 # Massage the mkdef.pl output to only contain global symbols
148 # The output we got is in Unix .map format, which has a global
149 # and a local section. We're only interested in the global
152 unless (disabled('shared')) {
155 map { s|;||; s|\s+||g; $_ }
156 grep { $in_global = 1 if m|global:|;
157 $in_global = 0 if m|local:|;
158 $in_global = 0 if m|\}|;
159 $in_global && m|;|; } @def_lines;
162 note "Number of lines in \@stlib_lines after massaging: ", scalar @stlib_lines;
163 unless (disabled('shared')) {
165 note "Number of lines in \@shlib_lines after massaging: ", scalar @shlib_lines;
166 note "Number of lines in \@def_lines after massaging: ", scalar @def_lines;
169 $stsymbols{$_} = [ @stlib_lines ];
170 unless (disabled('shared')) {
171 $shsymbols{$_} = [ @shlib_lines ];
172 $defsymbols{$_} = [ @def_lines ];
176 ######################################################################
177 # Check that there are no duplicate symbols in all our static libraries
182 foreach (sort keys %stlibname) {
183 foreach (@{$stsymbols{$_}}) {
187 my @duplicates = sort grep { $symbols{$_} > 1 } keys %symbols;
190 note join('\n', @duplicates);
192 ok(scalar @duplicates == 0, "checking no duplicate symbols in static libraries");
194 ######################################################################
195 # Check that the exported symbols in our shared libraries are consistent
196 # with our ordinals files.
197 # [1 test per library]
199 unless (disabled('shared')) {
200 foreach (sort keys %stlibname) {
201 # Maintain lists of symbols that are missing in the shared library,
206 my @sh_symbols = ( @{$shsymbols{$_}} );
207 my @def_symbols = ( @{$defsymbols{$_}} );
209 while (scalar @sh_symbols || scalar @def_symbols) {
210 my $sh_first = $sh_symbols[0];
211 my $def_first = $def_symbols[0];
213 if (!defined($sh_first)) {
214 push @missing, shift @def_symbols;
215 } elsif (!defined($def_first)) {
216 push @extra, shift @sh_symbols;
217 } elsif ($sh_first gt $def_first) {
218 push @missing, shift @def_symbols;
219 } elsif ($sh_first lt $def_first) {
220 push @extra, shift @sh_symbols;
227 if (scalar @missing) {
228 note "The following symbols are missing in $_:";
234 note "The following symbols are extra in $_:";
239 ok(scalar @missing == 0,
240 "check that there are no missing symbols in $_");