mkdef.pl 12 KB

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