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