]> git.ipfire.org Git - thirdparty/openssl.git/blob - util/mkdef.pl
eac4f50a1770e22ba009d170a6aaaa60038c2ff4
[thirdparty/openssl.git] / util / mkdef.pl
1 #! /usr/bin/env perl
2 # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the OpenSSL license (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 my $name = undef; # internal library/module name
25 my $ordinals_file = undef; # the ordinals file to use
26 my $version = undef; # the version to use for the library
27 my $OS = undef; # the operating system family
28 my $verbose = 0;
29 my $ctest = 0;
30
31 # For VMS, some modules may have case insensitive names
32 my $case_insensitive = 0;
33
34 GetOptions('name=s' => \$name,
35 'ordinals=s' => \$ordinals_file,
36 'version=s' => \$version,
37 'OS=s' => \$OS,
38 'ctest' => \$ctest,
39 'verbose' => \$verbose,
40 # For VMS
41 'case-insensitive' => \$case_insensitive)
42 or die "Error in command line arguments\n";
43
44 die "Please supply arguments\n"
45 unless $name && $ordinals_file && $OS;
46
47 # When building a "variant" shared library, with a custom SONAME, also customize
48 # all the symbol versions. This produces a shared object that can coexist
49 # without conflict in the same address space as a default build, or an object
50 # with a different variant tag.
51 #
52 # For example, with a target definition that includes:
53 #
54 # shlib_variant => "-opt",
55 #
56 # we build the following objects:
57 #
58 # $ perl -le '
59 # for (@ARGV) {
60 # if ($l = readlink) {
61 # printf "%s -> %s\n", $_, $l
62 # } else {
63 # print
64 # }
65 # }' *.so*
66 # libcrypto-opt.so.1.1
67 # libcrypto.so -> libcrypto-opt.so.1.1
68 # libssl-opt.so.1.1
69 # libssl.so -> libssl-opt.so.1.1
70 #
71 # whose SONAMEs and dependencies are:
72 #
73 # $ for l in *.so; do
74 # echo $l
75 # readelf -d $l | egrep 'SONAME|NEEDED.*(ssl|crypto)'
76 # done
77 # libcrypto.so
78 # 0x000000000000000e (SONAME) Library soname: [libcrypto-opt.so.1.1]
79 # libssl.so
80 # 0x0000000000000001 (NEEDED) Shared library: [libcrypto-opt.so.1.1]
81 # 0x000000000000000e (SONAME) Library soname: [libssl-opt.so.1.1]
82 #
83 # We case-fold the variant tag to upper case and replace all non-alnum
84 # characters with "_". This yields the following symbol versions:
85 #
86 # $ nm libcrypto.so | grep -w A
87 # 0000000000000000 A OPENSSL_OPT_1_1_0
88 # 0000000000000000 A OPENSSL_OPT_1_1_0a
89 # 0000000000000000 A OPENSSL_OPT_1_1_0c
90 # 0000000000000000 A OPENSSL_OPT_1_1_0d
91 # 0000000000000000 A OPENSSL_OPT_1_1_0f
92 # 0000000000000000 A OPENSSL_OPT_1_1_0g
93 # $ nm libssl.so | grep -w A
94 # 0000000000000000 A OPENSSL_OPT_1_1_0
95 # 0000000000000000 A OPENSSL_OPT_1_1_0d
96 #
97 (my $SO_VARIANT = uc($target{"shlib_variant"} // '')) =~ s/\W/_/g;
98
99 my $apiv = undef;
100 $apiv = sprintf "%x%02x%02x", split(/\./, $config{api})
101 if $config{api};
102
103 my $libname = $unified_info{sharednames}->{$name} // $name;
104
105 my %OS_data = (
106 solaris => { writer => \&writer_linux,
107 sort => sorter_linux(),
108 platforms => { UNIX => 1,
109 EXPORT_VAR_AS_FUNCTION => 0 } },
110 linux => 'solaris', # alias
111 "bsd-gcc" => 'solaris', # alias
112 aix => { writer => \&writer_aix,
113 sort => sorter_unix(),
114 platforms => { UNIX => 1,
115 EXPORT_VAR_AS_FUNCTION => 0 } },
116 VMS => { writer => \&writer_VMS,
117 sort => OpenSSL::Ordinals::by_number(),
118 platforms => { VMS => 1,
119 EXPORT_VAR_AS_FUNCTION => 0 } },
120 vms => 'VMS', # alias
121 WINDOWS => { writer => \&writer_windows,
122 sort => OpenSSL::Ordinals::by_name(),
123 platforms => { WIN32 => 1,
124 _WIN32 => 1,
125 EXPORT_VAR_AS_FUNCTION => 1 } },
126 windows => 'WINDOWS', # alias
127 WIN32 => 'WINDOWS', # alias
128 win32 => 'WIN32', # alias
129 32 => 'WIN32', # alias
130 NT => 'WIN32', # alias
131 nt => 'WIN32', # alias
132 mingw => 'WINDOWS', # alias
133 );
134
135 do {
136 die "Unknown operating system family $OS\n"
137 unless exists $OS_data{$OS};
138 $OS = $OS_data{$OS};
139 } while(ref($OS) eq '');
140
141 my %disabled_uc = map { my $x = uc $_; $x =~ s|-|_|g; $x => 1 } keys %disabled;
142
143 my %ordinal_opts = ();
144 $ordinal_opts{sort} = $OS->{sort} if $OS->{sort};
145 $ordinal_opts{filter} =
146 sub {
147 my $item = shift;
148 return
149 $item->exists()
150 && platform_filter($item)
151 && feature_filter($item);
152 };
153 my $ordinals = OpenSSL::Ordinals->new(from => $ordinals_file);
154
155 my $writer = $OS->{writer};
156 $writer = \&writer_ctest if $ctest;
157
158 $writer->($ordinals->items(%ordinal_opts));
159
160 exit 0;
161
162 sub platform_filter {
163 my $item = shift;
164 my %platforms = ( $item->platforms() );
165
166 # True if no platforms are defined
167 return 1 if scalar keys %platforms == 0;
168
169 # For any item platform tag, return the equivalence with the
170 # current platform settings if it exists there, return 0 otherwise
171 # if the item platform tag is true
172 for (keys %platforms) {
173 if (exists $OS->{platforms}->{$_}) {
174 return $platforms{$_} == $OS->{platforms}->{$_};
175 }
176 if ($platforms{$_}) {
177 return 0;
178 }
179 }
180
181 # Found no match? Then it's a go
182 return 1;
183 }
184
185 sub feature_filter {
186 my $item = shift;
187 my @features = ( $item->features() );
188
189 # True if no features are defined
190 return 1 if scalar @features == 0;
191
192 my $verdict = ! grep { $disabled_uc{$_} } @features;
193
194 if ($apiv) {
195 foreach (@features) {
196 next unless /^DEPRECATEDIN_(\d+)(?:_(\d+)_(\d+))?$/;
197 my $symdep = sprintf "%x%02x%02x", $1, ($2 // 0), ($3 // 0);
198 $verdict = 0 if $apiv ge $symdep;
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 while (++$last_num < $_->number()) {
327 push @slot_collection, $collector->(); # Just occupy a slot
328 }
329 my $type = {
330 FUNCTION => 'PROCEDURE',
331 VARIABLE => 'DATA'
332 } -> {$_->type()};
333 push @slot_collection, $collector->($_->name(), $type);
334 }
335
336 print <<"_____" if defined $version;
337 IDENTIFICATION=$version
338 _____
339 print <<"_____" unless $case_insensitive;
340 CASE_SENSITIVE=YES
341 _____
342 print <<"_____";
343 SYMBOL_VECTOR=(-
344 _____
345 # It's uncertain how long aggregated lines the linker can handle,
346 # but it has been observed that at least 1024 characters is ok.
347 # Either way, this means that we need to keep track of the total
348 # line length of each "SYMBOL_VECTOR" statement. Fortunately, we
349 # can have more than one of those...
350 my $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
351 while (@slot_collection) {
352 my $set = shift @slot_collection;
353 my $settextlength = 0;
354 foreach (@$set) {
355 $settextlength +=
356 + 3 # two space indentation and comma
357 + length($_)
358 + 1 # postdent
359 ;
360 }
361 $settextlength--; # only one space indentation on the first one
362 my $firstcomma = ',';
363
364 if ($symvtextcount + $settextlength > 1024) {
365 print <<"_____";
366 )
367 SYMBOL_VECTOR=(-
368 _____
369 $symvtextcount = 16; # The length of "SYMBOL_VECTOR=("
370 }
371 if ($symvtextcount == 16) {
372 $firstcomma = '';
373 }
374
375 my $indent = ' '.$firstcomma;
376 foreach (@$set) {
377 print <<"_____";
378 $indent$_ -
379 _____
380 $symvtextcount += length($indent) + length($_) + 1;
381 $indent = ' ,';
382 }
383 }
384 print <<"_____";
385 )
386 _____
387
388 if (defined $version) {
389 my ($libvmajor, $libvminor) = $version =~ /^(\d+)_(\d+)$/;
390 print <<"_____";
391 GSMATCH=LEQUAL,$libvmajor,$libvminor;
392 _____
393 }
394 }
395
396 sub writer_ctest {
397 print <<'_____';
398 /*
399 * Test file to check all DEF file symbols are present by trying
400 * to link to all of them. This is *not* intended to be run!
401 */
402
403 int main()
404 {
405 _____
406
407 for (@_) {
408 if ($_->type() eq 'VARIABLE') {
409 print "\textern int ", $_->name(), '; /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n";
410 } else {
411 print "\textern int ", $_->name(), '(); /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n";
412 }
413 }
414 print <<'_____';
415 }
416 _____
417 }