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