Fix a couple of problems with FreeLDR portability.
[reactos.git] / reactos / boot / freeldr / freeldr / reactos / imageldr.c
1 #include <freeldr.h>
2 #include <debug.h>
3 #undef DbgPrint
4
5 extern BOOLEAN FrLdrBootType;
6
7 ULONG_PTR NextModuleBase = KERNEL_BASE_PHYS;
8 PLOADER_MODULE CurrentModule = NULL;
9
10 PVOID
11 NTAPI
12 LdrPEGetExportByName(
13 IN PVOID BaseAddress,
14 IN PUCHAR SymbolName,
15 IN USHORT Hint
16 );
17
18 extern BOOLEAN FrLdrLoadDriver(PCHAR szFileName, INT nPos);
19
20 /* MODULE MANAGEMENT **********************************************************/
21
22 PLOADER_MODULE
23 NTAPI
24 LdrGetModuleObject(IN PCHAR ModuleName)
25 {
26 ULONG i;
27
28 for (i = 0; i < LoaderBlock.ModsCount; i++)
29 {
30 if (strstr(_strupr((PCHAR)reactos_modules[i].String), _strupr(ModuleName)))
31 {
32 return &reactos_modules[i];
33 }
34 }
35
36 return NULL;
37 }
38
39 NTSTATUS
40 NTAPI
41 LdrPEGetOrLoadModule(IN PCHAR ModuleName,
42 IN PCHAR ImportedName,
43 IN PLOADER_MODULE* ImportedModule)
44 {
45 NTSTATUS Status = STATUS_SUCCESS;
46
47 *ImportedModule = LdrGetModuleObject(ImportedName);
48 if (*ImportedModule == NULL)
49 {
50 if (!FrLdrLoadDriver(ImportedName, 0))
51 {
52 return STATUS_UNSUCCESSFUL;
53 }
54 else
55 {
56 return LdrPEGetOrLoadModule(ModuleName, ImportedName, ImportedModule);
57 }
58 }
59
60 return Status;
61 }
62
63 ULONG_PTR
64 NTAPI
65 FrLdrLoadModule(FILE *ModuleImage,
66 LPCSTR ModuleName,
67 PULONG ModuleSize)
68 {
69 ULONG LocalModuleSize;
70 PLOADER_MODULE ModuleData;
71 LPSTR NameBuffer;
72 LPSTR TempName;
73
74 /* Get current module data structure and module name string array */
75 ModuleData = &reactos_modules[LoaderBlock.ModsCount];
76
77 /* Get only the Module Name */
78 do {
79
80 TempName = strchr(ModuleName, '\\');
81
82 if(TempName) {
83 ModuleName = TempName + 1;
84 }
85
86 } while(TempName);
87 NameBuffer = reactos_module_strings[LoaderBlock.ModsCount];
88
89 /* Get Module Size */
90 LocalModuleSize = FsGetFileSize(ModuleImage);
91
92 /* Fill out Module Data Structure */
93 ModuleData->ModStart = NextModuleBase;
94 ModuleData->ModEnd = NextModuleBase + LocalModuleSize;
95
96 /* Save name */
97 strcpy(NameBuffer, ModuleName);
98 ModuleData->String = (ULONG_PTR)NameBuffer;
99
100 /* Load the file image */
101 FsReadFile(ModuleImage, LocalModuleSize, NULL, (PVOID)NextModuleBase);
102
103 /* Move to next memory block and increase Module Count */
104 NextModuleBase = ROUND_UP(ModuleData->ModEnd, PAGE_SIZE);
105 LoaderBlock.ModsCount++;
106 // DbgPrint("NextBase, ImageSize, ModStart, ModEnd %p %p %p %p\n",
107 // NextModuleBase, LocalModuleSize, ModuleData->ModStart, ModuleData->ModEnd);
108
109 /* Return Module Size if required */
110 if (ModuleSize != NULL) {
111 *ModuleSize = LocalModuleSize;
112 }
113
114 return(ModuleData->ModStart);
115 }
116
117 ULONG_PTR
118 NTAPI
119 FrLdrCreateModule(LPCSTR ModuleName)
120 {
121 PLOADER_MODULE ModuleData;
122 LPSTR NameBuffer;
123
124 /* Get current module data structure and module name string array */
125 ModuleData = &reactos_modules[LoaderBlock.ModsCount];
126 NameBuffer = reactos_module_strings[LoaderBlock.ModsCount];
127
128 /* Set up the structure */
129 ModuleData->ModStart = NextModuleBase;
130 ModuleData->ModEnd = -1;
131
132 /* Copy the name */
133 strcpy(NameBuffer, ModuleName);
134 ModuleData->String = (ULONG_PTR)NameBuffer;
135
136 /* Set the current Module */
137 CurrentModule = ModuleData;
138
139 /* Return Module Base Address */
140 return(ModuleData->ModStart);
141 }
142
143 BOOLEAN
144 NTAPI
145 FrLdrCloseModule(ULONG_PTR ModuleBase,
146 ULONG ModuleSize)
147 {
148 PLOADER_MODULE ModuleData = CurrentModule;
149
150 /* Make sure a module is opened */
151 if (ModuleData) {
152
153 /* Make sure this is the right module and that it hasn't been closed */
154 if ((ModuleBase == ModuleData->ModStart) && (ModuleData->ModEnd == (ULONG_PTR)-1)) {
155
156 /* Close the Module */
157 ModuleData->ModEnd = ModuleData->ModStart + ModuleSize;
158
159 /* Set the next Module Base and increase the number of modules */
160 NextModuleBase = ROUND_UP(ModuleData->ModEnd, PAGE_SIZE);
161 LoaderBlock.ModsCount++;
162
163 /* Close the currently opened module */
164 CurrentModule = NULL;
165
166 /* Success */
167 return(TRUE);
168 }
169 }
170
171 /* Failure path */
172 return(FALSE);
173 }
174
175 /* PE IMAGE LOADER ***********************************************************/
176
177 PVOID
178 NTAPI
179 LdrPEFixupForward(IN PCHAR ForwardName)
180 {
181 CHAR NameBuffer[128];
182 PCHAR p;
183 PLOADER_MODULE ModuleObject;
184
185 strcpy(NameBuffer, ForwardName);
186 p = strchr(NameBuffer, '.');
187 if (p == NULL) return NULL;
188 *p = 0;
189
190 ModuleObject = LdrGetModuleObject(NameBuffer);
191 if (!ModuleObject)
192 {
193 DbgPrint("LdrPEFixupForward: failed to find module %s\n", NameBuffer);
194 return NULL;
195 }
196
197 return LdrPEGetExportByName((PVOID)ModuleObject->ModStart, (PUCHAR)(p + 1), 0xffff);
198 }
199
200 PVOID
201 NTAPI
202 LdrPEGetExportByName(PVOID BaseAddress,
203 PUCHAR SymbolName,
204 USHORT Hint)
205 {
206 PIMAGE_EXPORT_DIRECTORY ExportDir;
207 PULONG * ExFunctions;
208 PULONG * ExNames;
209 USHORT * ExOrdinals;
210 PVOID ExName;
211 ULONG Ordinal;
212 PVOID Function;
213 LONG minn, maxn, mid, res;
214 ULONG ExportDirSize;
215
216 /* HAL and NTOS use a virtual address, switch it to physical mode */
217 if ((ULONG_PTR)BaseAddress & KSEG0_BASE)
218 {
219 BaseAddress = RVA(BaseAddress, -KSEG0_BASE);
220 }
221
222 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
223 RtlImageDirectoryEntryToData(BaseAddress,
224 TRUE,
225 IMAGE_DIRECTORY_ENTRY_EXPORT,
226 &ExportDirSize);
227 if (!ExportDir)
228 {
229 DbgPrint("LdrPEGetExportByName(): no export directory!\n");
230 return NULL;
231 }
232
233 /* The symbol names may be missing entirely */
234 if (!ExportDir->AddressOfNames)
235 {
236 DbgPrint("LdrPEGetExportByName(): symbol names missing entirely\n");
237 return NULL;
238 }
239
240 /*
241 * Get header pointers
242 */
243 ExNames = (PULONG *)RVA(BaseAddress, ExportDir->AddressOfNames);
244 ExOrdinals = (USHORT *)RVA(BaseAddress, ExportDir->AddressOfNameOrdinals);
245 ExFunctions = (PULONG *)RVA(BaseAddress, ExportDir->AddressOfFunctions);
246
247 /*
248 * Check the hint first
249 */
250 if (Hint < ExportDir->NumberOfNames)
251 {
252 ExName = RVA(BaseAddress, ExNames[Hint]);
253 if (strcmp(ExName, (PCHAR)SymbolName) == 0)
254 {
255 Ordinal = ExOrdinals[Hint];
256 Function = RVA(BaseAddress, ExFunctions[Ordinal]);
257 if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir &&
258 (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize)
259 {
260 Function = LdrPEFixupForward((PCHAR)Function);
261 if (Function == NULL)
262 {
263 DbgPrint("LdrPEGetExportByName(): failed to find %s\n", Function);
264 }
265 return Function;
266 }
267
268 if (Function != NULL) return Function;
269 }
270 }
271
272 /*
273 * Binary search
274 */
275 minn = 0;
276 maxn = ExportDir->NumberOfNames - 1;
277 while (minn <= maxn)
278 {
279 mid = (minn + maxn) / 2;
280
281 ExName = RVA(BaseAddress, ExNames[mid]);
282 res = strcmp(ExName, (PCHAR)SymbolName);
283 if (res == 0)
284 {
285 Ordinal = ExOrdinals[mid];
286 Function = RVA(BaseAddress, ExFunctions[Ordinal]);
287 if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir &&
288 (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize)
289 {
290 Function = LdrPEFixupForward((PCHAR)Function);
291 if (Function == NULL)
292 {
293 DbgPrint("1: failed to find %s\n", Function);
294 }
295 return Function;
296 }
297 if (Function != NULL)
298 {
299 return Function;
300 }
301 }
302 else if (res > 0)
303 {
304 maxn = mid - 1;
305 }
306 else
307 {
308 minn = mid + 1;
309 }
310 }
311
312 ExName = RVA(BaseAddress, ExNames[mid]);
313 DbgPrint("2: failed to find %s\n",SymbolName);
314 return (PVOID)NULL;
315 }
316
317 NTSTATUS
318 NTAPI
319 LdrPEProcessImportDirectoryEntry(PVOID DriverBase,
320 PLOADER_MODULE LoaderModule,
321 PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory)
322 {
323 PVOID* ImportAddressList;
324 PULONG FunctionNameList;
325
326 if (ImportModuleDirectory == NULL || ImportModuleDirectory->Name == 0)
327 {
328 return STATUS_UNSUCCESSFUL;
329 }
330
331 /* Get the import address list. */
332 ImportAddressList = (PVOID*)RVA(DriverBase, ImportModuleDirectory->FirstThunk);
333
334 /* Get the list of functions to import. */
335 if (ImportModuleDirectory->OriginalFirstThunk != 0)
336 {
337 FunctionNameList = (PULONG)RVA(DriverBase, ImportModuleDirectory->OriginalFirstThunk);
338 }
339 else
340 {
341 FunctionNameList = (PULONG)RVA(DriverBase, ImportModuleDirectory->FirstThunk);
342 }
343
344 /* Walk through function list and fixup addresses. */
345 while (*FunctionNameList != 0L)
346 {
347 if ((*FunctionNameList) & 0x80000000)
348 {
349 DbgPrint("Failed to import ordinal from %s\n", LoaderModule->String);
350 return STATUS_UNSUCCESSFUL;
351 }
352 else
353 {
354 IMAGE_IMPORT_BY_NAME *pe_name;
355 pe_name = RVA(DriverBase, *FunctionNameList);
356 *ImportAddressList = LdrPEGetExportByName((PVOID)LoaderModule->ModStart, pe_name->Name, pe_name->Hint);
357
358 /* Fixup the address to be virtual */
359 *ImportAddressList = RVA(*ImportAddressList, KSEG0_BASE);
360
361 //DbgPrint("Looked for: %s and found: %p\n", pe_name->Name, *ImportAddressList);
362 if ((*ImportAddressList) == NULL)
363 {
364 DbgPrint("Failed to import %s from %s\n", pe_name->Name, LoaderModule->String);
365 return STATUS_UNSUCCESSFUL;
366 }
367 }
368 ImportAddressList++;
369 FunctionNameList++;
370 }
371 return STATUS_SUCCESS;
372 }
373
374 NTSTATUS
375 NTAPI
376 LdrPEFixupImports(IN PVOID DllBase,
377 IN PCHAR DllName)
378 {
379 PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory;
380 PCHAR ImportedName;
381 NTSTATUS Status;
382 PLOADER_MODULE ImportedModule;
383 ULONG Size;
384
385 /* Process each import module */
386 ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR)
387 RtlImageDirectoryEntryToData(DllBase,
388 TRUE,
389 IMAGE_DIRECTORY_ENTRY_IMPORT,
390 &Size);
391 while (ImportModuleDirectory && ImportModuleDirectory->Name)
392 {
393 /* Check to make sure that import lib is kernel */
394 ImportedName = (PCHAR) DllBase + ImportModuleDirectory->Name;
395 //DbgPrint("Processing imports for file: %s into file: %s\n", DllName, ImportedName);
396
397 Status = LdrPEGetOrLoadModule(DllName, ImportedName, &ImportedModule);
398 if (!NT_SUCCESS(Status)) return Status;
399
400 Status = LdrPEProcessImportDirectoryEntry(DllBase, ImportedModule, ImportModuleDirectory);
401 if (!NT_SUCCESS(Status)) return Status;
402
403 //DbgPrint("Imports for file: %s into file: %s complete\n", DllName, ImportedName);
404 ImportModuleDirectory++;
405 }
406
407 return STATUS_SUCCESS;
408 }
409
410 ULONG
411 NTAPI
412 FrLdrReMapImage(IN PVOID Base,
413 IN PVOID LoadBase)
414 {
415 PIMAGE_NT_HEADERS NtHeader;
416 PIMAGE_SECTION_HEADER Section;
417 ULONG i, Size, DriverSize = 0;
418
419 /* Get the first section */
420 NtHeader = RtlImageNtHeader(Base);
421 Section = IMAGE_FIRST_SECTION(NtHeader);
422
423 /* Allocate memory for the driver */
424 DriverSize = NtHeader->OptionalHeader.SizeOfImage;
425 LoadBase = MmAllocateMemoryAtAddress(DriverSize, LoadBase, LoaderSystemCode);
426 ASSERT(LoadBase);
427
428 /* Copy headers over */
429 RtlMoveMemory(LoadBase, Base, NtHeader->OptionalHeader.SizeOfHeaders);
430
431 /* Copy image sections into virtual section */
432 for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
433 {
434 /* Get the size of this section and check if it's valid */
435 Size = Section[i].VirtualAddress + Section[i].Misc.VirtualSize;
436 if (Size <= DriverSize)
437 {
438 if (Section[i].SizeOfRawData)
439 {
440 /* Copy the data from the disk to the image */
441 RtlCopyMemory((PVOID)((ULONG_PTR)LoadBase +
442 Section[i].VirtualAddress),
443 (PVOID)((ULONG_PTR)Base +
444 Section[i].PointerToRawData),
445 Section[i].Misc.VirtualSize >
446 Section[i].SizeOfRawData ?
447 Section[i].SizeOfRawData :
448 Section[i].Misc.VirtualSize);
449 }
450 else
451 {
452 /* Clear the BSS area */
453 RtlZeroMemory((PVOID)((ULONG_PTR)LoadBase +
454 Section[i].VirtualAddress),
455 Section[i].Misc.VirtualSize);
456 }
457 }
458 }
459
460 /* Return the size of the mapped driver */
461 return DriverSize;
462 }
463
464 PVOID
465 NTAPI
466 FrLdrMapImage(IN FILE *Image,
467 IN PCHAR Name,
468 IN ULONG ImageType)
469 {
470 PVOID ImageBase, LoadBase, ReadBuffer;
471 ULONG ImageId = LoaderBlock.ModsCount;
472 ULONG ImageSize;
473 NTSTATUS Status = STATUS_SUCCESS;
474
475 /* Try to see, maybe it's loaded already */
476 if (LdrGetModuleObject(Name) != NULL)
477 {
478 /* It's loaded, return NULL. It would be wise to return
479 correct LoadBase, but it seems to be ignored almost everywhere */
480 return NULL;
481 }
482
483 /* Set the virtual (image) and physical (load) addresses */
484 LoadBase = (PVOID)NextModuleBase;
485 ImageBase = RVA(LoadBase, KSEG0_BASE);
486
487 /* Save the Image Size */
488 ImageSize = FsGetFileSize(Image);
489
490 /* Set the file pointer to zero */
491 FsSetFilePointer(Image, 0);
492
493 /* Allocate a temporary buffer for the read */
494 ReadBuffer = MmHeapAlloc(ImageSize);
495
496 /* Load the file image */
497 FsReadFile(Image, ImageSize, NULL, ReadBuffer);
498
499 /* Map it into virtual memory */
500 ImageSize = FrLdrReMapImage(ReadBuffer, LoadBase);
501
502 /* Free the temporary buffer */
503 MmHeapFree(ReadBuffer);
504
505 /* Calculate Difference between Real Base and Compiled Base*/
506 Status = LdrRelocateImageWithBias(LoadBase,
507 (ULONG_PTR)ImageBase -
508 (ULONG_PTR)LoadBase,
509 "FreeLdr",
510 STATUS_SUCCESS,
511 STATUS_UNSUCCESSFUL,
512 STATUS_UNSUCCESSFUL);
513 if (!NT_SUCCESS(Status))
514 {
515 /* Fail */
516 DbgPrint("Failed to relocate image: %s\n", Name);
517 return NULL;
518 }
519
520 /* Fill out Module Data Structure */
521 reactos_modules[ImageId].ModStart = (ULONG_PTR)ImageBase;
522 reactos_modules[ImageId].ModEnd = (ULONG_PTR)ImageBase + ImageSize;
523 strcpy(reactos_module_strings[ImageId], Name);
524 reactos_modules[ImageId].String = (ULONG_PTR)reactos_module_strings[ImageId];
525 LoaderBlock.ModsCount++;
526
527 /* Increase the next Load Base */
528 NextModuleBase = ROUND_UP(NextModuleBase + ImageSize, PAGE_SIZE);
529
530 /* Perform import fixups */
531 if (!NT_SUCCESS(LdrPEFixupImports(LoadBase, Name)))
532 {
533 /* Fixup failed, just don't include it in the list */
534 // NextModuleBase = OldNextModuleBase;
535 LoaderBlock.ModsCount = ImageId;
536 return NULL;
537 }
538
539 /* Return the final mapped address */
540 return LoadBase;
541 }
542
543 /* EOF */