]>
Commit | Line | Data |
---|---|---|
e0a65194 | 1 | #! /usr/bin/env perl |
8effd8fa | 2 | # Copyright 2018 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 RL |
31 | my $OS = undef; # the operating system family |
32 | my $verbose = 0; | |
33 | my $ctest = 0; | |
34 | ||
36d3acb9 RL |
35 | # For VMS, some modules may have case insensitive names |
36 | my $case_insensitive = 0; | |
37 | ||
8effd8fa RL |
38 | GetOptions('name=s' => \$name, |
39 | 'ordinals=s' => \$ordinals_file, | |
97624638 | 40 | 'version=s' => \$version, |
8effd8fa RL |
41 | 'OS=s' => \$OS, |
42 | 'ctest' => \$ctest, | |
36d3acb9 RL |
43 | 'verbose' => \$verbose, |
44 | # For VMS | |
45 | 'case-insensitive' => \$case_insensitive) | |
8effd8fa RL |
46 | or die "Error in command line arguments\n"; |
47 | ||
48 | die "Please supply arguments\n" | |
49 | unless $name && $ordinals_file && $OS; | |
3fa04f0d | 50 | |
822b5e26 VD |
51 | # When building a "variant" shared library, with a custom SONAME, also customize |
52 | # all the symbol versions. This produces a shared object that can coexist | |
53 | # without conflict in the same address space as a default build, or an object | |
54 | # with a different variant tag. | |
55 | # | |
56 | # For example, with a target definition that includes: | |
57 | # | |
58 | # shlib_variant => "-opt", | |
59 | # | |
60 | # we build the following objects: | |
61 | # | |
62 | # $ perl -le ' | |
63 | # for (@ARGV) { | |
64 | # if ($l = readlink) { | |
65 | # printf "%s -> %s\n", $_, $l | |
66 | # } else { | |
67 | ||
68 | # } | |
69 | # }' *.so* | |
70 | # libcrypto-opt.so.1.1 | |
71 | # libcrypto.so -> libcrypto-opt.so.1.1 | |
72 | # libssl-opt.so.1.1 | |
73 | # libssl.so -> libssl-opt.so.1.1 | |
74 | # | |
75 | # whose SONAMEs and dependencies are: | |
76 | # | |
77 | # $ for l in *.so; do | |
78 | # echo $l | |
79 | # readelf -d $l | egrep 'SONAME|NEEDED.*(ssl|crypto)' | |
80 | # done | |
81 | # libcrypto.so | |
82 | # 0x000000000000000e (SONAME) Library soname: [libcrypto-opt.so.1.1] | |
83 | # libssl.so | |
84 | # 0x0000000000000001 (NEEDED) Shared library: [libcrypto-opt.so.1.1] | |
85 | # 0x000000000000000e (SONAME) Library soname: [libssl-opt.so.1.1] | |
86 | # | |
87 | # We case-fold the variant tag to upper case and replace all non-alnum | |
88 | # characters with "_". This yields the following symbol versions: | |
89 | # | |
90 | # $ nm libcrypto.so | grep -w A | |
91 | # 0000000000000000 A OPENSSL_OPT_1_1_0 | |
92 | # 0000000000000000 A OPENSSL_OPT_1_1_0a | |
93 | # 0000000000000000 A OPENSSL_OPT_1_1_0c | |
94 | # 0000000000000000 A OPENSSL_OPT_1_1_0d | |
95 | # 0000000000000000 A OPENSSL_OPT_1_1_0f | |
96 | # 0000000000000000 A OPENSSL_OPT_1_1_0g | |
97 | # $ nm libssl.so | grep -w A | |
98 | # 0000000000000000 A OPENSSL_OPT_1_1_0 | |
99 | # 0000000000000000 A OPENSSL_OPT_1_1_0d | |
100 | # | |
8effd8fa RL |
101 | (my $SO_VARIANT = uc($target{"shlib_variant"} // '')) =~ s/\W/_/g; |
102 | ||
103 | my $apiv = undef; | |
104 | $apiv = sprintf "%x%02x%02x", split(/\./, $config{api}) | |
105 | if $config{api}; | |
106 | ||
9afc2b92 | 107 | my $libname = platform->sharedname($name); |
8effd8fa RL |
108 | |
109 | my %OS_data = ( | |
110 | solaris => { writer => \&writer_linux, | |
111 | sort => sorter_linux(), | |
211da00b | 112 | platforms => { UNIX => 1 } }, |
8effd8fa | 113 | linux => 'solaris', # alias |
a5fcce6b | 114 | "bsd-gcc" => 'solaris', # alias |
8effd8fa RL |
115 | aix => { writer => \&writer_aix, |
116 | sort => sorter_unix(), | |
211da00b | 117 | platforms => { UNIX => 1 } }, |
8effd8fa RL |
118 | VMS => { writer => \&writer_VMS, |
119 | sort => OpenSSL::Ordinals::by_number(), | |
211da00b | 120 | platforms => { VMS => 1 } }, |
8effd8fa RL |
121 | vms => 'VMS', # alias |
122 | WINDOWS => { writer => \&writer_windows, | |
123 | sort => OpenSSL::Ordinals::by_name(), | |
124 | platforms => { WIN32 => 1, | |
211da00b | 125 | _WIN32 => 1 } }, |
8effd8fa RL |
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 | } | |
948d0125 | 180 | |
8effd8fa RL |
181 | # Found no match? Then it's a go |
182 | return 1; | |
948d0125 RL |
183 | } |
184 | ||
8effd8fa RL |
185 | sub feature_filter { |
186 | my $item = shift; | |
8effd8fa | 187 | my @features = ( $item->features() ); |
d02b48c6 | 188 | |
8effd8fa | 189 | # True if no features are defined |
ab1e5495 | 190 | return 1 if scalar @features == 0; |
62dc5aad | 191 | |
ab1e5495 | 192 | my $verdict = ! grep { $disabled_uc{$_} } @features; |
62dc5aad | 193 | |
8effd8fa RL |
194 | if ($apiv) { |
195 | foreach (@features) { | |
2284f64c RL |
196 | next unless /^DEPRECATEDIN_(\d+)(?:_(\d+)_(\d+))?$/; |
197 | my $symdep = sprintf "%x%02x%02x", $1, ($2 // 0), ($3 // 0); | |
8effd8fa RL |
198 | $verdict = 0 if $apiv ge $symdep; |
199 | } | |
200 | } | |
d02b48c6 | 201 | |
8effd8fa | 202 | return $verdict; |
948d0125 RL |
203 | } |
204 | ||
8effd8fa RL |
205 | sub sorter_unix { |
206 | my $by_name = OpenSSL::Ordinals::by_name(); | |
207 | my %weight = ( | |
208 | 'FUNCTION' => 1, | |
209 | 'VARIABLE' => 2 | |
210 | ); | |
948d0125 | 211 | |
8effd8fa RL |
212 | return sub { |
213 | my $item1 = shift; | |
214 | my $item2 = shift; | |
451e60e9 | 215 | |
8effd8fa RL |
216 | my $verdict = $weight{$item1->type()} <=> $weight{$item2->type()}; |
217 | if ($verdict == 0) { | |
218 | $verdict = $by_name->($item1, $item2); | |
219 | } | |
220 | return $verdict; | |
221 | }; | |
47339f61 | 222 | } |
d02b48c6 | 223 | |
8effd8fa RL |
224 | sub sorter_linux { |
225 | my $by_version = OpenSSL::Ordinals::by_version(); | |
226 | my $by_unix = sorter_unix(); | |
62dc5aad | 227 | |
8effd8fa RL |
228 | return sub { |
229 | my $item1 = shift; | |
230 | my $item2 = shift; | |
62dc5aad | 231 | |
8effd8fa RL |
232 | my $verdict = $by_version->($item1, $item2); |
233 | if ($verdict == 0) { | |
234 | $verdict = $by_unix->($item1, $item2); | |
235 | } | |
236 | return $verdict; | |
237 | }; | |
62dc5aad RL |
238 | } |
239 | ||
8effd8fa RL |
240 | sub writer_linux { |
241 | my $thisversion = ''; | |
97624638 RL |
242 | my $currversion_s = ''; |
243 | my $prevversion_s = ''; | |
244 | my $indent = 0; | |
8effd8fa RL |
245 | |
246 | for (@_) { | |
247 | if ($thisversion && $_->version() ne $thisversion) { | |
97624638 RL |
248 | die "$ordinals_file: It doesn't make sense to have both versioned ", |
249 | "and unversioned symbols" | |
250 | if $thisversion eq '*'; | |
8effd8fa | 251 | print <<"_____"; |
97624638 | 252 | }${prevversion_s}; |
8effd8fa | 253 | _____ |
97624638 | 254 | $prevversion_s = " OPENSSL${SO_VARIANT}_$thisversion"; |
8effd8fa RL |
255 | $thisversion = ''; # Trigger start of next section |
256 | } | |
257 | unless ($thisversion) { | |
97624638 | 258 | $indent = 0; |
8effd8fa | 259 | $thisversion = $_->version(); |
97624638 RL |
260 | $currversion_s = ''; |
261 | $currversion_s = "OPENSSL${SO_VARIANT}_$thisversion " | |
262 | if $thisversion ne '*'; | |
8effd8fa | 263 | print <<"_____"; |
97624638 | 264 | ${currversion_s}{ |
8effd8fa RL |
265 | global: |
266 | _____ | |
267 | } | |
268 | print ' ', $_->name(), ";\n"; | |
269 | } | |
62dc5aad | 270 | |
8effd8fa RL |
271 | print <<"_____"; |
272 | local: *; | |
97624638 | 273 | }${prevversion_s}; |
8effd8fa | 274 | _____ |
12aefe78 DSH |
275 | } |
276 | ||
8effd8fa RL |
277 | sub writer_aix { |
278 | for (@_) { | |
279 | print $_->name(),"\n"; | |
280 | } | |
0b352c58 RL |
281 | } |
282 | ||
8effd8fa RL |
283 | sub writer_windows { |
284 | print <<"_____"; | |
d02b48c6 | 285 | ; |
8effd8fa | 286 | ; Definition file for the DLL version of the $libname library from OpenSSL |
d02b48c6 RE |
287 | ; |
288 | ||
8effd8fa | 289 | LIBRARY $libname |
d02b48c6 | 290 | |
8effd8fa RL |
291 | EXPORTS |
292 | _____ | |
293 | for (@_) { | |
294 | print " ",$_->name(),"\n"; | |
295 | } | |
296 | } | |
d02b48c6 | 297 | |
36d3acb9 RL |
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 | ||
8effd8fa RL |
319 | sub writer_VMS { |
320 | my @slot_collection = (); | |
36d3acb9 RL |
321 | my $collector = |
322 | $case_insensitive ? \&collect_VMS_uppercase : \&collect_VMS_mixedcase; | |
8effd8fa RL |
323 | |
324 | my $last_num = 0; | |
325 | foreach (@_) { | |
326 | while (++$last_num < $_->number()) { | |
36d3acb9 | 327 | push @slot_collection, $collector->(); # Just occupy a slot |
8effd8fa RL |
328 | } |
329 | my $type = { | |
330 | FUNCTION => 'PROCEDURE', | |
331 | VARIABLE => 'DATA' | |
332 | } -> {$_->type()}; | |
36d3acb9 | 333 | push @slot_collection, $collector->($_->name(), $type); |
8effd8fa | 334 | } |
d02b48c6 | 335 | |
97624638 RL |
336 | print <<"_____" if defined $version; |
337 | IDENTIFICATION=$version | |
338 | _____ | |
36d3acb9 | 339 | print <<"_____" unless $case_insensitive; |
8effd8fa | 340 | CASE_SENSITIVE=YES |
36d3acb9 RL |
341 | _____ |
342 | print <<"_____"; | |
8effd8fa RL |
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) { | |
36d3acb9 RL |
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 | |
8effd8fa RL |
362 | my $firstcomma = ','; |
363 | ||
36d3acb9 | 364 | if ($symvtextcount + $settextlength > 1024) { |
8effd8fa RL |
365 | print <<"_____"; |
366 | ) | |
367 | SYMBOL_VECTOR=(- | |
368 | _____ | |
369 | $symvtextcount = 16; # The length of "SYMBOL_VECTOR=(" | |
a388633d | 370 | } |
8effd8fa RL |
371 | if ($symvtextcount == 16) { |
372 | $firstcomma = ''; | |
373 | } | |
36d3acb9 RL |
374 | |
375 | my $indent = ' '.$firstcomma; | |
376 | foreach (@$set) { | |
377 | print <<"_____"; | |
378 | $indent$_ - | |
8effd8fa | 379 | _____ |
36d3acb9 RL |
380 | $symvtextcount += length($indent) + length($_) + 1; |
381 | $indent = ' ,'; | |
382 | } | |
8effd8fa RL |
383 | } |
384 | print <<"_____"; | |
385 | ) | |
386 | _____ | |
387 | ||
97624638 | 388 | if (defined $version) { |
d1c87578 RL |
389 | $version =~ /^(\d+)\.(\d+)\.(\d+)/; |
390 | my $libvmajor = $1; | |
391 | my $libvminor = $2 * 100 + $3; | |
97624638 | 392 | print <<"_____"; |
72818ef0 | 393 | GSMATCH=LEQUAL,$libvmajor,$libvminor |
8effd8fa | 394 | _____ |
97624638 | 395 | } |
e863d920 MC |
396 | } |
397 | ||
8effd8fa RL |
398 | sub writer_ctest { |
399 | print <<'_____'; | |
400 | /* | |
401 | * Test file to check all DEF file symbols are present by trying | |
402 | * to link to all of them. This is *not* intended to be run! | |
403 | */ | |
e863d920 | 404 | |
8effd8fa | 405 | int main() |
e863d920 | 406 | { |
8effd8fa | 407 | _____ |
e863d920 | 408 | |
8effd8fa RL |
409 | for (@_) { |
410 | if ($_->type() eq 'VARIABLE') { | |
411 | print "\textern int ", $_->name(), '; /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n"; | |
412 | } else { | |
413 | print "\textern int ", $_->name(), '(); /* type unknown */ /* ', $_->number(), ' ', $_->version(), " */\n"; | |
414 | } | |
415 | } | |
416 | print <<'_____'; | |
e863d920 | 417 | } |
8effd8fa | 418 | _____ |
7a556fb6 | 419 | } |