+function calls of type "pass" and "wrap"
[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 while (<>) {
65         chomp;
66         next if /^\s*$/;        # empty
67         next if /^\s*#.*/;      # comment
68         my($module,$symbol,$iswhat)=(/^\s*(\S+)\s+(\S+)(?:\s+(undef|pass|wrap))?\s*$/);
69         $iswhat="" if !defined $iswhat;
70         die "Invalid line" if !defined $symbol;
71         if ($symbol eq "<patch>") {
72                 die "Invalid line" if $iswhat;
73                 die "Symbols already present during <patch> for: $module" if $module{$module}{$symbol};
74                 $patch{$module}=1;
75                 next;
76                 }
77         die "Symbol already exists: $symbol" if exists $symbol{$symbol};
78         if ($iswhat eq "undef") {
79                 warn "Undefined symbol not in *.def files; 'data' type risk imminent: $symbol" if !$def{$symbol};
80                 die "Undefined 'data' type symbols are not safe: $symbol" if $def{$symbol} && $def{$symbol}{"type"} eq "data";
81                 delete $def{$symbol};
82                 }
83         if ($iswhat eq "pass" || $iswhat eq "wrap") {
84                 die "args count not fixed up for '$iswhat' type: ".$symbol."[".$def{$symbol}{"type"}."]" if !$def{$symbol}{"args4"};
85                 die "'$iswhat' not permitted if <patch> not specified for module on symbol: $symbol" if !$patch{$module};
86                 $def{$symbol}{$iswhat}=1;
87                 }
88         die "Symbol not in *.def files: $symbol" if $iswhat ne "undef" && !$def{$symbol};
89         $module{$module}{$symbol}=$iswhat ne "undef";
90         $symbol{$symbol}=$module;
91         }
92
93 # file header
94 print <<"HERE";
95 /* File generated automatically by captivesym.pl from "$ARGV" */
96 /* DO NOT EDIT! */
97
98 #include "config.h"
99
100 #include "captive/ldr_exports.h"        /* for captive_ModuleList_add_builtin() */
101 #include <glib/gtypes.h>
102 #include <glib/gmessages.h>
103 #include <glib/gmacros.h>
104
105
106 HERE
107
108 my $symbols_undef=0;
109 for my $symbol (sort keys(%symbol)) {
110         my $def=$def{$symbol};
111         if (!$def) {
112                 # use global symbol named '${symbol}' to cause symbol conflict if it is already defined
113                 print <<"HERE";
114 #define ${symbol}_undef ${symbol}
115 void ${symbol}(void)
116 {
117         g_error("%s: Function '$symbol' NOT IMPLEMENTED",G_STRLOC);
118 }
119 HERE
120                 $symbols_undef++;
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} || "data" eq $def{$symbol}{"type"});
228                         print "\t\t\t\"$symbol\",&${symbol}_",
229                                         ($def{$symbol}{"type"} || "undef"),
230                                         (($functype ne "patches") ? () : (",&${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 print STDERR "$0: Processed ".scalar(keys(%module))." modules, ".scalar(keys(%symbol))." symbols",
247                 " (".int(100*(1-$symbols_undef/scalar(keys(%symbol)))).'%'." implemented)\n";
248 print STDERR "$0: warning: $symbols_undef symbols are NOT IMPLEMENTED yet!\n" if $symbols_undef;
249 exit 0;
250
251
252 __END__
253
254 =head1 NAME
255
256 captivesym.pl - Generate source files based on .captivesym symbol file
257
258 =head1 SYNOPSIS
259
260 ./captivesym.pl path/to/ntoskrnl.def path/to/hal.def exports.captivesym E<gt>exports.c
261
262 =head1 DESCRIPTION
263
264 Source files with symbol call type definitions are identified by matching
265 pattern I<*.def>. The remaining files (I<.captivesym> ones) must
266 consist of lines with whitespace-separated lines as described below.
267
268 =over
269
270 =item (B<module>,E<lt>patchE<gt>)
271
272 Declare B<module> as mandatory W32 binary file to be patched by libcaptive.
273 Currently being used only for C<ntoskrnl.exe>.
274
275 Any function call even inside such module is trapped and redirected for
276 libcaptive processing even if it is just for debug-dumping of B<pass> type.
277
278 =item (B<module>,B<symbol>,[undef|pass|wrap])
279
280 =over
281
282 =item ""
283
284 Name without special attribute declares function fully implemented by GNU/Linux
285 code. Original W32 binary function will never be called.
286
287 You may fully implement function for both E<lt>patchE<gt>ed and
288 unE<lt>patchE<gt>ed modules.
289
290 =item undef
291
292 Optional "undef" specifies invocation of a generated stub function displaying
293 C<g_error()> message.
294
295 For "unpatched" modules you have to specify all the referenced symbols at least
296 as this "undef" symbol. For "patched" modules it is not needed for native
297 W32-PE binary modules importing such symbol but it is still required for W32
298 .so files to satisfy .so dynamic linker.
299
300 It is forbidden to "undef" C<DATA> type of items; you have to cope with it.
301
302 =item pass
303
304 Calls of this function are debug-dumped on its entry/exit but they are fully
305 left to be solved by W32 binary file being E<lt>patchE<gt>ed.
306
307 It is forbidden to specify "pass" for unE<lt>patchE<gt>ed modules.
308
309 =item wrap
310
311 Calls of this function are debug-dumped on its entry/exit. Execution is left
312 to be solved by your GNU/Linux implementation called B<functionname_wrap>.
313 You are allowed to call the original W32 binary function named
314 B<functionname_orig> but you have to use your own prototype declaration for it.
315 Both B<functionname_wrap> and B<functionname_orig> should be used with standard
316 GNU/Linux C compiler function call type notwithstanding any real W32
317 implementation details.
318
319 It is forbidden to specify "wrap" for unE<lt>patchE<gt>ed modules.
320
321 =back
322
323 =back
324
325 =begin comment
326
327         choose one:
328                 podchecker: *** WARNING: node 'http:$_' contains non-escaped | or /
329                 pod2html: cannot resolve L.lt.http:$_.gt.
330
331 =end comment
332
333 Source files I<*.def> are required to have on of three item types described at
334 L<http://msdn.microsoft.com/library/en-us/vclang/html/_core_argument_passing_and_naming_conventions.asp>
335 (B<argslength> must be dividable by B<4>):
336
337 =over
338
339 =item cdecl: functionname
340
341 =item stdcall: functionname@argslength
342
343 =item fastcall: @functionname@argslength
344
345 =item cdecl fixup: functionname:argslength
346
347 This item must follow (even in some other I<*.def> file) previous B<cdecl>
348 specification to specify the number of arguments as it is required for B<pass>
349 or B<wrap> type of B<cdecl> function calls.
350
351 =back
352
353 =head1 COPYRIGHT
354
355 Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
356
357 This program is free software; you can redistribute it and/or modify
358 it under the terms of the GNU General Public License as published by
359 the Free Software Foundation; exactly version 2 of June 1991 is required
360
361 This program is distributed in the hope that it will be useful,
362 but WITHOUT ANY WARRANTY; without even the implied warranty of
363 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
364 GNU General Public License for more details.
365
366 You should have received a copy of the GNU General Public License
367 along with this program; if not, write to the Free Software
368 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
369
370 =cut