+function calls of type "pass" and "wrap"
authorshort <>
Thu, 30 Jan 2003 11:32:10 +0000 (11:32 +0000)
committershort <>
Thu, 30 Jan 2003 11:32:10 +0000 (11:32 +0000)
+support of <patch>ed W32 modules (currently only ntoskrnl.exe)
+trap of all unspecified <patched>ed W32 modules exported functions

src/libcaptive/include/captive/ldr.h
src/libcaptive/include/captive/ldr_exports.h
src/libcaptive/ke/Makefile.am
src/libcaptive/ke/captivesym.pl
src/libcaptive/ldr/loader.c

index ed612f2..0f38ca0 100644 (file)
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
 NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,PUNICODE_STRING ModuleName,
     PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath);
 /* LdrLoadModule() declared in reactos includes */
+struct captive_ModuleList_patchpoint *captive_ModuleList_patchpoint_find(gconstpointer ExportAddress);
+G_CONST_RETURN gchar *captive_ModuleList_function_disable_find(gconstpointer ExportAddress);
 
 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
 /* Newlines prevent their inclusion by gtk-doc. */
index 68b941f..72c777a 100644 (file)
@@ -32,7 +32,7 @@ G_BEGIN_DECLS
 /**
  * captive_kernel_exports:
  *
- * Export all libcaptive symbols to reactos. It is done by *.def files used
+ * Export complete libcaptive modules to reactos. It is done by *.def files used
  * by dlltool(1) of Mingw32 compiler suite. We use native host OS compiler and
  * we also have just a limited set of functions over reactos itself.
  * We use our captive_ModuleList_add_builtin() to simulate PE headers exporting
@@ -41,14 +41,43 @@ G_BEGIN_DECLS
  * This function is generated automatically from #exports.captivesym file
  * by #captivesym.pl script.
  *
- * Multiple calls of this function are forbidden.
+ * Multiple calls of this function are forbidden. See also captive_kernel_patches().
  *
  * Returns: %TRUE if the export was successful.
  */
 gboolean captive_kernel_exports(void);
 
 
+/**
+ * captive_kernel_patches:
+ *
+ * Patches W32 libraries by libcaptive functions. It is done by *.def files used
+ * by dlltool(1) of Mingw32 compiler suite. We use native host OS compiler and
+ * we also have just a limited set of functions over reactos itself.
+ * We use our captive_ModuleList_patch() to modify existing module function
+ * tables generated from captive_ModuleList_add_builtin().
+ *
+ * This function is generated automatically from #exports.captivesym file
+ * by #captivesym.pl script.
+ *
+ * Multiple calls of this function are forbidden. See also captive_kernel_exports().
+ *
+ * Returns: %TRUE if the export was successful.
+ */
+gboolean captive_kernel_patches(void);
+
+
+struct captive_ModuleList_patchpoint {
+       guint8 *orig_w32_func;  /* direct pointer to W32 binary entry point */
+       guint8 *orig_w32_2ndinstr;      /* direct pointer to W32 binary second instruction after entry point */
+       void (*wrap_wrap_func)(void);   /* address of 'exports.c' debug-printing outer function */
+       guint8 orig_w32_func_byte;
+       guint8 orig_w32_2ndinstr_byte;
+       gboolean through_w32_func;      /* pass once through 'orig_w32_func' entry point without redirection to 'wrap_wrap_func' */
+       };
+
 gboolean captive_ModuleList_add_builtin(const gchar *FullName_utf8,...);
+gboolean captive_ModuleList_patch(const gchar *FullName_utf8,...);
 
 G_END_DECLS
 
index 3a4edbe..3398c9d 100644 (file)
@@ -45,4 +45,4 @@ libke_la_SOURCES+=exports.c
 
 # FIXME: a difference between "ntoskrnl/ntoskrnl.def" vs. "ntoskrnl/ntoskrnl.edf"?
 exports.c: exports.captivesym captivesym.pl exports.def $(top_srcdir)/reactos/ntoskrnl/ntoskrnl.def $(top_srcdir)/reactos/hal/hal/hal.def
-       perl captivesym.pl exports.def $(top_srcdir)/reactos/ntoskrnl/ntoskrnl.def $(top_srcdir)/reactos/hal/hal/hal.def $< >$@
+       perl captivesym.pl $(top_srcdir)/reactos/ntoskrnl/ntoskrnl.def $(top_srcdir)/reactos/hal/hal/hal.def exports.def $< >$@
index 369696a..87453ba 100755 (executable)
@@ -31,16 +31,24 @@ while ($ARGV[0] && $ARGV[0]=~/[.]def$/) {
                s/;.*//s;
                next if /^\s*$/s;
                next if /^(?:LIBRARY|EXPORTS)\b/s;
-               my($atsign,$symbol,$args,$isdata)=(/^\s*(\@)?(\S+?)(?:\@(\d+))?(\s+DATA)?\s*$/s);
+               my($atsign,$symbol,$args,$argscdecl,$isdata)=(/^\s*(\@)?(\S+?)(?:\@(\d+))?(?::(\d+))?(\s+DATA)?\s*$/s);
                die "Invalid line" if !defined $symbol;
-               #          popped args          doc  *.def
-               # cdecl    no     stack         _f   f
+               #          popped args          doc  *.def *.def2
+               # cdecl    no     stack         _f   f     f:4
                # stdcall  yes    stack         _f@4 f@4
                # fastcall yes    ecx,edx,stack @f@4 @f@4
-               die "Invalid attributes for data symbol" if $isdata && ($atsign || defined $args);
-               die "\@funcname without \@4 suffix not recognized" if $atsign && !defined $args;
-               die "Invalid \@$args number" if defined $args && ($args<0 || ($args%4));
-               die "Duplicate symbol: $symbol" if exists $def{$symbol};
+               die "Invalid attributes for data symbol: $symbol" if $isdata && ($atsign || defined $args);
+               die "\@funcname without \@4 suffix not recognized: $symbol" if $atsign && !defined $args;
+               die "Invalid \@$args number: $symbol" if defined $args && ($args<0 || ($args%4));
+               if (!$argscdecl) {
+                       die "Duplicate symbol: $symbol" if exists $def{$symbol};
+                       }
+               else {
+                       die "cdecl-fixup without previous declaration: $symbol" if !$def{$symbol};
+                       die "cdecl-fixup with non-cdecl previous declaration: $symbol" if $def{$symbol}{"type"} ne "cdecl";
+                       die "cdecl-fixup for already fix-uped cdecl: $symbol" if exists $def{$symbol}{"args4"};
+                       $args=$argscdecl;
+                       }
                $def{$symbol}={
                                "type"=>($isdata ? "data" : (!defined $args ? "cdecl" : (!$atsign ? "stdcall" : "fastcall"))),
                                (!defined $args ? () : ("args4"=>$args/4)),
@@ -51,22 +59,35 @@ while ($ARGV[0] && $ARGV[0]=~/[.]def$/) {
 
 # read source
 my %module;    # $module{'module'}{'symbol'}=1/""
-my %symbol;    # $symbol{'symbol'}=1/""
+my %symbol;    # $symbol{'symbol'}='module'
+my %patch;     # $patch{'module'}=1/undef
 while (<>) {
        chomp;
        next if /^\s*$/;        # empty
        next if /^\s*#.*/;      # comment
-       my($module,$symbol,$isundef)=(/^\s*(\S+)\s+(\S+)(\s+undef)?\s*$/);
+       my($module,$symbol,$iswhat)=(/^\s*(\S+)\s+(\S+)(?:\s+(undef|pass|wrap))?\s*$/);
+       $iswhat="" if !defined $iswhat;
        die "Invalid line" if !defined $symbol;
+       if ($symbol eq "<patch>") {
+               die "Invalid line" if $iswhat;
+               die "Symbols already present during <patch> for: $module" if $module{$module}{$symbol};
+               $patch{$module}=1;
+               next;
+               }
        die "Symbol already exists: $symbol" if exists $symbol{$symbol};
-       if ($isundef) {
+       if ($iswhat eq "undef") {
                warn "Undefined symbol not in *.def files; 'data' type risk imminent: $symbol" if !$def{$symbol};
                die "Undefined 'data' type symbols are not safe: $symbol" if $def{$symbol} && $def{$symbol}{"type"} eq "data";
                delete $def{$symbol};
                }
-       die "Symbol not in *.def files: $symbol" if !$isundef && !$def{$symbol};
-       $module{$module}{$symbol}=!$isundef;
-       $symbol{$symbol}=!$isundef;
+       if ($iswhat eq "pass" || $iswhat eq "wrap") {
+               die "args count not fixed up for '$iswhat' type: ".$symbol."[".$def{$symbol}{"type"}."]" if !$def{$symbol}{"args4"};
+               die "'$iswhat' not permitted if <patch> not specified for module on symbol: $symbol" if !$patch{$module};
+               $def{$symbol}{$iswhat}=1;
+               }
+       die "Symbol not in *.def files: $symbol" if $iswhat ne "undef" && !$def{$symbol};
+       $module{$module}{$symbol}=$iswhat ne "undef";
+       $symbol{$symbol}=$module;
        }
 
 # file header
@@ -97,70 +118,129 @@ void ${symbol}(void)
 }
 HERE
                $symbols_undef++;
+               next;
+               }
+       if ($patch{$symbol{$symbol}} && "data" ne $def->{"type"}) {
+               print "static struct captive_ModuleList_patchpoint ${symbol}_patchpoint;\n";
                }
-       elsif ("data" eq $def->{"type"}) {
+       if ("data" eq $def->{"type"}) {
+               die "'data' type not pass-able: $symbol" if $def->{"pass"} || $def->{"wrap"};
                print "extern void/* ==unknown */ ${symbol};\n",
                                "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
+               next;
                }
-       elsif ("cdecl" eq $def->{"type"}) {
-               # FIXME: g_log(,G_LOG_LEVEL_DEBUG,...) not implemented
+       if ("cdecl" eq $def->{"type"} && !defined $def->{"args4"} && !$def->{"pass"} && !$def->{"wrap"}) {
+               # g_log(,G_LOG_LEVEL_DEBUG,...) not possible if we do not know the arguments count
                print "void/* ==unknown */ ${symbol}(void/* ==unknown */);\n",
                                "#define ${symbol}_".$def->{"type"}." ${symbol}\n";
+               next;
+               }
+
+       die "Needed argument count for: $symbol" if !defined $def->{"args4"};
+       my @args_out=map("arg$_",0..($def->{"args4"}-1));
+       my @args_in=($def->{"type"} ne "fastcall" ? @args_out
+                       : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
+       my $attrib=""
+                       ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
+                       .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3)))");
+       for my $type ("clean","attrib") {
+               print
+                               "typedef guint64"
+                                               ." ".($type eq "clean" ? "" : $attrib)
+                                               ." (${symbol}_t_".$type.")(".(join(",",map("guint32 $_",
+                                                               ($type eq "clean" ? @args_out : @args_in))) || "void").");\n";
                }
-       else {
-               my @args_out=map("arg$_",0..($def->{"args4"}-1));
-               my @args_in=($def->{"type"} ne "fastcall" ? @args_out
-                               : ("stub_eax","arg1","arg0",@args_out[2..$#args_out]));
-               print "guint64 ${symbol}(".(join(",",map("guint32 $_",@args_out)) || "void").");\n",
-                               "static guint64 "
-                                                               ."__attribute__((__".(map(($_ ne "fastcall" ? $_ : "stdcall"),$def->{"type"}))[0]."__)) "
-                                                               .($def->{"type"} ne "fastcall" ? "" : "__attribute__((__regparm__(3))) ")
-                                                               ."${symbol}_".$def->{"type"}."(".(join(",",map("guint32 $_",@args_in)) || "void").")\n",
+       if ($def->{"wrap"}) {
+               print "${symbol}_t_clean ${symbol}_wrap;\n";
+               }
+       elsif (!$def->{"pass"}) {       # direct
+               print "${symbol}_t_clean $symbol;\n";
+               }
+       my @args_print=@args_out;
+       for my $pass ("outer","clean","inner") {
+               if ($pass eq "inner") {
+                       next if !$def->{"wrap"};        # use two passes only for $def->{"wrap"}
+                       # swap the roles of @args_in and @args_out
+                       my @args_xchg=@args_in;
+                       @args_in=@args_out;
+                       @args_out=@args_xchg;
+                       }
+               next if $pass eq "clean" && !$def->{"pass"} && !$def->{"wrap"};
+               print(
+                               "guint64".($pass eq "outer" ? " $attrib" : "")
+                                               ." ${symbol}"
+                                                               .($pass eq "clean" ? "" : 
+                                                                               ($pass eq "inner" ? "_orig" : "_".$def->{"type"}))
+                                                               ."(".(join(",",map("guint32 $_",($pass eq "clean" ? @args_out : @args_in))) || "void").")\n",
                                "{\n",
                                "guint64 r;\n",
+                               ($pass ne "outer" && $def->{"type"} eq "fastcall" ? "guint32 "
+                                               .join(",",map("$_=0xDEADF00D","stub_eax",
+                                                               ($def->{"args4"}<=0 ? "arg0" : ()),
+                                                               ($def->{"args4"}<=1 ? "arg1" : ())))
+                                               .";\n" : ""),
                                "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
-                                               ."\"%s(".join(",",map("0x%08x",@args_out)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_out))
+                                               ."\"%s".($def->{"wrap"} ? ";$pass" : "")
+                                                               ."(".join(",",map("0x%08x",@args_print)).")...\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print))
                                                .");\n",
-                               "\tr=${symbol}(".join(",",@args_out).");\n",
+                               "");
+               if ($def->{"pass"} || ($def->{"wrap"} && $pass eq "inner")) {
+                       print
+                                       "\tg_return_val_if_fail(${symbol}_patchpoint.orig_w32_func!=NULL,0);\n",
+                                       "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n",
+                                       "\t${symbol}_patchpoint.through_w32_func=TRUE;\n",
+                                       "\tr=(*(${symbol}_t_attrib *)${symbol}_patchpoint.orig_w32_func)(".join(",",@args_in).");\n",
+                                       "\tg_assert(${symbol}_patchpoint.through_w32_func==FALSE);\n";
+                       }
+               else {
+                       print
+                                       "\tr=${symbol}".($def->{"wrap"} ? "_wrap" : "")."(".join(",",@args_out).");\n";
+                       }
+               print
                                # We diplay just the lower 32-bit of the result EDX:EAX as it is usually not used,
                                # the only exception are _all{mul,div,*}(); EDX:EAX convention is always compatible.
                                "\tg_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"
-                                               ."\"... %s(".join(",",map("0x%08x",@args_out)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_out)).",(guint32)r"
+                                               ."\"... %s".($def->{"wrap"} ? ";$pass" : "")
+                                                               ."(".join(",",map("0x%08x",@args_print)).")=0x%08x\",".join(",","\"${symbol}\"",map("(unsigned)$_",@args_print)).",(guint32)r"
                                                .");\n",
                                "\treturn r;\n",
                                "}\n";
                }
        }
 
-# export function captive_kernel_exports()
-print <<"HERE";
+# write function captive_kernel_{exports,patches}()
+for my $functype ("exports","patches") {
+       print <<"HERE";
 
-gboolean captive_kernel_exports(void)
+gboolean captive_kernel_$functype(void)
 {
 gboolean errbool;
 
 HERE
-for my $module (sort keys(%module)) {
-       my $moduleref=$module{$module};
-       print <<"HERE";
-       errbool=captive_ModuleList_add_builtin("$module",
-HERE
-       for my $symbol (sort keys(%$moduleref)) {
-               my $value=${$moduleref}{$symbol};
-               print "\t\t\t\"$symbol\",&${symbol}_",
-                               ($def{$symbol}{"type"} || "undef"),
-                               ",\n";
-               }
-       print <<"HERE";
+       for my $module (sort keys(%module)) {
+               my $moduleref=$module{$module};
+               next if ($functype eq "patches") != defined $patch{$module};
+               print "\t\terrbool="
+                               .($functype eq "patches" ? "captive_ModuleList_patch" : "captive_ModuleList_add_builtin")
+                               ."(\"$module\",\n";
+               for my $symbol (sort keys(%$moduleref)) {
+                       next if $functype eq "patches" && (!$def{$symbol} || "data" eq $def{$symbol}{"type"});
+                       print "\t\t\t\"$symbol\",&${symbol}_",
+                                       ($def{$symbol}{"type"} || "undef"),
+                                       (($functype ne "patches") ? () : (",&${symbol}_patchpoint")),
+                                       ",\n";
+                       }
+               print <<"HERE";
                        NULL);
        g_return_val_if_fail(errbool,FALSE);
 
 HERE
-       }
-print <<"HERE";
+               }
+       print <<"HERE";
        return TRUE;
 }
 HERE
+       }
 
 # exit
 print STDERR "$0: Processed ".scalar(keys(%module))." modules, ".scalar(keys(%symbol))." symbols",
@@ -183,16 +263,63 @@ captivesym.pl - Generate source files based on .captivesym symbol file
 
 Source files with symbol call type definitions are identified by matching
 pattern I<*.def>. The remaining files (I<.captivesym> ones) must
-consist of lines with whitespace-separated
-(B<module>,B<symbol>,[undef]) items.
+consist of lines with whitespace-separated lines as described below.
+
+=over
+
+=item (B<module>,E<lt>patchE<gt>)
+
+Declare B<module> as mandatory W32 binary file to be patched by libcaptive.
+Currently being used only for C<ntoskrnl.exe>.
+
+Any function call even inside such module is trapped and redirected for
+libcaptive processing even if it is just for debug-dumping of B<pass> type.
+
+=item (B<module>,B<symbol>,[undef|pass|wrap])
 
 =over
 
+=item ""
+
+Name without special attribute declares function fully implemented by GNU/Linux
+code. Original W32 binary function will never be called.
+
+You may fully implement function for both E<lt>patchE<gt>ed and
+unE<lt>patchE<gt>ed modules.
+
 =item undef
 
 Optional "undef" specifies invocation of a generated stub function displaying
 C<g_error()> message.
 
+For "unpatched" modules you have to specify all the referenced symbols at least
+as this "undef" symbol. For "patched" modules it is not needed for native
+W32-PE binary modules importing such symbol but it is still required for W32
+.so files to satisfy .so dynamic linker.
+
+It is forbidden to "undef" C<DATA> type of items; you have to cope with it.
+
+=item pass
+
+Calls of this function are debug-dumped on its entry/exit but they are fully
+left to be solved by W32 binary file being E<lt>patchE<gt>ed.
+
+It is forbidden to specify "pass" for unE<lt>patchE<gt>ed modules.
+
+=item wrap
+
+Calls of this function are debug-dumped on its entry/exit. Execution is left
+to be solved by your GNU/Linux implementation called B<functionname_wrap>.
+You are allowed to call the original W32 binary function named
+B<functionname_orig> but you have to use your own prototype declaration for it.
+Both B<functionname_wrap> and B<functionname_orig> should be used with standard
+GNU/Linux C compiler function call type notwithstanding any real W32
+implementation details.
+
+It is forbidden to specify "wrap" for unE<lt>patchE<gt>ed modules.
+
+=back
+
 =back
 
 =begin comment
@@ -215,11 +342,17 @@ L<http://msdn.microsoft.com/library/en-us/vclang/html/_core_argument_passing_and
 
 =item fastcall: @functionname@argslength
 
+=item cdecl fixup: functionname:argslength
+
+This item must follow (even in some other I<*.def> file) previous B<cdecl>
+specification to specify the number of arguments as it is required for B<pass>
+or B<wrap> type of B<cdecl> function calls.
+
 =back
 
 =head1 COPYRIGHT
 
-Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
+Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
index 3af4654..3d6bb0f 100644 (file)
@@ -20,6 +20,7 @@
 #include "config.h"
 
 #include "captive/ldr.h"       /* self */
+#include "captive/ldr_exports.h"       /* self */
 #include "reactos/internal/ldr.h"      /* self */
 #include "captive/unicode.h"
 #include "captive/rtl-file.h"
 #include <gmodule.h>
 
 
+/* map: ExportAddress -> (struct captive_ModuleList_patchpoint *) */
+static GHashTable *captive_ModuleList_patchpoint_hash;
+
+/* initialize 'captive_ModuleList_patchpoint_hash' */
+static void captive_ModuleList_patchpoint_hash_init(void)
+{
+       if (captive_ModuleList_patchpoint_hash)
+               return;
+       captive_ModuleList_patchpoint_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
+}
+
+
+/* map: ExportAddress -> (CHAR *)funcname */
+static GHashTable *captive_ModuleList_function_disable_hash;
+
+/* initialize 'captive_ModuleList_function_disable_hash' */
+static void captive_ModuleList_function_disable_hash_init(void)
+{
+       if (captive_ModuleList_function_disable_hash)
+               return;
+       captive_ModuleList_function_disable_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
+}
+
+
 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
 NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
 
@@ -46,7 +71,7 @@ NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODUL
 VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
 
 
-static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8)
+static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8,const gchar *Filename_bslash_utf8)
 {
 MODULE_OBJECT *r;
 GModule *gmodule;
@@ -54,6 +79,7 @@ MODULE_TEXT_SECTION *ModuleTextSection;
 gboolean errbool;
 
        g_return_val_if_fail(Filename_utf8!=NULL,NULL);
+       g_return_val_if_fail(Filename_bslash_utf8!=NULL,NULL);
        g_return_val_if_fail(TRUE==g_module_supported(),NULL);
 
        gmodule=g_module_open(Filename_utf8,
@@ -70,7 +96,7 @@ gboolean errbool;
        r->Base=(PVOID)gmodule;
        r->Flags=MODULE_FLAG_COFF;      /* some weird value - reactos uses MODULE_FLAG_PE */
   
-       RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_utf8)->Buffer);
+       RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8)->Buffer);
   LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
 
        /* r->Length=0; */
@@ -106,7 +132,9 @@ NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObjectp)
 PVOID ModuleLoadBase;
 PMODULE_OBJECT Module;
 NTSTATUS err;
-const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
+gchar *Filename_utf8=(/* de-const */ gchar *)captive_UnicodeString_to_utf8_alloca(Filename);
+gchar *Filename_bslash_utf8,*s;
+UNICODE_STRING *Filename_bslash;
 
        *ModuleObjectp=NULL;
 
@@ -120,10 +148,16 @@ const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
        /* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
        g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
 
+       /* Filename=~s#/#\\#g -> Filename_bslash to satisfy basename-derivations by reactos. */
+       Filename_bslash_utf8=g_strdup(Filename_utf8);
+       for (s=Filename_bslash_utf8;(s=strchr(s,'/'));s++)
+               *s='\\';
+       Filename_bslash=captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8);
+
        if ((('M'<<8U)|('Z'<<0U))==GUINT16_FROM_BE(*(const guint16 *)ModuleLoadBase)) {
                g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"MZ/PE-32");
                /* examine/relocate the module */
-               err=LdrProcessModule(ModuleLoadBase,Filename,&Module);
+               err=LdrProcessModule(ModuleLoadBase,Filename_bslash,&Module);
                if (!NT_SUCCESS(err)) {
                        g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
                        goto err_captive_rtl_file_munmap;
@@ -131,7 +165,7 @@ const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
                }
        else {
                g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
-               Module=captive_LdrLoadModule_gmodule(Filename_utf8);
+               Module=captive_LdrLoadModule_gmodule(Filename_utf8,Filename_bslash_utf8);
                g_assert(Module!=NULL);
                }
 
@@ -140,7 +174,7 @@ const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
        *ModuleObjectp=Module;
 
        /* Hook for KDB on loading a driver. */
-       KDB_LOADDRIVER_HOOK(Filename,Module);
+       KDB_LOADDRIVER_HOOK(Filename_bslash,Module);
 
        return STATUS_SUCCESS;
 
@@ -227,15 +261,15 @@ IMAGE_DOS_HEADER *DosHeader;
 IMAGE_NT_HEADERS *NTHeaders;
 IMAGE_EXPORT_DIRECTORY *ExportDir;
 guint symi,symN;       /* index,number of passed stdarg sym_name/sym_val pairs */
-ptrdiff_t *NameList;   /* filled by sym_name; MODULEOBJECT_BASE_OFFSET()ed */
+ptrdiff_t *NameList;   /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
 WORD *OrdinalList;     /* linear list - libcaptive doesn't support Ordinals */
-ptrdiff_t *FunctionList;       /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
+ptrdiff_t *FunctionList;       /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
 
        g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
 
        captive_new0(ModuleObject);
        /* A lot of offsets in the structures are relative to ModuleObject->Base */
-#define MODULEOBJECT_BASE_OFFSET(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
+#define MODULEOBJECT_BASE_OFFSET_MINUS(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
 
        ModuleObject->Flags = MODULE_FLAG_PE;
 
@@ -270,14 +304,14 @@ ptrdiff_t *FunctionList;  /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
        ModuleObject->Base=DosHeader;   /* link DosHeader to ModuleObject */
        DosHeader->e_magic=IMAGE_DOS_MAGIC;
        captive_new0(NTHeaders);
-       DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET(NTHeaders);        /* link NTHeaders to DosHeader */
+       DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET_MINUS(NTHeaders);  /* link NTHeaders to DosHeader */
        NTHeaders->Signature=IMAGE_PE_MAGIC;
        NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
        NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
                        =sizeof(*ExportDir);    /* in bytes; just prevent LdrPEFixupForward() invocation */
        captive_new0(ExportDir);
        NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
-                       =MODULEOBJECT_BASE_OFFSET(ExportDir);   /* link ExportDir to NTHeaders */
+                       =MODULEOBJECT_BASE_OFFSET_MINUS(ExportDir);     /* link ExportDir to NTHeaders */
        ExportDir->Base=0;      /* base Ordinal number; Ordinals are not supported by libcaptive */
 
        /* count the number of symbols */
@@ -290,26 +324,25 @@ ptrdiff_t *FunctionList;  /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
 
        /* allocate and link the symbol tables */
        captive_newn(NameList,symN);
-       ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET(NameList); /* link NameList to ExportDir */
+       ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(NameList);   /* link NameList to ExportDir */
        captive_newn(OrdinalList,symN);
-       ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET(OrdinalList);        /* link OrdinalList to ExportDir */
+       ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(OrdinalList);  /* link OrdinalList to ExportDir */
        captive_newn(FunctionList,symN);
-       ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET(FunctionList); /* link FunctionList to ExportDir */
+       ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(FunctionList);   /* link FunctionList to ExportDir */
 
        /* fill in symbol tables */
        va_start(ap,FullName_utf8);
        for (symi=0;symi<symN;symi++) {
                captive_va_arg(sym_name,ap);
                captive_va_arg(sym_val ,ap);
-               NameList[symi]=MODULEOBJECT_BASE_OFFSET(sym_name);
+               NameList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_name);
                OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
-               FunctionList[symi]=MODULEOBJECT_BASE_OFFSET(sym_val);
+               FunctionList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
                }
        va_end(ap);
 
        /* successful module info build */
        InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
-#undef MODULEOBJECT_BASE_OFFSET        /* no longer valid */
        return TRUE;
 
 err_g_free_ModuleObject:
@@ -317,3 +350,250 @@ err_g_free_ModuleObject:
 /* err */
        g_return_val_if_reached(FALSE);
 }
+
+
+static gsize instruction_length(const guint8 *instr)
+{
+       if ((instr[0] & 0xF8)==0x50)    /* push %regular-register */
+               return 1;
+       if ((instr[0] & 0xF8)==0x58)    /* pop  %regular-register */
+               return 1;
+       if ((instr[0] & 0xE7)==0x06)    /* push %segment-register */
+               return 1;
+       if ((instr[0] & 0xE7)==0x07 && instr[0]!=0x0F)  /* pop  %segment-register */
+               return 1;
+       switch (instr[0]) {
+               case 0x33:      /* xor GB,Ev */
+                       return 1+1;
+               case 0x64:      /* prefix %fs */
+                       return instruction_length(instr+1);
+               case 0x68:      /* push $quad-byte */
+                       return 1+4;
+               case 0x6A:      /* push $single-byte */
+                       return 1+1;
+               case 0x83:
+                       switch (instr[1]) {
+                               case 0x01:      /* addl $byte,(%ecx) */
+                                       return 1+1+1;
+                               case 0x3D:      /* cmpl $byte,immediate-quad-indirect */
+                                       return 1+1+4+1; /* 83 3D immediate-quad-indirect byte */
+                               default: g_assert_not_reached();
+                               }
+               case 0x8A:      /* mov ??,?? */
+               case 0x8B:      /* mov Gb,Eb */
+                       switch (instr[1]) {
+                               case 0x0D:      /* mov immediate-quad-indirect,%ecx */
+                                       return 1+1+4;
+                               case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
+                               case 0x4C:      /* mov #offset(%reg,#mult),%reg */
+                               case 0x54:      /* mov #offset(%reg,#mult),%reg */
+                                       return 1+1+1+1; /* 8B 44 address-mode offset */
+                               default: g_assert_not_reached();
+                               }
+               case 0x8D:      /* lea Gb,M */
+                       switch (instr[1]) {
+                               case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
+                                       return 4;       /* 8B 44 address-mode offset */
+                               default: g_assert_not_reached();
+                               }
+               case 0x9C:      /* pushf */
+                       return 1;
+               case 0x9D:      /* popf */
+                       return 1;
+               case 0xA1:      /* mov immediate-quad-indirect,%eax */
+                       return 1+4;
+               case 0xB8:      /* mov $immediate-quad,%eax */
+                       return 1+4;
+               case 0xC2:      /* ret $immediate-double */
+                       return 1+2;
+               case 0xCC:      /* int $0x3 */
+                       return 1;
+               case 0xFA:      /* cli */
+                       return 1;
+               case 0xFB:      /* sti */
+                       return 1;
+               case 0xFF:      /* inc/dec Ev */
+                       return 2;
+               default: g_assert_not_reached();
+               }
+       g_assert_not_reached(); /* TODO:fill the table */
+       /* NOTREACHED */
+       return 0;
+}
+
+
+       /* A lot of offsets in the structures are relative to ModuleObject->Base */
+#define MODULEOBJECT_BASE_OFFSET_PLUS(addr) ((gpointer)((char *)addr+(ptrdiff_t)ModuleObject->Base))
+
+static void captive_ModuleList_patch_function_disable
+               (CHAR *funcname,PVOID *ExportAddressp,MODULE_OBJECT *ModuleObject /* user_data */)
+{
+PVOID ExportAddress;
+
+       g_return_if_fail(funcname!=NULL);
+       g_return_if_fail(ExportAddressp!=NULL);
+       g_return_if_fail(ModuleObject!=NULL);
+
+       ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+       g_return_if_fail(ExportAddress!=NULL);
+
+       /* Some alised name; safe to ignore as we checked for possible W32 binary 0xF4
+        * hits during our initialization in captive_ModuleList_patch().
+        */
+       if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress)
+               return;
+
+       captive_ModuleList_function_disable_hash_init();
+
+       *(guint8 *)ExportAddress=0xF4; /* hlt */
+       /* We may get aliased here; already existing entry is therefore allowed. */
+       g_hash_table_insert(captive_ModuleList_function_disable_hash,
+                       ExportAddress,  /* key */
+                       funcname);      /* value */
+}
+
+
+/**
+ * captive_ModuleList_patch:
+ * @FullName_utf8: String to find #PMODULE_OBJECT by #FullName.
+ * @...: (const gchar *sym_name,void (*sym_val)(void),struct captive_ModuleList_patchpoint *patchpoint) symbol list terminated by %NULL.
+ *
+ * Patches existing @FullName_utf8 module to use for function named #sym_name
+ * pointer to the handler #sym_val. If #patchpoint is not %NULL it gets assigned the original
+ * pointer value (used for %pass keyword in #exports.captivesym).
+ * 
+ * Put here 0xF4 'hlt' instead of 0xCC 'int $0x3; breakpoint'
+ * as 'hlt' will generate handled SIGSEGV instead of SIGTRAP which
+ * is used by gdb(1) during debugging.
+ * See also libcaptive/ps/signal.c/ sigaction_SIGSEGV().
+ *
+ * Returns: %TRUE if the module was successfuly added.
+ */
+gboolean captive_ModuleList_patch(const gchar *FullName_utf8,...)
+{
+MODULE_OBJECT *ModuleObject;
+va_list ap;
+IMAGE_EXPORT_DIRECTORY *ExportDir;
+ptrdiff_t *NameList;   /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
+WORD *OrdinalList;     /* linear list - libcaptive doesn't support Ordinals */
+ptrdiff_t *FunctionList;       /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
+ULONG ExportDirSize;
+USHORT Idx;
+PVOID ExportAddress,*ExportAddressp;
+const gchar *sym_name;
+void (*sym_val)(void);
+struct captive_ModuleList_patchpoint *patchpoint;
+GHashTable *exportdir_hash;
+gboolean errbool;
+
+       g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
+
+       captive_ModuleList_patchpoint_hash_init();
+
+       ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(FullName_utf8)));
+       g_return_val_if_fail(ModuleObject!=NULL,FALSE);
+
+       ExportDir=(PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(ModuleObject->Base,
+                       TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT,&ExportDirSize);
+       g_return_val_if_fail(ExportDir!=NULL,FALSE);
+
+       NameList    =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNames);
+       OrdinalList =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNameOrdinals);
+       FunctionList=MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfFunctions);
+
+       /* map (CHAR *)funcname->(PVOID *)ExportAddressp */
+       exportdir_hash=g_hash_table_new(g_str_hash,g_str_equal);
+
+       /* Initialize 'exportdir_hash' by all W32 exported functions. */
+       for (Idx=0;Idx<ExportDir->NumberOfNames;Idx++) {
+CHAR *funcname=MODULEOBJECT_BASE_OFFSET_PLUS(NameList[Idx]);
+
+               ExportAddressp=(PVOID *)(FunctionList+OrdinalList[Idx]);
+               ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+               if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress) {
+                       g_error("%s: Function already patched although we did not touch it yet: %s",G_STRLOC,funcname);
+                       g_assert_not_reached();
+                       }
+               g_assert(NULL==g_hash_table_lookup(exportdir_hash,funcname));
+               g_hash_table_insert(exportdir_hash,
+                               funcname,       /* key */
+                               ExportAddressp);        /* value */
+               }
+
+       /* Patch wished functions and remove them from 'exportdir_hash'. */
+       va_start(ap,FullName_utf8);
+       while (captive_va_arg(sym_name,ap)) {
+               captive_va_arg(sym_val ,ap);
+               g_assert(sym_val!=NULL);
+               captive_va_arg(patchpoint,ap);
+               g_assert(patchpoint!=NULL);
+               ExportAddressp=g_hash_table_lookup(exportdir_hash,sym_name);
+               if (ExportAddressp==NULL) {
+                       g_error("%s: Function not found for patchpoint: %s",G_STRLOC,sym_name);
+                       g_assert_not_reached();
+                       }
+               errbool=g_hash_table_remove(exportdir_hash,sym_name);
+               g_assert(errbool==TRUE);
+               ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+               if (((ULONG)ExportAddress >= (ULONG)ExportDir) &&
+                               ((ULONG)ExportAddress <  (ULONG)ExportDir + ExportDirSize))
+                       g_assert_not_reached(); /* LdrPEFixupForward() needed */
+               patchpoint->orig_w32_func=ExportAddress;
+               g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_func);
+               patchpoint->orig_w32_2ndinstr=patchpoint->orig_w32_func
+                               +instruction_length((guint8 *)patchpoint->orig_w32_func);
+               g_assert(0xF4 /* hlt */ !=*patchpoint->orig_w32_2ndinstr);
+               patchpoint->wrap_wrap_func=sym_val;
+               patchpoint->orig_w32_func_byte=*patchpoint->orig_w32_func;
+               patchpoint->orig_w32_2ndinstr_byte=*patchpoint->orig_w32_2ndinstr;
+               patchpoint->through_w32_func=FALSE;
+               g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_func));
+               g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+                               patchpoint->orig_w32_func,      /* key */
+                               patchpoint);    /* value */
+               g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpoint->orig_w32_2ndinstr));
+               g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+                               patchpoint->orig_w32_2ndinstr,  /* key */
+                               patchpoint);    /* value */
+               *(guint8 *)ExportAddress=0xF4;  /* hlt */
+               *ExportAddressp=(PVOID)MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
+               }
+       va_end(ap);
+
+       /* The remaining entries of 'exportdir_hash' are W32 native functions
+        * unspecified by .captivesym file; we patch them as not-implemented ones.
+        */
+       g_hash_table_foreach(exportdir_hash,
+                       (GHFunc)captive_ModuleList_patch_function_disable,      /* func */
+                       ModuleObject);  /* used_data; unused */
+
+       g_hash_table_destroy(exportdir_hash);
+
+#undef MODULEOBJECT_BASE_OFFSET_PLUS   /* no longer valid */
+#undef MODULEOBJECT_BASE_OFFSET_MINUS  /* no longer valid */
+       return TRUE;
+}
+
+
+struct captive_ModuleList_patchpoint *captive_ModuleList_patchpoint_find(gconstpointer ExportAddress)
+{
+struct captive_ModuleList_patchpoint *r;
+
+       g_return_val_if_fail(ExportAddress!=NULL,NULL);
+
+       captive_ModuleList_patchpoint_hash_init();
+
+       r=g_hash_table_lookup(captive_ModuleList_patchpoint_hash,ExportAddress);
+       g_return_val_if_fail(r!=NULL,NULL);
+       g_assert(r->orig_w32_func==ExportAddress || r->orig_w32_2ndinstr==ExportAddress);
+
+       return r;
+}
+
+
+G_CONST_RETURN gchar *captive_ModuleList_function_disable_find(gconstpointer ExportAddress)
+{
+       g_return_val_if_fail(ExportAddress!=NULL,NULL);
+
+       return g_hash_table_lookup(captive_ModuleList_function_disable_hash,ExportAddress);     /* funcname */
+}