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