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