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