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