]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/BasePeCoffLib/BasePeCoff.c
1. PostCodeLib.
[mirror_edk2.git] / MdePkg / Library / BasePeCoffLib / BasePeCoff.c
1 /** @file
2 Tiano PE/COFF loader.
3
4 Copyright (c) 2006, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 Module Name: PeCoffLoader.c
14
15 **/
16
17
18
19
20 STATIC
21 RETURN_STATUS
22 PeCoffLoaderGetPeHeader (
23 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
24 OUT EFI_IMAGE_NT_HEADERS *PeHdr,
25 OUT EFI_TE_IMAGE_HEADER *TeHdr
26 );
27
28 STATIC
29 RETURN_STATUS
30 PeCoffLoaderCheckImageType (
31 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
32 IN EFI_IMAGE_NT_HEADERS *PeHdr,
33 IN EFI_TE_IMAGE_HEADER *TeHdr
34 );
35
36 STATIC
37 VOID *
38 PeCoffLoaderImageAddress (
39 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
40 IN UINTN Address
41 );
42
43 RETURN_STATUS
44 PeCoffLoaderRelocateImageEx (
45 IN UINT16 *Reloc,
46 IN OUT CHAR8 *Fixup,
47 IN OUT CHAR8 **FixupData,
48 IN UINT64 Adjust
49 );
50
51
52
53 /**
54 Retrieves the PE or TE Header from a PE/COFF or TE image.
55
56 @param ImageContext The context of the image being loaded.
57 @param PeHdr The buffer in which to return the PE header.
58 @param TeHdr The buffer in which to return the TE header.
59
60 @retval RETURN_SUCCESS The PE or TE Header is read.
61 @retval Other The error status from reading the PE/COFF or TE image using the ImageRead function.
62
63 **/
64 STATIC
65 RETURN_STATUS
66 PeCoffLoaderGetPeHeader (
67 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
68 OUT EFI_IMAGE_NT_HEADERS *PeHdr,
69 OUT EFI_TE_IMAGE_HEADER *TeHdr
70 )
71 {
72 RETURN_STATUS Status;
73 EFI_IMAGE_DOS_HEADER DosHdr;
74 UINTN Size;
75
76 ImageContext->IsTeImage = FALSE;
77 //
78 // Read the DOS image headers
79 //
80 Size = sizeof (EFI_IMAGE_DOS_HEADER);
81 Status = ImageContext->ImageRead (
82 ImageContext->Handle,
83 0,
84 &Size,
85 &DosHdr
86 );
87 if (RETURN_ERROR (Status)) {
88 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
89 return Status;
90 }
91
92 ImageContext->PeCoffHeaderOffset = 0;
93 if (DosHdr.e_magic == EFI_IMAGE_DOS_SIGNATURE) {
94 //
95 // DOS image header is present, so read the PE header after the DOS image header
96 //
97 ImageContext->PeCoffHeaderOffset = DosHdr.e_lfanew;
98 }
99 //
100 // Read the PE/COFF Header
101 //
102 Size = sizeof (EFI_IMAGE_NT_HEADERS);
103 Status = ImageContext->ImageRead (
104 ImageContext->Handle,
105 ImageContext->PeCoffHeaderOffset,
106 &Size,
107 PeHdr
108 );
109 if (RETURN_ERROR (Status)) {
110 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
111 return Status;
112 }
113 //
114 // Check the PE/COFF Header Signature. If not, then try to read a TE header
115 //
116 if (PeHdr->Signature != EFI_IMAGE_NT_SIGNATURE) {
117 Size = sizeof (EFI_TE_IMAGE_HEADER);
118 Status = ImageContext->ImageRead (
119 ImageContext->Handle,
120 0,
121 &Size,
122 TeHdr
123 );
124 if (TeHdr->Signature != EFI_TE_IMAGE_HEADER_SIGNATURE) {
125 return RETURN_UNSUPPORTED;
126 }
127
128 ImageContext->IsTeImage = TRUE;
129 }
130
131 return RETURN_SUCCESS;
132 }
133
134 /**
135 Checks the PE or TE header of a PE/COFF or TE image to determine if it supported.
136
137 @param ImageContext The context of the image being loaded.
138 @param PeHdr The buffer in which to return the PE header.
139 @param TeHdr The buffer in which to return the TE header.
140
141 @retval RETURN_SUCCESS The PE/COFF or TE image is supported.
142 @retval RETURN_UNSUPPORTED The PE/COFF or TE image is not supported.
143
144 **/
145 STATIC
146 RETURN_STATUS
147 PeCoffLoaderCheckImageType (
148 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
149 IN EFI_IMAGE_NT_HEADERS *PeHdr,
150 IN EFI_TE_IMAGE_HEADER *TeHdr
151 )
152 {
153 //
154 // See if the machine type is supported. We support a native machine type (IA-32/Itanium-based)
155 // and the machine type for the Virtual Machine.
156 //
157 if (ImageContext->IsTeImage == FALSE) {
158 ImageContext->Machine = PeHdr->FileHeader.Machine;
159 } else {
160 ImageContext->Machine = TeHdr->Machine;
161 }
162
163 if (!(EFI_IMAGE_MACHINE_TYPE_SUPPORTED (ImageContext->Machine))) {
164 ImageContext->ImageError = IMAGE_ERROR_INVALID_MACHINE_TYPE;
165 return RETURN_UNSUPPORTED;
166 }
167
168 //
169 // See if the image type is supported. We support EFI Applications,
170 // EFI Boot Service Drivers, and EFI Runtime Drivers.
171 //
172 if (ImageContext->IsTeImage == FALSE) {
173 ImageContext->ImageType = PeHdr->OptionalHeader.Subsystem;
174 } else {
175 ImageContext->ImageType = (UINT16) (TeHdr->Subsystem);
176 }
177
178
179 return RETURN_SUCCESS;
180 }
181
182 /**
183 Retrieves information about a PE/COFF image.
184
185 Computes the PeCoffHeaderOffset, ImageAddress, ImageSize, DestinationAddress, CodeView,
186 PdbPointer, RelocationsStripped, SectionAlignment, SizeOfHeaders, and DebugDirectoryEntryRva
187 fields of the ImageContext structure. If ImageContext is NULL, then return RETURN_INVALID_PARAMETER.
188 If the PE/COFF image accessed through the ImageRead service in the ImageContext structure is not
189 a supported PE/COFF image type, then return RETURN_UNSUPPORTED. If any errors occur while
190 computing the fields of ImageContext, then the error status is returned in the ImageError field of
191 ImageContext.
192
193 @param ImageContext Pointer to the image context structure that describes the PE/COFF
194 image that needs to be examined by this function.
195
196 @retval RETURN_SUCCESS The information on the PE/COFF image was collected.
197 @retval RETURN_INVALID_PARAMETER ImageContext is NULL.
198 @retval RETURN_UNSUPPORTED The PE/COFF image is not supported.
199 @retval Others The error status from reading the PE/COFF image
200 using the ImageContext->ImageRead() function.
201
202 **/
203 RETURN_STATUS
204 EFIAPI
205 PeCoffLoaderGetImageInfo (
206 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
207 )
208 {
209 RETURN_STATUS Status;
210 EFI_IMAGE_NT_HEADERS PeHdr;
211 EFI_TE_IMAGE_HEADER TeHdr;
212 EFI_IMAGE_DATA_DIRECTORY *DebugDirectoryEntry;
213 UINTN Size;
214 UINTN Index;
215 UINTN DebugDirectoryEntryRva;
216 UINTN DebugDirectoryEntryFileOffset;
217 UINTN SectionHeaderOffset;
218 EFI_IMAGE_SECTION_HEADER SectionHeader;
219 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY DebugEntry;
220
221 if (NULL == ImageContext) {
222 return RETURN_INVALID_PARAMETER;
223 }
224 //
225 // Assume success
226 //
227 ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
228
229 Status = PeCoffLoaderGetPeHeader (ImageContext, &PeHdr, &TeHdr);
230 if (RETURN_ERROR (Status)) {
231 return Status;
232 }
233 //
234 // Verify machine type
235 //
236 Status = PeCoffLoaderCheckImageType (ImageContext, &PeHdr, &TeHdr);
237 if (RETURN_ERROR (Status)) {
238 return Status;
239 }
240 //
241 // Retrieve the base address of the image
242 //
243 if (!(ImageContext->IsTeImage)) {
244 ImageContext->ImageAddress = PeHdr.OptionalHeader.ImageBase;
245 } else {
246 ImageContext->ImageAddress = (PHYSICAL_ADDRESS) (TeHdr.ImageBase);
247 }
248 //
249 // Initialize the alternate destination address to 0 indicating that it
250 // should not be used.
251 //
252 ImageContext->DestinationAddress = 0;
253
254 //
255 // Initialize the codeview pointer.
256 //
257 ImageContext->CodeView = NULL;
258 ImageContext->PdbPointer = NULL;
259
260 //
261 // Three cases with regards to relocations:
262 // - Image has base relocs, RELOCS_STRIPPED==0 => image is relocatable
263 // - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable
264 // - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but
265 // has no base relocs to apply
266 // Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.
267 //
268 // Look at the file header to determine if relocations have been stripped, and
269 // save this info in the image context for later use.
270 //
271 if ((!(ImageContext->IsTeImage)) && ((PeHdr.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) {
272 ImageContext->RelocationsStripped = TRUE;
273 } else {
274 ImageContext->RelocationsStripped = FALSE;
275 }
276
277 if (!(ImageContext->IsTeImage)) {
278 ImageContext->ImageSize = (UINT64) PeHdr.OptionalHeader.SizeOfImage;
279 ImageContext->SectionAlignment = PeHdr.OptionalHeader.SectionAlignment;
280 ImageContext->SizeOfHeaders = PeHdr.OptionalHeader.SizeOfHeaders;
281
282 //
283 // Modify ImageSize to contain .PDB file name if required and initialize
284 // PdbRVA field...
285 //
286 if (PeHdr.OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
287 DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(PeHdr.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
288
289 DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
290
291 //
292 // Determine the file offset of the debug directory... This means we walk
293 // the sections to find which section contains the RVA of the debug
294 // directory
295 //
296 DebugDirectoryEntryFileOffset = 0;
297
298 SectionHeaderOffset = (UINTN)(
299 ImageContext->PeCoffHeaderOffset +
300 sizeof (UINT32) +
301 sizeof (EFI_IMAGE_FILE_HEADER) +
302 PeHdr.FileHeader.SizeOfOptionalHeader
303 );
304
305 for (Index = 0; Index < PeHdr.FileHeader.NumberOfSections; Index++) {
306 //
307 // Read section header from file
308 //
309 Size = sizeof (EFI_IMAGE_SECTION_HEADER);
310 Status = ImageContext->ImageRead (
311 ImageContext->Handle,
312 SectionHeaderOffset,
313 &Size,
314 &SectionHeader
315 );
316 if (RETURN_ERROR (Status)) {
317 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
318 return Status;
319 }
320
321 if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
322 DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
323 DebugDirectoryEntryFileOffset =
324 DebugDirectoryEntryRva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData;
325 break;
326 }
327
328 SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
329 }
330
331 if (DebugDirectoryEntryFileOffset != 0) {
332 for (Index = 0; Index < DebugDirectoryEntry->Size; Index++) {
333 //
334 // Read next debug directory entry
335 //
336 Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
337 Status = ImageContext->ImageRead (
338 ImageContext->Handle,
339 DebugDirectoryEntryFileOffset,
340 &Size,
341 &DebugEntry
342 );
343 if (RETURN_ERROR (Status)) {
344 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
345 return Status;
346 }
347
348 if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
349 ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index * sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY));
350 if (DebugEntry.RVA == 0 && DebugEntry.FileOffset != 0) {
351 ImageContext->ImageSize += DebugEntry.SizeOfData;
352 }
353
354 return RETURN_SUCCESS;
355 }
356 }
357 }
358 }
359 } else {
360 ImageContext->ImageSize = 0;
361 ImageContext->SectionAlignment = 4096;
362 ImageContext->SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN) TeHdr.BaseOfCode - (UINTN) TeHdr.StrippedSize;
363
364 DebugDirectoryEntry = &TeHdr.DataDirectory[1];
365 DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
366 SectionHeaderOffset = (UINTN) (sizeof (EFI_TE_IMAGE_HEADER));
367
368 DebugDirectoryEntryFileOffset = 0;
369
370 for (Index = 0; Index < TeHdr.NumberOfSections;) {
371 //
372 // Read section header from file
373 //
374 Size = sizeof (EFI_IMAGE_SECTION_HEADER);
375 Status = ImageContext->ImageRead (
376 ImageContext->Handle,
377 SectionHeaderOffset,
378 &Size,
379 &SectionHeader
380 );
381 if (RETURN_ERROR (Status)) {
382 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
383 return Status;
384 }
385
386 if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
387 DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
388 DebugDirectoryEntryFileOffset = DebugDirectoryEntryRva -
389 SectionHeader.VirtualAddress +
390 SectionHeader.PointerToRawData +
391 sizeof (EFI_TE_IMAGE_HEADER) -
392 TeHdr.StrippedSize;
393
394 //
395 // File offset of the debug directory was found, if this is not the last
396 // section, then skip to the last section for calculating the image size.
397 //
398 if (Index < (UINTN) TeHdr.NumberOfSections - 1) {
399 SectionHeaderOffset += (TeHdr.NumberOfSections - 1 - Index) * sizeof (EFI_IMAGE_SECTION_HEADER);
400 Index = TeHdr.NumberOfSections - 1;
401 continue;
402 }
403 }
404
405 //
406 // In Te image header there is not a field to describe the ImageSize.
407 // Actually, the ImageSize equals the RVA plus the VirtualSize of
408 // the last section mapped into memory (Must be rounded up to
409 // a mulitple of Section Alignment). Per the PE/COFF specification, the
410 // section headers in the Section Table must appear in order of the RVA
411 // values for the corresponding sections. So the ImageSize can be determined
412 // by the RVA and the VirtualSize of the last section header in the
413 // Section Table.
414 //
415 if ((++Index) == (UINTN) TeHdr.NumberOfSections) {
416 ImageContext->ImageSize = (SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize +
417 ImageContext->SectionAlignment - 1) & ~(ImageContext->SectionAlignment - 1);
418 }
419
420 SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
421 }
422
423 if (DebugDirectoryEntryFileOffset != 0) {
424 for (Index = 0; Index < DebugDirectoryEntry->Size; Index++) {
425 //
426 // Read next debug directory entry
427 //
428 Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
429 Status = ImageContext->ImageRead (
430 ImageContext->Handle,
431 DebugDirectoryEntryFileOffset,
432 &Size,
433 &DebugEntry
434 );
435 if (RETURN_ERROR (Status)) {
436 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
437 return Status;
438 }
439
440 if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
441 ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index * sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY));
442 return RETURN_SUCCESS;
443 }
444 }
445 }
446 }
447
448 return RETURN_SUCCESS;
449 }
450
451 /**
452 Converts an image address to the loaded address.
453
454 @param ImageContext The context of the image being loaded.
455 @param Address The address to be converted to the loaded address.
456
457 @return The converted address or NULL if the address can not be converted.
458
459 **/
460 STATIC
461 VOID *
462 PeCoffLoaderImageAddress (
463 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
464 IN UINTN Address
465 )
466 {
467 if (Address >= ImageContext->ImageSize) {
468 ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
469 return NULL;
470 }
471
472 return (CHAR8 *) ((UINTN) ImageContext->ImageAddress + Address);
473 }
474
475 /**
476 Applies relocation fixups to a PE/COFF image that was loaded with PeCoffLoaderLoadImage().
477
478 If the DestinationAddress field of ImageContext is 0, then use the ImageAddress field of
479 ImageContext as the relocation base address. Otherwise, use the DestinationAddress field
480 of ImageContext as the relocation base address. The caller must allocate the relocation
481 fixup log buffer and fill in the FixupData field of ImageContext prior to calling this function.
482 If ImageContext is NULL, then ASSERT().
483
484 @param ImageContext Pointer to the image context structure that describes the PE/COFF
485 image that is being relocated.
486
487 @retval RETURN_SUCCESS The PE/COFF image was relocated.
488 Extended status information is in the ImageError field of ImageContext.
489 @retval RETURN_LOAD_ERROR The image in not a valid PE/COFF image.
490 Extended status information is in the ImageError field of ImageContext.
491 @retval RETURN_UNSUPPORTED A relocation record type is not supported.
492 Extended status information is in the ImageError field of ImageContext.
493
494 **/
495 RETURN_STATUS
496 EFIAPI
497 PeCoffLoaderRelocateImage (
498 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
499 )
500 {
501 RETURN_STATUS Status;
502 EFI_IMAGE_NT_HEADERS *PeHdr;
503 EFI_TE_IMAGE_HEADER *TeHdr;
504 EFI_IMAGE_DATA_DIRECTORY *RelocDir;
505 UINT64 Adjust;
506 EFI_IMAGE_BASE_RELOCATION *RelocBase;
507 EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd;
508 UINT16 *Reloc;
509 UINT16 *RelocEnd;
510 CHAR8 *Fixup;
511 CHAR8 *FixupBase;
512 UINT16 *F16;
513 UINT32 *F32;
514 CHAR8 *FixupData;
515 PHYSICAL_ADDRESS BaseAddress;
516
517 ASSERT (ImageContext != NULL);
518
519 PeHdr = NULL;
520 TeHdr = NULL;
521 //
522 // Assume success
523 //
524 ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
525
526 //
527 // If there are no relocation entries, then we are done
528 //
529 if (ImageContext->RelocationsStripped) {
530 return RETURN_SUCCESS;
531 }
532
533 //
534 // If the destination address is not 0, use that rather than the
535 // image address as the relocation target.
536 //
537 if (ImageContext->DestinationAddress != 0) {
538 BaseAddress = ImageContext->DestinationAddress;
539 } else {
540 BaseAddress = ImageContext->ImageAddress;
541 }
542
543 if (!(ImageContext->IsTeImage)) {
544 PeHdr = (EFI_IMAGE_NT_HEADERS *)((UINTN)ImageContext->ImageAddress +
545 ImageContext->PeCoffHeaderOffset);
546
547 Adjust = (UINT64) BaseAddress - PeHdr->OptionalHeader.ImageBase;
548 PeHdr->OptionalHeader.ImageBase = (UINTN)BaseAddress;
549
550 //
551 // Find the relocation block
552 //
553 // Per the PE/COFF spec, you can't assume that a given data directory
554 // is present in the image. You have to check the NumberOfRvaAndSizes in
555 // the optional header to verify a desired directory entry is there.
556 //
557 if (PeHdr->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
558 RelocDir = &PeHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
559 RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress);
560 RelocBaseEnd = PeCoffLoaderImageAddress (
561 ImageContext,
562 RelocDir->VirtualAddress + RelocDir->Size - 1
563 );
564 } else {
565 //
566 // Set base and end to bypass processing below.
567 //
568 RelocBase = RelocBaseEnd = 0;
569 }
570 } else {
571 TeHdr = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
572 Adjust = (UINT64) (BaseAddress - TeHdr->ImageBase);
573 TeHdr->ImageBase = (UINT64) (BaseAddress);
574
575 //
576 // Find the relocation block
577 //
578 RelocDir = &TeHdr->DataDirectory[0];
579 RelocBase = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(
580 ImageContext->ImageAddress +
581 RelocDir->VirtualAddress +
582 sizeof(EFI_TE_IMAGE_HEADER) -
583 TeHdr->StrippedSize
584 );
585 RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *) ((UINTN) RelocBase + (UINTN) RelocDir->Size - 1);
586 }
587
588 //
589 // Run the relocation information and apply the fixups
590 //
591 FixupData = ImageContext->FixupData;
592 while (RelocBase < RelocBaseEnd) {
593
594 Reloc = (UINT16 *) ((CHAR8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
595 RelocEnd = (UINT16 *) ((CHAR8 *) RelocBase + RelocBase->SizeOfBlock);
596 if (!(ImageContext->IsTeImage)) {
597 FixupBase = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress);
598 } else {
599 FixupBase = (CHAR8 *)(UINTN)(ImageContext->ImageAddress +
600 RelocBase->VirtualAddress +
601 sizeof(EFI_TE_IMAGE_HEADER) -
602 TeHdr->StrippedSize
603 );
604 }
605
606 if ((CHAR8 *) RelocEnd < (CHAR8 *) ((UINTN) ImageContext->ImageAddress) ||
607 (CHAR8 *) RelocEnd > (CHAR8 *)((UINTN)ImageContext->ImageAddress +
608 (UINTN)ImageContext->ImageSize)) {
609 ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
610 return RETURN_LOAD_ERROR;
611 }
612
613 //
614 // Run this relocation record
615 //
616 while (Reloc < RelocEnd) {
617
618 Fixup = FixupBase + (*Reloc & 0xFFF);
619 switch ((*Reloc) >> 12) {
620 case EFI_IMAGE_REL_BASED_ABSOLUTE:
621 break;
622
623 case EFI_IMAGE_REL_BASED_HIGH:
624 F16 = (UINT16 *) Fixup;
625 *F16 = (UINT16) ((*F16 << 16) + (UINT16) Adjust);
626 if (FixupData != NULL) {
627 *(UINT16 *) FixupData = *F16;
628 FixupData = FixupData + sizeof (UINT16);
629 }
630 break;
631
632 case EFI_IMAGE_REL_BASED_LOW:
633 F16 = (UINT16 *) Fixup;
634 *F16 = (UINT16) (*F16 + (UINT16) Adjust);
635 if (FixupData != NULL) {
636 *(UINT16 *) FixupData = *F16;
637 FixupData = FixupData + sizeof (UINT16);
638 }
639 break;
640
641 case EFI_IMAGE_REL_BASED_HIGHLOW:
642 F32 = (UINT32 *) Fixup;
643 *F32 = *F32 + (UINT32) Adjust;
644 if (FixupData != NULL) {
645 FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
646 *(UINT32 *) FixupData = *F32;
647 FixupData = FixupData + sizeof (UINT32);
648 }
649 break;
650
651 case EFI_IMAGE_REL_BASED_HIGHADJ:
652 //
653 // Return the same EFI_UNSUPPORTED return code as
654 // PeCoffLoaderRelocateImageEx() returns if it does not recognize
655 // the relocation type.
656 //
657 ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
658 return RETURN_UNSUPPORTED;
659
660 default:
661 Status = PeCoffLoaderRelocateImageEx (Reloc, Fixup, &FixupData, Adjust);
662 if (RETURN_ERROR (Status)) {
663 ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
664 return Status;
665 }
666 }
667
668 //
669 // Next relocation record
670 //
671 Reloc += 1;
672 }
673
674 //
675 // Next reloc block
676 //
677 RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
678 }
679
680 return RETURN_SUCCESS;
681 }
682
683 /**
684 Loads a PE/COFF image into memory.
685
686 Loads the PE/COFF image accessed through the ImageRead service of ImageContext into the buffer
687 specified by the ImageAddress and ImageSize fields of ImageContext. The caller must allocate
688 the load buffer and fill in the ImageAddress and ImageSize fields prior to calling this function.
689 The EntryPoint, FixupDataSize, CodeView, and PdbPointer fields of ImageContext are computed.
690
691 @param ImageContext Pointer to the image context structure that describes the PE/COFF
692 image that is being loaded.
693
694 @retval RETURN_SUCCESS The PE/COFF image was loaded into the buffer specified by
695 the ImageAddress and ImageSize fields of ImageContext.
696 Extended status information is in the ImageError field of ImageContext.
697 @retval RETURN_BUFFER_TOO_SMALL The caller did not provide a large enough buffer.
698 Extended status information is in the ImageError field of ImageContext.
699 @retval RETURN_LOAD_ERROR The PE/COFF image is an EFI Runtime image with no relocations.
700 Extended status information is in the ImageError field of ImageContext.
701 @retval RETURN_INVALID_PARAMETER The image address is invalid.
702 Extended status information is in the ImageError field of ImageContext.
703
704 **/
705 RETURN_STATUS
706 EFIAPI
707 PeCoffLoaderLoadImage (
708 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
709 )
710 {
711 RETURN_STATUS Status;
712 EFI_IMAGE_NT_HEADERS *PeHdr;
713 EFI_TE_IMAGE_HEADER *TeHdr;
714 PE_COFF_LOADER_IMAGE_CONTEXT CheckContext;
715 EFI_IMAGE_SECTION_HEADER *FirstSection;
716 EFI_IMAGE_SECTION_HEADER *Section;
717 UINTN NumberOfSections;
718 UINTN Index;
719 CHAR8 *Base;
720 CHAR8 *End;
721 CHAR8 *MaxEnd;
722 EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
723 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
724 UINTN Size;
725 UINT32 TempDebugEntryRva;
726
727 PeHdr = NULL;
728 TeHdr = NULL;
729 //
730 // Assume success
731 //
732 ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
733
734 //
735 // Copy the provided context info into our local version, get what we
736 // can from the original image, and then use that to make sure everything
737 // is legit.
738 //
739 CopyMem (&CheckContext, ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
740
741 Status = PeCoffLoaderGetImageInfo (&CheckContext);
742 if (RETURN_ERROR (Status)) {
743 return Status;
744 }
745
746 //
747 // Make sure there is enough allocated space for the image being loaded
748 //
749 if (ImageContext->ImageSize < CheckContext.ImageSize) {
750 ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_SIZE;
751 return RETURN_BUFFER_TOO_SMALL;
752 }
753
754 //
755 // If there's no relocations, then make sure it's not a runtime driver,
756 // and that it's being loaded at the linked address.
757 //
758 if (CheckContext.RelocationsStripped) {
759 //
760 // If the image does not contain relocations and it is a runtime driver
761 // then return an error.
762 //
763 if (CheckContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) {
764 ImageContext->ImageError = IMAGE_ERROR_INVALID_SUBSYSTEM;
765 return RETURN_LOAD_ERROR;
766 }
767 //
768 // If the image does not contain relocations, and the requested load address
769 // is not the linked address, then return an error.
770 //
771 if (CheckContext.ImageAddress != ImageContext->ImageAddress) {
772 ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
773 return RETURN_INVALID_PARAMETER;
774 }
775 }
776 //
777 // Make sure the allocated space has the proper section alignment
778 //
779 if (!(ImageContext->IsTeImage)) {
780 if ((ImageContext->ImageAddress & (CheckContext.SectionAlignment - 1)) != 0) {
781 ImageContext->ImageError = IMAGE_ERROR_INVALID_SECTION_ALIGNMENT;
782 return RETURN_INVALID_PARAMETER;
783 }
784 }
785 //
786 // Read the entire PE/COFF or TE header into memory
787 //
788 if (!(ImageContext->IsTeImage)) {
789 Status = ImageContext->ImageRead (
790 ImageContext->Handle,
791 0,
792 &ImageContext->SizeOfHeaders,
793 (VOID *) (UINTN) ImageContext->ImageAddress
794 );
795
796 PeHdr = (EFI_IMAGE_NT_HEADERS *)
797 ((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset);
798
799 FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
800 (UINTN)ImageContext->ImageAddress +
801 ImageContext->PeCoffHeaderOffset +
802 sizeof(UINT32) +
803 sizeof(EFI_IMAGE_FILE_HEADER) +
804 PeHdr->FileHeader.SizeOfOptionalHeader
805 );
806 NumberOfSections = (UINTN) (PeHdr->FileHeader.NumberOfSections);
807 } else {
808 Status = ImageContext->ImageRead (
809 ImageContext->Handle,
810 0,
811 &ImageContext->SizeOfHeaders,
812 (void *) (UINTN) ImageContext->ImageAddress
813 );
814
815 TeHdr = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
816
817 FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
818 (UINTN)ImageContext->ImageAddress +
819 sizeof(EFI_TE_IMAGE_HEADER)
820 );
821 NumberOfSections = (UINTN) (TeHdr->NumberOfSections);
822
823 }
824
825 if (RETURN_ERROR (Status)) {
826 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
827 return RETURN_LOAD_ERROR;
828 }
829
830 //
831 // Load each section of the image
832 //
833 Section = FirstSection;
834 for (Index = 0, MaxEnd = NULL; Index < NumberOfSections; Index++) {
835
836 //
837 // Compute sections address
838 //
839 Base = PeCoffLoaderImageAddress (ImageContext, Section->VirtualAddress);
840 End = PeCoffLoaderImageAddress (
841 ImageContext,
842 Section->VirtualAddress + Section->Misc.VirtualSize - 1
843 );
844 if (ImageContext->IsTeImage) {
845 Base = (CHAR8 *) ((UINTN) Base + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
846 End = (CHAR8 *) ((UINTN) End + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
847 }
848
849 if (End > MaxEnd) {
850 MaxEnd = End;
851 }
852 //
853 // If the base start or end address resolved to 0, then fail.
854 //
855 if ((Base == NULL) || (End == NULL)) {
856 ImageContext->ImageError = IMAGE_ERROR_SECTION_NOT_LOADED;
857 return RETURN_LOAD_ERROR;
858 }
859
860 //
861 // Read the section
862 //
863 Size = (UINTN) Section->Misc.VirtualSize;
864 if ((Size == 0) || (Size > Section->SizeOfRawData)) {
865 Size = (UINTN) Section->SizeOfRawData;
866 }
867
868 if (Section->SizeOfRawData) {
869 if (!(ImageContext->IsTeImage)) {
870 Status = ImageContext->ImageRead (
871 ImageContext->Handle,
872 Section->PointerToRawData,
873 &Size,
874 Base
875 );
876 } else {
877 Status = ImageContext->ImageRead (
878 ImageContext->Handle,
879 Section->PointerToRawData + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize,
880 &Size,
881 Base
882 );
883 }
884
885 if (RETURN_ERROR (Status)) {
886 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
887 return Status;
888 }
889 }
890
891 //
892 // If raw size is less then virt size, zero fill the remaining
893 //
894
895 if (Size < Section->Misc.VirtualSize) {
896 ZeroMem (Base + Size, Section->Misc.VirtualSize - Size);
897 }
898
899 //
900 // Next Section
901 //
902 Section += 1;
903 }
904
905 //
906 // Get image's entry point
907 //
908 if (!(ImageContext->IsTeImage)) {
909 ImageContext->EntryPoint = (PHYSICAL_ADDRESS) (UINTN) PeCoffLoaderImageAddress (
910 ImageContext,
911 PeHdr->OptionalHeader.AddressOfEntryPoint
912 );
913 } else {
914 ImageContext->EntryPoint = (PHYSICAL_ADDRESS) (
915 (UINTN)ImageContext->ImageAddress +
916 (UINTN)TeHdr->AddressOfEntryPoint +
917 (UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
918 (UINTN) TeHdr->StrippedSize
919 );
920 }
921
922 //
923 // Determine the size of the fixup data
924 //
925 // Per the PE/COFF spec, you can't assume that a given data directory
926 // is present in the image. You have to check the NumberOfRvaAndSizes in
927 // the optional header to verify a desired directory entry is there.
928 //
929 if (!(ImageContext->IsTeImage)) {
930 if (PeHdr->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
931 DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)
932 &PeHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
933 ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
934 } else {
935 ImageContext->FixupDataSize = 0;
936 }
937 } else {
938 DirectoryEntry = &TeHdr->DataDirectory[0];
939 ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
940 }
941 //
942 // Consumer must allocate a buffer for the relocation fixup log.
943 // Only used for runtime drivers.
944 //
945 ImageContext->FixupData = NULL;
946
947 //
948 // Load the Codeview info if present
949 //
950 if (ImageContext->DebugDirectoryEntryRva != 0) {
951 if (!(ImageContext->IsTeImage)) {
952 DebugEntry = PeCoffLoaderImageAddress (
953 ImageContext,
954 ImageContext->DebugDirectoryEntryRva
955 );
956 } else {
957 DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)(UINTN)(
958 ImageContext->ImageAddress +
959 ImageContext->DebugDirectoryEntryRva +
960 sizeof(EFI_TE_IMAGE_HEADER) -
961 TeHdr->StrippedSize
962 );
963 }
964
965 if (DebugEntry != NULL) {
966 TempDebugEntryRva = DebugEntry->RVA;
967 if (DebugEntry->RVA == 0 && DebugEntry->FileOffset != 0) {
968 Section--;
969 if ((UINTN) Section->SizeOfRawData < Section->Misc.VirtualSize) {
970 TempDebugEntryRva = Section->VirtualAddress + Section->Misc.VirtualSize;
971 } else {
972 TempDebugEntryRva = Section->VirtualAddress + Section->SizeOfRawData;
973 }
974 }
975
976 if (TempDebugEntryRva != 0) {
977 if (!(ImageContext->IsTeImage)) {
978 ImageContext->CodeView = PeCoffLoaderImageAddress (ImageContext, TempDebugEntryRva);
979 } else {
980 ImageContext->CodeView = (VOID *)(
981 (UINTN)ImageContext->ImageAddress +
982 (UINTN)TempDebugEntryRva +
983 (UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
984 (UINTN) TeHdr->StrippedSize
985 );
986 }
987
988 if (ImageContext->CodeView == NULL) {
989 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
990 return RETURN_LOAD_ERROR;
991 }
992
993 if (DebugEntry->RVA == 0) {
994 Size = DebugEntry->SizeOfData;
995 if (!(ImageContext->IsTeImage)) {
996 Status = ImageContext->ImageRead (
997 ImageContext->Handle,
998 DebugEntry->FileOffset,
999 &Size,
1000 ImageContext->CodeView
1001 );
1002 } else {
1003 Status = ImageContext->ImageRead (
1004 ImageContext->Handle,
1005 DebugEntry->FileOffset + sizeof (EFI_TE_IMAGE_HEADER) - TeHdr->StrippedSize,
1006 &Size,
1007 ImageContext->CodeView
1008 );
1009 //
1010 // Should we apply fix up to this field according to the size difference between PE and TE?
1011 // Because now we maintain TE header fields unfixed, this field will also remain as they are
1012 // in original PE image.
1013 //
1014 }
1015
1016 if (RETURN_ERROR (Status)) {
1017 ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
1018 return RETURN_LOAD_ERROR;
1019 }
1020
1021 DebugEntry->RVA = TempDebugEntryRva;
1022 }
1023
1024 switch (*(UINT32 *) ImageContext->CodeView) {
1025 case CODEVIEW_SIGNATURE_NB10:
1026 ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY);
1027 break;
1028
1029 case CODEVIEW_SIGNATURE_RSDS:
1030 ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY);
1031 break;
1032
1033 default:
1034 break;
1035 }
1036 }
1037 }
1038 }
1039
1040 return Status;
1041 }