Cosmetic: Compiler warning prevention
[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         s/#.*$//s;
67         next if /^\s*$/s;       # empty
68         next if /^\s*#.*/s;     # comment
69         my($module,$symbol,$iswhat)=(/^\s*(\S+)\s+(\S+)(?:\s+(undef|pass|wrap))?\s*$/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 #include <string.h>     /* for built-in: strncmp,memmove */
108
109
110 HERE
111
112 for my $symbol (sort keys(%symbol)) {
113         my $def=$def{$symbol};
114         if (!$def) {
115                 # use global symbol named '${symbol}' to cause symbol conflict if it is already defined
116                 print <<"HERE";
117 #define ${symbol}_undef ${symbol}
118 void ${symbol}(void)
119 {
120         g_error("%s: Function '$symbol' NOT IMPLEMENTED",G_STRLOC);
121 }
122 HERE
123                 next;
124                 }
125         if ($patch{$symbol{$symbol}} && "data" ne $def->{"type"}) {
126                 print "static struct captive_ModuleList_patchpoint ${symbol}_patchpoint;\n";
127                 }
128         if ("data" eq $def->{"type"}) {
129                 die "'data' type not pass-able: $symbol" if $def->{"pass"} || $def->{"wrap"};
130                 print "extern void/* ==unknown */ ${symbol};\n";
131                 print "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
132                 next;
133                 }
134         if ("cdecl" eq $def->{"type"} && !defined $def->{"args4"} && !$def->{"pass"} && !$def->{"wrap"}) {
135                 # g_log(,G_LOG_LEVEL_DEBUG,...) not possible if we do not know the arguments count
136                 my %forbidden=map(($_=>1),qw(strncmp memmove)); # Prevent: conflicting types for built-in function ...
137                 print "void/* ==unknown */ ${symbol}(void/* ==unknown */);\n" if !$forbidden{$symbol};
138                 print "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
139                 next;
140                 }
141
142         die "Needed argument count for: $symbol" if !defined $def->{"args4"};
143         my @args_out=map("arg$_",0..($def->{"args4"}-1));
144         my @args_in=($def->{"type"} ne "fastcall" ? @args_out
145                         : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
146         my $attrib=""
147                         ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
148                         .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3)))");
149         for my $type ("clean","attrib") {
150                 print
151                                 "typedef guint64"
152                                                 ." ".($type eq "clean" ? "" : $attrib)
153                                                 ." (${symbol}_t_".$type.")(".(join(",",map("guint32 $_",
154                                                                 ($type eq "clean" ? @args_out : @args_in))) || "void").");\n";
155                 }
156         if ($def->{"wrap"}) {
157                 print "${symbol}_t_clean ${symbol}_wrap;\n";
158                 }
159         elsif (!$def->{"pass"}) {       # direct
160                 print "${symbol}_t_clean $symbol;\n";
161                 }
162         my @args_print=@args_out;
163         for my $pass ("outer","clean","inner") {
164                 if ($pass eq "inner") {
165                         next if !$def->{"wrap"};        # use two passes only for $def->{"wrap"}
166                         # swap the roles of @args_in and @args_out
167                         my @args_xchg=@args_in;
168                         @args_in=@args_out;
169                         @args_out=@args_xchg;
170                         }
171                 next if $pass eq "clean" && !$def->{"pass"} && !$def->{"wrap"};
172                 print(
173                                 "guint64".($pass eq "outer" ? " $attrib" : "")
174                                                 ." ${symbol}"
175                                                                 .($pass eq "clean" ? "" : 
176                                                                                 ($pass eq "inner" ? "_orig" : "_".$def->{"type"}))
177                                                                 ."(".(join(",",map("guint32 $_",($pass eq "clean" ? @args_out : @args_in))) || "void").")\n",
178                                 "{\n",
179                                 "guint64 r;\n",
180                                 ($pass ne "outer" && $def->{"type"} eq "fastcall" ? "guint32 "
181                                                 .join(",",map("$_=0xDEADF00D","stub_eax",
182                                                                 ($def->{"args4"}<=0 ? "arg0" : ()),
183                                                                 ($def->{"args4"}<=1 ? "arg1" : ())))
184                                                 .";\n" : ""),
185                                 "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
186                                                 ."\"%s".($def->{"wrap"} ? ";$pass" : "")
187                                                                 ."(".join(",",map("0x%08x",@args_print)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print))
188                                                 .");\n",
189                                 "");
190                 if ($def->{"pass"} || ($def->{"wrap"} && $pass eq "inner")) {
191                         print
192                                         "\tg_return_val_if_fail(${symbol}_patchpoint.orig_w32_func!=NULL,0);\n",
193                                         "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n",
194                                         "\t${symbol}_patchpoint.through_w32_func=TRUE;\n",
195                                         "\tr=(*(${symbol}_t_attrib *)${symbol}_patchpoint.orig_w32_func)(".join(",",@args_in).");\n",
196                                         "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n";
197                         }
198                 else {
199                         print
200                                         "\tr=${symbol}".($def->{"wrap"} ? "_wrap" : "")."(".join(",",@args_out).");\n";
201                         }
202                 print
203                                 # We diplay just the lower 32-bit of the result EDX:EAX as it is usually not used,
204                                 # the only exception are _all{mul,div,*}(); EDX:EAX convention is always compatible.
205                                 "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
206                                                 ."\"... %s".($def->{"wrap"} ? ";$pass" : "")
207                                                                 ."(".join(",",map("0x%08x",@args_print)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print)).",(guint32)r"
208                                                 .");\n",
209                                 "\treturn r;\n",
210                                 "}\n";
211                 }
212         }
213
214 # write function captive_kernel_{exports,patches}()
215 for my $functype ("exports","patches") {
216         print <<"HERE";
217
218 gboolean captive_kernel_$functype(void)
219 {
220 gboolean errbool;
221
222 HERE
223         for my $module (sort keys(%module)) {
224                 my $moduleref=$module{$module};
225                 next if ($functype eq "patches") != defined $patch{$module};
226                 print "\t\terrbool="
227                                 .($functype eq "patches" ? "captive_ModuleList_patch" : "captive_ModuleList_add_builtin")
228                                 ."(\"$module\",\n";
229                 for my $symbol (sort keys(%$moduleref)) {
230                         next if $functype eq "patches" && !$def{$symbol};
231                         print "\t\t\t\"$symbol\",&${symbol}_",
232                                         ($def{$symbol}{"type"} || "undef"),
233                                         (($functype ne "patches") ? () : (",".("data" eq $def{$symbol}{"type"} ? "NULL" : "&${symbol}_patchpoint"))),
234                                         ",\n";
235                         }
236                 print <<"HERE";
237                         NULL);
238         g_return_val_if_fail(errbool,FALSE);
239
240 HERE
241                 }
242         print <<"HERE";
243         return TRUE;
244 }
245 HERE
246         }
247
248 # exit
249 my $total=0;
250 $total+=$_ for (values(%stats));
251 my $statstring;
252 for my $statname (sort keys(%stats)) {
253         $statstring.=" ".($statname || "define")."=".$stats{$statname}."(".int(100*$stats{$statname}/$total)."%)";
254         }
255 print STDERR "$0: Processed ".scalar(keys(%module))." modules:".$statstring."\n";
256 exit 0;
257
258
259 __END__
260
261 =head1 NAME
262
263 captivesym.pl - Generate source files based on .captivesym symbol file
264
265 =head1 SYNOPSIS
266
267 ./captivesym.pl path/to/ntoskrnl.def path/to/hal.def exports.captivesym E<gt>exports.c
268
269 =head1 DESCRIPTION
270
271 Source files with symbol call type definitions are identified by matching
272 pattern I<*.def>. The remaining files (I<.captivesym> ones) must
273 consist of lines with whitespace-separated lines as described below.
274
275 =over
276
277 =item (B<module>,E<lt>patchE<gt>)
278
279 Declare B<module> as mandatory W32 binary file to be patched by libcaptive.
280 Currently being used only for C<ntoskrnl.exe>.
281
282 Any function call even inside such module is trapped and redirected for
283 libcaptive processing even if it is just for debug-dumping of B<pass> type.
284
285 =item (B<module>,B<symbol>,[undef|pass|wrap])
286
287 =over
288
289 =item ""
290
291 Name without special attribute declares function fully implemented by GNU/Linux
292 code. Original W32 binary function will never be called.
293
294 You may fully implement function for both E<lt>patchE<gt>ed and
295 unE<lt>patchE<gt>ed modules.
296
297 =item undef
298
299 Optional "undef" specifies invocation of a generated stub function displaying
300 C<g_error()> message.
301
302 For "unpatched" modules you have to specify all the referenced symbols at least
303 as this "undef" symbol. For "patched" modules it is not needed for native
304 W32-PE binary modules importing such symbol but it is still required for W32
305 .so files to satisfy .so dynamic linker.
306
307 It is forbidden to "undef" C<DATA> type of items; you have to cope with it.
308
309 =item pass
310
311 Calls of this function are debug-dumped on its entry/exit but they are fully
312 left to be solved by W32 binary file being E<lt>patchE<gt>ed.
313
314 It is forbidden to specify "pass" for unE<lt>patchE<gt>ed modules.
315
316 =item wrap
317
318 Calls of this function are debug-dumped on its entry/exit. Execution is left
319 to be solved by your GNU/Linux implementation called B<functionname_wrap>.
320 You are allowed to call the original W32 binary function named
321 B<functionname_orig> but you have to use your own prototype declaration for it.
322 Both B<functionname_wrap> and B<functionname_orig> should be used with standard
323 GNU/Linux C compiler function call type notwithstanding any real W32
324 implementation details.
325
326 It is forbidden to specify "wrap" for unE<lt>patchE<gt>ed modules.
327
328 =back
329
330 =back
331
332 =begin comment
333
334         choose one:
335                 podchecker: *** WARNING: node 'http:$_' contains non-escaped | or /
336                 pod2html: cannot resolve L.lt.http:$_.gt.
337
338 =end comment
339
340 Source files I<*.def> are required to have on of three item types described at
341 L<http://msdn.microsoft.com/library/en-us/vclang/html/_core_argument_passing_and_naming_conventions.asp>
342 (B<argslength> must be dividable by B<4>):
343
344 =over
345
346 =item cdecl: functionname
347
348 =item stdcall: functionname@argslength
349
350 =item fastcall: @functionname@argslength
351
352 =item cdecl fixup: functionname:argslength
353
354 This item must follow (even in some other I<*.def> file) previous B<cdecl>
355 specification to specify the number of arguments as it is required for B<pass>
356 or B<wrap> type of B<cdecl> function calls.
357
358 =back
359
360 =head1 COPYRIGHT
361
362 Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
363
364 This program is free software; you can redistribute it and/or modify
365 it under the terms of the GNU General Public License as published by
366 the Free Software Foundation; exactly version 2 of June 1991 is required
367
368 This program is distributed in the hope that it will be useful,
369 but WITHOUT ANY WARRANTY; without even the implied warranty of
370 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
371 GNU General Public License for more details.
372
373 You should have received a copy of the GNU General Public License
374 along with this program; if not, write to the Free Software
375 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
376
377 =cut