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