1 1.1 christos #! /usr/bin/env perl 2 1.1 christos # -*- mode: Perl -*- 3 1.1.1.2 christos # Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved. 4 1.1 christos # 5 1.1.1.3 christos # Licensed under the Apache License 2.0 (the "License"). You may not use 6 1.1 christos # this file except in compliance with the License. You can obtain a copy 7 1.1 christos # in the file LICENSE in the source distribution or at 8 1.1 christos # https://www.openssl.org/source/license.html 9 1.1 christos 10 1.1 christos use strict; 11 1.1 christos use File::Spec::Functions qw(devnull); 12 1.1.1.3 christos use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file); 13 1.1 christos use OpenSSL::Test::Utils; 14 1.1 christos 15 1.1.1.3 christos BEGIN { 16 1.1.1.3 christos setup("test_symbol_presence"); 17 1.1.1.3 christos } 18 1.1 christos 19 1.1.1.3 christos use lib srctop_dir('Configurations'); 20 1.1.1.3 christos use lib bldtop_dir('.'); 21 1.1.1.3 christos use platform; 22 1.1.1.3 christos 23 1.1.1.3 christos plan skip_all => "Test is disabled on NonStop" if config('target') =~ m|^nonstop|; 24 1.1.1.3 christos # MacOS arranges symbol names differently 25 1.1.1.3 christos plan skip_all => "Test is disabled on MacOS" if config('target') =~ m|^darwin|; 26 1.1.1.3 christos plan skip_all => "This is unsupported on MSYS, MinGW or MSWin32" 27 1.1.1.3 christos if $^O eq 'msys' or $^O eq 'MSWin32' or config('target') =~ m|^mingw|; 28 1.1 christos plan skip_all => "Only useful when building shared libraries" 29 1.1 christos if disabled("shared"); 30 1.1 christos 31 1.1 christos my @libnames = ("crypto", "ssl"); 32 1.1 christos my $testcount = scalar @libnames; 33 1.1 christos 34 1.1 christos plan tests => $testcount * 2; 35 1.1 christos 36 1.1 christos note 37 1.1 christos "NOTE: developer test! It's possible that it won't run on your\n", 38 1.1 christos "platform, and that's perfectly fine. This is mainly for developers\n", 39 1.1 christos "on Unix to check that our shared libraries are consistent with the\n", 40 1.1 christos "ordinals (util/*.num in the source tree), something that should be\n", 41 1.1 christos "good enough a check for the other platforms as well.\n"; 42 1.1 christos 43 1.1 christos foreach my $libname (@libnames) { 44 1.1 christos SKIP: 45 1.1 christos { 46 1.1.1.3 christos my $shlibname = platform->sharedlib("lib$libname"); 47 1.1.1.3 christos my $shlibpath = bldtop_file($shlibname); 48 1.1 christos *OSTDERR = *STDERR; 49 1.1 christos *OSTDOUT = *STDOUT; 50 1.1 christos open STDERR, ">", devnull(); 51 1.1 christos open STDOUT, ">", devnull(); 52 1.1.1.2 christos my @nm_lines = map { s|\R$||; $_ } `nm -DPg $shlibpath 2> /dev/null`; 53 1.1 christos close STDERR; 54 1.1 christos close STDOUT; 55 1.1 christos *STDERR = *OSTDERR; 56 1.1 christos *STDOUT = *OSTDOUT; 57 1.1.1.2 christos skip "Can't run 'nm -DPg $shlibpath' => $?... ignoring", 2 58 1.1 christos unless $? == 0; 59 1.1 christos 60 1.1 christos my $bldtop = bldtop_dir(); 61 1.1 christos my @def_lines; 62 1.1 christos indir $bldtop => sub { 63 1.1 christos my $mkdefpath = srctop_file("util", "mkdef.pl"); 64 1.1.1.3 christos my $libnumpath = srctop_file("util", "lib$libname.num"); 65 1.1.1.3 christos @def_lines = map { s|\R$||; $_ } `$^X $mkdefpath --ordinals $libnumpath --name $libname --OS linux 2> /dev/null`; 66 1.1.1.3 christos ok($? == 0, "running 'cd $bldtop; $^X $mkdefpath --ordinals $libnumpath --name $libname --OS linux' => $?"); 67 1.1 christos }, create => 0, cleanup => 0; 68 1.1 christos 69 1.1 christos note "Number of lines in \@nm_lines before massaging: ", scalar @nm_lines; 70 1.1 christos note "Number of lines in \@def_lines before massaging: ", scalar @def_lines; 71 1.1 christos 72 1.1 christos # Massage the nm output to only contain defined symbols 73 1.1.1.4 christos # Common symbols need separate treatment 74 1.1.1.4 christos my %commons; 75 1.1.1.4 christos foreach (@nm_lines) { 76 1.1.1.4 christos if (m|^(.*) C .*|) { 77 1.1.1.4 christos $commons{$1}++; 78 1.1.1.4 christos } 79 1.1.1.4 christos } 80 1.1.1.4 christos foreach (sort keys %commons) { 81 1.1.1.4 christos note "Common symbol: $_"; 82 1.1.1.4 christos } 83 1.1.1.4 christos 84 1.1.1.2 christos @nm_lines = 85 1.1.1.2 christos sort 86 1.1.1.4 christos ( map { 87 1.1.1.4 christos # Drop the first space and everything following it 88 1.1.1.4 christos s| .*||; 89 1.1.1.4 christos # Drop OpenSSL dynamic version information if there is any 90 1.1.1.4 christos s|\@\@.+$||; 91 1.1.1.4 christos # Return the result 92 1.1.1.4 christos $_ 93 1.1.1.4 christos } 94 1.1.1.4 christos # Drop any symbol starting with a double underscore, they 95 1.1.1.4 christos # are reserved for the compiler / system ABI and are none 96 1.1.1.4 christos # of our business 97 1.1.1.4 christos grep !m|^__|, 98 1.1.1.4 christos # Only look at external definitions 99 1.1.1.4 christos grep m|.* [BDST] .*|, 100 1.1.1.4 christos @nm_lines ), 101 1.1.1.4 christos keys %commons; 102 1.1 christos 103 1.1 christos # Massage the mkdef.pl output to only contain global symbols 104 1.1 christos # The output we got is in Unix .map format, which has a global 105 1.1 christos # and a local section. We're only interested in the global 106 1.1 christos # section. 107 1.1 christos my $in_global = 0; 108 1.1 christos @def_lines = 109 1.1 christos sort 110 1.1 christos map { s|;||; s|\s+||g; $_ } 111 1.1 christos grep { $in_global = 1 if m|global:|; 112 1.1 christos $in_global = 0 if m|local:|; 113 1.1 christos $in_global = 0 if m|\}|; 114 1.1 christos $in_global && m|;|; } @def_lines; 115 1.1 christos 116 1.1 christos note "Number of lines in \@nm_lines after massaging: ", scalar @nm_lines; 117 1.1 christos note "Number of lines in \@def_lines after massaging: ", scalar @def_lines; 118 1.1 christos 119 1.1 christos # Maintain lists of symbols that are missing in the shared library, 120 1.1 christos # or that are extra. 121 1.1 christos my @missing = (); 122 1.1 christos my @extra = (); 123 1.1 christos 124 1.1 christos while (scalar @nm_lines || scalar @def_lines) { 125 1.1 christos my $nm_first = $nm_lines[0]; 126 1.1 christos my $def_first = $def_lines[0]; 127 1.1 christos 128 1.1 christos if (!defined($nm_first)) { 129 1.1 christos push @missing, shift @def_lines; 130 1.1 christos } elsif (!defined($def_first)) { 131 1.1 christos push @extra, shift @nm_lines; 132 1.1 christos } elsif ($nm_first gt $def_first) { 133 1.1 christos push @missing, shift @def_lines; 134 1.1 christos } elsif ($nm_first lt $def_first) { 135 1.1 christos push @extra, shift @nm_lines; 136 1.1 christos } else { 137 1.1 christos shift @def_lines; 138 1.1 christos shift @nm_lines; 139 1.1 christos } 140 1.1 christos } 141 1.1 christos 142 1.1 christos if (scalar @missing) { 143 1.1.1.3 christos note "The following symbols are missing in ${shlibname}:"; 144 1.1 christos foreach (@missing) { 145 1.1 christos note " $_"; 146 1.1 christos } 147 1.1 christos } 148 1.1 christos if (scalar @extra) { 149 1.1.1.3 christos note "The following symbols are extra in ${shlibname}:"; 150 1.1 christos foreach (@extra) { 151 1.1 christos note " $_"; 152 1.1 christos } 153 1.1 christos } 154 1.1 christos ok(scalar @missing == 0, 155 1.1.1.3 christos "check that there are no missing symbols in ${shlibname}"); 156 1.1 christos } 157 1.1 christos } 158