rand: remove unimplemented librandom stub code
[openssl.git] / test / recipes / 01-test_symbol_presence.t
1 #! /usr/bin/env perl
2 # -*- mode: Perl -*-
3 # Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
4 #
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
9
10 use strict;
11 use File::Spec::Functions qw(devnull);
12 use IPC::Cmd;
13 use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file);
14 use OpenSSL::Test::Utils;
15
16 BEGIN {
17     setup("test_symbol_presence");
18 }
19
20 use lib srctop_dir('Configurations');
21 use lib bldtop_dir('.');
22 use platform;
23
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');
29
30 note
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";
37
38 my %stlibname;
39 my %shlibname;
40 my %stlibpath;
41 my %shlibpath;
42 my %defpath;
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');
48 }
49
50 my $testcount
51     =  1                        # Check for static library symbols duplicates
52     ;
53 $testcount
54     += (scalar keys %shlibpath) # Check for missing symbols in shared lib
55     unless disabled('shared');
56
57 plan tests => $testcount;
58
59 ######################################################################
60 # Collect symbols
61 # [3 tests per library]
62
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";
69     my @stlib_lines;
70     my @shlib_lines;
71     *OSTDERR = *STDERR;
72     *OSTDOUT = *STDOUT;
73     open STDERR, ">", devnull();
74     open STDOUT, ">", devnull();
75     @stlib_lines = map { s|\R$||; $_ } `$stlib_cmd`;
76     if ($? != 0) {
77         note "running '$stlib_cmd' => $?";
78         @stlib_lines = ();
79     }
80     unless (disabled('shared')) {
81         @shlib_lines = map { s|\R$||; $_ } `$shlib_cmd`;
82         if ($? != 0) {
83             note "running '$shlib_cmd' => $?";
84             @shlib_lines = ();
85         }
86     }
87     close STDERR;
88     close STDOUT;
89     *STDERR = *OSTDERR;
90     *STDOUT = *OSTDOUT;
91
92     my $bldtop = bldtop_dir();
93     my @def_lines;
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`;
100             if ($? != 0) {
101                 note "running 'cd $bldtop; $def_cmd' => $?";
102                 @def_lines = ();
103             }
104         }, create => 0, cleanup => 0;
105     }
106
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;
111     }
112
113     # Massage the nm output to only contain defined symbols
114     my @arrays = ( \@stlib_lines );
115     push @arrays, \@shlib_lines unless disabled('shared');
116     foreach (@arrays) {
117         my %commons;
118         foreach (@$_) {
119             if (m|^(.*) C .*|) {
120                 $commons{$1}++;
121             }
122         }
123         foreach (sort keys %commons) {
124             note "Common symbol: $_";
125         }
126
127         @$_ =
128             sort
129             ( map {
130                   # Drop the first space and everything following it
131                   s| .*||;
132                   # Drop OpenSSL dynamic version information if there is any
133                   s|\@\@.+$||;
134                   # Return the result
135                   $_
136               }
137               # Drop any symbol starting with a double underscore, they
138               # are reserved for the compiler / system ABI and are none
139               # of our business
140               grep !m|^__|,
141               # Only look at external definitions
142               grep m|.* [BDST] .*|,
143               @$_ ),
144             keys %commons;
145     }
146
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
150     # section.
151     my $in_global = 0;
152     unless (disabled('shared')) {
153         @def_lines =
154             sort
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;
160     }
161
162     note "Number of lines in \@stlib_lines after massaging: ", scalar @stlib_lines;
163     unless (disabled('shared')) {
164
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;
167     }
168
169     $stsymbols{$_} = [ @stlib_lines ];
170     unless (disabled('shared')) {
171         $shsymbols{$_} = [ @shlib_lines ];
172         $defsymbols{$_} = [ @def_lines ];
173     }
174 }
175
176 ######################################################################
177 # Check that there are no duplicate symbols in all our static libraries
178 # combined
179 # [1 test]
180
181 my %symbols;
182 foreach (sort keys %stlibname) {
183     foreach (@{$stsymbols{$_}}) {
184         $symbols{$_}++;
185     }
186 }
187 my @duplicates = sort grep { $symbols{$_} > 1 } keys %symbols;
188 if (@duplicates) {
189     note "Duplicates:";
190     note join('\n', @duplicates);
191 }
192 ok(scalar @duplicates == 0, "checking no duplicate symbols in static libraries");
193
194 ######################################################################
195 # Check that the exported symbols in our shared libraries are consistent
196 # with our ordinals files.
197 # [1 test per library]
198
199 unless (disabled('shared')) {
200     foreach (sort keys %stlibname) {
201         # Maintain lists of symbols that are missing in the shared library,
202         # or that are extra.
203         my @missing = ();
204         my @extra = ();
205
206         my @sh_symbols = ( @{$shsymbols{$_}} );
207         my @def_symbols = ( @{$defsymbols{$_}} );
208
209         while (scalar @sh_symbols || scalar @def_symbols) {
210             my $sh_first = $sh_symbols[0];
211             my $def_first = $def_symbols[0];
212
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;
221             } else {
222                 shift @def_symbols;
223                 shift @sh_symbols;
224             }
225         }
226
227         if (scalar @missing) {
228             note "The following symbols are missing in $_:";
229             foreach (@missing) {
230                 note "  $_";
231             }
232         }
233         if (scalar @extra) {
234             note "The following symbols are extra in $_:";
235             foreach (@extra) {
236                 note "  $_";
237             }
238         }
239         ok(scalar @missing == 0,
240            "check that there are no missing symbols in $_");
241     }
242 }