123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- #! /usr/bin/env perl
- # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
- #
- # Licensed under the OpenSSL license (the "License"). You may not use
- # this file except in compliance with the License. You can obtain a copy
- # in the file LICENSE in the source distribution or at
- # https://www.openssl.org/source/license.html
- # Generate a linker version script suitable for the given platform
- # from a given ordinals file.
- use strict;
- use warnings;
- use Getopt::Long;
- use FindBin;
- use lib "$FindBin::Bin/perl";
- use OpenSSL::Ordinals;
- use lib '.';
- use configdata;
- my $name = undef; # internal library/module name
- my $ordinals_file = undef; # the ordinals file to use
- my $version = undef; # the version to use for the library
- my $OS = undef; # the operating system family
- my $verbose = 0;
- my $ctest = 0;
- # For VMS, some modules may have case insensitive names
- my $case_insensitive = 0;
- GetOptions('name=s' => \$name,
- 'ordinals=s' => \$ordinals_file,
- 'version=s' => \$version,
- 'OS=s' => \$OS,
- 'ctest' => \$ctest,
- 'verbose' => \$verbose,
- # For VMS
- 'case-insensitive' => \$case_insensitive)
- or die "Error in command line arguments\n";
- die "Please supply arguments\n"
- unless $name && $ordinals_file && $OS;
- # When building a "variant" shared library, with a custom SONAME, also customize
- # all the symbol versions. This produces a shared object that can coexist
- # without conflict in the same address space as a default build, or an object
- # with a different variant tag.
- #
- # For example, with a target definition that includes:
- #
- # shlib_variant => "-opt",
- #
- # we build the following objects:
- #
- # $ perl -le '
- # for (@ARGV) {
- # if ($l = readlink) {
- # printf "%s -> %s\n", $_, $l
- # } else {
- # print
- # }
- # }' *.so*
- # libcrypto-opt.so.1.1
- # libcrypto.so -> libcrypto-opt.so.1.1
- # libssl-opt.so.1.1
- # libssl.so -> libssl-opt.so.1.1
- #
- # whose SONAMEs and dependencies are:
- #
- # $ for l in *.so; do
- # echo $l
- # readelf -d $l | egrep 'SONAME|NEEDED.*(ssl|crypto)'
- # done
- # libcrypto.so
- # 0x000000000000000e (SONAME) Library soname: [libcrypto-opt.so.1.1]
- # libssl.so
- # 0x0000000000000001 (NEEDED) Shared library: [libcrypto-opt.so.1.1]
- # 0x000000000000000e (SONAME) Library soname: [libssl-opt.so.1.1]
- #
- # We case-fold the variant tag to upper case and replace all non-alnum
- # characters with "_". This yields the following symbol versions:
- #
- # $ nm libcrypto.so | grep -w A
- # 0000000000000000 A OPENSSL_OPT_1_1_0
- # 0000000000000000 A OPENSSL_OPT_1_1_0a
- # 0000000000000000 A OPENSSL_OPT_1_1_0c
- # 0000000000000000 A OPENSSL_OPT_1_1_0d
- # 0000000000000000 A OPENSSL_OPT_1_1_0f
- # 0000000000000000 A OPENSSL_OPT_1_1_0g
- # $ nm libssl.so | grep -w A
- # 0000000000000000 A OPENSSL_OPT_1_1_0
- # 0000000000000000 A OPENSSL_OPT_1_1_0d
- #
- (my $SO_VARIANT = uc($target{"shlib_variant"} // '')) =~ s/\W/_/g;
- my $apiv = undef;
- $apiv = sprintf "%x%02x%02x", split(/\./, $config{api})
- if $config{api};
- my $libname = $unified_info{sharednames}->{$name} // $name;
- my %OS_data = (
- solaris => { writer => \&writer_linux,
- sort => sorter_linux(),
- platforms => { UNIX => 1,
- EXPORT_VAR_AS_FUNCTION => 0 } },
- linux => 'solaris', # alias
- "bsd-gcc" => 'solaris', # alias
- aix => { writer => \&writer_aix,
- sort => sorter_unix(),
- platforms => { UNIX => 1,
- EXPORT_VAR_AS_FUNCTION => 0 } },
- VMS => { writer => \&writer_VMS,
- sort => OpenSSL::Ordinals::by_number(),
- platforms => { VMS => 1,
- EXPORT_VAR_AS_FUNCTION => 0 } },
- vms => 'VMS', # alias
- WINDOWS => { writer => \&writer_windows,
- sort => OpenSSL::Ordinals::by_name(),
- platforms => { WIN32 => 1,
- _WIN32 => 1,
- EXPORT_VAR_AS_FUNCTION => 1 } },
- windows => 'WINDOWS', # alias
- WIN32 => 'WINDOWS', # alias
- win32 => 'WIN32', # alias
- 32 => 'WIN32', # alias
- NT => 'WIN32', # alias
- nt => 'WIN32', # alias
- mingw => 'WINDOWS', # alias
- );
- do {
- die "Unknown operating system family $OS\n"
- unless exists $OS_data{$OS};
- $OS = $OS_data{$OS};
- } while(ref($OS) eq '');
- my %disabled_uc = map { my $x = uc $_; $x =~ s|-|_|g; $x => 1 } keys %disabled;
- my %ordinal_opts = ();
- $ordinal_opts{sort} = $OS->{sort} if $OS->{sort};
- $ordinal_opts{filter} =
- sub {
- my $item = shift;
- return
- $item->exists()
- && platform_filter($item)
- && feature_filter($item);
- };
- my $ordinals = OpenSSL::Ordinals->new(from => $ordinals_file);
- my $writer = $OS->{writer};
- $writer = \&writer_ctest if $ctest;
- $writer->($ordinals->items(%ordinal_opts));
- exit 0;
- sub platform_filter {
- my $item = shift;
- my %platforms = ( $item->platforms() );
- # True if no platforms are defined
- return 1 if scalar keys %platforms == 0;
- # For any item platform tag, return the equivalence with the
- # current platform settings if it exists there, return 0 otherwise
- # if the item platform tag is true
- for (keys %platforms) {
- if (exists $OS->{platforms}->{$_}) {
- return $platforms{$_} == $OS->{platforms}->{$_};
- }
- if ($platforms{$_}) {
- return 0;
- }
- }
- # Found no match? Then it's a go
- return 1;
- }
- sub feature_filter {
- my $item = shift;
- my @features = ( $item->features() );
- # True if no features are defined
- return 1 if scalar @features == 0;
- my $verdict = ! grep { $disabled_uc{$_} } @features;
- if ($apiv) {
- foreach (@features) {
- next unless /^DEPRECATEDIN_(\d+)_(\d+)_(\d+)$/;
- my $symdep = sprintf "%x%02x%02x", $1, $2, $3;
- $verdict = 0 if $apiv ge $symdep;
- }
- }
- return $verdict;
- }
- sub sorter_unix {
- my $by_name = OpenSSL::Ordinals::by_name();
- my %weight = (
- 'FUNCTION' => 1,
- 'VARIABLE' => 2
- );
- return sub {
- my $item1 = shift;
- my $item2 = shift;
- my $verdict = $weight{$item1->type()} <=> $weight{$item2->type()};
- if ($verdict == 0) {
- $verdict = $by_name->($item1, $item2);
- }
- return $verdict;
- };
- }
- sub sorter_linux {
- my $by_version = OpenSSL::Ordinals::by_version();
- my $by_unix = sorter_unix();
- return sub {
- my $item1 = shift;
- my $item2 = shift;
- my $verdict = $by_version->($item1, $item2);
- if ($verdict == 0) {
- $verdict = $by_unix->($item1, $item2);
- }
- return $verdict;
- };
- }
- sub writer_linux {
- my $thisversion = '';
- my $currversion_s = '';
- my $prevversion_s = '';
- my $indent = 0;
- for (@_) {
- if ($thisversion && $_->version() ne $thisversion) {
- die "$ordinals_file: It doesn't make sense to have both versioned ",
- "and unversioned symbols"
- if $thisversion eq '*';
- print <<"_____";
- }${prevversion_s};
- _____
- $prevversion_s = " OPENSSL${SO_VARIANT}_$thisversion";
- $thisversion = ''; # Trigger start of next section
- }
- unless ($thisversion) {
- $indent = 0;
- $thisversion = $_->version();
- $currversion_s = '';
- $currversion_s = "OPENSSL${SO_VARIANT}_$thisversion "
- if $thisversion ne '*';
- print <<"_____";
- ${currversion_s}{
- global:
- _____
- }
- print ' ', $_->name(), ";\n";
- }
- print <<"_____";
- local: *;
- }${prevversion_s};
- _____
- }
- sub writer_aix {
- for (@_) {
- print $_->name(),"\n";
- }
- }
- sub writer_windows {
- print <<"_____";
- ;
- ; Definition file for the DLL version of the $libname library from OpenSSL
- ;
- LIBRARY $libname
- EXPORTS
- _____
- for (@_) {
- print " ",$_->name(),"\n";
- }
- }
- sub collect_VMS_mixedcase {
- return [ 'SPARE', 'SPARE' ] unless @_;
- my $s = shift;
- my $s_uc = uc($s);
- my $type = shift;
- return [ "$s=$type", 'SPARE' ] if $s_uc eq $s;
- return [ "$s_uc/$s=$type", "$s=$type" ];
- }
- sub collect_VMS_uppercase {
- return [ 'SPARE' ] unless @_;
- my $s = shift;
- my $s_uc = uc($s);
- my $type = shift;
- return [ "$s_uc=$type" ];
- }
- sub writer_VMS {
- my @slot_collection = ();
- my $collector =
- $case_insensitive ? \&collect_VMS_uppercase : \&collect_VMS_mixedcase;
- my $last_num = 0;
- foreach (@_) {
- while (++$last_num < $_->number()) {
- push @slot_collection, $collector->(); # Just occupy a slot
- }
- my $type = {
- FUNCTION => 'PROCEDURE',
- VARIABLE => 'DATA'
- } -> {$_->type()};
- push @slot_collection, $collector->($_->name(), $type);
- }
- print <<"_____" if defined $version;
- IDENTIFICATION=$version
- _____
- print <<"_____" unless $case_insensitive;
- CASE_SENSITIVE=YES
- _____
- print <<"_____";
- SYMBOL_VECTOR=(-
- _____
- # It's uncertain how long aggregated lines the linker can handle,
- # but it has been observed that at least 1024 characters is ok.
- # Either way, this means that we need to keep track of the total
- # line length of each "SYMBOL_VECTOR" statement. Fortunately, we
- # can have more than one of those...
- my $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
- while (@slot_collection) {
- my $set = shift @slot_collection;
- my $settextlength = 0;
- foreach (@$set) {
- $settextlength +=
- + 3 # two space indentation and comma
- + length($_)
- + 1 # postdent
- ;
- }
- $settextlength--; # only one space indentation on the first one
- my $firstcomma = ',';
- if ($symvtextcount + $settextlength > 1024) {
- print <<"_____";
- )
- SYMBOL_VECTOR=(-
- _____
- $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
- }
- if ($symvtextcount == 16) {
- $firstcomma = '';
- }
- my $indent = ' '.$firstcomma;
- foreach (@$set) {
- print <<"_____";
- $indent$_ -
- _____
- $symvtextcount += length($indent) + length($_) + 1;
- $indent = ' ,';
- }
- }
- print <<"_____";
- )
- _____
- if (defined $version) {
- my ($libvmajor, $libvminor, $libvedit, $libvpatch) =
- $version =~ /^(\d+)_(\d+)_(\d+)([a-z]{0,2})(?:-.*)?$/;
- my $libvpatchnum = 0;
- for (split '', $libvpatch // '') {
- $libvpatchnum += ord(lc($_)) - 96;
- # To compensate because the letter 'z' is always followed by
- # another, i.e. doesn't add any value on its own
- $libvpatchnum-- if lc($_) eq 'z';
- }
- my $match1 = $libvmajor * 100 + $libvminor;
- my $match2 = $libvedit * 100 + $libvpatchnum;
- print <<"_____";
- GSMATCH=LEQUAL,$match1,$match2
- _____
- }
- }
- sub writer_ctest {
- print <<'_____';
- /*
- * Test file to check all DEF file symbols are present by trying
- * to link to all of them. This is *not* intended to be run!
- */
- int main()
- {
- _____
- for (@_) {
- if ($_->type() eq 'VARIABLE') {
- print "\textern int ", $_->name(), '; /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n";
- } else {
- print "\textern int ", $_->name(), '(); /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n";
- }
- }
- print <<'_____';
- }
- _____
- }
|