]> granicus.if.org Git - postgresql/blob - src/tools/msvc/gendef.pl
8ccaab35519e472117d60243ce4cb5ff87cd71cd
[postgresql] / src / tools / msvc / gendef.pl
1 my @def;
2
3 use warnings;
4 use strict;
5 use 5.8.0;
6 use List::Util qw(max);
7
8 #
9 # Script that generates a .DEF file for all objects in a directory
10 #
11 # src/tools/msvc/gendef.pl
12 #
13
14 sub dumpsyms
15 {
16         my ($objfile, $symfile) = @_;
17         system("dumpbin /symbols /out:symbols.out $_ >NUL")
18           && die "Could not call dumpbin";
19         rename("symbols.out", $symfile);
20 }
21
22 # Given a symbol file path, loops over its contents
23 # and returns a list of symbols of interest as a dictionary
24 # of 'symbolname' -> symtype, where symtype is:
25 #
26 #     0    a CODE symbol, left undecorated in the .DEF
27 #     1    A DATA symbol, i.e. global var export
28 #
29 sub extract_syms
30 {
31         my ($symfile, $def) = @_;
32         open(F, "<$symfile") || die "Could not open $symfile for $_\n";
33         while (<F>)
34         {
35
36         # Expected symbol lines look like:
37         #
38         # 0   1        2      3            4            5 6
39         # IDX SYMBOL   SECT   SYMTYPE      SYMSTATIC      SYMNAME
40         # ------------------------------------------------------------------------
41         # 02E 00000130 SECTA  notype       External     | _standbyState
42         # 02F 00000009 SECT9  notype       Static       | _LocalRecoveryInProgress
43         # 064 00000020 SECTC  notype ()    Static       | _XLogCheckBuffer
44         # 065 00000000 UNDEF  notype ()    External     | _BufferGetTag
45         #
46         # See http://msdn.microsoft.com/en-us/library/b842y285.aspx
47         #
48         # We're not interested in the symbol index or offset.
49         #
50         # SECT[ION] is only examined to see whether the symbol is defined in a
51         # COFF section of the local object file; if UNDEF, it's a symbol to be
52         # resolved at link time from another object so we can't export it.
53         #
54         # SYMTYPE is always notype for C symbols as there's no typeinfo and no
55         # way to get the symbol type from name (de)mangling. However, we care
56         # if "notype" is suffixed by "()" or not. The presence of () means the
57         # symbol is a function, the absence means it isn't.
58         #
59         # SYMSTATIC indicates whether it's a compilation-unit local "static"
60         # symbol ("Static"), or whether it's available for use from other
61         # compilation units ("External"). We export all symbols that aren't
62         # static as part of the whole program DLL interface to produce UNIX-like
63         # default linkage.
64         #
65         # SYMNAME is, obviously, the symbol name. The leading underscore
66         # indicates that the _cdecl calling convention is used. See
67         # http://www.unixwiz.net/techtips/win32-callconv.html
68         # http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified
69         #
70                 s/notype \(\)/func/g;
71                 s/notype/data/g;
72
73                 my @pieces = split;
74
75                 # Skip file and section headers and other non-symbol entries
76                 next unless defined($pieces[0]) and $pieces[0] =~ /^[A-F0-9]{3,}$/;
77
78                 # Skip blank symbol names
79                 next unless $pieces[6];
80
81                 # Skip externs used from another compilation unit
82                 next if ($pieces[2] eq "UNDEF");
83
84                 # Skip static symbols
85                 next unless ($pieces[4] eq "External");
86
87                 # Skip some more MSVC-generated crud
88                 next if $pieces[6] =~ /^@/;
89                 next if $pieces[6] =~ /^\(/;
90
91                 # __real and __xmm are out-of-line floating point literals and
92                 # (for __xmm) their SIMD equivalents. They shouldn't be part
93                 # of the DLL interface.
94                 next if $pieces[6] =~ /^__real/;
95                 next if $pieces[6] =~ /^__xmm/;
96
97                 # __imp entries are imports from other DLLs, eg __imp__malloc .
98                 # (We should never have one of these that hasn't already been skipped
99                 # by the UNDEF test above, though).
100                 next if $pieces[6] =~ /^__imp/;
101
102                 # More under-documented internal crud
103                 next if $pieces[6] =~ /NULL_THUNK_DATA$/;
104                 next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/;
105                 next if $pieces[6] =~ /^__NULL_IMPORT/;
106
107                 # Skip string literals
108                 next if $pieces[6] =~ /^\?\?_C/;
109
110                 # We assume that if a symbol is defined as data, then as a function,
111                 # the linker will reject the binary anyway. So it's OK to just pick
112                 # whatever came last.
113                 $def->{ $pieces[6] } = $pieces[3];
114         }
115         close(F);
116 }
117
118 sub writedef
119 {
120         my ($deffile, $platform, $def) = @_;
121         open(DEF, ">$deffile") || die "Could not write to $deffile\n";
122         print DEF "EXPORTS\n";
123         foreach my $f (sort keys %{$def})
124         {
125                 my $isdata = $def->{$f} eq 'data';
126
127                 # Strip the leading underscore for win32, but not x64
128                 $f =~ s/^_//
129                   unless ($platform eq "x64");
130
131                 # Emit just the name if it's a function symbol, or emit the name
132                 # decorated with the DATA option for variables.
133                 if ($isdata)
134                 {
135                         print DEF "  $f DATA\n";
136                 }
137                 else
138                 {
139                         print DEF "  $f\n";
140                 }
141         }
142         close(DEF);
143 }
144
145
146 sub usage
147 {
148         die(    "Usage: gendef.pl <modulepath> <platform>\n"
149                   . "    modulepath: path to dir with obj files, no trailing slash"
150                   . "    platform: Win32 | x64");
151 }
152
153 usage()
154   unless scalar(@ARGV) == 2
155           && (   ($ARGV[0] =~ /\\([^\\]+$)/)
156                   && ($ARGV[1] eq 'Win32' || $ARGV[1] eq 'x64'));
157 my $defname  = uc $1;
158 my $deffile  = "$ARGV[0]/$defname.def";
159 my $platform = $ARGV[1];
160
161 # if the def file exists and is newer than all input object files, skip
162 # its creation
163 if (-f $deffile
164         && (-M $deffile > max(map { -M } <$ARGV[0]/*.obj>)))
165 {
166         print "Not re-generating $defname.DEF, file already exists.\n";
167         exit(0);
168 }
169
170 print "Generating $defname.DEF from directory $ARGV[0], platform $platform\n";
171
172 my %def = ();
173
174 while (<$ARGV[0]/*.obj>)
175 {
176         my $objfile = $_;
177         my $symfile = $objfile;
178         $symfile =~ s/\.obj$/.sym/i;
179         dumpsyms($objfile, $symfile);
180         print ".";
181         extract_syms($symfile, \%def);
182 }
183 print "\n";
184
185 writedef($deffile, $platform, \%def);
186
187 print "Generated " . scalar(keys(%def)) . " symbols\n";