mirror of https://github.com/acidanthera/audk.git
478 lines
14 KiB
C
478 lines
14 KiB
C
/** @file
|
|
ELF library
|
|
|
|
Copyright (c) 2019 - 2021, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "ElfLibInternal.h"
|
|
|
|
/**
|
|
Return the section header specified by Index.
|
|
|
|
@param ImageBase The image base.
|
|
@param Index The section index.
|
|
|
|
@return Pointer to the section header.
|
|
**/
|
|
Elf32_Shdr *
|
|
GetElf32SectionByIndex (
|
|
IN UINT8 *ImageBase,
|
|
IN UINT32 Index
|
|
)
|
|
{
|
|
Elf32_Ehdr *Ehdr;
|
|
|
|
Ehdr = (Elf32_Ehdr *)ImageBase;
|
|
if (Index >= Ehdr->e_shnum) {
|
|
return NULL;
|
|
}
|
|
|
|
return (Elf32_Shdr *)(ImageBase + Ehdr->e_shoff + Index * Ehdr->e_shentsize);
|
|
}
|
|
|
|
/**
|
|
Return the segment header specified by Index.
|
|
|
|
@param ImageBase The image base.
|
|
@param Index The segment index.
|
|
|
|
@return Pointer to the segment header.
|
|
**/
|
|
Elf32_Phdr *
|
|
GetElf32SegmentByIndex (
|
|
IN UINT8 *ImageBase,
|
|
IN UINT32 Index
|
|
)
|
|
{
|
|
Elf32_Ehdr *Ehdr;
|
|
|
|
Ehdr = (Elf32_Ehdr *)ImageBase;
|
|
if (Index >= Ehdr->e_phnum) {
|
|
return NULL;
|
|
}
|
|
|
|
return (Elf32_Phdr *)(ImageBase + Ehdr->e_phoff + Index * Ehdr->e_phentsize);
|
|
}
|
|
|
|
/**
|
|
Return the section header specified by the range.
|
|
|
|
@param ImageBase The image base.
|
|
@param Offset The section offset.
|
|
@param Size The section size.
|
|
|
|
@return Pointer to the section header.
|
|
**/
|
|
Elf32_Shdr *
|
|
GetElf32SectionByRange (
|
|
IN UINT8 *ImageBase,
|
|
IN UINT32 Offset,
|
|
IN UINT32 Size
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
Elf32_Ehdr *Ehdr;
|
|
Elf32_Shdr *Shdr;
|
|
|
|
Ehdr = (Elf32_Ehdr *)ImageBase;
|
|
|
|
Shdr = (Elf32_Shdr *)(ImageBase + Ehdr->e_shoff);
|
|
for (Index = 0; Index < Ehdr->e_shnum; Index++) {
|
|
if ((Shdr->sh_offset == Offset) && (Shdr->sh_size == Size)) {
|
|
return Shdr;
|
|
}
|
|
|
|
Shdr = ELF_NEXT_ENTRY (Elf32_Shdr, Shdr, Ehdr->e_shentsize);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Fix up the image based on the relocation entries.
|
|
|
|
@param Rela Relocation entries.
|
|
@param RelaSize Total size of relocation entries.
|
|
@param RelaEntrySize Relocation entry size.
|
|
@param RelaType Type of relocation entry.
|
|
@param Delta The delta between preferred image base and the actual image base.
|
|
@param DynamicLinking TRUE when fixing up according to dynamic relocation.
|
|
|
|
@retval EFI_SUCCESS The image fix up is processed successfully.
|
|
**/
|
|
EFI_STATUS
|
|
ProcessRelocation32 (
|
|
IN Elf32_Rela *Rela,
|
|
IN UINT32 RelaSize,
|
|
IN UINT32 RelaEntrySize,
|
|
IN UINT32 RelaType,
|
|
IN INTN Delta,
|
|
IN BOOLEAN DynamicLinking
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT32 *Ptr;
|
|
UINT32 Type;
|
|
|
|
for ( Index = 0
|
|
; RelaEntrySize * Index < RelaSize
|
|
; Index++, Rela = ELF_NEXT_ENTRY (Elf32_Rela, Rela, RelaEntrySize)
|
|
)
|
|
{
|
|
//
|
|
// r_offset is the virtual address of the storage unit affected by the relocation.
|
|
//
|
|
Ptr = (UINT32 *)(UINTN)(Rela->r_offset + Delta);
|
|
Type = ELF32_R_TYPE (Rela->r_info);
|
|
switch (Type) {
|
|
case R_386_NONE:
|
|
case R_386_PC32:
|
|
//
|
|
// No fixup entry required.
|
|
//
|
|
break;
|
|
|
|
case R_386_32:
|
|
if (DynamicLinking) {
|
|
//
|
|
// Dynamic section doesn't contain entries of this type.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));
|
|
ASSERT (FALSE);
|
|
} else {
|
|
*Ptr += (UINT32)Delta;
|
|
}
|
|
|
|
break;
|
|
|
|
case R_386_RELATIVE:
|
|
if (DynamicLinking) {
|
|
//
|
|
// A: Represents the addend used to compute the value of the relocatable field.
|
|
// B: Represents the base address at which a shared object has been loaded into memory during execution.
|
|
// Generally, a shared object is built with a 0 base virtual address, but the execution address will be different.
|
|
//
|
|
// B (Base Address) in ELF spec is slightly different:
|
|
// An executable or shared object file's base address (on platforms that support the concept) is calculated during
|
|
// execution from three values: the virtual memory load address, the maximum page size, and the lowest virtual address
|
|
// of a program's loadable segment. To compute the base address, one determines the memory address associated with the
|
|
// lowest p_vaddr value for a PT_LOAD segment. This address is truncated to the nearest multiple of the maximum page size.
|
|
// The corresponding p_vaddr value itself is also truncated to the nearest multiple of the maximum page size.
|
|
//
|
|
// *** The base address is the difference between the truncated memory address and the truncated p_vaddr value. ***
|
|
//
|
|
// Delta in this function is B.
|
|
//
|
|
// Calculation: B + A
|
|
//
|
|
if (RelaType == SHT_RELA) {
|
|
*Ptr = (UINT32)Delta + Rela->r_addend;
|
|
} else {
|
|
//
|
|
// A is stored in the field of relocation for REL type.
|
|
//
|
|
*Ptr = (UINT32)Delta + *Ptr;
|
|
}
|
|
} else {
|
|
//
|
|
// non-Dynamic section doesn't contain entries of this type.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));
|
|
ASSERT (FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Relocate the DYN type image.
|
|
|
|
@param ElfCt Point to image context.
|
|
|
|
@retval EFI_SUCCESS The relocation succeeds.
|
|
@retval EFI_UNSUPPORTED The image doesn't contain a dynamic section.
|
|
**/
|
|
EFI_STATUS
|
|
RelocateElf32Dynamic (
|
|
IN ELF_IMAGE_CONTEXT *ElfCt
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
Elf32_Phdr *Phdr;
|
|
Elf32_Shdr *DynShdr;
|
|
Elf32_Shdr *RelShdr;
|
|
Elf32_Dyn *Dyn;
|
|
UINT32 RelaAddress;
|
|
UINT32 RelaCount;
|
|
UINT32 RelaSize;
|
|
UINT32 RelaEntrySize;
|
|
UINT32 RelaType;
|
|
|
|
//
|
|
// 1. Locate the dynamic section.
|
|
//
|
|
// If an object file participates in dynamic linking, its program header table
|
|
// will have an element of type PT_DYNAMIC.
|
|
// This ``segment'' contains the .dynamic section. A special symbol, _DYNAMIC,
|
|
// labels the section, which contains an array of Elf32_Dyn or Elf64_Dyn.
|
|
//
|
|
DynShdr = NULL;
|
|
for (Index = 0; Index < ElfCt->PhNum; Index++) {
|
|
Phdr = GetElf32SegmentByIndex (ElfCt->FileBase, Index);
|
|
ASSERT (Phdr != NULL);
|
|
if (Phdr->p_type == PT_DYNAMIC) {
|
|
//
|
|
// Verify the existence of the dynamic section.
|
|
//
|
|
DynShdr = GetElf32SectionByRange (ElfCt->FileBase, Phdr->p_offset, Phdr->p_filesz);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// It's abnormal a DYN ELF doesn't contain a dynamic section.
|
|
//
|
|
ASSERT (DynShdr != NULL);
|
|
if (DynShdr == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ASSERT (DynShdr->sh_type == SHT_DYNAMIC);
|
|
ASSERT (DynShdr->sh_entsize >= sizeof (*Dyn));
|
|
|
|
//
|
|
// 2. Locate the relocation section from the dynamic section.
|
|
//
|
|
RelaAddress = MAX_UINT32;
|
|
RelaSize = 0;
|
|
RelaCount = 0;
|
|
RelaEntrySize = 0;
|
|
RelaType = 0;
|
|
for ( Index = 0, Dyn = (Elf32_Dyn *)(ElfCt->FileBase + DynShdr->sh_offset)
|
|
; Index < DynShdr->sh_size / DynShdr->sh_entsize
|
|
; Index++, Dyn = ELF_NEXT_ENTRY (Elf32_Dyn, Dyn, DynShdr->sh_entsize)
|
|
)
|
|
{
|
|
switch (Dyn->d_tag) {
|
|
case DT_RELA:
|
|
case DT_REL:
|
|
//
|
|
// DT_REL represent program virtual addresses.
|
|
// A file's virtual addresses might not match the memory virtual addresses during execution.
|
|
// When interpreting addresses contained in the dynamic structure, the dynamic linker computes actual addresses,
|
|
// based on the original file value and the memory base address.
|
|
// For consistency, files do not contain relocation entries to ``correct'' addresses in the dynamic structure.
|
|
//
|
|
RelaAddress = Dyn->d_un.d_ptr;
|
|
RelaType = (Dyn->d_tag == DT_RELA) ? SHT_RELA : SHT_REL;
|
|
break;
|
|
case DT_RELACOUNT:
|
|
case DT_RELCOUNT:
|
|
RelaCount = Dyn->d_un.d_val;
|
|
break;
|
|
case DT_RELENT:
|
|
case DT_RELAENT:
|
|
RelaEntrySize = Dyn->d_un.d_val;
|
|
break;
|
|
case DT_RELSZ:
|
|
case DT_RELASZ:
|
|
RelaSize = Dyn->d_un.d_val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RelaAddress == MAX_UINT32) {
|
|
ASSERT (RelaCount == 0);
|
|
ASSERT (RelaEntrySize == 0);
|
|
ASSERT (RelaSize == 0);
|
|
//
|
|
// It's fine that a DYN ELF doesn't contain relocation section.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Verify the existence of the relocation section.
|
|
//
|
|
RelShdr = NULL;
|
|
for (Index = 0; Index < ElfCt->ShNum; Index++) {
|
|
RelShdr = GetElf32SectionByIndex (ElfCt->FileBase, Index);
|
|
ASSERT (RelShdr != NULL);
|
|
if ((RelShdr->sh_addr == RelaAddress) && (RelShdr->sh_size == RelaSize)) {
|
|
break;
|
|
}
|
|
|
|
RelShdr = NULL;
|
|
}
|
|
|
|
if (RelShdr == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ASSERT (RelShdr->sh_type == RelaType);
|
|
ASSERT (RelShdr->sh_entsize == RelaEntrySize);
|
|
|
|
//
|
|
// 3. Process the relocation section.
|
|
//
|
|
ProcessRelocation32 (
|
|
(Elf32_Rela *)(ElfCt->FileBase + RelShdr->sh_offset),
|
|
RelShdr->sh_size,
|
|
RelShdr->sh_entsize,
|
|
RelShdr->sh_type,
|
|
(UINTN)ElfCt->ImageAddress - (UINTN)ElfCt->PreferredImageAddress,
|
|
TRUE
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Relocate all sections in a ELF image.
|
|
|
|
@param[in] ElfCt ELF image context pointer.
|
|
|
|
@retval EFI_UNSUPPORTED Relocation is not supported.
|
|
@retval EFI_SUCCESS ELF image was relocated successfully.
|
|
**/
|
|
EFI_STATUS
|
|
RelocateElf32Sections (
|
|
IN ELF_IMAGE_CONTEXT *ElfCt
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
Elf32_Ehdr *Ehdr;
|
|
Elf32_Shdr *RelShdr;
|
|
Elf32_Shdr *Shdr;
|
|
UINT32 Index;
|
|
UINTN Delta;
|
|
|
|
Ehdr = (Elf32_Ehdr *)ElfCt->FileBase;
|
|
if (Ehdr->e_machine != EM_386) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Delta = (UINTN)ElfCt->ImageAddress - (UINTN)ElfCt->PreferredImageAddress;
|
|
ElfCt->EntryPoint = (UINTN)(Ehdr->e_entry + Delta);
|
|
|
|
//
|
|
// 1. Relocate dynamic ELF using the relocation section pointed by dynamic section
|
|
//
|
|
if (Ehdr->e_type == ET_DYN) {
|
|
DEBUG ((DEBUG_INFO, "DYN ELF: Relocate using dynamic sections...\n"));
|
|
Status = RelocateElf32Dynamic (ElfCt);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 2. Executable ELF: Fix up the delta between actual image address and preferred image address.
|
|
//
|
|
// Linker already fixed up EXEC ELF based on the preferred image address.
|
|
// A ELF loader in modern OS only loads it into the preferred image address.
|
|
// The below relocation is unneeded in that case.
|
|
// But the ELF loader in firmware supports to load the image to a different address.
|
|
// The below relocation is needed in this case.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "EXEC ELF: Fix actual/preferred base address delta ...\n"));
|
|
for ( Index = 0, RelShdr = (Elf32_Shdr *)(ElfCt->FileBase + Ehdr->e_shoff)
|
|
; Index < Ehdr->e_shnum
|
|
; Index++, RelShdr = ELF_NEXT_ENTRY (Elf32_Shdr, RelShdr, Ehdr->e_shentsize)
|
|
)
|
|
{
|
|
if ((RelShdr->sh_type != SHT_REL) && (RelShdr->sh_type != SHT_RELA)) {
|
|
continue;
|
|
}
|
|
|
|
Shdr = GetElf32SectionByIndex (ElfCt->FileBase, RelShdr->sh_info);
|
|
if ((Shdr->sh_flags & SHF_ALLOC) == SHF_ALLOC) {
|
|
//
|
|
// Only fix up sections that occupy memory during process execution.
|
|
//
|
|
ProcessRelocation32 (
|
|
(Elf32_Rela *)((UINT8 *)Ehdr + RelShdr->sh_offset),
|
|
RelShdr->sh_size,
|
|
RelShdr->sh_entsize,
|
|
RelShdr->sh_type,
|
|
Delta,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Load ELF image which has 32-bit architecture.
|
|
|
|
Caller should set Context.ImageAddress to a proper value, either pointing to
|
|
a new allocated memory whose size equal to Context.ImageSize, or pointing
|
|
to Context.PreferredImageAddress.
|
|
|
|
@param[in] ElfCt ELF image context pointer.
|
|
|
|
@retval EFI_SUCCESS ELF binary is loaded successfully.
|
|
@retval Others Loading ELF binary fails.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
LoadElf32Image (
|
|
IN ELF_IMAGE_CONTEXT *ElfCt
|
|
)
|
|
{
|
|
Elf32_Ehdr *Ehdr;
|
|
Elf32_Phdr *Phdr;
|
|
UINT16 Index;
|
|
UINTN Delta;
|
|
|
|
ASSERT (ElfCt != NULL);
|
|
|
|
//
|
|
// Per the sprit of ELF, loading to memory only consumes info from program headers.
|
|
//
|
|
Ehdr = (Elf32_Ehdr *)ElfCt->FileBase;
|
|
|
|
for ( Index = 0, Phdr = (Elf32_Phdr *)(ElfCt->FileBase + Ehdr->e_phoff)
|
|
; Index < Ehdr->e_phnum
|
|
; Index++, Phdr = ELF_NEXT_ENTRY (Elf32_Phdr, Phdr, Ehdr->e_phentsize)
|
|
)
|
|
{
|
|
//
|
|
// Skip segments that don't require load (type tells, or size is 0)
|
|
//
|
|
if ((Phdr->p_type != PT_LOAD) ||
|
|
(Phdr->p_memsz == 0))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The memory offset of segment relative to the image base
|
|
// Note: CopyMem() does nothing when the dst equals to src.
|
|
//
|
|
Delta = Phdr->p_paddr - (UINT32)(UINTN)ElfCt->PreferredImageAddress;
|
|
CopyMem (ElfCt->ImageAddress + Delta, ElfCt->FileBase + Phdr->p_offset, Phdr->p_filesz);
|
|
ZeroMem (ElfCt->ImageAddress + Delta + Phdr->p_filesz, Phdr->p_memsz - Phdr->p_filesz);
|
|
}
|
|
|
|
//
|
|
// Relocate when new new image base is not the preferred image base.
|
|
//
|
|
if (ElfCt->ImageAddress != ElfCt->PreferredImageAddress) {
|
|
RelocateElf32Sections (ElfCt);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|