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