mirror of https://github.com/acidanthera/audk.git
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);
|
|
}
|