]>
Commit | Line | Data |
---|---|---|
e0a65194 | 1 | #! /usr/bin/env perl |
b6461792 | 2 | # Copyright 2018-2024 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 | # | |
d7f3a2cc | 92 | # We case-fold the variant tag to uppercase and replace all non-alnum |
822b5e26 VD |
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 } }, |
b332dbb3 | 120 | "aix-solib" => 'aix', # alias |
8effd8fa RL |
121 | VMS => { writer => \&writer_VMS, |
122 | sort => OpenSSL::Ordinals::by_number(), | |
211da00b | 123 | platforms => { VMS => 1 } }, |
8effd8fa RL |
124 | vms => 'VMS', # alias |
125 | WINDOWS => { writer => \&writer_windows, | |
126 | sort => OpenSSL::Ordinals::by_name(), | |
127 | platforms => { WIN32 => 1, | |
211da00b | 128 | _WIN32 => 1 } }, |
8effd8fa RL |
129 | windows => 'WINDOWS', # alias |
130 | WIN32 => 'WINDOWS', # alias | |
131 | win32 => 'WIN32', # alias | |
132 | 32 => 'WIN32', # alias | |
133 | NT => 'WIN32', # alias | |
134 | nt => 'WIN32', # alias | |
135 | mingw => 'WINDOWS', # alias | |
7a032be7 RL |
136 | nonstop => { writer => \&writer_nonstop, |
137 | sort => OpenSSL::Ordinals::by_name(), | |
138 | platforms => { TANDEM => 1 } }, | |
8effd8fa RL |
139 | ); |
140 | ||
141 | do { | |
142 | die "Unknown operating system family $OS\n" | |
143 | unless exists $OS_data{$OS}; | |
144 | $OS = $OS_data{$OS}; | |
145 | } while(ref($OS) eq ''); | |
146 | ||
147 | my %disabled_uc = map { my $x = uc $_; $x =~ s|-|_|g; $x => 1 } keys %disabled; | |
148 | ||
149 | my %ordinal_opts = (); | |
150 | $ordinal_opts{sort} = $OS->{sort} if $OS->{sort}; | |
151 | $ordinal_opts{filter} = | |
152 | sub { | |
153 | my $item = shift; | |
154 | return | |
155 | $item->exists() | |
156 | && platform_filter($item) | |
157 | && feature_filter($item); | |
158 | }; | |
159 | my $ordinals = OpenSSL::Ordinals->new(from => $ordinals_file); | |
160 | ||
161 | my $writer = $OS->{writer}; | |
162 | $writer = \&writer_ctest if $ctest; | |
163 | ||
164 | $writer->($ordinals->items(%ordinal_opts)); | |
165 | ||
166 | exit 0; | |
167 | ||
168 | sub platform_filter { | |
169 | my $item = shift; | |
170 | my %platforms = ( $item->platforms() ); | |
171 | ||
172 | # True if no platforms are defined | |
173 | return 1 if scalar keys %platforms == 0; | |
174 | ||
175 | # For any item platform tag, return the equivalence with the | |
176 | # current platform settings if it exists there, return 0 otherwise | |
177 | # if the item platform tag is true | |
178 | for (keys %platforms) { | |
179 | if (exists $OS->{platforms}->{$_}) { | |
180 | return $platforms{$_} == $OS->{platforms}->{$_}; | |
181 | } | |
182 | if ($platforms{$_}) { | |
183 | return 0; | |
184 | } | |
185 | } | |
948d0125 | 186 | |
8effd8fa RL |
187 | # Found no match? Then it's a go |
188 | return 1; | |
948d0125 RL |
189 | } |
190 | ||
8effd8fa RL |
191 | sub feature_filter { |
192 | my $item = shift; | |
8effd8fa | 193 | my @features = ( $item->features() ); |
d02b48c6 | 194 | |
8effd8fa | 195 | # True if no features are defined |
ab1e5495 | 196 | return 1 if scalar @features == 0; |
62dc5aad | 197 | |
ab1e5495 | 198 | my $verdict = ! grep { $disabled_uc{$_} } @features; |
62dc5aad | 199 | |
a6a4d0ac | 200 | if ($disabled{deprecated}) { |
8effd8fa | 201 | foreach (@features) { |
a6a4d0ac RL |
202 | next unless /^DEPRECATEDIN_(\d+)_(\d+)(?:_(\d+))?$/; |
203 | my $symdep = $1 * 10000 + $2 * 100 + ($3 // 0); | |
204 | $verdict = 0 if $config{api} >= $symdep; | |
205 | print STDERR "DEBUG: \$symdep = $symdep, \$verdict = $verdict\n" | |
0eb15466 | 206 | if $debug && $1 == 0; |
8effd8fa RL |
207 | } |
208 | } | |
d02b48c6 | 209 | |
8effd8fa | 210 | return $verdict; |
948d0125 RL |
211 | } |
212 | ||
8effd8fa RL |
213 | sub sorter_unix { |
214 | my $by_name = OpenSSL::Ordinals::by_name(); | |
215 | my %weight = ( | |
216 | 'FUNCTION' => 1, | |
217 | 'VARIABLE' => 2 | |
218 | ); | |
948d0125 | 219 | |
8effd8fa RL |
220 | return sub { |
221 | my $item1 = shift; | |
222 | my $item2 = shift; | |
451e60e9 | 223 | |
8effd8fa RL |
224 | my $verdict = $weight{$item1->type()} <=> $weight{$item2->type()}; |
225 | if ($verdict == 0) { | |
226 | $verdict = $by_name->($item1, $item2); | |
227 | } | |
228 | return $verdict; | |
229 | }; | |
47339f61 | 230 | } |
d02b48c6 | 231 | |
8effd8fa RL |
232 | sub sorter_linux { |
233 | my $by_version = OpenSSL::Ordinals::by_version(); | |
234 | my $by_unix = sorter_unix(); | |
62dc5aad | 235 | |
8effd8fa RL |
236 | return sub { |
237 | my $item1 = shift; | |
238 | my $item2 = shift; | |
62dc5aad | 239 | |
8effd8fa RL |
240 | my $verdict = $by_version->($item1, $item2); |
241 | if ($verdict == 0) { | |
242 | $verdict = $by_unix->($item1, $item2); | |
243 | } | |
244 | return $verdict; | |
245 | }; | |
62dc5aad RL |
246 | } |
247 | ||
8effd8fa RL |
248 | sub writer_linux { |
249 | my $thisversion = ''; | |
97624638 RL |
250 | my $currversion_s = ''; |
251 | my $prevversion_s = ''; | |
252 | my $indent = 0; | |
8effd8fa RL |
253 | |
254 | for (@_) { | |
255 | if ($thisversion && $_->version() ne $thisversion) { | |
97624638 RL |
256 | die "$ordinals_file: It doesn't make sense to have both versioned ", |
257 | "and unversioned symbols" | |
258 | if $thisversion eq '*'; | |
8effd8fa | 259 | print <<"_____"; |
97624638 | 260 | }${prevversion_s}; |
8effd8fa | 261 | _____ |
97624638 | 262 | $prevversion_s = " OPENSSL${SO_VARIANT}_$thisversion"; |
8effd8fa RL |
263 | $thisversion = ''; # Trigger start of next section |
264 | } | |
265 | unless ($thisversion) { | |
97624638 | 266 | $indent = 0; |
8effd8fa | 267 | $thisversion = $_->version(); |
97624638 RL |
268 | $currversion_s = ''; |
269 | $currversion_s = "OPENSSL${SO_VARIANT}_$thisversion " | |
270 | if $thisversion ne '*'; | |
8effd8fa | 271 | print <<"_____"; |
97624638 | 272 | ${currversion_s}{ |
8effd8fa RL |
273 | global: |
274 | _____ | |
275 | } | |
276 | print ' ', $_->name(), ";\n"; | |
277 | } | |
62dc5aad | 278 | |
8effd8fa RL |
279 | print <<"_____"; |
280 | local: *; | |
97624638 | 281 | }${prevversion_s}; |
8effd8fa | 282 | _____ |
12aefe78 DSH |
283 | } |
284 | ||
8effd8fa RL |
285 | sub writer_aix { |
286 | for (@_) { | |
287 | print $_->name(),"\n"; | |
288 | } | |
0b352c58 RL |
289 | } |
290 | ||
7a032be7 RL |
291 | sub writer_nonstop { |
292 | for (@_) { | |
293 | print "-export ",$_->name(),"\n"; | |
294 | } | |
295 | } | |
296 | ||
8effd8fa RL |
297 | sub writer_windows { |
298 | print <<"_____"; | |
d02b48c6 | 299 | ; |
8effd8fa | 300 | ; Definition file for the DLL version of the $libname library from OpenSSL |
d02b48c6 RE |
301 | ; |
302 | ||
491a1e33 | 303 | LIBRARY "$libname" |
d02b48c6 | 304 | |
8effd8fa RL |
305 | EXPORTS |
306 | _____ | |
307 | for (@_) { | |
491a1e33 TI |
308 | print " ",$_->name(); |
309 | if (platform->can('export2internal')) { | |
310 | print "=". platform->export2internal($_->name()); | |
311 | } | |
312 | print "\n"; | |
8effd8fa RL |
313 | } |
314 | } | |
d02b48c6 | 315 | |
36d3acb9 RL |
316 | sub collect_VMS_mixedcase { |
317 | return [ 'SPARE', 'SPARE' ] unless @_; | |
318 | ||
319 | my $s = shift; | |
320 | my $s_uc = uc($s); | |
321 | my $type = shift; | |
322 | ||
323 | return [ "$s=$type", 'SPARE' ] if $s_uc eq $s; | |
324 | return [ "$s_uc/$s=$type", "$s=$type" ]; | |
325 | } | |
326 | ||
327 | sub collect_VMS_uppercase { | |
328 | return [ 'SPARE' ] unless @_; | |
329 | ||
330 | my $s = shift; | |
331 | my $s_uc = uc($s); | |
332 | my $type = shift; | |
333 | ||
334 | return [ "$s_uc=$type" ]; | |
335 | } | |
336 | ||
8effd8fa RL |
337 | sub writer_VMS { |
338 | my @slot_collection = (); | |
36d3acb9 RL |
339 | my $collector = |
340 | $case_insensitive ? \&collect_VMS_uppercase : \&collect_VMS_mixedcase; | |
8effd8fa RL |
341 | |
342 | my $last_num = 0; | |
343 | foreach (@_) { | |
e4f2d539 RL |
344 | my $this_num = $_->number(); |
345 | $this_num = $last_num + 1 if $this_num =~ m|^\?|; | |
346 | ||
347 | while (++$last_num < $this_num) { | |
36d3acb9 | 348 | push @slot_collection, $collector->(); # Just occupy a slot |
8effd8fa RL |
349 | } |
350 | my $type = { | |
351 | FUNCTION => 'PROCEDURE', | |
352 | VARIABLE => 'DATA' | |
353 | } -> {$_->type()}; | |
36d3acb9 | 354 | push @slot_collection, $collector->($_->name(), $type); |
8effd8fa | 355 | } |
d02b48c6 | 356 | |
97624638 RL |
357 | print <<"_____" if defined $version; |
358 | IDENTIFICATION=$version | |
359 | _____ | |
36d3acb9 | 360 | print <<"_____" unless $case_insensitive; |
8effd8fa | 361 | CASE_SENSITIVE=YES |
36d3acb9 RL |
362 | _____ |
363 | print <<"_____"; | |
8effd8fa RL |
364 | SYMBOL_VECTOR=(- |
365 | _____ | |
366 | # It's uncertain how long aggregated lines the linker can handle, | |
367 | # but it has been observed that at least 1024 characters is ok. | |
368 | # Either way, this means that we need to keep track of the total | |
369 | # line length of each "SYMBOL_VECTOR" statement. Fortunately, we | |
370 | # can have more than one of those... | |
371 | my $symvtextcount = 16; # The length of "SYMBOL_VECTOR=(" | |
372 | while (@slot_collection) { | |
36d3acb9 RL |
373 | my $set = shift @slot_collection; |
374 | my $settextlength = 0; | |
375 | foreach (@$set) { | |
376 | $settextlength += | |
377 | + 3 # two space indentation and comma | |
378 | + length($_) | |
379 | + 1 # postdent | |
380 | ; | |
381 | } | |
382 | $settextlength--; # only one space indentation on the first one | |
8effd8fa RL |
383 | my $firstcomma = ','; |
384 | ||
36d3acb9 | 385 | if ($symvtextcount + $settextlength > 1024) { |
8effd8fa RL |
386 | print <<"_____"; |
387 | ) | |
388 | SYMBOL_VECTOR=(- | |
389 | _____ | |
390 | $symvtextcount = 16; # The length of "SYMBOL_VECTOR=(" | |
a388633d | 391 | } |
8effd8fa RL |
392 | if ($symvtextcount == 16) { |
393 | $firstcomma = ''; | |
394 | } | |
36d3acb9 RL |
395 | |
396 | my $indent = ' '.$firstcomma; | |
397 | foreach (@$set) { | |
398 | print <<"_____"; | |
399 | $indent$_ - | |
8effd8fa | 400 | _____ |
36d3acb9 RL |
401 | $symvtextcount += length($indent) + length($_) + 1; |
402 | $indent = ' ,'; | |
403 | } | |
8effd8fa RL |
404 | } |
405 | print <<"_____"; | |
406 | ) | |
407 | _____ | |
408 | ||
97624638 | 409 | if (defined $version) { |
d1c87578 RL |
410 | $version =~ /^(\d+)\.(\d+)\.(\d+)/; |
411 | my $libvmajor = $1; | |
412 | my $libvminor = $2 * 100 + $3; | |
97624638 | 413 | print <<"_____"; |
72818ef0 | 414 | GSMATCH=LEQUAL,$libvmajor,$libvminor |
8effd8fa | 415 | _____ |
97624638 | 416 | } |
e863d920 MC |
417 | } |
418 | ||
8effd8fa RL |
419 | sub writer_ctest { |
420 | print <<'_____'; | |
421 | /* | |
422 | * Test file to check all DEF file symbols are present by trying | |
423 | * to link to all of them. This is *not* intended to be run! | |
424 | */ | |
e863d920 | 425 | |
8effd8fa | 426 | int main() |
e863d920 | 427 | { |
8effd8fa | 428 | _____ |
e863d920 | 429 | |
e4f2d539 | 430 | my $last_num = 0; |
8effd8fa | 431 | for (@_) { |
e4f2d539 RL |
432 | my $this_num = $_->number(); |
433 | $this_num = $last_num + 1 if $this_num =~ m|^\?|; | |
434 | ||
8effd8fa | 435 | if ($_->type() eq 'VARIABLE') { |
e4f2d539 RL |
436 | print "\textern int ", $_->name(), '; /* type unknown */ /* ', |
437 | $this_num, ' ', $_->version(), " */\n"; | |
8effd8fa | 438 | } else { |
e4f2d539 RL |
439 | print "\textern int ", $_->name(), '(); /* type unknown */ /* ', |
440 | $this_num, ' ', $_->version(), " */\n"; | |
8effd8fa | 441 | } |
e4f2d539 RL |
442 | |
443 | $last_num = $this_num; | |
8effd8fa RL |
444 | } |
445 | print <<'_____'; | |
e863d920 | 446 | } |
8effd8fa | 447 | _____ |
7a556fb6 | 448 | } |