git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			217 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*++
 | |
| 
 | |
| Copyright (c) 2006, Intel Corporation                                                         
 | |
| All rights reserved. This program and the accompanying materials                          
 | |
| are licensed and made available under the terms and conditions of the BSD License         
 | |
| which accompanies this distribution.  The full text of the license may be found at        
 | |
| http://opensource.org/licenses/bsd-license.php                                            
 | |
|                                                                                           
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|   PeHotRelocate.c
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
| 
 | |
| --*/
 | |
| 
 | |
| #include "Runtime.h"
 | |
| 
 | |
| STATIC
 | |
| VOID *
 | |
| RuntimePeImageAddress (
 | |
|   IN   RUNTIME_IMAGE_RELOCATION_DATA  *Image,
 | |
|   IN   UINTN                          Address
 | |
|   )
 | |
| /*++
 | |
| 
 | |
| Routine Description:
 | |
| 
 | |
|   Converts an image address to the loaded address
 | |
| 
 | |
| Arguments:
 | |
| 
 | |
|   Image         - The relocation data of the image being loaded
 | |
| 
 | |
|   Address       - The address to be converted to the loaded address
 | |
| 
 | |
| Returns:
 | |
| 
 | |
|   NULL if the address can not be converted, otherwise, the converted address
 | |
| 
 | |
| --*/
 | |
| {
 | |
|   if (Address >= (Image->ImageSize) << EFI_PAGE_SHIFT) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   return (CHAR8 *) ((UINTN) Image->ImageBase + Address);
 | |
| }
 | |
| 
 | |
| VOID
 | |
| RelocatePeImageForRuntime (
 | |
|   RUNTIME_IMAGE_RELOCATION_DATA *Image
 | |
|   )
 | |
| {
 | |
|   CHAR8                     *OldBase;
 | |
|   CHAR8                     *NewBase;
 | |
|   EFI_IMAGE_DOS_HEADER      *DosHdr;
 | |
|   EFI_IMAGE_NT_HEADERS      *PeHdr;
 | |
|   UINT32                    NumberOfRvaAndSizes;
 | |
|   EFI_IMAGE_DATA_DIRECTORY  *DataDirectory;
 | |
|   EFI_IMAGE_DATA_DIRECTORY  *RelocDir;
 | |
|   EFI_IMAGE_BASE_RELOCATION *RelocBase;
 | |
|   EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd;
 | |
|   UINT16                    *Reloc;
 | |
|   UINT16                    *RelocEnd;
 | |
|   CHAR8                     *Fixup;
 | |
|   CHAR8                     *FixupBase;
 | |
|   UINT16                    *F16;
 | |
|   UINT32                    *F32;
 | |
|   CHAR8                     *FixupData;
 | |
|   UINTN                     Adjust;
 | |
|   EFI_STATUS                Status;
 | |
| 
 | |
|   OldBase = (CHAR8 *) ((UINTN) Image->ImageBase);
 | |
|   NewBase = (CHAR8 *) ((UINTN) Image->ImageBase);
 | |
| 
 | |
|   Status  = RuntimeDriverConvertPointer (0, (VOID **) &NewBase);
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   Adjust = (UINTN) NewBase - (UINTN) OldBase;
 | |
| 
 | |
|   //
 | |
|   // Find the image's relocate dir info
 | |
|   //
 | |
|   DosHdr = (EFI_IMAGE_DOS_HEADER *) OldBase;
 | |
|   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
 | |
|     //
 | |
|     // Valid DOS header so get address of PE header
 | |
|     //
 | |
|     PeHdr = (EFI_IMAGE_NT_HEADERS *) (((CHAR8 *) DosHdr) + DosHdr->e_lfanew);
 | |
|   } else {
 | |
|     //
 | |
|     // No Dos header so assume image starts with PE header.
 | |
|     //
 | |
|     PeHdr = (EFI_IMAGE_NT_HEADERS *) OldBase;
 | |
|   }
 | |
| 
 | |
|   if (PeHdr->Signature != EFI_IMAGE_NT_SIGNATURE) {
 | |
|     //
 | |
|     // Not a valid PE image so Exit
 | |
|     //
 | |
|     return ;
 | |
|   }
 | |
|   //
 | |
|   // Get some data from the PE type dependent data
 | |
|   //
 | |
|   NumberOfRvaAndSizes = PeHdr->OptionalHeader.NumberOfRvaAndSizes;
 | |
|   DataDirectory       = &PeHdr->OptionalHeader.DataDirectory[0];
 | |
| 
 | |
|   //
 | |
|   // Find the relocation block
 | |
|   //
 | |
|   // Per the PE/COFF spec, you can't assume that a given data directory
 | |
|   // is present in the image. You have to check the NumberOfRvaAndSizes in
 | |
|   // the optional header to verify a desired directory entry is there.
 | |
|   //
 | |
|   if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
 | |
|     RelocDir      = DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC;
 | |
|     RelocBase     = RuntimePeImageAddress (Image, RelocDir->VirtualAddress);
 | |
|     RelocBaseEnd  = RuntimePeImageAddress (Image, RelocDir->VirtualAddress + RelocDir->Size);
 | |
|   } else {
 | |
|     //
 | |
|     // Cannot find relocations, cannot continue
 | |
|     //
 | |
|     ASSERT (FALSE);
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   ASSERT (RelocBase != NULL && RelocBaseEnd != NULL);
 | |
| 
 | |
|   //
 | |
|   // Run the whole relocation block. And re-fixup data that has not been
 | |
|   // modified. The FixupData is used to see if the image has been modified
 | |
|   // since it was relocated. This is so data sections that have been updated
 | |
|   // by code will not be fixed up, since that would set them back to
 | |
|   // defaults.
 | |
|   //
 | |
|   FixupData = Image->RelocationData;
 | |
|   while (RelocBase < RelocBaseEnd) {
 | |
| 
 | |
|     Reloc     = (UINT16 *) ((UINT8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
 | |
|     RelocEnd  = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock);
 | |
|     FixupBase = (CHAR8 *) ((UINTN) Image->ImageBase) + RelocBase->VirtualAddress;
 | |
| 
 | |
|     //
 | |
|     // Run this relocation record
 | |
|     //
 | |
|     while (Reloc < RelocEnd) {
 | |
| 
 | |
|       Fixup = FixupBase + (*Reloc & 0xFFF);
 | |
|       switch ((*Reloc) >> 12) {
 | |
| 
 | |
|       case EFI_IMAGE_REL_BASED_ABSOLUTE:
 | |
|         break;
 | |
| 
 | |
|       case EFI_IMAGE_REL_BASED_HIGH:
 | |
|         F16 = (UINT16 *) Fixup;
 | |
|         if (*(UINT16 *) FixupData == *F16) {
 | |
|           *F16 = (UINT16) ((*F16 << 16) + ((UINT16) Adjust & 0xffff));
 | |
|         }
 | |
| 
 | |
|         FixupData = FixupData + sizeof (UINT16);
 | |
|         break;
 | |
| 
 | |
|       case EFI_IMAGE_REL_BASED_LOW:
 | |
|         F16 = (UINT16 *) Fixup;
 | |
|         if (*(UINT16 *) FixupData == *F16) {
 | |
|           *F16 = (UINT16) (*F16 + ((UINT16) Adjust & 0xffff));
 | |
|         }
 | |
| 
 | |
|         FixupData = FixupData + sizeof (UINT16);
 | |
|         break;
 | |
| 
 | |
|       case EFI_IMAGE_REL_BASED_HIGHLOW:
 | |
|         F32       = (UINT32 *) Fixup;
 | |
|         FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
 | |
|         if (*(UINT32 *) FixupData == *F32) {
 | |
|           *F32 = *F32 + (UINT32) Adjust;
 | |
|         }
 | |
| 
 | |
|         FixupData = FixupData + sizeof (UINT32);
 | |
|         break;
 | |
| 
 | |
|       case EFI_IMAGE_REL_BASED_HIGHADJ:
 | |
|         //
 | |
|         // Not implemented, but not used in EFI 1.0
 | |
|         //
 | |
|         ASSERT (FALSE);
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         //
 | |
|         // Only Itanium requires ConvertPeImage_Ex
 | |
|         //
 | |
|         Status = PeHotRelocateImageEx (Reloc, Fixup, &FixupData, Adjust);
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           return ;
 | |
|         }
 | |
|       }
 | |
|       //
 | |
|       // Next relocation record
 | |
|       //
 | |
|       Reloc += 1;
 | |
|     }
 | |
|     //
 | |
|     // next reloc block
 | |
|     //
 | |
|     RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
 | |
|   }
 | |
| 
 | |
|   FlushCpuCache (Image->ImageBase, (UINT64) Image->ImageSize);
 | |
| }
 |