instruction_length(): Extended for 0xF0 (lock prefix).
[captive.git] / src / libcaptive / ldr / loader.c
index 6819d18..48fe811 100644 (file)
 #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/file.h"
+#include "captive/rtl-file.h"
 #include <glib/gtypes.h>
 #include <glib/gmessages.h>
 #include <glib/gmem.h>
 #include <sys/mman.h>  /* for PROT_READ, MAP_SHARED */
 #include "captive/macros.h"
 #include <stdarg.h>
-#include "reactos/ddk/rtl.h"   /* for InsertTailList() */
+#include "reactos/ntos/rtl.h"  /* for InsertTailList() */
 #include "reactos/internal/debug.h"    /* for assert() */
 #include <glib/gutils.h>       /* for g_path_get_basename() */
+#include <gmodule.h>
+#include "captive/options-module.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);
+PVOID LdrGetExportAddress(PMODULE_OBJECT ModuleObject,char *Name,unsigned short Hint);
+
+
+/* 'ntoskrnl/ldr/loader.c' file-scoped declaration: */
+VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
+
+
+static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8,const gchar *Filename_bslash_utf8)
+{
+MODULE_OBJECT *r;
+GModule *gmodule;
+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,
+                       0);     /* Flags; !LAZY */
+       if (!gmodule)
+               g_message(g_module_error());
+       g_assert(gmodule!=NULL);
+
+       captive_new0(r);
+
+       errbool=g_module_symbol(gmodule,"DriverEntry",&r->EntryPoint);
+       g_assert(errbool==TRUE);
+
+       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_bslash_utf8)->Buffer);
+  LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
+
+       /* r->Length=0; */
+  
+  /* Insert module */
+       InsertTailList(&ModuleListHead,&r->ListEntry);
+
+       captive_new0(ModuleTextSection);
+       /* ModuleTextSection->Base=0; */
+       /* ModuleTextSection->Length=0; */
+       ModuleTextSection->Name=g_memdup(r->BaseName.Buffer,
+                       (captive_ucs2_strlen(r->BaseName.Buffer)+1)*sizeof(captive_ucs2));
+       /* ModuleTextSection->OptionalHeader=NULL; */
+       InsertTailList(&ModuleTextListHead,&ModuleTextSection->ListEntry);
+       r->TextSection=ModuleTextSection;
+
+       return r;
+}
 
 
 /**
- * LdrLoadModule:
- * @Filename: Filename of module to load based on host OS disk.
+ * captive_LdrLoadModule:
+ * @options_module: #captive_options_module structure describing the module to load.
  * Loading of already loaded module is forbidden.
- * @ModuleObject: Returns initialized module object.
+ * @ModuleObjectp: Returns initialized module object.
  *
  * Load and initialize module to reactos using host OS functions.
  *
  * Returns: STATUS_SUCCESS if the module was loaded successfuly during the call.
  */
-NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObjectp)
+NTSTATUS captive_LdrLoadModule(struct captive_options_module *options_module,PMODULE_OBJECT *ModuleObjectp)
 {
-PVOID ModuleLoadBase;
 PMODULE_OBJECT Module;
 NTSTATUS err;
+gchar *Filename_utf8=options_module->pathname_utf8;
+gchar *Filename_bslash_utf8,*s;
+UNICODE_STRING *Filename_bslash;
 
        *ModuleObjectp=NULL;
 
-       /* Open the Module */
-       ModuleLoadBase=captive_file_mmap(
-                       NULL,   /* lenp */
-                       captive_UnicodeString_to_utf8_alloca(Filename), /* path */
-                       O_RDONLY,       /* open_flags */
-                       PROT_READ,      /* mmap_prot */
-                       MAP_SHARED);    /* mmap_flags */
-       /* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
-       g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
-
-       /* examine/relocate the module */
-       err=LdrProcessModule(ModuleLoadBase,Filename,&Module);
-       if (!NT_SUCCESS(err)) {
-               g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
-               goto err_captive_file_munmap;
+       /* Filename=~s#/#\\#g -> Filename_bslash to satisfy basename-derivations by reactos. */
+       Filename_bslash_utf8=(/* de-const as we do not change the length */ gchar *)captive_strdup_alloca(Filename_utf8);
+       for (s=Filename_bslash_utf8;(s=strchr(s,'/'));s++)
+               *s='\\';
+       Filename_bslash=captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8);
+
+       switch (options_module->type) {
+               case CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY: g_assert_not_reached();
+
+               case CAPTIVE_OPTIONS_MODULE_TYPE_PE32:
+                       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(options_module->u.pe32.base,Filename_bslash,&Module);
+                       if (!NT_SUCCESS(err)) {
+                               g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",(gulong)err);
+                               goto err;
+                               }
+                       break;
+
+               case CAPTIVE_OPTIONS_MODULE_TYPE_GMODULE:
+                       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
+                       Module=captive_LdrLoadModule_gmodule(Filename_utf8,Filename_bslash_utf8);
+                       g_assert(Module!=NULL);
                }
 
        /* we were successful */
-       captive_file_munmap(ModuleLoadBase);
        *ModuleObjectp=Module;
 
        /* Hook for KDB on loading a driver. */
-       KDB_LOADDRIVER_HOOK(Filename,Module);
+       KDB_LOADDRIVER_HOOK(Filename_bslash,Module);
 
        return STATUS_SUCCESS;
 
-       /* Error recoveries */
-err_captive_file_munmap:
-       captive_file_munmap(ModuleLoadBase);
-
+err:
        g_return_val_if_reached(err);
 }
 
 
+NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObject)
+{
+       return STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+
 /**
  * captive_LdrpLoadAndCallImage:
  * @ModuleObjectp: Returns PMODULE_OBJECT successfuly loaded.
- * @ModuleName: Filename of module to load based on host OS disk.
+ * @options_module: #captive_options_module structure describing the module to load.
  * Loading of already loaded module is forbidden despite original
  * LdrpLoadAndCallImage().
  * @DriverEntry_DriverObject: argument #DriverObject of #PDRIVER_INITIALIZE call.
@@ -108,27 +198,31 @@ err_captive_file_munmap:
  * Returns: STATUS_SUCCESS if the driver module was loaded and initialized
  * successfuly during this call. Ignore returned @ModuleObjectp if function failed.
  */
-NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,PUNICODE_STRING ModuleName,
+NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,struct captive_options_module *options_module,
                PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath)
 {
 PDRIVER_INITIALIZE DriverEntry;
 PMODULE_OBJECT ModuleObject_tmp;
 NTSTATUS err;
 
+#if 0  /* TODO */
        /* check for duplicity */
        g_return_val_if_fail(LdrGetModuleObject(ModuleName)==NULL,STATUS_IMAGE_ALREADY_LOADED);
+#endif
 
        /* provide our temporary storage if the caller doesn't want to know ModuleObject address */
        if (!ModuleObjectp)
                ModuleObjectp=&ModuleObject_tmp;
 
        /* load the module */
-       err=LdrLoadModule(ModuleName,ModuleObjectp);
+       err=captive_LdrLoadModule(options_module,ModuleObjectp);
        g_return_val_if_fail(NT_SUCCESS(err),err);
 
        /* initialize the module */
        DriverEntry=(PDRIVER_INITIALIZE)(*ModuleObjectp)->EntryPoint;
-       err=(*DriverEntry)(DriverEntry_DriverObject,DriverEntry_RegistryPath);
+       err=(NTSTATUS)captive_stdcall_call_8((CaptiveStdCallFunc8)DriverEntry,
+                       DriverEntry_DriverObject,
+                       DriverEntry_RegistryPath);
        if (!NT_SUCCESS(err))
                goto err_LdrUnloadModule;
 
@@ -168,15 +262,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;
 
@@ -211,14 +305,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 */
@@ -231,26 +325,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:
@@ -258,3 +351,306 @@ 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 0x80:
+                       switch (instr[1]) {
+                               case 0x3D:      /* cmpb $byte,immediate-quad-indirect */
+                                       return 1+1+4+1; /* 80 3D immediate-quad-indirect byte */
+                               case 0x7C:      /* cmpb $byte,#immediate(%reg,1) */
+                                       return 1+1+1+1+1;       /* 80 7C address-mode immediate byte */
+                               default: g_assert_not_reached();
+                               }
+               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 */
+                               case 0x7C:      /* cmpl $byte,#immediate(%reg,1) */
+                                       return 1+1+1+1+1;       /* 80 7C address-mode immediate 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 0x15:      /* mov immediate-quad-indirect,%edx */
+                                       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 0x8F:      /* lea Gb,M */
+                       switch (instr[1]) {
+                               case 0x04:
+                                       switch (instr[2]) {
+                                               case 0x24:      /* popl (%esp,1) */
+                                                       return 3;
+                                               default: g_assert_not_reached();
+                                               }
+                               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 0xF0:      /* lock prefix */
+                       return 1+instruction_length(instr+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,*patchpointpatch;
+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) {
+                       /* FIXME: 'data' type symbols should be permitted to have 0xF4 byte */
+                       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);
+               /* 'sym_val' may be NULL if 'data' type && "pass"ed */
+               captive_va_arg(patchpoint,ap);  /* 'data' type if ==NULL */
+               captive_va_arg(patchpointpatch,ap);     /* 'data' type or 'pass' in non-debug mode if ==NULL */
+               ExportAddressp=g_hash_table_lookup(exportdir_hash,sym_name);
+               if (ExportAddressp==NULL) {
+                       g_message("%s: Function not found for patchpoint (ignoring): %s",G_STRLOC,sym_name);
+                       continue;
+                       }
+               errbool=g_hash_table_remove(exportdir_hash,sym_name);
+               g_assert(errbool==TRUE);
+               if (!sym_val) { /* 'data' type && "pass"ed => do not corrupt it by 0xF4 */
+                       g_assert(!patchpoint);
+                       continue;
+                       }
+               ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
+               *ExportAddressp=(PVOID)MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
+               if (((ULONG)ExportAddress >= (ULONG)ExportDir) &&
+                               ((ULONG)ExportAddress <  (ULONG)ExportDir + ExportDirSize))
+                       g_assert_not_reached(); /* LdrPEFixupForward() needed */
+               if (!patchpoint) /* 'data' type && !"pass"ed => do not corrupt it by 0xF4 */
+                       continue;
+               patchpoint->orig_w32_func=ExportAddress;
+               if (!patchpointpatch)   /* 'pass' in non-debug mode */
+                       continue;
+               if (0xF4 /* hlt */ ==*patchpointpatch->orig_w32_func)   /* Already patched by name-aliased function? */
+                       continue;
+               g_assert(0xF4 /* hlt */ !=*patchpointpatch->orig_w32_func);
+               patchpointpatch->orig_w32_2ndinstr=patchpointpatch->orig_w32_func
+                               +instruction_length((guint8 *)patchpointpatch->orig_w32_func);
+               g_assert(0xF4 /* hlt */ !=*patchpointpatch->orig_w32_2ndinstr);
+               patchpointpatch->wrap_wrap_func=sym_val;
+               patchpointpatch->orig_w32_func_byte=*patchpointpatch->orig_w32_func;
+               patchpointpatch->orig_w32_2ndinstr_byte=*patchpointpatch->orig_w32_2ndinstr;
+               patchpointpatch->through_w32_func=FALSE;
+               g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpointpatch->orig_w32_func));
+               g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+                               patchpointpatch->orig_w32_func, /* key */
+                               patchpointpatch);       /* value */
+               g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpointpatch->orig_w32_2ndinstr));
+               g_hash_table_insert(captive_ModuleList_patchpoint_hash,
+                               patchpointpatch->orig_w32_2ndinstr,     /* key */
+                               patchpointpatch);       /* value */
+               *(guint8 *)ExportAddress=0xF4;  /* hlt */
+               }
+       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 */
+}
+
+
+void *captive_Module_GetExportAddress(const gchar *ModuleName_utf8,const gchar *FunctionName)
+{
+MODULE_OBJECT *ModuleObject;
+void *r;
+
+       g_return_val_if_fail(ModuleName_utf8!=NULL,NULL);
+       g_return_val_if_fail(FunctionName!=NULL,NULL);
+
+       ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(ModuleName_utf8)));
+       g_return_val_if_fail(ModuleObject!=NULL,NULL);
+
+       r=LdrGetExportAddress(
+                       ModuleObject,   /* ModuleObject */
+                       (/* de-const */char *)FunctionName,     /* Name */
+                       -1);    /*Hint*/
+       g_return_val_if_fail(r!=NULL,NULL);
+
+       return r;
+}