bootstrap
[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/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
39
40 /* reactos/ntoskrnl/ldr/loader.c file-scope global declaration: */
41 NTSTATUS LdrProcessModule(PVOID ModuleLoadBase,PUNICODE_STRING ModuleName,PMODULE_OBJECT *ModuleObject);
42
43
44 /**
45  * LdrLoadModule:
46  * @Filename: Filename of module to load based on host OS disk.
47  * Loading of already loaded module is forbidden.
48  * @ModuleObject: Returns initialized module object.
49  *
50  * Load and initialize module to reactos using host OS functions.
51  *
52  * Returns: STATUS_SUCCESS if the module was loaded successfuly during the call.
53  */
54 NTSTATUS LdrLoadModule(PUNICODE_STRING Filename,PMODULE_OBJECT *ModuleObjectp)
55 {
56 PVOID ModuleLoadBase;
57 PMODULE_OBJECT Module;
58 NTSTATUS err;
59
60         *ModuleObjectp=NULL;
61
62         /* Open the Module */
63         ModuleLoadBase=captive_file_mmap(
64                         NULL,   /* lenp */
65                         captive_UnicodeString_to_utf8_alloca(Filename), /* path */
66                         O_RDONLY,       /* open_flags */
67                         PROT_READ,      /* mmap_prot */
68                         MAP_SHARED);    /* mmap_flags */
69         /* FIXME: convert errno instead of STATUS_INSUFFICIENT_RESOURCES */
70         g_return_val_if_fail(ModuleLoadBase!=NULL,STATUS_INSUFFICIENT_RESOURCES);
71
72         /* examine/relocate the module */
73         err=LdrProcessModule(ModuleLoadBase,Filename,&Module);
74         if (!NT_SUCCESS(err)) {
75                 g_error("LdrLoadModule(): LdrProcessModule()=0x%08lX",err);
76                 goto err_captive_file_munmap;
77                 }
78
79         /* we were successful */
80         captive_file_munmap(ModuleLoadBase);
81         *ModuleObjectp=Module;
82
83         /* Hook for KDB on loading a driver. */
84         KDB_LOADDRIVER_HOOK(Filename,Module);
85
86         return STATUS_SUCCESS;
87
88         /* Error recoveries */
89 err_captive_file_munmap:
90         captive_file_munmap(ModuleLoadBase);
91
92         g_return_val_if_reached(err);
93 }
94
95
96 /**
97  * captive_LdrpLoadAndCallImage:
98  * @ModuleObjectp: Returns PMODULE_OBJECT successfuly loaded.
99  * @ModuleName: Filename of module to load based on host OS disk.
100  * Loading of already loaded module is forbidden despite original
101  * LdrpLoadAndCallImage().
102  * @DriverEntry_DriverObject: argument #DriverObject of #PDRIVER_INITIALIZE call.
103  * @DriverEntry_RegistryPath: argument #RegistryPath of #PDRIVER_INITIALIZE call.
104  *
105  * Corresponds to reactos LdrpLoadAndCallImage() but it also provides arguments
106  * to pass to #PDRIVER_INITIALIZE call of module driver initialization.
107  *
108  * Returns: STATUS_SUCCESS if the driver module was loaded and initialized
109  * successfuly during this call. Ignore returned @ModuleObjectp if function failed.
110  */
111 NTSTATUS captive_LdrpLoadAndCallImage(PMODULE_OBJECT *ModuleObjectp,PUNICODE_STRING ModuleName,
112                 PDRIVER_OBJECT DriverEntry_DriverObject,PUNICODE_STRING DriverEntry_RegistryPath)
113 {
114 PDRIVER_INITIALIZE DriverEntry;
115 PMODULE_OBJECT ModuleObject_tmp;
116 NTSTATUS err;
117
118         /* check for duplicity */
119         g_return_val_if_fail(LdrGetModuleObject(ModuleName)==NULL,STATUS_IMAGE_ALREADY_LOADED);
120
121         /* provide our temporary storage if the caller doesn't want to know ModuleObject address */
122         if (!ModuleObjectp)
123                 ModuleObjectp=&ModuleObject_tmp;
124
125         /* load the module */
126         err=LdrLoadModule(ModuleName,ModuleObjectp);
127         g_return_val_if_fail(NT_SUCCESS(err),err);
128
129         /* initialize the module */
130         DriverEntry=(PDRIVER_INITIALIZE)(*ModuleObjectp)->EntryPoint;
131         err=(*DriverEntry)(DriverEntry_DriverObject,DriverEntry_RegistryPath);
132         if (!NT_SUCCESS(err))
133                 goto err_LdrUnloadModule;
134
135         /* assumed STATUS_SUCCESS */
136         return err;
137
138 err_LdrUnloadModule:
139         /* errors ignored */
140         LdrUnloadModule(*ModuleObjectp);
141         *ModuleObjectp=NULL;
142 /* err: */
143         g_return_val_if_reached(err);
144 }
145
146
147 /**
148  * captive_ModuleList_add_builtin:
149  * @FullName_utf8: String to fill in #PMODULE_OBJECT->FullName.
150  * @...: (const gchar *sym_name,void *sym_val) symbol list terminated by %NULL.
151  *
152  * Adds simulated built-in module to #ModuleListHead module list.
153  * It can be used for the functionality of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement().
154  * libcaptive does not support Ordinals - we just pretend liner (%0-based)
155  * Ordinal numbers of the functions in given @... stdargs order.
156  *
157  * Returns: %TRUE if the module was successfuly added.
158  */
159 gboolean captive_ModuleList_add_builtin(const gchar *FullName_utf8,...)
160 {
161 MODULE_OBJECT *ModuleObject;
162 BOOLEAN errbool;
163 va_list ap;
164 const gchar *sym_name;
165 const void *sym_val;
166 gchar *basename_utf8;
167 IMAGE_DOS_HEADER *DosHeader;
168 IMAGE_NT_HEADERS *NTHeaders;
169 IMAGE_EXPORT_DIRECTORY *ExportDir;
170 guint symi,symN;        /* index,number of passed stdarg sym_name/sym_val pairs */
171 ptrdiff_t *NameList;    /* filled by sym_name; MODULEOBJECT_BASE_OFFSET()ed */
172 WORD *OrdinalList;      /* linear list - libcaptive doesn't support Ordinals */
173 ptrdiff_t *FunctionList;        /* filled by sym_val; MODULEOBJECT_BASE_OFFSET()ed */
174
175         g_return_val_if_fail(FullName_utf8!=NULL,FALSE);
176
177         captive_new0(ModuleObject);
178         /* A lot of offsets in the structures are relative to ModuleObject->Base */
179 #define MODULEOBJECT_BASE_OFFSET(addr) ((ptrdiff_t)((char *)addr-(char *)ModuleObject->Base))
180
181         ModuleObject->Flags = MODULE_FLAG_PE;
182
183         /* fill ModuleObject->FullName
184          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
185          * we put there '/'-based UNIX filenames in libcaptive.
186          */
187         errbool=RtlCreateUnicodeString(&ModuleObject->FullName,captive_utf8_to_UnicodeString_alloca(FullName_utf8)->Buffer);
188         if (!errbool) {
189                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
190                 goto err_g_free_ModuleObject;
191                 }
192
193         /* fill ModuleObject->BaseName
194          * reactos/ntoskrnl/ldr/loader.c/LdrpBuildModuleBaseName() does this one
195          * but it is 'static' for that file and also it is stricly backslash based.
196          * ModuleObject->FullName should be probably "\\SYSTEM32\\..." style but
197          * we put there '/'-based UNIX filenames in libcaptive.
198          */
199         basename_utf8=g_path_get_basename(FullName_utf8);
200         errbool=RtlCreateUnicodeString(&ModuleObject->BaseName,captive_utf8_to_UnicodeString_alloca(basename_utf8)->Buffer);
201         g_free(basename_utf8);
202         if (!errbool) {
203                 g_error("captive_ModuleList_add_builtin: RtlCreateUnicodeString()==FALSE");
204                 goto err_g_free_ModuleObject;
205                 }
206
207         /* We must satisfy reactos/ntoskrnl/ldr/rtl.c/RtlImageDirectoryEntryToData(,,IMAGE_DIRECTORY_ENTRY_EXPORT)
208          * prepare module headers
209          */
210         captive_new0(DosHeader);
211         ModuleObject->Base=DosHeader;   /* link DosHeader to ModuleObject */
212         DosHeader->e_magic=IMAGE_DOS_MAGIC;
213         captive_new0(NTHeaders);
214         DosHeader->e_lfanew=MODULEOBJECT_BASE_OFFSET(NTHeaders);        /* link NTHeaders to DosHeader */
215         NTHeaders->Signature=IMAGE_PE_MAGIC;
216         NTHeaders->OptionalHeader.NumberOfRvaAndSizes=IMAGE_DIRECTORY_ENTRY_EXPORT+1;
217         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size
218                         =sizeof(*ExportDir);    /* in bytes; just prevent LdrPEFixupForward() invocation */
219         captive_new0(ExportDir);
220         NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
221                         =MODULEOBJECT_BASE_OFFSET(ExportDir);   /* link ExportDir to NTHeaders */
222         ExportDir->Base=0;      /* base Ordinal number; Ordinals are not supported by libcaptive */
223
224         /* count the number of symbols */
225         va_start(ap,FullName_utf8);
226         for (symN=0;captive_va_arg(sym_name,ap);symN++)
227                 captive_va_arg(sym_val,ap);
228         va_end(ap);
229         ExportDir->NumberOfNames=symN;
230         ExportDir->NumberOfFunctions=symN;
231
232         /* allocate and link the symbol tables */
233         captive_newn(NameList,symN);
234         ExportDir->AddressOfNames=(PDWORD *)MODULEOBJECT_BASE_OFFSET(NameList); /* link NameList to ExportDir */
235         captive_newn(OrdinalList,symN);
236         ExportDir->AddressOfNameOrdinals=(PWORD *)MODULEOBJECT_BASE_OFFSET(OrdinalList);        /* link OrdinalList to ExportDir */
237         captive_newn(FunctionList,symN);
238         ExportDir->AddressOfFunctions=(PDWORD *)MODULEOBJECT_BASE_OFFSET(FunctionList); /* link FunctionList to ExportDir */
239
240         /* fill in symbol tables */
241         va_start(ap,FullName_utf8);
242         for (symi=0;symi<symN;symi++) {
243                 captive_va_arg(sym_name,ap);
244                 captive_va_arg(sym_val ,ap);
245                 NameList[symi]=MODULEOBJECT_BASE_OFFSET(sym_name);
246                 OrdinalList[symi]=symi; /* Ordinals are not supported by libcaptive */
247                 FunctionList[symi]=MODULEOBJECT_BASE_OFFSET(sym_val);
248                 }
249         va_end(ap);
250
251         /* successful module info build */
252         InsertTailList(&ModuleListHead,&ModuleObject->ListEntry);
253 #undef MODULEOBJECT_BASE_OFFSET /* no longer valid */
254         return TRUE;
255
256 err_g_free_ModuleObject:
257         g_free(ModuleObject);
258 /* err */
259         g_return_val_if_reached(FALSE);
260 }