Fixed export of <patch>ed 'data' symbols
[captive.git] / src / libcaptive / ke / captivesym.pl
1 #! /usr/bin/perl
2
3 # $Id$
4 # Generate source files based on .captivesym symbol file
5 # Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use vars qw($VERSION);
22 $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
23 use strict;
24 use warnings;
25
26
27 my %def;
28 while ($ARGV[0] && $ARGV[0]=~/[.]def$/) {
29         map({ open DEF,"<$_" or die "open(\"$_\"): $!"; } shift());
30         while (<DEF>) {
31                 s/;.*//s;
32                 next if /^\s*$/s;
33                 next if /^(?:LIBRARY|EXPORTS)\b/s;
34                 my($atsign,$symbol,$args,$argscdecl,$isdata)=(/^\s*(\@)?(\S+?)(?:\@(\d+))?(?::(\d+))?(\s+DATA)?\s*$/s);
35                 die "Invalid line" if !defined $symbol;
36                 #          popped args          doc  *.def *.def2
37                 # cdecl    no     stack         _f   f     f:4
38                 # stdcall  yes    stack         _f@4 f@4
39                 # fastcall yes    ecx,edx,stack @f@4 @f@4
40                 die "Invalid attributes for data symbol: $symbol" if $isdata && ($atsign || defined $args);
41                 die "\@funcname without \@4 suffix not recognized: $symbol" if $atsign && !defined $args;
42                 die "Invalid \@$args number: $symbol" if defined $args && ($args<0 || ($args%4));
43                 if (!$argscdecl) {
44                         die "Duplicate symbol: $symbol" if exists $def{$symbol};
45                         }
46                 else {
47                         die "cdecl-fixup without previous declaration: $symbol" if !$def{$symbol};
48                         die "cdecl-fixup with non-cdecl previous declaration: $symbol" if $def{$symbol}{"type"} ne "cdecl";
49                         die "cdecl-fixup for already fix-uped cdecl: $symbol" if exists $def{$symbol}{"args4"};
50                         $args=$argscdecl;
51                         }
52                 $def{$symbol}={
53                                 "type"=>($isdata ? "data" : (!defined $args ? "cdecl" : (!$atsign ? "stdcall" : "fastcall"))),
54                                 (!defined $args ? () : ("args4"=>$args/4)),
55                                 };
56                 }
57         close DEF or warn "close(DEF): $!";
58         }
59
60 # read source
61 my %module;     # $module{'module'}{'symbol'}=1/""
62 my %symbol;     # $symbol{'symbol'}='module'
63 my %patch;      # $patch{'module'}=1/undef
64 my %stats;      # $stats{'iswhat'}=42
65 while (<>) {
66         chomp;
67         next if /^\s*$/;        # empty
68         next if /^\s*#.*/;      # comment
69         my($module,$symbol,$iswhat)=(/^\s*(\S+)\s+(\S+)(?:\s+(undef|pass|wrap))?\s*$/);
70         $iswhat="" if !defined $iswhat;
71         die "Invalid line" if !defined $symbol;
72         if ($symbol eq "<patch>") {
73                 die "Invalid line" if $iswhat;
74                 die "Symbols already present during <patch> for: $module" if $module{$module}{$symbol};
75                 $patch{$module}=1;
76                 next;
77                 }
78         die "Symbol already exists: $symbol" if exists $symbol{$symbol};
79         if ($iswhat eq "undef") {
80                 warn "Undefined symbol not in *.def files; 'data' type risk imminent: $symbol" if !$def{$symbol};
81                 die "Undefined 'data' type symbols are not safe: $symbol" if $def{$symbol} && $def{$symbol}{"type"} eq "data";
82                 delete $def{$symbol};
83                 }
84         die "Symbol not in *.def files: $symbol" if $iswhat ne "undef" && !$def{$symbol};
85         if ($iswhat eq "pass" || $iswhat eq "wrap") {
86                 die "args count not fixed up for '$iswhat' type: ".$symbol."[".$def{$symbol}{"type"}."]" if !$def{$symbol}{"args4"};
87                 die "'$iswhat' not permitted if <patch> not specified for module on symbol: $symbol" if !$patch{$module};
88                 $def{$symbol}{$iswhat}=1;
89                 }
90         $module{$module}{$symbol}=$iswhat ne "undef";
91         $symbol{$symbol}=$module;
92         $stats{$iswhat}++;
93         }
94
95 # file header
96 print <<"HERE";
97 /* File generated automatically by captivesym.pl from "$ARGV" */
98 /* DO NOT EDIT! */
99
100 #include "config.h"
101
102 #include "captive/ldr_exports.h"        /* for captive_ModuleList_add_builtin() */
103 #include <glib/gtypes.h>
104 #include <glib/gmessages.h>
105 #include <glib/gmacros.h>
106
107
108 HERE
109
110 for my $symbol (sort keys(%symbol)) {
111         my $def=$def{$symbol};
112         if (!$def) {
113                 # use global symbol named '${symbol}' to cause symbol conflict if it is already defined
114                 print <<"HERE";
115 #define ${symbol}_undef ${symbol}
116 void ${symbol}(void)
117 {
118         g_error("%s: Function '$symbol' NOT IMPLEMENTED",G_STRLOC);
119 }
120 HERE
121                 next;
122                 }
123         if ($patch{$symbol{$symbol}} && "data" ne $def->{"type"}) {
124                 print "static struct captive_ModuleList_patchpoint ${symbol}_patchpoint;\n";
125                 }
126         if ("data" eq $def->{"type"}) {
127                 die "'data' type not pass-able: $symbol" if $def->{"pass"} || $def->{"wrap"};
128                 print "extern void/* ==unknown */ ${symbol};\n",
129                                 "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
130                 next;
131                 }
132         if ("cdecl" eq $def->{"type"} && !defined $def->{"args4"} && !$def->{"pass"} && !$def->{"wrap"}) {
133                 # g_log(,G_LOG_LEVEL_DEBUG,...) not possible if we do not know the arguments count
134                 print "void/* ==unknown */ ${symbol}(void/* ==unknown */);\n",
135                                 "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
136                 next;
137                 }
138
139         die "Needed argument count for: $symbol" if !defined $def->{"args4"};
140         my @args_out=map("arg$_",0..($def->{"args4"}-1));
141         my @args_in=($def->{"type"} ne "fastcall" ? @args_out
142                         : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
143         my $attrib=""
144                         ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
145                         .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3)))");
146         for my $type ("clean","attrib") {
147                 print
148                                 "typedef guint64"
149                                                 ." ".($type eq "clean" ? "" : $attrib)
150                                                 ." (${symbol}_t_".$type.")(".(join(",",map("guint32 $_",
151                                                                 ($type eq "clean" ? @args_out : @args_in))) || "void").");\n";
152                 }
153         if ($def->{"wrap"}) {
154                 print "${symbol}_t_clean ${symbol}_wrap;\n";
155                 }
156         elsif (!$def->{"pass"}) {       # direct
157                 print "${symbol}_t_clean $symbol;\n";
158                 }
159         my @args_print=@args_out;
160         for my $pass ("outer","clean","inner") {
161                 if ($pass eq "inner") {
162                         next if !$def->{"wrap"};        # use two passes only for $def->{"wrap"}
163                         # swap the roles of @args_in and @args_out
164                         my @args_xchg=@args_in;
165                         @args_in=@args_out;
166                         @args_out=@args_xchg;
167                         }
168                 next if $pass eq "clean" && !$def->{"pass"} && !$def->{"wrap"};
169                 print(
170                                 "guint64".($pass eq "outer" ? " $attrib" : "")
171                                                 ." ${symbol}"
172                                                                 .($pass eq "clean" ? "" : 
173                                                                                 ($pass eq "inner" ? "_orig" : "_".$def->{"type"}))
174                                                                 ."(".(join(",",map("guint32 $_",($pass eq "clean" ? @args_out : @args_in))) || "void").")\n",
175                                 "{\n",
176                                 "guint64 r;\n",
177                                 ($pass ne "outer" && $def->{"type"} eq "fastcall" ? "guint32 "
178                                                 .join(",",map("$_=0xDEADF00D","stub_eax",
179                                                                 ($def->{"args4"}<=0 ? "arg0" : ()),
180                                                                 ($def->{"args4"}<=1 ? "arg1" : ())))
181                                                 .";\n" : ""),
182                                 "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
183                                                 ."\"%s".($def->{"wrap"} ? ";$pass" : "")
184                                                                 ."(".join(",",map("0x%08x",@args_print)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print))
185                                                 .");\n",
186                                 "");
187                 if ($def->{"pass"} || ($def->{"wrap"} && $pass eq "inner")) {
188                         print
189                                         "\tg_return_val_if_fail(${symbol}_patchpoint.orig_w32_func!=NULL,0);\n",
190                                         "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n",
191                                         "\t${symbol}_patchpoint.through_w32_func=TRUE;\n",
192                                         "\tr=(*(${symbol}_t_attrib *)${symbol}_patchpoint.orig_w32_func)(".join(",",@args_in).");\n",
193                                         "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n";
194                         }
195                 else {
196                         print
197                                         "\tr=${symbol}".($def->{"wrap"} ? "_wrap" : "")."(".join(",",@args_out).");\n";
198                         }
199                 print
200                                 # We diplay just the lower 32-bit of the result EDX:EAX as it is usually not used,
201                                 # the only exception are _all{mul,div,*}(); EDX:EAX convention is always compatible.
202                                 "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
203                                                 ."\"... %s".($def->{"wrap"} ? ";$pass" : "")
204                                                                 ."(".join(",",map("0x%08x",@args_print)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print)).",(guint32)r"
205                                                 .");\n",
206                                 "\treturn r;\n",
207                                 "}\n";
208                 }
209         }
210
211 # write function captive_kernel_{exports,patches}()
212 for my $functype ("exports","patches") {
213         print <<"HERE";
214
215 gboolean captive_kernel_$functype(void)
216 {
217 gboolean errbool;
218
219 HERE
220         for my $module (sort keys(%module)) {
221                 my $moduleref=$module{$module};
222                 next if ($functype eq "patches") != defined $patch{$module};
223                 print "\t\terrbool="
224                                 .($functype eq "patches" ? "captive_ModuleList_patch" : "captive_ModuleList_add_builtin")
225                                 ."(\"$module\",\n";
226                 for my $symbol (sort keys(%$moduleref)) {
227                         next if $functype eq "patches" && !$def{$symbol};
228                         print "\t\t\t\"$symbol\",&${symbol}_",
229                                         ($def{$symbol}{"type"} || "undef"),
230                                         (($functype ne "patches") ? () : (",".("data" eq $def{$symbol}{"type"} ? "NULL" : "&${symbol}_patchpoint"))),
231                                         ",\n";
232                         }
233                 print <<"HERE";
234                         NULL);
235         g_return_val_if_fail(errbool,FALSE);
236
237 HERE
238                 }
239         print <<"HERE";
240         return TRUE;
241 }
242 HERE
243         }
244
245 # exit
246 my $total=0;
247 $total+=$_ for (values(%stats));
248 my $statstring;
249 for my $statname (sort keys(%stats)) {
250         $statstring.=" ".($statname || "define")."=".$stats{$statname}."(".int(100*$stats{$statname}/$total)."%)";
251         }
252 print STDERR "$0: Processed ".scalar(keys(%module))." modules:".$statstring."\n";
253 exit 0;
254
255
256 __END__
257
258 =head1 NAME
259
260 captivesym.pl - Generate source files based on .captivesym symbol file
261
262 =head1 SYNOPSIS
263
264 ./captivesym.pl path/to/ntoskrnl.def path/to/hal.def exports.captivesym E<gt>exports.c
265
266 =head1 DESCRIPTION
267
268 Source files with symbol call type definitions are identified by matching
269 pattern I<*.def>. The remaining files (I<.captivesym> ones) must
270 consist of lines with whitespace-separated lines as described below.
271
272 =over
273
274 =item (B<module>,E<lt>patchE<gt>)
275
276 Declare B<module> as mandatory W32 binary file to be patched by libcaptive.
277 Currently being used only for C<ntoskrnl.exe>.
278
279 Any function call even inside such module is trapped and redirected for
280 libcaptive processing even if it is just for debug-dumping of B<pass> type.
281
282 =item (B<module>,B<symbol>,[undef|pass|wrap])
283
284 =over
285
286 =item ""
287
288 Name without special attribute declares function fully implemented by GNU/Linux
289 code. Original W32 binary function will never be called.
290
291 You may fully implement function for both E<lt>patchE<gt>ed and
292 unE<lt>patchE<gt>ed modules.
293
294 =item undef
295
296 Optional "undef" specifies invocation of a generated stub function displaying
297 C<g_error()> message.
298
299 For "unpatched" modules you have to specify all the referenced symbols at least
300 as this "undef" symbol. For "patched" modules it is not needed for native
301 W32-PE binary modules importing such symbol but it is still required for W32
302 .so files to satisfy .so dynamic linker.
303
304 It is forbidden to "undef" C<DATA> type of items; you have to cope with it.
305
306 =item pass
307
308 Calls of this function are debug-dumped on its entry/exit but they are fully
309 left to be solved by W32 binary file being E<lt>patchE<gt>ed.
310
311 It is forbidden to specify "pass" for unE<lt>patchE<gt>ed modules.
312
313 =item wrap
314
315 Calls of this function are debug-dumped on its entry/exit. Execution is left
316 to be solved by your GNU/Linux implementation called B<functionname_wrap>.
317 You are allowed to call the original W32 binary function named
318 B<functionname_orig> but you have to use your own prototype declaration for it.
319 Both B<functionname_wrap> and B<functionname_orig> should be used with standard
320 GNU/Linux C compiler function call type notwithstanding any real W32
321 implementation details.
322
323 It is forbidden to specify "wrap" for unE<lt>patchE<gt>ed modules.
324
325 =back
326
327 =back
328
329 =begin comment
330
331         choose one:
332                 podchecker: *** WARNING: node 'http:$_' contains non-escaped | or /
333                 pod2html: cannot resolve L.lt.http:$_.gt.
334
335 =end comment
336
337 Source files I<*.def> are required to have on of three item types described at
338 L<http://msdn.microsoft.com/library/en-us/vclang/html/_core_argument_passing_and_naming_conventions.asp>
339 (B<argslength> must be dividable by B<4>):
340
341 =over
342
343 =item cdecl: functionname
344
345 =item stdcall: functionname@argslength
346
347 =item fastcall: @functionname@argslength
348
349 =item cdecl fixup: functionname:argslength
350
351 This item must follow (even in some other I<*.def> file) previous B<cdecl>
352 specification to specify the number of arguments as it is required for B<pass>
353 or B<wrap> type of B<cdecl> function calls.
354
355 =back
356
357 =head1 COPYRIGHT
358
359 Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
360
361 This program is free software; you can redistribute it and/or modify
362 it under the terms of the GNU General Public License as published by
363 the Free Software Foundation; exactly version 2 of June 1991 is required
364
365 This program is distributed in the hope that it will be useful,
366 but WITHOUT ANY WARRANTY; without even the implied warranty of
367 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
368 GNU General Public License for more details.
369
370 You should have received a copy of the GNU General Public License
371 along with this program; if not, write to the Free Software
372 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
373
374 =cut