Initial original import from: fuse-2.4.2-2.fc4
[captive.git] / src / libcaptive / ldr / loader.c
1 /* $Id$
2  * reactos ldr/ (loader) emulation of libcaptive
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/ldr.h"        /* self */
23 #include "captive/ldr_exports.h"        /* self */
24 #include "reactos/internal/ldr.h"       /* self */
25 #include "captive/unicode.h"
26 #include "captive/rtl-file.h"
27 #include <glib/gtypes.h>
28 #include <glib/gmessages.h>
29 #include <glib/gmem.h>
30 #include "reactos/napi/types.h"
31 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
32 #include <fcntl.h>
33 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
34 #include "captive/macros.h"
35 #include <stdarg.h>
36 #include "reactos/ntos/rtl.h"   /* for InsertTailList() */
37 #include "reactos/internal/debug.h"     /* for assert() */
38 #include <glib/gutils.h>        /* for g_path_get_basename() */
39 #include <gmodule.h>
40 #include "captive/options-module.h"
41
42
43 /* map: ExportAddress -> (struct captive_ModuleList_patchpoint *) */
44 static GHashTable *captive_ModuleList_patchpoint_hash;
45
46 /* initialize 'captive_ModuleList_patchpoint_hash' */
47 static void captive_ModuleList_patchpoint_hash_init(void)
48 {
49         if (captive_ModuleList_patchpoint_hash)
50                 return;
51         captive_ModuleList_patchpoint_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
52 }
53
54
55 /* map: ExportAddress -> (CHAR *)funcname */
56 static GHashTable *captive_ModuleList_function_disable_hash;
57
58 /* initialize 'captive_ModuleList_function_disable_hash' */
59 static void captive_ModuleList_function_disable_hash_init(void)
60 {
61         if (captive_ModuleList_function_disable_hash)
62                 return;
63         captive_ModuleList_function_disable_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
64 }
65
66
67 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
68 NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
69 PVOID LdrGetExportAddress(PMODULE_OBJECT ModuleObject,char *Name,unsigned short Hint);
70
71
72 /* 'ntoskrnl/ldr/loader.c' file-scoped declaration: */
73 VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
74
75
76 static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8,const gchar *Filename_bslash_utf8)
77 {
78 MODULE_OBJECT *r;
79 GModule *gmodule;
80 MODULE_TEXT_SECTION *ModuleTextSection;
81 gboolean errbool;
82
83         g_return_val_if_fail(Filename_utf8!=NULL,NULL);
84         g_return_val_if_fail(Filename_bslash_utf8!=NULL,NULL);
85         g_return_val_if_fail(TRUE==g_module_supported(),NULL);
86
87         gmodule=g_module_open(Filename_utf8,
88                         0);     /* Flags; !LAZY */
89         if (!gmodule)
90                 g_message(g_module_error());
91         g_assert(gmodule!=NULL);
92
93         captive_new0(r);
94
95         errbool=g_module_symbol(gmodule,"DriverEntry",&r->EntryPoint);
96         g_assert(errbool==TRUE);
97
98         r->Base=(PVOID)gmodule;
99         r->Flags=MODULE_FLAG_COFF;      /* some weird value - reactos uses MODULE_FLAG_PE */
100   
101         RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8)->Buffer);
102   LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
103
104         /* r->Length=0; */
105   
106   /* Insert module */
107         InsertTailList(&ModuleListHead,&r->ListEntry);
108
109         captive_new0(ModuleTextSection);
110         /* ModuleTextSection->Base=0; */
111         /* ModuleTextSection->Length=0; */
112         ModuleTextSection->Name=g_memdup(r->BaseName.Buffer,
113                         (captive_ucs2_strlen(r->BaseName.Buffer)+1)*sizeof(captive_ucs2));
114         /* ModuleTextSection->OptionalHeader=NULL; */
115         InsertTailList(&ModuleTextListHead,&ModuleTextSection->ListEntry);
116         r->TextSection=ModuleTextSection;
117
118         return r;
119 }
120
121
122 /**
123  * captive_LdrLoadModule:
124  * @options_module: #captive_options_module structure describing the module to load.
125  * Loading of already loaded module is forbidden.
126  * @ModuleObjectp: Returns initialized module object.
127  *
128  * Load and initialize module to reactos using host OS functions.
129  *
130  * Returns: STATUS_SUCCESS if the module was loaded successfuly during the call.
131  */
132 NTSTATUS captive_LdrLoadModule(struct captive_options_module *options_module,PMODULE_OBJECT *ModuleObjectp)
133 {
134 PMODULE_OBJECT Module;
135 NTSTATUS err;
136 gchar *Filename_utf8=options_module->pathname_utf8;
137 gchar *Filename_bslash_utf8,*s;
138 UNICODE_STRING *Filename_bslash;
139
140         *ModuleObjectp=NULL;
141
142         /* Filename=~s#/#\\#g -> Filename_bslash to satisfy basename-derivations by reactos. */
143         Filename_bslash_utf8=(/* de-const as we do not change the length */ gchar *)captive_strdup_alloca(Filename_utf8);
144         for (s=Filename_bslash_utf8;(s=strchr(s,'/'));s++)
145                 *s='\\';
146         Filename_bslash=captive_utf8_to_UnicodeString_alloca(Filename_bslash_utf8);
147
148         switch (options_module->type) {
149                 case CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY: g_assert_not_reached();
150
151                 case CAPTIVE_OPTIONS_MODULE_TYPE_PE32:
152                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"MZ/PE-32");
153                         /* examine/relocate the module */
154                         err=LdrProcessModule(options_module->u.pe32.base,Filename_bslash,&Module);
155                         if (!NT_SUCCESS(err)) {
156                                 g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",(gulong)err);
157                                 goto err;
158                                 }
159                         break;
160
161                 case CAPTIVE_OPTIONS_MODULE_TYPE_GMODULE:
162                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
163                         Module=captive_LdrLoadModule_gmodule(Filename_utf8,Filename_bslash_utf8);
164                         g_assert(Module!=NULL);
165                 }
166
167         /* we were successful */
168         *ModuleObjectp=Module;
169
170         /* Hook for KDB on loading a driver. */
171         KDB_LOADDRIVER_HOOK(Filename_bslash,Module);
172
173         return STATUS_SUCCESS;
174
175 err:
176         g_return_val_if_reached(err);
177 }
178
179
180 NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObject)
181 {
182         return STATUS_OBJECT_NAME_NOT_FOUND;
183 }
184
185
186 /**
187  * captive_LdrpLoadAndCallImage:
188  * @ModuleObjectp: Returns PMODULE_OBJECT successfuly loaded.
189  * @options_module: #captive_options_module structure describing the module to load.
190  * Loading of already loaded module is forbidden despite original
191  * LdrpLoadAndCallImage().
192  * @DriverEntry_DriverObject: argument #DriverObject of #PDRIVER_INITIALIZE call.
193  * @DriverEntry_RegistryPath: argument #RegistryPath of #PDRIVER_INITIALIZE call.
194  *
195  * Corresponds to reactos LdrpLoadAndCallImage() but it also provides arguments
196  * to pass to #PDRIVER_INITIALIZE call of module driver initialization.
197  *
198  * Returns: STATUS_SUCCESS if the driver module was loaded and initialized
199  * successfuly during this call. Ignore returned @ModuleObjectp if function failed.
200  */
201 NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,struct captive_options_module *options_module,
202                 PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath)
203 {
204 PDRIVER_INITIALIZE DriverEntry;
205 PMODULE_OBJECT ModuleObject_tmp;
206 NTSTATUS err;
207
208 #if 0   /* TODO */
209         /* check for duplicity */
210         g_return_val_if_fail(LdrGetModuleObject(ModuleName)==NULL,STATUS_IMAGE_ALREADY_LOADED);
211 #endif
212
213         /* provide our temporary storage if the caller doesn't want to know ModuleObject address */
214         if (!ModuleObjectp)
215                 ModuleObjectp=&ModuleObject_tmp;
216
217         /* load the module */
218         err=captive_LdrLoadModule(options_module,ModuleObjectp);
219         g_return_val_if_fail(NT_SUCCESS(err),err);
220
221         /* initialize the module */
222         DriverEntry=(PDRIVER_INITIALIZE)(*ModuleObjectp)->EntryPoint;
223         err=(NTSTATUS)captive_stdcall_call_8((CaptiveStdCallFunc8)DriverEntry,
224                         DriverEntry_DriverObject,
225                         DriverEntry_RegistryPath);
226         if (!NT_SUCCESS(err))
227                 goto err_LdrUnloadModule;
228
229         /* assumed STATUS_SUCCESS */
230         return err;
231
232 err_LdrUnloadModule:
233         /* errors ignored */
234         LdrUnloadModule(*ModuleObjectp);
235         *ModuleObjectp=NULL;
236 /* err: */
237         g_return_val_if_reached(err);
238 }
239
240
241 /**
242  * captive_ModuleList_add_builtin:
243  * @FullName_utf8: String to fill in #PMODULE_OBJECT->FullName.
244  * @...: (const gchar *sym_name,void *sym_val) symbol list terminated by %NULL.
245  *
246  * Adds simulated built-in module to #ModuleListHead module list.
247  * It can be used for the functionality of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement().
248  * libcaptive does not support Ordinals - we just pretend liner (%0-based)
249  * Ordinal numbers of the functions in given @... stdargs order.
250  *
251  * Returns: %TRUE if the module was successfuly added.
252  */
253 gboolean captive_ModuleList_add_builtin(const gchar *FullName_utf8,...)
254 {
255 MODULE_OBJECT *ModuleObject;
256 BOOLEAN errbool;
257 va_list ap;
258 const gchar *sym_name;
259 const void *sym_val;
260 gchar *basename_utf8;
261 IMAGE_DOS_HEADER *DosHeader;
262 IMAGE_NT_HEADERS *NTHeaders;
263 IMAGE_EXPORT_DIRECTORY *ExportDir;
264 guint symi,symN;        /* index,number of passed stdarg sym_name/sym_val pairs */
265 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
266 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
267 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_MINUS()ed */
268
269         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
270
271         captive_new0(ModuleObject);
272         /* A lot of offsets in the structures are relative to ModuleObject->Base */
273 #define MODULEOBJECT_BASE_OFFSET_MINUS(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
274
275         ModuleObject->Flags = MODULE_FLAG_PE;
276
277         /* fill ModuleObject->FullName
278          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
279          * we put there '/'-based UNIX filenames in libcaptive.
280          */
281         errbool=RtlCreateUnicodeString(&ModuleObject->FullName,captive_utf8_to_UnicodeString_alloca(FullName_utf8)->Buffer);
282         if (!errbool) {
283                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
284                 goto err_g_free_ModuleObject;
285                 }
286
287         /* fill ModuleObject->BaseName
288          * reactos/ntoskrnl/ldr/loader.c/LdrpBuildModuleBaseName() does this one
289          * but it is 'static' for that file and also it is stricly backslash based.
290          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
291          * we put there '/'-based UNIX filenames in libcaptive.
292          */
293         basename_utf8=g_path_get_basename(FullName_utf8);
294         errbool=RtlCreateUnicodeString(&ModuleObject->BaseName,captive_utf8_to_UnicodeString_alloca(basename_utf8)->Buffer);
295         g_free(basename_utf8);
296         if (!errbool) {
297                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
298                 goto err_g_free_ModuleObject;
299                 }
300
301         /* We must satisfy reactos/ntoskrnl/ldr/rtl.c/RtlImageDirectoryEntryToData(,,IMAGE_DIRECTORY_ENTRY_EXPORT)
302          * prepare module headers
303          */
304         captive_new0(DosHeader);
305         ModuleObject->Base=DosHeader;   /* link DosHeader to ModuleObject */
306         DosHeader->e_magic=IMAGE_DOS_MAGIC;
307         captive_new0(NTHeaders);
308         DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET_MINUS(NTHeaders);  /* link NTHeaders to DosHeader */
309         NTHeaders->Signature=IMAGE_PE_MAGIC;
310         NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
311         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
312                         =sizeof(*ExportDir);    /* in bytes; just prevent LdrPEFixupForward() invocation */
313         captive_new0(ExportDir);
314         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
315                         =MODULEOBJECT_BASE_OFFSET_MINUS(ExportDir);     /* link ExportDir to NTHeaders */
316         ExportDir->Base=0;      /* base Ordinal number; Ordinals are not supported by libcaptive */
317
318         /* count the number of symbols */
319         va_start(ap,FullName_utf8);
320         for (symN=0;captive_va_arg(sym_name,ap);symN++)
321                 captive_va_arg(sym_val,ap);
322         va_end(ap);
323         ExportDir->NumberOfNames=symN;
324         ExportDir->NumberOfFunctions=symN;
325
326         /* allocate and link the symbol tables */
327         captive_newn(NameList,symN);
328         ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(NameList);   /* link NameList to ExportDir */
329         captive_newn(OrdinalList,symN);
330         ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(OrdinalList);  /* link OrdinalList to ExportDir */
331         captive_newn(FunctionList,symN);
332         ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET_MINUS(FunctionList);   /* link FunctionList to ExportDir */
333
334         /* fill in symbol tables */
335         va_start(ap,FullName_utf8);
336         for (symi=0;symi<symN;symi++) {
337                 captive_va_arg(sym_name,ap);
338                 captive_va_arg(sym_val ,ap);
339                 NameList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_name);
340                 OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
341                 FunctionList[symi]=MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
342                 }
343         va_end(ap);
344
345         /* successful module info build */
346         InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
347         return TRUE;
348
349 err_g_free_ModuleObject:
350         g_free(ModuleObject);
351 /* err */
352         g_return_val_if_reached(FALSE);
353 }
354
355 static void instruction_fail(const guint8 *instr_base,gint offset_last,const gchar *location)
356 {
357 char instr_buf[4*3+1],*s;
358
359         g_return_if_fail(offset_last<=(gint)((sizeof(instr_buf)-1)/3));
360
361         s=instr_buf;
362         while (offset_last-->=0)
363                 s+=sprintf(s," %02X",*instr_base++);
364         g_error("instruction_length(): Unhandled at %s:%s",location,instr_buf);
365         /* NOTREACHED */
366 }
367
368 static gsize instruction_length(const guint8 *instr)
369 {
370 #define INSTRUCTION_FAIL(offset_last) instruction_fail(instr,(offset_last),G_STRLOC)
371
372         if ((instr[0] & 0xF8)==0x50)    /* push %regular-register */
373                 return 1;
374         if ((instr[0] & 0xF8)==0x58)    /* pop  %regular-register */
375                 return 1;
376         if ((instr[0] & 0xE7)==0x06)    /* push %segment-register */
377                 return 1;
378         if ((instr[0] & 0xE7)==0x07 && instr[0]!=0x0F)  /* pop  %segment-register */
379                 return 1;
380         switch (instr[0]) {
381                 case 0x33:      /* xor GB,Ev */
382                         return 1+1;
383                 case 0x64:      /* prefix %fs */
384                         return instruction_length(instr+1);
385                 case 0x68:      /* push $quad-byte */
386                         return 1+4;
387                 case 0x6A:      /* push $single-byte */
388                         return 1+1;
389                 case 0x80:
390                         switch (instr[1]) {
391                                 case 0x3D:      /* cmpb $byte,immediate-quad-indirect */
392                                         return 1+1+4+1; /* 80 3D immediate-quad-indirect byte */
393                                 case 0x7C:      /* cmpb $byte,#immediate(%reg,1) */
394                                         return 1+1+1+1+1;       /* 80 7C address-mode immediate byte */
395                                 default: INSTRUCTION_FAIL(1);
396                                 }
397                 case 0x83:
398                         switch (instr[1]) {
399                                 case 0x01:      /* addl $byte,(%ecx) */
400                                         return 1+1+1;
401                                 case 0x3D:      /* cmpl $byte,immediate-quad-indirect */
402                                         return 1+1+4+1; /* 83 3D immediate-quad-indirect byte */
403                                 case 0x7C:      /* cmpl $byte,#immediate(%reg,1) */
404                                         return 1+1+1+1+1;       /* 80 7C address-mode immediate byte */
405                                 default: INSTRUCTION_FAIL(1);
406                                 }
407                 case 0x8A:      /* mov ??,?? */
408                 case 0x8B:      /* mov Gb,Eb */
409                         switch (instr[1]) {
410                                 case 0x0D:      /* mov immediate-quad-indirect,%ecx */
411                                         return 1+1+4;
412                                 case 0x15:      /* mov immediate-quad-indirect,%edx */
413                                         return 1+1+4;
414                                 case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
415                                 case 0x4C:      /* mov #offset(%reg,#mult),%reg */
416                                 case 0x54:      /* mov #offset(%reg,#mult),%reg */
417                                         return 1+1+1+1; /* 8B 44 address-mode offset */
418                                 case 0xC0:      /* mov %eax,%eax */
419                                         return 2;
420                                 case 0xFF:      /* mov %edi,%edi */
421                                         return 2;
422                                 default: INSTRUCTION_FAIL(1);
423                                 }
424                 case 0x8D:      /* lea Gb,M */
425                         switch (instr[1]) {
426                                 case 0x44:      /* mov #offset(%reg,%reg,#mult),%reg */
427                                         return 4;       /* 8B 44 address-mode offset */
428                                 default: INSTRUCTION_FAIL(1);
429                                 }
430                 case 0x8F:      /* lea Gb,M */
431                         switch (instr[1]) {
432                                 case 0x04:
433                                         switch (instr[2]) {
434                                                 case 0x24:      /* popl (%esp,1) */
435                                                         return 3;
436                                                 default: INSTRUCTION_FAIL(2);
437                                                 }
438                                 default: INSTRUCTION_FAIL(1);
439                                 }
440                 case 0x9C:      /* pushf */
441                         return 1;
442                 case 0x9D:      /* popf */
443                         return 1;
444                 case 0xA1:      /* mov immediate-quad-indirect,%eax */
445                         return 1+4;
446                 case 0xB8:      /* mov $immediate-quad,%eax */
447                         return 1+4;
448                 case 0xC2:      /* ret $immediate-double */
449                         return 1+2;
450                 case 0xCC:      /* int $0x3 */
451                         return 1;
452                 case 0xF0:      /* lock prefix */
453                         return 1+instruction_length(instr+1);
454                 case 0xFA:      /* cli */
455                         return 1;
456                 case 0xFB:      /* sti */
457                         return 1;
458                 case 0xFF:      /* inc/dec Ev */
459                         return 2;
460                 default: INSTRUCTION_FAIL(0);
461                 }
462         g_assert_not_reached(); /* TODO:fill the table */
463         /* NOTREACHED */
464         return 0;
465
466 #undef INSTRUCTION_FAIL
467 }
468
469
470         /* A lot of offsets in the structures are relative to ModuleObject->Base */
471 #define MODULEOBJECT_BASE_OFFSET_PLUS(addr) ((gpointer)((char *)addr+(ptrdiff_t)ModuleObject->Base))
472
473 static void captive_ModuleList_patch_function_disable
474                 (CHAR *funcname,PVOID *ExportAddressp,MODULE_OBJECT *ModuleObject /* user_data */)
475 {
476 PVOID ExportAddress;
477
478         g_return_if_fail(funcname!=NULL);
479         g_return_if_fail(ExportAddressp!=NULL);
480         g_return_if_fail(ModuleObject!=NULL);
481
482         ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
483         g_return_if_fail(ExportAddress!=NULL);
484
485         /* Some alised name; safe to ignore as we checked for possible W32 binary 0xF4
486          * hits during our initialization in captive_ModuleList_patch().
487          */
488         if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress)
489                 return;
490
491         captive_ModuleList_function_disable_hash_init();
492
493         *(guint8 *)ExportAddress=0xF4; /* hlt */
494         /* We may get aliased here; already existing entry is therefore allowed. */
495         g_hash_table_insert(captive_ModuleList_function_disable_hash,
496                         ExportAddress,  /* key */
497                         funcname);      /* value */
498 }
499
500
501 /**
502  * captive_ModuleList_patch:
503  * @FullName_utf8: String to find #PMODULE_OBJECT by #FullName.
504  * @...: (const gchar *sym_name,void (*sym_val)(void),struct captive_ModuleList_patchpoint *patchpoint) symbol list terminated by %NULL.
505  *
506  * Patches existing @FullName_utf8 module to use for function named #sym_name
507  * pointer to the handler #sym_val. If #patchpoint is not %NULL it gets assigned the original
508  * pointer value (used for %pass keyword in #exports.captivesym).
509  * 
510  * Put here 0xF4 'hlt' instead of 0xCC 'int $0x3; breakpoint'
511  * as 'hlt' will generate handled SIGSEGV instead of SIGTRAP which
512  * is used by gdb(1) during debugging.
513  * See also libcaptive/ps/signal.c/ sigaction_SIGSEGV().
514  *
515  * Returns: %TRUE if the module was successfuly added.
516  */
517 gboolean captive_ModuleList_patch(const gchar *FullName_utf8,...)
518 {
519 MODULE_OBJECT *ModuleObject;
520 va_list ap;
521 IMAGE_EXPORT_DIRECTORY *ExportDir;
522 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
523 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
524 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET_PLUS()ed */
525 ULONG ExportDirSize;
526 USHORT Idx;
527 PVOID ExportAddress,*ExportAddressp;
528 const gchar *sym_name;
529 void (*sym_val)(void);
530 struct captive_ModuleList_patchpoint *patchpoint,*patchpointpatch;
531 GHashTable *exportdir_hash;
532 gboolean errbool;
533
534         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
535
536         captive_ModuleList_patchpoint_hash_init();
537
538         ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(FullName_utf8)));
539         g_return_val_if_fail(ModuleObject!=NULL,FALSE);
540
541         ExportDir=(PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(ModuleObject->Base,
542                         TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT,&ExportDirSize);
543         g_return_val_if_fail(ExportDir!=NULL,FALSE);
544
545         NameList    =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNames);
546         OrdinalList =MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfNameOrdinals);
547         FunctionList=MODULEOBJECT_BASE_OFFSET_PLUS(ExportDir->AddressOfFunctions);
548
549         /* map (CHAR *)funcname->(PVOID *)ExportAddressp */
550         exportdir_hash=g_hash_table_new(g_str_hash,g_str_equal);
551
552         /* Initialize 'exportdir_hash' by all W32 exported functions. */
553         for (Idx=0;Idx<ExportDir->NumberOfNames;Idx++) {
554 CHAR *funcname=MODULEOBJECT_BASE_OFFSET_PLUS(NameList[Idx]);
555
556                 ExportAddressp=(PVOID *)(FunctionList+OrdinalList[Idx]);
557                 ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
558                 if (0xF4 /* hlt */ ==*(guint8 *)ExportAddress) {
559                         /* FIXME: 'data' type symbols should be permitted to have 0xF4 byte */
560                         g_error("%s: Function already patched although we did not touch it yet: %s",G_STRLOC,funcname);
561                         g_assert_not_reached();
562                         }
563                 g_assert(NULL==g_hash_table_lookup(exportdir_hash,funcname));
564                 g_hash_table_insert(exportdir_hash,
565                                 funcname,       /* key */
566                                 ExportAddressp);        /* value */
567                 }
568
569         /* Patch wished functions and remove them from 'exportdir_hash'. */
570         va_start(ap,FullName_utf8);
571         while (captive_va_arg(sym_name,ap)) {
572                 captive_va_arg(sym_val ,ap);
573                 /* 'sym_val' may be NULL if 'data' type && "pass"ed */
574                 captive_va_arg(patchpoint,ap);  /* 'data' type if ==NULL */
575                 captive_va_arg(patchpointpatch,ap);     /* 'data' type or 'pass' in non-debug mode if ==NULL */
576                 ExportAddressp=g_hash_table_lookup(exportdir_hash,sym_name);
577                 if (ExportAddressp==NULL) {
578                         g_message("%s: Function not found for patchpoint (ignoring): %s",G_STRLOC,sym_name);
579                         continue;
580                         }
581                 errbool=g_hash_table_remove(exportdir_hash,sym_name);
582                 g_assert(errbool==TRUE);
583                 if (!sym_val) { /* 'data' type && "pass"ed => do not corrupt it by 0xF4 */
584                         g_assert(!patchpoint);
585                         continue;
586                         }
587                 ExportAddress=(PVOID)MODULEOBJECT_BASE_OFFSET_PLUS(*ExportAddressp);
588                 *ExportAddressp=(PVOID)MODULEOBJECT_BASE_OFFSET_MINUS(sym_val);
589                 if (((ULONG)ExportAddress >= (ULONG)ExportDir) &&
590                                 ((ULONG)ExportAddress <  (ULONG)ExportDir + ExportDirSize))
591                         g_assert_not_reached(); /* LdrPEFixupForward() needed */
592                 if (!patchpoint) /* 'data' type && !"pass"ed => do not corrupt it by 0xF4 */
593                         continue;
594                 patchpoint->orig_w32_func=ExportAddress;
595                 if (!patchpointpatch)   /* 'pass' in non-debug mode */
596                         continue;
597                 if (0xF4 /* hlt */ ==*patchpointpatch->orig_w32_func)   /* Already patched by name-aliased function? */
598                         continue;
599                 g_assert(0xF4 /* hlt */ !=*patchpointpatch->orig_w32_func);
600                 patchpointpatch->orig_w32_2ndinstr=patchpointpatch->orig_w32_func
601                                 +instruction_length((guint8 *)patchpointpatch->orig_w32_func);
602                 g_assert(0xF4 /* hlt */ !=*patchpointpatch->orig_w32_2ndinstr);
603                 patchpointpatch->wrap_wrap_func=sym_val;
604                 patchpointpatch->orig_w32_func_byte=*patchpointpatch->orig_w32_func;
605                 patchpointpatch->orig_w32_2ndinstr_byte=*patchpointpatch->orig_w32_2ndinstr;
606                 patchpointpatch->through_w32_func=FALSE;
607                 g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpointpatch->orig_w32_func));
608                 g_hash_table_insert(captive_ModuleList_patchpoint_hash,
609                                 patchpointpatch->orig_w32_func, /* key */
610                                 patchpointpatch);       /* value */
611                 g_assert(NULL==g_hash_table_lookup(captive_ModuleList_patchpoint_hash,patchpointpatch->orig_w32_2ndinstr));
612                 g_hash_table_insert(captive_ModuleList_patchpoint_hash,
613                                 patchpointpatch->orig_w32_2ndinstr,     /* key */
614                                 patchpointpatch);       /* value */
615                 *(guint8 *)ExportAddress=0xF4;  /* hlt */
616                 }
617         va_end(ap);
618
619         /* The remaining entries of 'exportdir_hash' are W32 native functions
620          * unspecified by .captivesym file; we patch them as not-implemented ones.
621          */
622         g_hash_table_foreach(exportdir_hash,
623                         (GHFunc)captive_ModuleList_patch_function_disable,      /* func */
624                         ModuleObject);  /* used_data; unused */
625
626         g_hash_table_destroy(exportdir_hash);
627
628 #undef MODULEOBJECT_BASE_OFFSET_PLUS    /* no longer valid */
629 #undef MODULEOBJECT_BASE_OFFSET_MINUS   /* no longer valid */
630         return TRUE;
631 }
632
633
634 struct captive_ModuleList_patchpoint *captive_ModuleList_patchpoint_find(gconstpointer ExportAddress)
635 {
636 struct captive_ModuleList_patchpoint *r;
637
638         g_return_val_if_fail(ExportAddress!=NULL,NULL);
639
640         captive_ModuleList_patchpoint_hash_init();
641
642         r=g_hash_table_lookup(captive_ModuleList_patchpoint_hash,ExportAddress);
643         g_return_val_if_fail(r!=NULL,NULL);
644         g_assert(r->orig_w32_func==ExportAddress || r->orig_w32_2ndinstr==ExportAddress);
645
646         return r;
647 }
648
649
650 G_CONST_RETURN gchar *captive_ModuleList_function_disable_find(gconstpointer ExportAddress)
651 {
652         g_return_val_if_fail(ExportAddress!=NULL,NULL);
653
654         return g_hash_table_lookup(captive_ModuleList_function_disable_hash,ExportAddress);     /* funcname */
655 }
656
657
658 void *captive_Module_GetExportAddress(const gchar *ModuleName_utf8,const gchar *FunctionName)
659 {
660 MODULE_OBJECT *ModuleObject;
661 void *r;
662
663         g_return_val_if_fail(ModuleName_utf8!=NULL,NULL);
664         g_return_val_if_fail(FunctionName!=NULL,NULL);
665
666         ModuleObject=LdrGetModuleObject(captive_utf8_to_UnicodeString_alloca(g_path_get_basename(ModuleName_utf8)));
667         g_return_val_if_fail(ModuleObject!=NULL,NULL);
668
669         r=LdrGetExportAddress(
670                         ModuleObject,   /* ModuleObject */
671                         (/* de-const */char *)FunctionName,     /* Name */
672                         -1);    /*Hint*/
673         g_return_val_if_fail(r!=NULL,NULL);
674
675         return r;
676 }