audk/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf32Lib.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;
}