2 # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
4 # Licensed under the Apache License 2.0 (the "License"). You may not use
5 # this file except in compliance with the License. You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
9 # Generate a linker version script suitable for the given platform
10 # from a given ordinals file.
17 use lib "$FindBin::Bin/perl";
19 use OpenSSL::Ordinals;
24 use File::Spec::Functions;
25 use lib catdir($config{sourcedir}, 'Configurations');
28 my $name = undef; # internal library/module name
29 my $ordinals_file = undef; # the ordinals file to use
30 my $version = undef; # the version to use for the library
31 my $OS = undef; # the operating system family
36 # For VMS, some modules may have case insensitive names
37 my $case_insensitive = 0;
39 GetOptions('name=s' => \$name,
40 'ordinals=s' => \$ordinals_file,
41 'version=s' => \$version,
44 'verbose' => \$verbose,
46 'case-insensitive' => \$case_insensitive)
47 or die "Error in command line arguments\n";
49 die "Please supply arguments\n"
50 unless $name && $ordinals_file && $OS;
52 # When building a "variant" shared library, with a custom SONAME, also customize
53 # all the symbol versions. This produces a shared object that can coexist
54 # without conflict in the same address space as a default build, or an object
55 # with a different variant tag.
57 # For example, with a target definition that includes:
59 # shlib_variant => "-opt",
61 # we build the following objects:
65 # if ($l = readlink) {
66 # printf "%s -> %s\n", $_, $l
71 # libcrypto-opt.so.1.1
72 # libcrypto.so -> libcrypto-opt.so.1.1
74 # libssl.so -> libssl-opt.so.1.1
76 # whose SONAMEs and dependencies are:
80 # readelf -d $l | egrep 'SONAME|NEEDED.*(ssl|crypto)'
83 # 0x000000000000000e (SONAME) Library soname: [libcrypto-opt.so.1.1]
85 # 0x0000000000000001 (NEEDED) Shared library: [libcrypto-opt.so.1.1]
86 # 0x000000000000000e (SONAME) Library soname: [libssl-opt.so.1.1]
88 # We case-fold the variant tag to upper case and replace all non-alnum
89 # characters with "_". This yields the following symbol versions:
91 # $ nm libcrypto.so | grep -w A
92 # 0000000000000000 A OPENSSL_OPT_1_1_0
93 # 0000000000000000 A OPENSSL_OPT_1_1_0a
94 # 0000000000000000 A OPENSSL_OPT_1_1_0c
95 # 0000000000000000 A OPENSSL_OPT_1_1_0d
96 # 0000000000000000 A OPENSSL_OPT_1_1_0f
97 # 0000000000000000 A OPENSSL_OPT_1_1_0g
98 # $ nm libssl.so | grep -w A
99 # 0000000000000000 A OPENSSL_OPT_1_1_0
100 # 0000000000000000 A OPENSSL_OPT_1_1_0d
102 (my $SO_VARIANT = uc($target{"shlib_variant"} // '')) =~ s/\W/_/g;
104 my $libname = platform->sharedname($name);
107 solaris => { writer => \&writer_linux,
108 sort => sorter_linux(),
109 platforms => { UNIX => 1 } },
110 linux => 'solaris', # alias
111 "bsd-gcc" => 'solaris', # alias
112 aix => { writer => \&writer_aix,
113 sort => sorter_unix(),
114 platforms => { UNIX => 1 } },
115 VMS => { writer => \&writer_VMS,
116 sort => OpenSSL::Ordinals::by_number(),
117 platforms => { VMS => 1 } },
118 vms => 'VMS', # alias
119 WINDOWS => { writer => \&writer_windows,
120 sort => OpenSSL::Ordinals::by_name(),
121 platforms => { WIN32 => 1,
123 windows => 'WINDOWS', # alias
124 WIN32 => 'WINDOWS', # alias
125 win32 => 'WIN32', # alias
126 32 => 'WIN32', # alias
127 NT => 'WIN32', # alias
128 nt => 'WIN32', # alias
129 mingw => 'WINDOWS', # alias
133 die "Unknown operating system family $OS\n"
134 unless exists $OS_data{$OS};
136 } while(ref($OS) eq '');
138 my %disabled_uc = map { my $x = uc $_; $x =~ s|-|_|g; $x => 1 } keys %disabled;
140 my %ordinal_opts = ();
141 $ordinal_opts{sort} = $OS->{sort} if $OS->{sort};
142 $ordinal_opts{filter} =
147 && platform_filter($item)
148 && feature_filter($item);
150 my $ordinals = OpenSSL::Ordinals->new(from => $ordinals_file);
152 my $writer = $OS->{writer};
153 $writer = \&writer_ctest if $ctest;
155 $writer->($ordinals->items(%ordinal_opts));
159 sub platform_filter {
161 my %platforms = ( $item->platforms() );
163 # True if no platforms are defined
164 return 1 if scalar keys %platforms == 0;
166 # For any item platform tag, return the equivalence with the
167 # current platform settings if it exists there, return 0 otherwise
168 # if the item platform tag is true
169 for (keys %platforms) {
170 if (exists $OS->{platforms}->{$_}) {
171 return $platforms{$_} == $OS->{platforms}->{$_};
173 if ($platforms{$_}) {
178 # Found no match? Then it's a go
184 my @features = ( $item->features() );
186 # True if no features are defined
187 return 1 if scalar @features == 0;
189 my $verdict = ! grep { $disabled_uc{$_} } @features;
191 if ($disabled{deprecated}) {
192 foreach (@features) {
193 next unless /^DEPRECATEDIN_(\d+)_(\d+)(?:_(\d+))?$/;
194 my $symdep = $1 * 10000 + $2 * 100 + ($3 // 0);
195 $verdict = 0 if $config{api} >= $symdep;
196 print STDERR "DEBUG: \$symdep = $symdep, \$verdict = $verdict\n"
197 if $debug && $1 == 0;
205 my $by_name = OpenSSL::Ordinals::by_name();
215 my $verdict = $weight{$item1->type()} <=> $weight{$item2->type()};
217 $verdict = $by_name->($item1, $item2);
224 my $by_version = OpenSSL::Ordinals::by_version();
225 my $by_unix = sorter_unix();
231 my $verdict = $by_version->($item1, $item2);
233 $verdict = $by_unix->($item1, $item2);
240 my $thisversion = '';
241 my $currversion_s = '';
242 my $prevversion_s = '';
246 if ($thisversion && $_->version() ne $thisversion) {
247 die "$ordinals_file: It doesn't make sense to have both versioned ",
248 "and unversioned symbols"
249 if $thisversion eq '*';
253 $prevversion_s = " OPENSSL${SO_VARIANT}_$thisversion";
254 $thisversion = ''; # Trigger start of next section
256 unless ($thisversion) {
258 $thisversion = $_->version();
260 $currversion_s = "OPENSSL${SO_VARIANT}_$thisversion "
261 if $thisversion ne '*';
267 print ' ', $_->name(), ";\n";
278 print $_->name(),"\n";
285 ; Definition file for the DLL version of the $libname library from OpenSSL
293 print " ",$_->name(),"\n";
297 sub collect_VMS_mixedcase {
298 return [ 'SPARE', 'SPARE' ] unless @_;
304 return [ "$s=$type", 'SPARE' ] if $s_uc eq $s;
305 return [ "$s_uc/$s=$type", "$s=$type" ];
308 sub collect_VMS_uppercase {
309 return [ 'SPARE' ] unless @_;
315 return [ "$s_uc=$type" ];
319 my @slot_collection = ();
321 $case_insensitive ? \&collect_VMS_uppercase : \&collect_VMS_mixedcase;
325 my $this_num = $_->number();
326 $this_num = $last_num + 1 if $this_num =~ m|^\?|;
328 while (++$last_num < $this_num) {
329 push @slot_collection, $collector->(); # Just occupy a slot
332 FUNCTION => 'PROCEDURE',
335 push @slot_collection, $collector->($_->name(), $type);
338 print <<"_____" if defined $version;
339 IDENTIFICATION=$version
341 print <<"_____" unless $case_insensitive;
347 # It's uncertain how long aggregated lines the linker can handle,
348 # but it has been observed that at least 1024 characters is ok.
349 # Either way, this means that we need to keep track of the total
350 # line length of each "SYMBOL_VECTOR" statement. Fortunately, we
351 # can have more than one of those...
352 my $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
353 while (@slot_collection) {
354 my $set = shift @slot_collection;
355 my $settextlength = 0;
358 + 3 # two space indentation and comma
363 $settextlength--; # only one space indentation on the first one
364 my $firstcomma = ',';
366 if ($symvtextcount + $settextlength > 1024) {
371 $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
373 if ($symvtextcount == 16) {
377 my $indent = ' '.$firstcomma;
382 $symvtextcount += length($indent) + length($_) + 1;
390 if (defined $version) {
391 $version =~ /^(\d+)\.(\d+)\.(\d+)/;
393 my $libvminor = $2 * 100 + $3;
395 GSMATCH=LEQUAL,$libvmajor,$libvminor
403 * Test file to check all DEF file symbols are present by trying
404 * to link to all of them. This is *not* intended to be run!
413 my $this_num = $_->number();
414 $this_num = $last_num + 1 if $this_num =~ m|^\?|;
416 if ($_->type() eq 'VARIABLE') {
417 print "\textern int ", $_->name(), '; /* type unknown */ /* ',
418 $this_num, ' ', $_->version(), " */\n";
420 print "\textern int ", $_->name(), '(); /* type unknown */ /* ',
421 $this_num, ' ', $_->version(), " */\n";
424 $last_num = $this_num;