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