362 lines
10 KiB
Perl
362 lines
10 KiB
Perl
#! /usr/bin/env perl
|
|
# Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License 2.0 (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;
|
|
|
|
use File::Spec::Functions;
|
|
use lib catdir($config{sourcedir}, 'Configurations');
|
|
use platform;
|
|
|
|
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;
|
|
my $debug = 0;
|
|
|
|
my $symbol_prefix = $config{symbol_prefix}||"";
|
|
|
|
GetOptions('name=s' => \$name,
|
|
'ordinals=s' => \$ordinals_file,
|
|
'version=s' => \$version,
|
|
'OS=s' => \$OS,
|
|
'ctest' => \$ctest,
|
|
'verbose' => \$verbose,
|
|
'debug' => \$debug
|
|
)
|
|
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 $libname = platform->sharedname($name);
|
|
|
|
my %OS_data = (
|
|
solaris => { writer => \&writer_linux,
|
|
sort => sorter_linux(),
|
|
platforms => { UNIX => 1 } },
|
|
"solaris-gcc" => 'solaris', # alias
|
|
linux => 'solaris', # alias
|
|
"bsd-gcc" => 'solaris', # alias
|
|
aix => { writer => \&writer_aix,
|
|
sort => sorter_unix(),
|
|
platforms => { UNIX => 1 } },
|
|
WINDOWS => { writer => \&writer_windows,
|
|
sort => OpenSSL::Ordinals::by_name(),
|
|
platforms => { WIN32 => 1,
|
|
_WIN32 => 1 } },
|
|
windows => 'WINDOWS', # alias
|
|
WIN32 => 'WINDOWS', # alias
|
|
win32 => 'WIN32', # alias
|
|
32 => 'WIN32', # alias
|
|
NT => 'WIN32', # alias
|
|
nt => 'WIN32', # alias
|
|
mingw => 'WINDOWS', # alias
|
|
nonstop => { writer => \&writer_nonstop,
|
|
sort => OpenSSL::Ordinals::by_name(),
|
|
platforms => { TANDEM => 1 } },
|
|
);
|
|
|
|
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 %dso_symbols = ();
|
|
|
|
foreach my $f (catfile($config{sourcedir}, "util/engines.num"),
|
|
catfile($config{sourcedir}, "util/providers.num")) {
|
|
open IN, $f or die "Couldn't open $f: $!\n";
|
|
while(<IN>) {
|
|
print STDERR "DEBUG: \$_=\"$_\"\n" if $debug;
|
|
$_ =~ qr/([\w_\d]+)\s+(?:\d+)\s+(?:\S+)/x;
|
|
$dso_symbols{$1} = $1;
|
|
}
|
|
close IN;
|
|
}
|
|
|
|
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 ($disabled{deprecated}) {
|
|
foreach (@features) {
|
|
next unless /^DEPRECATEDIN_(\d+)_(\d+)(?:_(\d+))?$/;
|
|
my $symdep = $1 * 10000 + $2 * 100 + ($3 // 0);
|
|
$verdict = 0 if $config{api} >= $symdep;
|
|
print STDERR "DEBUG: name = ", $item->name(),", \$symdep = $symdep, \$verdict = $verdict\n"
|
|
if $debug && $verdict == 0;
|
|
}
|
|
} elsif ($disabled{'deprecated-0.9.8'}) {
|
|
foreach (@features) {
|
|
next unless /^DEPRECATEDIN_(\d+)_(\d+)(?:_(\d+))?$/;
|
|
my $symdep = $1 * 10000 + $2 * 100 + ($3 // 0);
|
|
$verdict = 0 if $symdep == 908;
|
|
print STDERR "DEBUG: name = ", $item->name(),", \$symdep = $symdep, \$verdict = $verdict\n"
|
|
if $debug && $verdict == 0;
|
|
}
|
|
}
|
|
|
|
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 ' ', alias($_->name()), ";\n";
|
|
}
|
|
|
|
print <<"_____";
|
|
local: *;
|
|
}${prevversion_s};
|
|
_____
|
|
}
|
|
|
|
sub writer_aix {
|
|
for (@_) {
|
|
print alias($_->name()),"\n";
|
|
}
|
|
}
|
|
|
|
sub writer_nonstop {
|
|
for (@_) {
|
|
print "-export ",alias($_->name()),"\n";
|
|
}
|
|
}
|
|
|
|
sub writer_windows {
|
|
print <<"_____";
|
|
;
|
|
; Definition file for the DLL version of the $libname library from OpenSSL
|
|
;
|
|
|
|
LIBRARY "$libname"
|
|
|
|
EXPORTS
|
|
_____
|
|
for (@_) {
|
|
print " ",$symbol_prefix.$_->name();
|
|
if (platform->can('export2internal')) {
|
|
print "=". platform->export2internal($symbol_prefix.$_->name());
|
|
}
|
|
print "\n";
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
_____
|
|
|
|
my $last_num = 0;
|
|
for (@_) {
|
|
my $this_num = $_->number();
|
|
$this_num = $last_num + 1 if $this_num =~ m|^\?|;
|
|
|
|
if ($_->type() eq 'VARIABLE') {
|
|
print "\textern int ", alias($_->name()), '; /* type unknown */ /* ',
|
|
$this_num, ' ', $_->version(), " */\n";
|
|
} else {
|
|
print "\textern int ", alias($_->name()), '(); /* type unknown */ /* ',
|
|
$this_num, ' ', $_->version(), " */\n";
|
|
}
|
|
|
|
$last_num = $this_num;
|
|
}
|
|
print <<'_____';
|
|
}
|
|
_____
|
|
}
|
|
|
|
sub alias {
|
|
(my $name) = @_;
|
|
return defined($dso_symbols{$name}) ? $name : $symbol_prefix.$name;
|
|
}
|