Implemented loader functions for COFF Drivers and BIN apps, and stubs for PE apps
[reactos.git] / reactos / ntoskrnl / ldr / loader.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ldr/loader.c
5 * PURPOSE: Loaders for PE executables
6 * PROGRAMMER: Rex Jolliff (rex@lvcablemodem.com)
7 * UPDATE HISTORY:
8 * DW 22/05/98 Created
9 * RJJ 10/12/98 Completed loader function and added hooks for MZ/PE
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <internal/i386/segment.h>
15 #include <internal/kernel.h>
16 #include <internal/linkage.h>
17 #include <internal/module.h>
18 #include <internal/string.h>
19 #include <internal/symbol.h>
20
21 #include <ddk/ntddk.h>
22 #include <ddk/li.h>
23
24 //#define NDEBUG
25 #include <internal/debug.h>
26
27 #include "pe.h"
28
29 /* FUNCTIONS *****************************************************************/
30
31 /* COFF Driver load support **************************************************/
32 static BOOLEAN LdrCOFFDoRelocations(module *Module, unsigned int SectionIndex);
33 static BOOLEAN LdrCOFFDoAddr32Reloc(module *Module, SCNHDR *Section, RELOC *Relocation);
34 static BOOLEAN LdrCOFFDoReloc32Reloc(module *Module, SCNHDR *Section, RELOC *Relocation);
35 static void LdrCOFFGetSymbolName(module *Module, unsigned int Idx, char *Name);
36 static unsigned int LdrCOFFGetSymbolValue(module *Module, unsigned int Idx);
37 static unsigned int LdrCOFFGetKernelSymbolAddr(char *Name);
38 static unsigned int LdrCOFFGetSymbolValueByName(module *Module, char *SymbolName, unsigned int Idx);
39
40 static NTSTATUS
41 LdrCOFFProcessDriver(HANDLE FileHandle)
42 {
43 BOOLEAN FoundEntry;
44 char SymbolName[255];
45 int i;
46 NTSTATUS Status;
47 PVOID ModuleLoadBase;
48 ULONG EntryOffset;
49 FILHDR *FileHeader;
50 AOUTHDR *AOUTHeader;
51 module *Module;
52 FILE_STANDARD_INFORMATION FileStdInfo;
53 PDRIVER_INITIALIZE EntryRoutine;
54
55 /* Get the size of the file for the section */
56 Status = ZwQueryInformationFile(FileHandle,
57 NULL,
58 &FileStdInfo,
59 sizeof(FileStdInfo),
60 FileStandardInformation);
61 if (!NT_SUCCESS(Status))
62 {
63 return Status;
64 }
65 CHECKPOINT;
66
67 /* Allocate nonpageable memory for driver */
68 ModuleLoadBase = ExAllocatePool(NonPagedPool,
69 GET_LARGE_INTEGER_LOW_PART(FileStdInfo.AllocationSize));
70 if (ModuleLoadBase == NULL)
71 {
72 return STATUS_INSUFFICIENT_RESOURCES;
73 }
74 CHECKPOINT;
75
76 /* Load driver into memory chunk */
77 Status = ZwReadFile(FileHandle,
78 0, 0, 0, 0,
79 ModuleLoadBase,
80 GET_LARGE_INTEGER_LOW_PART(FileStdInfo.AllocationSize),
81 0, 0);
82 if (!NT_SUCCESS(Status))
83 {
84 ExFreePool(ModuleLoadBase);
85 return Status;
86 }
87 CHECKPOINT;
88
89 /* Get header pointers */
90 FileHeader = ModuleLoadBase;
91 AOUTHeader = ModuleLoadBase + FILHSZ;
92 CHECKPOINT;
93
94 /* Check COFF magic value */
95 if (I386BADMAG(*FileHeader))
96 {
97 DbgPrint("Module has bad magic value (%x)\n",
98 FileHeader->f_magic);
99 ExFreePool(ModuleLoadBase);
100 return STATUS_UNSUCCESSFUL;
101 }
102 CHECKPOINT;
103
104 /* Allocate and initialize a module definition structure */
105 Module = (module *) ExAllocatePool(NonPagedPool, sizeof(module));
106 if (Module == NULL)
107 {
108 ExFreePool(ModuleLoadBase);
109 return STATUS_INSUFFICIENT_RESOURCES;
110 }
111 Module->sym_list = (SYMENT *)(ModuleLoadBase + FileHeader->f_symptr);
112 Module->str_tab = (char *)(ModuleLoadBase + FileHeader->f_symptr +
113 FileHeader->f_nsyms * SYMESZ);
114 Module->scn_list = (SCNHDR *)(ModuleLoadBase + FILHSZ +
115 FileHeader->f_opthdr);
116 Module->size = 0;
117 Module->raw_data_off = (ULONG) ModuleLoadBase;
118 Module->nsyms = FileHeader->f_nsyms;
119 CHECKPOINT;
120
121 /* Determine the length of the module */
122 for (i = 0; i < FileHeader->f_nscns; i++)
123 {
124 DPRINT("Section name: %.8s\n", Module->scn_list[i].s_name);
125 DPRINT("size %x vaddr %x size %x\n",
126 Module->size,
127 Module->scn_list[i].s_vaddr,
128 Module->scn_list[i].s_size);
129 if (Module->scn_list[i].s_flags & STYP_TEXT)
130 {
131 Module->text_base = Module->scn_list[i].s_vaddr;
132 }
133 if (Module->scn_list[i].s_flags & STYP_DATA)
134 {
135 Module->data_base = Module->scn_list[i].s_vaddr;
136 }
137 if (Module->scn_list[i].s_flags & STYP_BSS)
138 {
139 Module->bss_base = Module->scn_list[i].s_vaddr;
140 }
141 if (Module->size <
142 (Module->scn_list[i].s_vaddr + Module->scn_list[i].s_size))
143 {
144 Module->size = Module->size + Module->scn_list[i].s_vaddr +
145 Module->scn_list[i].s_size;
146 }
147 }
148 CHECKPOINT;
149
150 /* Allocate a section for the module */
151 Module->base = (unsigned int) MmAllocateSection(Module->size);
152 if (Module->base == 0)
153 {
154 DbgPrint("Failed to alloc section for module\n");
155 ExFreePool(Module);
156 ExFreePool(ModuleLoadBase);
157 return STATUS_INSUFFICIENT_RESOURCES;
158 }
159 CHECKPOINT;
160
161 /* Adjust section vaddrs for allocated area */
162 Module->data_base = Module->data_base + Module->base;
163 Module->text_base = Module->text_base + Module->base;
164 Module->bss_base = Module->bss_base + Module->base;
165
166 /* Relocate module and fixup imports */
167 for (i = 0; i < FileHeader->f_nscns; i++)
168 {
169 if (Module->scn_list[i].s_flags & STYP_TEXT ||
170 Module->scn_list[i].s_flags & STYP_DATA)
171 {
172 memcpy((PVOID)(Module->base + Module->scn_list[i].s_vaddr),
173 (PVOID)(ModuleLoadBase + Module->scn_list[i].s_scnptr),
174 Module->scn_list[i].s_size);
175 if (!LdrCOFFDoRelocations(Module, i))
176 {
177 DPRINT("Relocation failed for section %s\n",
178 Module->scn_list[i].s_name);
179 ExFreePool(Module);
180 ExFreePool(ModuleLoadBase);
181 return STATUS_UNSUCCESSFUL;
182 }
183 }
184 if (Module->scn_list[i].s_flags & STYP_BSS)
185 {
186 memset((PVOID)(Module->base + Module->scn_list[i].s_vaddr),
187 0,
188 Module->scn_list[i].s_size);
189 }
190 }
191
192 DbgPrint("Module base: %x\n", Module->base);
193
194 /* Find the entry point */
195 EntryOffset = 0L;
196 FoundEntry = FALSE;
197 for (i = 0; i < FileHeader->f_nsyms; i++)
198 {
199 LdrCOFFGetSymbolName(Module, i, SymbolName);
200 if (!strcmp(SymbolName, "_DriverEntry"))
201 {
202 EntryOffset = Module->sym_list[i].e_value;
203 FoundEntry = TRUE;
204 DPRINT("Found entry at %x\n", EntryOffset);
205 }
206 }
207 if (!FoundEntry)
208 {
209 DbgPrint("No module entry point defined\n");
210 ExFreePool(Module);
211 ExFreePool(ModuleLoadBase);
212 return STATUS_UNSUCCESSFUL;
213 }
214
215 /* Call the module initalization routine */
216 EntryRoutine = (PDRIVER_INITIALIZE)(Module->base + EntryOffset);
217
218 return InitalizeLoadedDriver(EntryRoutine);
219 }
220
221 /* LdrCOFFDoRelocations
222 * FUNCTION: Do the relocations for a module section
223 * ARGUMENTS:
224 * Module = Pointer to the module
225 * SectionIndex = Index of the section to be relocated
226 * RETURNS: Success or failure
227 */
228
229 static BOOLEAN
230 LdrCOFFDoRelocations(module *Module, unsigned int SectionIndex)
231 {
232 SCNHDR *Section = &Module->scn_list[SectionIndex];
233 RELOC *Relocation = (RELOC *)(Module->raw_data_off + Section->s_relptr);
234 int j;
235
236 DPRINT("SectionIndex %d Name %.8s Relocs %d\n",
237 SectionIndex,
238 Module->scn_list[SectionIndex].s_name,
239 Section->s_nreloc);
240
241 for (j = 0; j < Section->s_nreloc; j++)
242 {
243 DbgPrint("vaddr %x ", Relocation->r_vaddr);
244 DbgPrint("symndex %x ", Relocation->r_symndx);
245
246 switch (Relocation->r_type)
247 {
248 case RELOC_ADDR32:
249 if (!LdrCOFFDoAddr32Reloc(Module, Section, Relocation))
250 {
251 return FALSE;
252 }
253 break;
254
255 case RELOC_REL32:
256 if (!LdrCOFFDoReloc32Reloc(Module, Section, Relocation))
257 {
258 return FALSE;
259 }
260 break;
261
262 default:
263 DbgPrint("%.8s: Unknown relocation type %x at %d in module\n",
264 Module->scn_list[SectionIndex].s_name,
265 Relocation->r_type,
266 j);
267 return FALSE;
268 }
269 Relocation++;
270 }
271 DPRINT("%.8s: relocations done\n", Module->scn_list[SectionIndex].s_name);
272
273 return TRUE;
274 }
275
276 /*
277 * FUNCTION: Performs a addr32 relocation on a loaded module
278 * ARGUMENTS:
279 * mod = module to perform the relocation on
280 * scn = Section to perform the relocation in
281 * reloc = Pointer to a data structure describing the relocation
282 * RETURNS: Success or failure
283 * NOTE: This fixes up a relocation needed when changing the base address of a
284 * module
285 */
286
287 static BOOLEAN
288 LdrCOFFDoAddr32Reloc(module *Module, SCNHDR *Section, RELOC *Relocation)
289 {
290 unsigned int Value;
291 unsigned int *Location;
292
293 Value = LdrCOFFGetSymbolValue(Module, Relocation->r_symndx);
294 Location = (unsigned int *)(Module->base + Relocation->r_vaddr);
295 DbgPrint("ADDR32 loc %x value %x *loc %x ", Location, Value, *Location);
296 *Location = (*Location) + Module->base;
297
298 return TRUE;
299 }
300
301 /*
302 * FUNCTION: Performs a reloc32 relocation on a loaded module
303 * ARGUMENTS:
304 * mod = module to perform the relocation on
305 * scn = Section to perform the relocation in
306 * reloc = Pointer to a data structure describing the relocation
307 * RETURNS: Success or failure
308 * NOTE: This fixes up an undefined reference to a kernel function in a module
309 */
310
311 static BOOLEAN
312 LdrCOFFDoReloc32Reloc(module *Module, SCNHDR *Section, RELOC *Relocation)
313 {
314 char Name[255];
315 unsigned int Value;
316 unsigned int *Location;
317
318 memset(Name, 0, 255);
319 LdrCOFFGetSymbolName(Module, Relocation->r_symndx, Name);
320 Value = (unsigned int) LdrCOFFGetKernelSymbolAddr(Name);
321 if (Value == 0L)
322 {
323 Value = LdrCOFFGetSymbolValueByName(Module, Name, Relocation->r_symndx);
324 if (Value == 0L)
325 {
326 DbgPrint("Undefined symbol %s in module\n", Name);
327 return FALSE;
328 }
329 Location = (unsigned int *)(Module->base + Relocation->r_vaddr);
330 // (*Location) = (*Location) + Value + Module->base - Section->s_vaddr;
331 (*Location) = (*Location);
332 DPRINT("Module->base %x Section->s_vaddr %x\n",
333 Module->base,
334 Section->s_vaddr);
335 }
336 else
337 {
338 DPRINT("REL32 value %x name %s\n", Value, Name);
339 Location = (unsigned int *)(Module->base + Relocation->r_vaddr);
340 DPRINT("old %x ", *Location);
341 DPRINT("Module->base %x Section->s_vaddr %x\n",
342 Module->base,
343 Section->s_vaddr);
344 (*Location) = (*Location) + Value - Module->base + Section->s_vaddr;
345 DPRINT("new %x\n", *Location);
346 }
347
348 return TRUE;
349 }
350
351 /*
352 * FUNCTION: Get the name of a symbol from a loaded module by ordinal
353 * ARGUMENTS:
354 * mod = module
355 * i = index of symbol
356 * name (OUT) = pointer to a string where the symbol name will be
357 * stored
358 */
359
360 static void
361 LdrCOFFGetSymbolName(module *Module, unsigned int Idx, char *Name)
362 {
363 if (Module->sym_list[Idx].e.e_name[0] != 0)
364 {
365 strncpy(Name, Module->sym_list[Idx].e.e_name, 8);
366 Name[8] = '\0';
367 }
368 else
369 {
370 strcpy(Name, &Module->str_tab[Module->sym_list[Idx].e.e.e_offset]);
371 }
372 }
373
374 /*
375 * FUNCTION: Get the value of a module defined symbol
376 * ARGUMENTS:
377 * mod = module
378 * i = index of symbol
379 * RETURNS: The value of the symbol
380 * NOTE: This fixes up references to known sections
381 */
382
383 static unsigned int
384 LdrCOFFGetSymbolValue(module *Module, unsigned int Idx)
385 {
386 char Name[255];
387
388 LdrCOFFGetSymbolName(Module, Idx, Name);
389 DbgPrint("name %s ", Name);
390
391 /* Check if the symbol is a section we have relocated */
392 if (strcmp(Name, ".text") == 0)
393 {
394 return Module->text_base;
395 }
396 if (strcmp(Name, ".data") == 0)
397 {
398 return Module->data_base;
399 }
400 if (strcmp(Name, ".bss") == 0)
401 {
402 return Module->bss_base;
403 }
404
405 return Module->sym_list[Idx].e_value;
406 }
407
408 /*
409 * FUNCTION: Get the address of a kernel symbol
410 * ARGUMENTS:
411 * name = symbol name
412 * RETURNS: The address of the symbol on success
413 * NULL on failure
414 */
415
416 static unsigned int
417 LdrCOFFGetKernelSymbolAddr(char *Name)
418 {
419 int i = 0;
420
421 while (symbol_table[i].name != NULL)
422 {
423 if (strcmp(symbol_table[i].name, Name) == 0)
424 {
425 return symbol_table[i].value;
426 }
427 i++;
428 }
429
430 return 0L;
431 }
432
433 static unsigned int
434 LdrCOFFGetSymbolValueByName(module *Module,
435 char *SymbolName,
436 unsigned int Idx)
437 {
438 unsigned int i;
439 char Name[255];
440
441 DPRINT("LdrCOFFGetSymbolValueByName(sname %s, idx %x)\n", SymbolName, Idx);
442
443 for (i = 0; i < Module->nsyms; i++)
444 {
445 LdrCOFFGetSymbolName(Module, i, Name);
446 DPRINT("Scanning %s Value %x\n", Name, Module->sym_list[i].e_value);
447 if (strcmp(Name, SymbolName) == 0)
448 {
449 DPRINT("Returning %x\n", Module->sym_list[i].e_value);
450 return Module->sym_list[i].e_value;
451 }
452 }
453
454 return 0L;
455 }
456
457
458
459
460 static NTSTATUS
461 LdrProcessPEDriver(HANDLE FileHandle, PIMAGE_DOS_HEADER DosHeader)
462 {
463 return STATUS_NOT_IMPLEMENTED;
464 }
465
466 NTSTATUS
467 LdrLoadDriver(PUNICODE_STRING Filename)
468 /*
469 * FUNCTION: Loads a kernel driver
470 * ARGUMENTS:
471 * FileName = Driver to load
472 * RETURNS: Status
473 */
474 {
475 char BlockBuffer[512];
476 NTSTATUS Status;
477 HANDLE FileHandle;
478 OBJECT_ATTRIBUTES FileObjectAttributes;
479 PIMAGE_DOS_HEADER PEDosHeader;
480
481 /* Open the Driver */
482 InitializeObjectAttributes(&FileObjectAttributes,
483 Filename,
484 0,
485 NULL,
486 NULL);
487 Status = ZwOpenFile(&FileHandle, 0, &FileObjectAttributes, NULL, 0, 0);
488 if (!NT_SUCCESS(Status))
489 {
490 return Status;
491 }
492
493 /* Read first block of image to determine type */
494 Status = ZwReadFile(FileHandle, 0, 0, 0, 0, BlockBuffer, 512, 0, 0);
495 if (!NT_SUCCESS(Status))
496 {
497 ZwClose(FileHandle);
498 return Status;
499 }
500
501 /* If MZ header exists */
502 PEDosHeader = (PIMAGE_DOS_HEADER) BlockBuffer;
503 if (PEDosHeader->e_magic == 0x54AD && PEDosHeader->e_lfanew != 0L)
504 {
505 Status = LdrProcessPEDriver(FileHandle, PEDosHeader);
506 if (!NT_SUCCESS(Status))
507 {
508 ZwClose(FileHandle);
509 return Status;
510 }
511 }
512 if (PEDosHeader->e_magic == 0x54AD)
513 {
514 ZwClose(FileHandle);
515 return STATUS_NOT_IMPLEMENTED;
516 }
517 else /* Assume coff format and load */
518 {
519 Status = LdrCOFFProcessDriver(FileHandle);
520 if (!NT_SUCCESS(Status))
521 {
522 ZwClose(FileHandle);
523 return Status;
524 }
525 }
526
527 return STATUS_NOT_IMPLEMENTED;
528 }
529
530 static NTSTATUS
531 LdrProcessMZImage(HANDLE ProcessHandle,
532 HANDLE FileHandle,
533 PIMAGE_DOS_HEADER DosHeader)
534 {
535
536 /* FIXME: map VDM into low memory */
537 /* FIXME: Build/Load image sections */
538
539 return STATUS_NOT_IMPLEMENTED;
540 }
541
542 static NTSTATUS
543 LdrProcessPEImage(HANDLE ProcessHandle,
544 HANDLE FileHandle,
545 PIMAGE_DOS_HEADER DosHeader)
546 {
547 // PIMAGE_NT_HEADERS PEHeader;
548 // PIMAGE_SECTION_HEADER Sections;
549
550 // FIXME: Check architechture
551 // FIXME: Build/Load image sections
552 // FIXME: do relocations code sections
553 // FIXME: resolve imports
554 // FIXME: do fixups
555
556 return STATUS_NOT_IMPLEMENTED;
557 }
558
559 /*
560 * FUNCTION: Loads a PE executable into the specified process
561 * ARGUMENTS:
562 * Filename = File to load
563 * ProcessHandle = handle
564 * RETURNS: Status
565 */
566
567 NTSTATUS
568 LdrLoadImage(PUNICODE_STRING Filename, HANDLE ProcessHandle)
569 {
570 char BlockBuffer[512];
571 NTSTATUS Status;
572 ULONG SectionSize;
573 HANDLE FileHandle;
574 HANDLE ThreadHandle;
575 OBJECT_ATTRIBUTES FileObjectAttributes;
576 PIMAGE_DOS_HEADER PEDosHeader;
577 CONTEXT Context;
578 HANDLE SectionHandle;
579 PVOID BaseAddress;
580
581 /* FIXME: should DLLs be named sections? */
582
583 /* Open the image file */
584 InitializeObjectAttributes(&FileObjectAttributes,
585 Filename,
586 0,
587 NULL,
588 NULL);
589 Status = ZwOpenFile(&FileHandle, 0, &FileObjectAttributes, NULL, 0, 0);
590 if (!NT_SUCCESS(Status))
591 {
592 return Status;
593 }
594
595 /* Read first block of image to determine type */
596 Status = ZwReadFile(FileHandle, 0, 0, 0, 0, BlockBuffer, 512, 0, 0);
597 if (!NT_SUCCESS(Status))
598 {
599 ZwClose(FileHandle);
600 return Status;
601 }
602
603 /* If MZ header exists */
604 PEDosHeader = (PIMAGE_DOS_HEADER) BlockBuffer;
605 if (PEDosHeader->e_magic == 0x54AD && PEDosHeader->e_lfanew != 0L)
606 {
607 Status = LdrProcessPEImage(ProcessHandle,
608 FileHandle,
609 PEDosHeader);
610 if (!NT_SUCCESS(Status))
611 {
612 return Status;
613 }
614 }
615 else if (PEDosHeader->e_magic == 0x54AD)
616 {
617 Status = LdrProcessMZImage(ProcessHandle,
618 FileHandle,
619 PEDosHeader);
620 if (!NT_SUCCESS(Status))
621 {
622 return Status;
623 }
624 }
625 else /* Assume bin format and load */
626 {
627 FILE_STANDARD_INFORMATION FileStdInfo;
628
629 /* Get the size of the file for the section */
630 Status = ZwQueryInformationFile(FileHandle,
631 NULL,
632 &FileStdInfo,
633 sizeof(FileStdInfo),
634 FileStandardInformation);
635 if (!NT_SUCCESS(Status))
636 {
637 ZwClose(FileHandle);
638 return Status;
639 }
640
641 /* Create the section for the code */
642 Status = ZwCreateSection(&SectionHandle,
643 SECTION_ALL_ACCESS,
644 NULL,
645 NULL,
646 PAGE_READWRITE,
647 MEM_COMMIT,
648 FileHandle);
649 ZwClose(FileHandle);
650 if (!NT_SUCCESS(Status))
651 {
652 return Status;
653 }
654
655 /* Map a view of the section into the desired process */
656 BaseAddress = (PVOID)0x10000;
657 SectionSize = GET_LARGE_INTEGER_LOW_PART(FileStdInfo.AllocationSize);
658 Status = ZwMapViewOfSection(SectionHandle,
659 ProcessHandle,
660 &BaseAddress,
661 0,
662 SectionSize,
663 NULL,
664 &SectionSize,
665 0,
666 MEM_COMMIT,
667 PAGE_READWRITE);
668 if (!NT_SUCCESS(Status))
669 {
670 /* FIXME: destroy the section here */
671
672 return Status;
673 }
674
675 /* Setup the context for the initial thread */
676 memset(&Context,0,sizeof(CONTEXT));
677 Context.SegSs = USER_DS;
678 Context.Esp = 0x2000;
679 Context.EFlags = 0x202;
680 Context.SegCs = USER_CS;
681 Context.Eip = 0x10000;
682 Context.SegDs = USER_DS;
683 Context.SegEs = USER_DS;
684 Context.SegFs = USER_DS;
685 Context.SegGs = USER_DS;
686
687 /* Create the stack for the process */
688 BaseAddress = (PVOID) 0x1000;
689 SectionSize = 0x1000;
690 Status = ZwAllocateVirtualMemory(ProcessHandle,
691 &BaseAddress,
692 0,
693 &SectionSize,
694 MEM_COMMIT,
695 PAGE_READWRITE);
696 if (!NT_SUCCESS(Status))
697 {
698 /* FIXME: unmap the section here */
699 /* FIXME: destroy the section here */
700
701 return Status;
702 }
703
704 /* Create the initial thread */
705 Status = ZwCreateThread(&ThreadHandle,
706 THREAD_ALL_ACCESS,
707 NULL,
708 ProcessHandle,
709 NULL,
710 &Context,
711 NULL,
712 FALSE);
713 if (!NT_SUCCESS(Status))
714 {
715 /* FIXME: destroy the stack memory block here */
716 /* FIXME: unmap the section here */
717 /* FIXME: destroy the section here */
718
719 return Status;
720 }
721 }
722 /* FIXME: {else} could check for a.out, ELF, COFF, etc. images here... */
723
724 return Status;
725 }
726