Rename OSSL_SERIALIZER / OSSL_DESERIALIZER to OSSL_ENCODE / OSSL_DECODE
[openssl.git] / util / mkdef.pl
1 #! /usr/bin/env perl
2 # Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved.
3 #
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
8
9 # Generate a linker version script suitable for the given platform
10 # from a given ordinals file.
11
12 use strict;
13 use warnings;
14
15 use Getopt::Long;
16 use FindBin;
17 use lib "$FindBin::Bin/perl";
18
19 use OpenSSL::Ordinals;
20
21 use lib '.';
22 use configdata;
23
24 use File::Spec::Functions;
25 use lib catdir($config{sourcedir}, 'Configurations');
26 use platform;
27
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
32 my $verbose = 0;
33 my $ctest = 0;
34 my $debug = 0;
35
36 # For VMS, some modules may have case insensitive names
37 my $case_insensitive = 0;
38
39 GetOptions('name=s'     => \$name,
40            'ordinals=s' => \$ordinals_file,
41            'version=s'  => \$version,
42            'OS=s'       => \$OS,
43            'ctest'      => \$ctest,
44            'verbose'    => \$verbose,
45            # For VMS
46            'case-insensitive' => \$case_insensitive)
47     or die "Error in command line arguments\n";
48
49 die "Please supply arguments\n"
50     unless $name && $ordinals_file && $OS;
51
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.
56 #
57 # For example, with a target definition that includes:
58 #
59 #         shlib_variant => "-opt",
60 #
61 # we build the following objects:
62 #
63 # $ perl -le '
64 #     for (@ARGV) {
65 #         if ($l = readlink) {
66 #             printf "%s -> %s\n", $_, $l
67 #         } else {
68 #             print
69 #         }
70 #     }' *.so*
71 # libcrypto-opt.so.1.1
72 # libcrypto.so -> libcrypto-opt.so.1.1
73 # libssl-opt.so.1.1
74 # libssl.so -> libssl-opt.so.1.1
75 #
76 # whose SONAMEs and dependencies are:
77 #
78 # $ for l in *.so; do
79 #     echo $l
80 #     readelf -d $l | egrep 'SONAME|NEEDED.*(ssl|crypto)'
81 #   done
82 # libcrypto.so
83 #  0x000000000000000e (SONAME)             Library soname: [libcrypto-opt.so.1.1]
84 # libssl.so
85 #  0x0000000000000001 (NEEDED)             Shared library: [libcrypto-opt.so.1.1]
86 #  0x000000000000000e (SONAME)             Library soname: [libssl-opt.so.1.1]
87 #
88 # We case-fold the variant tag to upper case and replace all non-alnum
89 # characters with "_".  This yields the following symbol versions:
90 #
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
101 #
102 (my $SO_VARIANT = uc($target{"shlib_variant"} // '')) =~ s/\W/_/g;
103
104 my $libname = platform->sharedname($name);
105
106 my %OS_data = (
107     solaris     => { writer     => \&writer_linux,
108                      sort       => sorter_linux(),
109                      platforms  => { UNIX                       => 1 } },
110     "solaris-gcc" => 'solaris', # alias
111     linux       => 'solaris',   # alias
112     "bsd-gcc"   => 'solaris',   # alias
113     aix         => { writer     => \&writer_aix,
114                      sort       => sorter_unix(),
115                      platforms  => { UNIX                       => 1 } },
116     VMS         => { writer     => \&writer_VMS,
117                      sort       => OpenSSL::Ordinals::by_number(),
118                      platforms  => { VMS                        => 1 } },
119     vms         => 'VMS',       # alias
120     WINDOWS     => { writer     => \&writer_windows,
121                      sort       => OpenSSL::Ordinals::by_name(),
122                      platforms  => { WIN32                      => 1,
123                                      _WIN32                     => 1 } },
124     windows     => 'WINDOWS',   # alias
125     WIN32       => 'WINDOWS',   # alias
126     win32       => 'WIN32',     # alias
127     32          => 'WIN32',     # alias
128     NT          => 'WIN32',     # alias
129     nt          => 'WIN32',     # alias
130     mingw       => 'WINDOWS',   # alias
131    );
132
133 do {
134     die "Unknown operating system family $OS\n"
135         unless exists $OS_data{$OS};
136     $OS = $OS_data{$OS};
137 } while(ref($OS) eq '');
138
139 my %disabled_uc = map { my $x = uc $_; $x =~ s|-|_|g; $x => 1 } keys %disabled;
140
141 my %ordinal_opts = ();
142 $ordinal_opts{sort} = $OS->{sort} if $OS->{sort};
143 $ordinal_opts{filter} =
144     sub {
145         my $item = shift;
146         return
147             $item->exists()
148             && platform_filter($item)
149             && feature_filter($item);
150     };
151 my $ordinals = OpenSSL::Ordinals->new(from => $ordinals_file);
152
153 my $writer = $OS->{writer};
154 $writer = \&writer_ctest if $ctest;
155
156 $writer->($ordinals->items(%ordinal_opts));
157
158 exit 0;
159
160 sub platform_filter {
161     my $item = shift;
162     my %platforms = ( $item->platforms() );
163
164     # True if no platforms are defined
165     return 1 if scalar keys %platforms == 0;
166
167     # For any item platform tag, return the equivalence with the
168     # current platform settings if it exists there, return 0 otherwise
169     # if the item platform tag is true
170     for (keys %platforms) {
171         if (exists $OS->{platforms}->{$_}) {
172             return $platforms{$_} == $OS->{platforms}->{$_};
173         }
174         if ($platforms{$_}) {
175             return 0;
176         }
177     }
178
179     # Found no match?  Then it's a go
180     return 1;
181 }
182
183 sub feature_filter {
184     my $item = shift;
185     my @features = ( $item->features() );
186
187     # True if no features are defined
188     return 1 if scalar @features == 0;
189
190     my $verdict = ! grep { $disabled_uc{$_} } @features;
191
192     if ($disabled{deprecated}) {
193         foreach (@features) {
194             next unless /^DEPRECATEDIN_(\d+)_(\d+)(?:_(\d+))?$/;
195             my $symdep = $1 * 10000 + $2 * 100 + ($3 // 0);
196             $verdict = 0 if $config{api} >= $symdep;
197             print STDERR "DEBUG: \$symdep = $symdep, \$verdict = $verdict\n"
198                 if $debug && $1 == 0;
199         }
200     }
201
202     return $verdict;
203 }
204
205 sub sorter_unix {
206     my $by_name = OpenSSL::Ordinals::by_name();
207     my %weight = (
208         'FUNCTION'      => 1,
209         'VARIABLE'      => 2
210        );
211
212     return sub {
213         my $item1 = shift;
214         my $item2 = shift;
215
216         my $verdict = $weight{$item1->type()} <=> $weight{$item2->type()};
217         if ($verdict == 0) {
218             $verdict = $by_name->($item1, $item2);
219         }
220         return $verdict;
221     };
222 }
223
224 sub sorter_linux {
225     my $by_version = OpenSSL::Ordinals::by_version();
226     my $by_unix = sorter_unix();
227
228     return sub {
229         my $item1 = shift;
230         my $item2 = shift;
231
232         my $verdict = $by_version->($item1, $item2);
233         if ($verdict == 0) {
234             $verdict = $by_unix->($item1, $item2);
235         }
236         return $verdict;
237     };
238 }
239
240 sub writer_linux {
241     my $thisversion = '';
242     my $currversion_s = '';
243     my $prevversion_s = '';
244     my $indent = 0;
245
246     for (@_) {
247         if ($thisversion && $_->version() ne $thisversion) {
248             die "$ordinals_file: It doesn't make sense to have both versioned ",
249                 "and unversioned symbols"
250                 if $thisversion eq '*';
251             print <<"_____";
252 }${prevversion_s};
253 _____
254             $prevversion_s = " OPENSSL${SO_VARIANT}_$thisversion";
255             $thisversion = '';  # Trigger start of next section
256         }
257         unless ($thisversion) {
258             $indent = 0;
259             $thisversion = $_->version();
260             $currversion_s = '';
261             $currversion_s = "OPENSSL${SO_VARIANT}_$thisversion "
262                 if $thisversion ne '*';
263             print <<"_____";
264 ${currversion_s}{
265     global:
266 _____
267         }
268         print '        ', $_->name(), ";\n";
269     }
270
271     print <<"_____";
272     local: *;
273 }${prevversion_s};
274 _____
275 }
276
277 sub writer_aix {
278     for (@_) {
279         print $_->name(),"\n";
280     }
281 }
282
283 sub writer_windows {
284     print <<"_____";
285 ;
286 ; Definition file for the DLL version of the $libname library from OpenSSL
287 ;
288
289 LIBRARY         $libname
290
291 EXPORTS
292 _____
293     for (@_) {
294         print "    ",$_->name(),"\n";
295     }
296 }
297
298 sub collect_VMS_mixedcase {
299     return [ 'SPARE', 'SPARE' ] unless @_;
300
301     my $s = shift;
302     my $s_uc = uc($s);
303     my $type = shift;
304
305     return [ "$s=$type", 'SPARE' ] if $s_uc eq $s;
306     return [ "$s_uc/$s=$type", "$s=$type" ];
307 }
308
309 sub collect_VMS_uppercase {
310     return [ 'SPARE' ] unless @_;
311
312     my $s = shift;
313     my $s_uc = uc($s);
314     my $type = shift;
315
316     return [ "$s_uc=$type" ];
317 }
318
319 sub writer_VMS {
320     my @slot_collection = ();
321     my $collector =
322         $case_insensitive ? \&collect_VMS_uppercase : \&collect_VMS_mixedcase;
323
324     my $last_num = 0;
325     foreach (@_) {
326         my $this_num = $_->number();
327         $this_num = $last_num + 1 if $this_num =~ m|^\?|;
328
329         while (++$last_num < $this_num) {
330             push @slot_collection, $collector->(); # Just occupy a slot
331         }
332         my $type = {
333             FUNCTION    => 'PROCEDURE',
334             VARIABLE    => 'DATA'
335            } -> {$_->type()};
336         push @slot_collection, $collector->($_->name(), $type);
337     }
338
339     print <<"_____" if defined $version;
340 IDENTIFICATION=$version
341 _____
342     print <<"_____" unless $case_insensitive;
343 CASE_SENSITIVE=YES
344 _____
345     print <<"_____";
346 SYMBOL_VECTOR=(-
347 _____
348     # It's uncertain how long aggregated lines the linker can handle,
349     # but it has been observed that at least 1024 characters is ok.
350     # Either way, this means that we need to keep track of the total
351     # line length of each "SYMBOL_VECTOR" statement.  Fortunately, we
352     # can have more than one of those...
353     my $symvtextcount = 16;     # The length of "SYMBOL_VECTOR=("
354     while (@slot_collection) {
355         my $set = shift @slot_collection;
356         my $settextlength = 0;
357         foreach (@$set) {
358             $settextlength +=
359                 + 3             # two space indentation and comma
360                 + length($_)
361                 + 1             # postdent
362                 ;
363         }
364         $settextlength--;       # only one space indentation on the first one
365         my $firstcomma = ',';
366
367         if ($symvtextcount + $settextlength > 1024) {
368             print <<"_____";
369 )
370 SYMBOL_VECTOR=(-
371 _____
372             $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
373         }
374         if ($symvtextcount == 16) {
375             $firstcomma = '';
376         }
377
378         my $indent = ' '.$firstcomma;
379         foreach (@$set) {
380             print <<"_____";
381 $indent$_ -
382 _____
383             $symvtextcount += length($indent) + length($_) + 1;
384             $indent = '  ,';
385         }
386     }
387     print <<"_____";
388 )
389 _____
390
391     if (defined $version) {
392         $version =~ /^(\d+)\.(\d+)\.(\d+)/;
393         my $libvmajor = $1;
394         my $libvminor = $2 * 100 + $3;
395         print <<"_____";
396 GSMATCH=LEQUAL,$libvmajor,$libvminor
397 _____
398     }
399 }
400
401 sub writer_ctest {
402     print <<'_____';
403 /*
404  * Test file to check all DEF file symbols are present by trying
405  * to link to all of them. This is *not* intended to be run!
406  */
407
408 int main()
409 {
410 _____
411
412     my $last_num = 0;
413     for (@_) {
414         my $this_num = $_->number();
415         $this_num = $last_num + 1 if $this_num =~ m|^\?|;
416
417         if ($_->type() eq 'VARIABLE') {
418             print "\textern int ", $_->name(), '; /* type unknown */ /* ',
419                   $this_num, ' ', $_->version(), " */\n";
420         } else {
421             print "\textern int ", $_->name(), '(); /* type unknown */ /* ',
422                   $this_num, ' ', $_->version(), " */\n";
423         }
424
425         $last_num = $this_num;
426     }
427     print <<'_____';
428 }
429 _____
430 }