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 10use strict; 11use File::Spec::Functions qw(devnull); 12use IPC::Cmd; 13use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file); 14use OpenSSL::Test::Utils; 15 16BEGIN { 17 setup("test_symbol_presence"); 18} 19 20use lib srctop_dir('Configurations'); 21use lib bldtop_dir('.'); 22use platform; 23 24plan skip_all => "Test is disabled on NonStop" if config('target') =~ m|^nonstop|; 25# MacOS arranges symbol names differently 26plan skip_all => "Test is disabled on MacOS" if config('target') =~ m|^darwin|; 27plan skip_all => "This is unsupported on platforms that don't have 'nm'" 28 unless IPC::Cmd::can_run('nm'); 29 30note 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 38my %stlibname; 39my %shlibname; 40my %stlibpath; 41my %shlibpath; 42my %defpath; 43foreach (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 50my $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 57plan tests => $testcount; 58 59###################################################################### 60# Collect symbols 61# [3 tests per library] 62 63my %stsymbols; # Static library symbols 64my %shsymbols; # Shared library symbols 65my %defsymbols; # Symbols taken from ordinals 66foreach (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 181my %symbols; 182foreach (sort keys %stlibname) { 183 foreach (@{$stsymbols{$_}}) { 184 $symbols{$_}++; 185 } 186} 187my @duplicates = sort grep { $symbols{$_} > 1 } keys %symbols; 188if (@duplicates) { 189 note "Duplicates:"; 190 note join('\n', @duplicates); 191} 192ok(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 199unless (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} 243