summaryrefslogtreecommitdiffstats
path: root/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c')
-rw-r--r--UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c b/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c
new file mode 100644
index 0000000000..e364807007
--- /dev/null
+++ b/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c
@@ -0,0 +1,460 @@
+/** @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.
+**/
+Elf64_Shdr *
+GetElf64SectionByIndex (
+ IN UINT8 *ImageBase,
+ IN UINT32 Index
+ )
+{
+ Elf64_Ehdr *Ehdr;
+
+ Ehdr = (Elf64_Ehdr *)ImageBase;
+ if (Index >= Ehdr->e_shnum) {
+ return NULL;
+ }
+
+ return (Elf64_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.
+**/
+Elf64_Phdr *
+GetElf64SegmentByIndex (
+ IN UINT8 *ImageBase,
+ IN UINT32 Index
+ )
+{
+ Elf64_Ehdr *Ehdr;
+
+ Ehdr = (Elf64_Ehdr *)ImageBase;
+ if (Index >= Ehdr->e_phnum) {
+ return NULL;
+ }
+
+ return (Elf64_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.
+**/
+Elf64_Shdr *
+GetElf64SectionByRange (
+ IN UINT8 *ImageBase,
+ IN UINT64 Offset,
+ IN UINT64 Size
+ )
+{
+ UINT32 Index;
+ Elf64_Ehdr *Ehdr;
+ Elf64_Shdr *Shdr;
+
+ Ehdr = (Elf64_Ehdr *)ImageBase;
+
+ Shdr = (Elf64_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 (Elf64_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
+ProcessRelocation64 (
+ IN Elf64_Rela *Rela,
+ IN UINT64 RelaSize,
+ IN UINT64 RelaEntrySize,
+ IN UINT64 RelaType,
+ IN INTN Delta,
+ IN BOOLEAN DynamicLinking
+ )
+{
+ UINTN Index;
+ UINT64 *Ptr;
+ UINT32 Type;
+
+ for ( Index = 0
+ ; MultU64x64 (RelaEntrySize, Index) < RelaSize
+ ; Index++, Rela = ELF_NEXT_ENTRY (Elf64_Rela, Rela, RelaEntrySize)
+ ) {
+ //
+ // r_offset is the virtual address of the storage unit affected by the relocation.
+ //
+ Ptr = (UINT64 *)(UINTN)(Rela->r_offset + Delta);
+ Type = ELF64_R_TYPE(Rela->r_info);
+ switch (Type) {
+ case R_X86_64_NONE:
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ break;
+
+ case R_X86_64_64:
+ if (DynamicLinking) {
+ //
+ // Dynamic section doesn't contain entries of this type.
+ //
+ DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));
+ ASSERT (FALSE);
+ } else {
+ *Ptr += Delta;
+ }
+ break;
+
+ case R_X86_64_32:
+ //
+ // Dynamic section doesn't contain entries of this type.
+ //
+ DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));
+ ASSERT (FALSE);
+ break;
+
+ case R_X86_64_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) {
+ ASSERT (*Ptr == 0);
+ *Ptr = Delta + Rela->r_addend;
+ } else {
+ //
+ // A is stored in the field of relocation for REL type.
+ //
+ *Ptr = 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
+RelocateElf64Dynamic (
+ IN ELF_IMAGE_CONTEXT *ElfCt
+ )
+{
+ UINT32 Index;
+ Elf64_Phdr *Phdr;
+ Elf64_Shdr *DynShdr;
+ Elf64_Shdr *RelShdr;
+ Elf64_Dyn *Dyn;
+ UINT64 RelaOffset;
+ UINT64 RelaCount;
+ UINT64 RelaSize;
+ UINT64 RelaEntrySize;
+ UINT64 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 = GetElf64SegmentByIndex (ElfCt->FileBase, Index);
+ ASSERT (Phdr != NULL);
+ if (Phdr->p_type == PT_DYNAMIC) {
+ //
+ // Verify the existence of the dynamic section.
+ //
+ DynShdr = GetElf64SectionByRange (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.
+ //
+ RelaOffset = MAX_UINT64;
+ RelaSize = 0;
+ RelaCount = 0;
+ RelaEntrySize = 0;
+ RelaType = 0;
+ for ( Index = 0, Dyn = (Elf64_Dyn *) (ElfCt->FileBase + DynShdr->sh_offset)
+ ; Index < DivU64x64Remainder (DynShdr->sh_size, DynShdr->sh_entsize, NULL)
+ ; Index++, Dyn = ELF_NEXT_ENTRY (Elf64_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.
+ //
+ RelaOffset = Dyn->d_un.d_ptr - (UINTN) ElfCt->PreferredImageAddress;
+ 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 (RelaOffset == MAX_UINT64) {
+ 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 = GetElf64SectionByRange (ElfCt->FileBase, RelaOffset, RelaSize);
+ ASSERT (RelShdr != NULL);
+ if (RelShdr == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+ ASSERT (RelShdr->sh_type == RelaType);
+ ASSERT (RelShdr->sh_entsize == RelaEntrySize);
+
+ //
+ // 3. Process the relocation section.
+ //
+ ProcessRelocation64 (
+ (Elf64_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
+RelocateElf64Sections (
+ IN ELF_IMAGE_CONTEXT *ElfCt
+ )
+{
+ EFI_STATUS Status;
+ Elf64_Ehdr *Ehdr;
+ Elf64_Shdr *RelShdr;
+ Elf64_Shdr *Shdr;
+ UINT32 Index;
+ UINTN Delta;
+
+ Ehdr = (Elf64_Ehdr *)ElfCt->FileBase;
+ if (Ehdr->e_machine != EM_X86_64) {
+ 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 = RelocateElf64Dynamic (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 = (Elf64_Shdr *) (ElfCt->FileBase + Ehdr->e_shoff)
+ ; Index < Ehdr->e_shnum
+ ; Index++, RelShdr = ELF_NEXT_ENTRY (Elf64_Shdr, RelShdr, Ehdr->e_shentsize)
+ ) {
+ if ((RelShdr->sh_type != SHT_REL) && (RelShdr->sh_type != SHT_RELA)) {
+ continue;
+ }
+ Shdr = GetElf64SectionByIndex (ElfCt->FileBase, RelShdr->sh_info);
+ if ((Shdr->sh_flags & SHF_ALLOC) == SHF_ALLOC) {
+ //
+ // Only fix up sections that occupy memory during process execution.
+ //
+ ProcessRelocation64 (
+ (Elf64_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 64-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
+LoadElf64Image (
+ IN ELF_IMAGE_CONTEXT *ElfCt
+ )
+{
+ Elf64_Ehdr *Ehdr;
+ Elf64_Phdr *Phdr;
+ UINT16 Index;
+ UINTN Delta;
+
+ ASSERT (ElfCt != NULL);
+
+ //
+ // Per the sprit of ELF, loading to memory only consumes info from program headers.
+ //
+ Ehdr = (Elf64_Ehdr *)ElfCt->FileBase;
+
+ for ( Index = 0, Phdr = (Elf64_Phdr *)(ElfCt->FileBase + Ehdr->e_phoff)
+ ; Index < Ehdr->e_phnum
+ ; Index++, Phdr = ELF_NEXT_ENTRY (Elf64_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 = (UINTN) Phdr->p_paddr - (UINTN) ElfCt->PreferredImageAddress;
+ CopyMem (ElfCt->ImageAddress + Delta, ElfCt->FileBase + (UINTN) Phdr->p_offset, (UINTN) Phdr->p_filesz);
+ ZeroMem (ElfCt->ImageAddress + Delta + (UINTN) Phdr->p_filesz, (UINTN) (Phdr->p_memsz - Phdr->p_filesz));
+ }
+
+ //
+ // Relocate when new new image base is not the preferred image base.
+ //
+ if (ElfCt->ImageAddress != ElfCt->PreferredImageAddress) {
+ RelocateElf64Sections (ElfCt);
+ }
+
+ return EFI_SUCCESS;
+}