mkdef.pl 12 KB

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