captive_file_* -> captive_rtl_file_*
[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 "reactos/internal/ldr.h"       /* self */
24 #include "captive/unicode.h"
25 #include "captive/rtl-file.h"
26 #include <glib/gtypes.h>
27 #include <glib/gmessages.h>
28 #include <glib/gmem.h>
29 #include "reactos/napi/types.h"
30 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
31 #include <fcntl.h>
32 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
33 #include "captive/macros.h"
34 #include <stdarg.h>
35 #include "reactos/ddk/rtl.h"    /* for InsertTailList() */
36 #include "reactos/internal/debug.h"     /* for assert() */
37 #include <glib/gutils.h>        /* for g_path_get_basename() */
38 #include <gmodule.h>
39
40
41 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
42 NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
43
44
45 /* 'ntoskrnl/ldr/loader.c' file-scoped declaration: */
46 VOID LdrpBuildModuleBaseName(PUNICODE_STRING BaseName,PUNICODE_STRING FullName);
47
48
49 static MODULE_OBJECT *captive_LdrLoadModule_gmodule(const gchar *Filename_utf8)
50 {
51 MODULE_OBJECT *r;
52 GModule *gmodule;
53 MODULE_TEXT_SECTION *ModuleTextSection;
54 gboolean errbool;
55
56         g_return_val_if_fail(Filename_utf8!=NULL,NULL);
57         g_return_val_if_fail(TRUE==g_module_supported(),NULL);
58
59         gmodule=g_module_open(Filename_utf8,
60                         0);     /* Flags; !LAZY */
61         if (!gmodule)
62                 g_message(g_module_error());
63         g_assert(gmodule!=NULL);
64
65         captive_new0(r);
66
67         errbool=g_module_symbol(gmodule,"DriverEntry",&r->EntryPoint);
68         g_assert(errbool==TRUE);
69
70         r->Base=(PVOID)gmodule;
71         r->Flags=MODULE_FLAG_COFF;      /* some weird value - reactos uses MODULE_FLAG_PE */
72   
73         RtlCreateUnicodeString(&r->FullName,captive_utf8_to_UnicodeString_alloca(Filename_utf8)->Buffer);
74   LdrpBuildModuleBaseName(&r->BaseName,&r->FullName);
75
76         /* r->Length=0; */
77   
78   /* Insert module */
79         InsertTailList(&ModuleListHead,&r->ListEntry);
80
81         captive_new0(ModuleTextSection);
82         /* ModuleTextSection->Base=0; */
83         /* ModuleTextSection->Length=0; */
84         ModuleTextSection->Name=g_memdup(r->BaseName.Buffer,
85                         (captive_ucs2_strlen(r->BaseName.Buffer)+1)*sizeof(captive_ucs2));
86         /* ModuleTextSection->OptionalHeader=NULL; */
87         InsertTailList(&ModuleTextListHead,&ModuleTextSection->ListEntry);
88         r->TextSection=ModuleTextSection;
89
90         return r;
91 }
92
93
94 /**
95  * LdrLoadModule:
96  * @Filename: Filename of module to load based on host OS disk.
97  * Loading of already loaded module is forbidden.
98  * @ModuleObject: Returns initialized module object.
99  *
100  * Load and initialize module to reactos using host OS functions.
101  *
102  * Returns: STATUS_SUCCESS if the module was loaded successfuly during the call.
103  */
104 NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObjectp)
105 {
106 PVOID ModuleLoadBase;
107 PMODULE_OBJECT Module;
108 NTSTATUS err;
109 const gchar *Filename_utf8=captive_UnicodeString_to_utf8_alloca(Filename);
110
111         *ModuleObjectp=NULL;
112
113         /* Open the Module */
114         ModuleLoadBase=captive_rtl_file_mmap(
115                         NULL,   /* lenp */
116                         Filename_utf8,  /* path */
117                         O_RDONLY,       /* open_flags */
118                         PROT_READ,      /* mmap_prot */
119                         MAP_SHARED);    /* mmap_flags */
120         /* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
121         g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
122
123         if ((('M'<<8U)|('Z'<<0U))==GUINT16_FROM_BE(*(const guint16 *)ModuleLoadBase)) {
124                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"MZ/PE-32");
125                 /* examine/relocate the module */
126                 err=LdrProcessModule(ModuleLoadBase,Filename,&Module);
127                 if (!NT_SUCCESS(err)) {
128                         g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
129                         goto err_captive_rtl_file_munmap;
130                         }
131                 }
132         else {
133                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Loading module format: %s",G_STRLOC,"native .so");
134                 Module=captive_LdrLoadModule_gmodule(Filename_utf8);
135                 g_assert(Module!=NULL);
136                 }
137
138         /* we were successful */
139         captive_rtl_file_munmap(ModuleLoadBase);
140         *ModuleObjectp=Module;
141
142         /* Hook for KDB on loading a driver. */
143         KDB_LOADDRIVER_HOOK(Filename,Module);
144
145         return STATUS_SUCCESS;
146
147         /* Error recoveries */
148 err_captive_rtl_file_munmap:
149         captive_rtl_file_munmap(ModuleLoadBase);
150
151         g_return_val_if_reached(err);
152 }
153
154
155 /**
156  * captive_LdrpLoadAndCallImage:
157  * @ModuleObjectp: Returns PMODULE_OBJECT successfuly loaded.
158  * @ModuleName: Filename of module to load based on host OS disk.
159  * Loading of already loaded module is forbidden despite original
160  * LdrpLoadAndCallImage().
161  * @DriverEntry_DriverObject: argument #DriverObject of #PDRIVER_INITIALIZE call.
162  * @DriverEntry_RegistryPath: argument #RegistryPath of #PDRIVER_INITIALIZE call.
163  *
164  * Corresponds to reactos LdrpLoadAndCallImage() but it also provides arguments
165  * to pass to #PDRIVER_INITIALIZE call of module driver initialization.
166  *
167  * Returns: STATUS_SUCCESS if the driver module was loaded and initialized
168  * successfuly during this call. Ignore returned @ModuleObjectp if function failed.
169  */
170 NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,PUNICODE_STRING ModuleName,
171                 PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath)
172 {
173 PDRIVER_INITIALIZE DriverEntry;
174 PMODULE_OBJECT ModuleObject_tmp;
175 NTSTATUS err;
176
177         /* check for duplicity */
178         g_return_val_if_fail(LdrGetModuleObject(ModuleName)==NULL,STATUS_IMAGE_ALREADY_LOADED);
179
180         /* provide our temporary storage if the caller doesn't want to know ModuleObject address */
181         if (!ModuleObjectp)
182                 ModuleObjectp=&ModuleObject_tmp;
183
184         /* load the module */
185         err=LdrLoadModule(ModuleName,ModuleObjectp);
186         g_return_val_if_fail(NT_SUCCESS(err),err);
187
188         /* initialize the module */
189         DriverEntry=(PDRIVER_INITIALIZE)(*ModuleObjectp)->EntryPoint;
190         err=(*DriverEntry)(DriverEntry_DriverObject,DriverEntry_RegistryPath);
191         if (!NT_SUCCESS(err))
192                 goto err_LdrUnloadModule;
193
194         /* assumed STATUS_SUCCESS */
195         return err;
196
197 err_LdrUnloadModule:
198         /* errors ignored */
199         LdrUnloadModule(*ModuleObjectp);
200         *ModuleObjectp=NULL;
201 /* err: */
202         g_return_val_if_reached(err);
203 }
204
205
206 /**
207  * captive_ModuleList_add_builtin:
208  * @FullName_utf8: String to fill in #PMODULE_OBJECT->FullName.
209  * @...: (const gchar *sym_name,void *sym_val) symbol list terminated by %NULL.
210  *
211  * Adds simulated built-in module to #ModuleListHead module list.
212  * It can be used for the functionality of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement().
213  * libcaptive does not support Ordinals - we just pretend liner (%0-based)
214  * Ordinal numbers of the functions in given @... stdargs order.
215  *
216  * Returns: %TRUE if the module was successfuly added.
217  */
218 gboolean captive_ModuleList_add_builtin(const gchar *FullName_utf8,...)
219 {
220 MODULE_OBJECT *ModuleObject;
221 BOOLEAN errbool;
222 va_list ap;
223 const gchar *sym_name;
224 const void *sym_val;
225 gchar *basename_utf8;
226 IMAGE_DOS_HEADER *DosHeader;
227 IMAGE_NT_HEADERS *NTHeaders;
228 IMAGE_EXPORT_DIRECTORY *ExportDir;
229 guint symi,symN;        /* index,number of passed stdarg sym_name/sym_val pairs */
230 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET()ed */
231 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
232 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
233
234         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
235
236         captive_new0(ModuleObject);
237         /* A lot of offsets in the structures are relative to ModuleObject->Base */
238 #define MODULEOBJECT_BASE_OFFSET(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
239
240         ModuleObject->Flags = MODULE_FLAG_PE;
241
242         /* fill ModuleObject->FullName
243          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
244          * we put there '/'-based UNIX filenames in libcaptive.
245          */
246         errbool=RtlCreateUnicodeString(&ModuleObject->FullName,captive_utf8_to_UnicodeString_alloca(FullName_utf8)->Buffer);
247         if (!errbool) {
248                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
249                 goto err_g_free_ModuleObject;
250                 }
251
252         /* fill ModuleObject->BaseName
253          * reactos/ntoskrnl/ldr/loader.c/LdrpBuildModuleBaseName() does this one
254          * but it is 'static' for that file and also it is stricly backslash based.
255          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
256          * we put there '/'-based UNIX filenames in libcaptive.
257          */
258         basename_utf8=g_path_get_basename(FullName_utf8);
259         errbool=RtlCreateUnicodeString(&ModuleObject->BaseName,captive_utf8_to_UnicodeString_alloca(basename_utf8)->Buffer);
260         g_free(basename_utf8);
261         if (!errbool) {
262                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
263                 goto err_g_free_ModuleObject;
264                 }
265
266         /* We must satisfy reactos/ntoskrnl/ldr/rtl.c/RtlImageDirectoryEntryToData(,,IMAGE_DIRECTORY_ENTRY_EXPORT)
267          * prepare module headers
268          */
269         captive_new0(DosHeader);
270         ModuleObject->Base=DosHeader;   /* link DosHeader to ModuleObject */
271         DosHeader->e_magic=IMAGE_DOS_MAGIC;
272         captive_new0(NTHeaders);
273         DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET(NTHeaders);        /* link NTHeaders to DosHeader */
274         NTHeaders->Signature=IMAGE_PE_MAGIC;
275         NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
276         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
277                         =sizeof(*ExportDir);    /* in bytes; just prevent LdrPEFixupForward() invocation */
278         captive_new0(ExportDir);
279         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
280                         =MODULEOBJECT_BASE_OFFSET(ExportDir);   /* link ExportDir to NTHeaders */
281         ExportDir->Base=0;      /* base Ordinal number; Ordinals are not supported by libcaptive */
282
283         /* count the number of symbols */
284         va_start(ap,FullName_utf8);
285         for (symN=0;captive_va_arg(sym_name,ap);symN++)
286                 captive_va_arg(sym_val,ap);
287         va_end(ap);
288         ExportDir->NumberOfNames=symN;
289         ExportDir->NumberOfFunctions=symN;
290
291         /* allocate and link the symbol tables */
292         captive_newn(NameList,symN);
293         ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET(NameList); /* link NameList to ExportDir */
294         captive_newn(OrdinalList,symN);
295         ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET(OrdinalList);        /* link OrdinalList to ExportDir */
296         captive_newn(FunctionList,symN);
297         ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET(FunctionList); /* link FunctionList to ExportDir */
298
299         /* fill in symbol tables */
300         va_start(ap,FullName_utf8);
301         for (symi=0;symi<symN;symi++) {
302                 captive_va_arg(sym_name,ap);
303                 captive_va_arg(sym_val ,ap);
304                 NameList[symi]=MODULEOBJECT_BASE_OFFSET(sym_name);
305                 OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
306                 FunctionList[symi]=MODULEOBJECT_BASE_OFFSET(sym_val);
307                 }
308         va_end(ap);
309
310         /* successful module info build */
311         InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
312 #undef MODULEOBJECT_BASE_OFFSET /* no longer valid */
313         return TRUE;
314
315 err_g_free_ModuleObject:
316         g_free(ModuleObject);
317 /* err */
318         g_return_val_if_reached(FALSE);
319 }