summaryrefslogtreecommitdiffstats
path: root/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c')
-rw-r--r--UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c473
1 files changed, 473 insertions, 0 deletions
diff --git a/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c b/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c
new file mode 100644
index 0000000000..531b3486d2
--- /dev/null
+++ b/UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfLib.c
@@ -0,0 +1,473 @@
+/** @file
+ ELF library
+
+ Copyright (c) 2019 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "ElfLibInternal.h"
+
+/**
+ Check if the ELF image is valid.
+
+ @param[in] ImageBase Memory address of an image.
+
+ @retval TRUE if valid.
+
+**/
+BOOLEAN
+IsElfFormat (
+ IN CONST UINT8 *ImageBase
+ )
+{
+ Elf32_Ehdr *Elf32Hdr;
+ Elf64_Ehdr *Elf64Hdr;
+
+ ASSERT (ImageBase != NULL);
+
+ Elf32Hdr = (Elf32_Ehdr *)ImageBase;
+
+ //
+ // Start with correct signature "\7fELF"
+ //
+ if ((Elf32Hdr->e_ident[EI_MAG0] != ELFMAG0) ||
+ (Elf32Hdr->e_ident[EI_MAG1] != ELFMAG1) ||
+ (Elf32Hdr->e_ident[EI_MAG1] != ELFMAG1) ||
+ (Elf32Hdr->e_ident[EI_MAG2] != ELFMAG2)
+ ) {
+ return FALSE;
+ }
+
+ //
+ // Support little-endian only
+ //
+ if (Elf32Hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+ return FALSE;
+ }
+
+ //
+ // Check 32/64-bit architecture
+ //
+ if (Elf32Hdr->e_ident[EI_CLASS] == ELFCLASS64) {
+ Elf64Hdr = (Elf64_Ehdr *)Elf32Hdr;
+ Elf32Hdr = NULL;
+ } else if (Elf32Hdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ Elf64Hdr = NULL;
+ } else {
+ return FALSE;
+ }
+
+ if (Elf64Hdr != NULL) {
+ //
+ // Support intel architecture only for now
+ //
+ if (Elf64Hdr->e_machine != EM_X86_64) {
+ return FALSE;
+ }
+
+ //
+ // Support ELF types: EXEC (Executable file), DYN (Shared object file)
+ //
+ if ((Elf64Hdr->e_type != ET_EXEC) && (Elf64Hdr->e_type != ET_DYN)) {
+ return FALSE;
+ }
+
+ //
+ // Support current ELF version only
+ //
+ if (Elf64Hdr->e_version != EV_CURRENT) {
+ return FALSE;
+ }
+ } else {
+ //
+ // Support intel architecture only for now
+ //
+ if (Elf32Hdr->e_machine != EM_386) {
+ return FALSE;
+ }
+
+ //
+ // Support ELF types: EXEC (Executable file), DYN (Shared object file)
+ //
+ if ((Elf32Hdr->e_type != ET_EXEC) && (Elf32Hdr->e_type != ET_DYN)) {
+ return FALSE;
+ }
+
+ //
+ // Support current ELF version only
+ //
+ if (Elf32Hdr->e_version != EV_CURRENT) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ Calculate a ELF file size.
+
+ @param[in] ElfCt ELF image context pointer.
+ @param[out] FileSize Return the file size.
+
+ @retval EFI_INVALID_PARAMETER ElfCt or SecPos is NULL.
+ @retval EFI_NOT_FOUND Could not find the section.
+ @retval EFI_SUCCESS Section posistion was filled successfully.
+**/
+EFI_STATUS
+CalculateElfFileSize (
+ IN ELF_IMAGE_CONTEXT *ElfCt,
+ OUT UINTN *FileSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN FileSize1;
+ UINTN FileSize2;
+ Elf32_Ehdr *Elf32Hdr;
+ Elf64_Ehdr *Elf64Hdr;
+ UINTN Offset;
+ UINTN Size;
+
+ if ((ElfCt == NULL) || (FileSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Use last section as end of file
+ Status = GetElfSectionPos (ElfCt, ElfCt->ShNum - 1, &Offset, &Size);
+ if (EFI_ERROR(Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ FileSize1 = Offset + Size;
+
+ // Use end of section header as end of file
+ FileSize2 = 0;
+ if (ElfCt->EiClass == ELFCLASS32) {
+ Elf32Hdr = (Elf32_Ehdr *)ElfCt->FileBase;
+ FileSize2 = Elf32Hdr->e_shoff + Elf32Hdr->e_shentsize * Elf32Hdr->e_shnum;
+ } else if (ElfCt->EiClass == ELFCLASS64) {
+ Elf64Hdr = (Elf64_Ehdr *)ElfCt->FileBase;
+ FileSize2 = (UINTN)(Elf64Hdr->e_shoff + Elf64Hdr->e_shentsize * Elf64Hdr->e_shnum);
+ }
+
+ *FileSize = MAX(FileSize1, FileSize2);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get a ELF program segment loading info.
+
+ @param[in] ImageBase Image base.
+ @param[in] EiClass ELF class.
+ @param[in] Index ELF segment index.
+ @param[out] SegInfo The pointer to the segment info.
+
+ @retval EFI_INVALID_PARAMETER ElfCt or SecPos is NULL.
+ @retval EFI_NOT_FOUND Could not find the section.
+ @retval EFI_SUCCESS Section posistion was filled successfully.
+**/
+EFI_STATUS
+GetElfSegmentInfo (
+ IN UINT8 *ImageBase,
+ IN UINT32 EiClass,
+ IN UINT32 Index,
+ OUT SEGMENT_INFO *SegInfo
+ )
+{
+ Elf32_Phdr *Elf32Phdr;
+ Elf64_Phdr *Elf64Phdr;
+
+ if ((ImageBase == NULL) || (SegInfo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EiClass == ELFCLASS32) {
+ Elf32Phdr = GetElf32SegmentByIndex (ImageBase, Index);
+ if (Elf32Phdr != NULL) {
+ SegInfo->PtType = Elf32Phdr->p_type;
+ SegInfo->Offset = Elf32Phdr->p_offset;
+ SegInfo->Length = Elf32Phdr->p_filesz;
+ SegInfo->MemLen = Elf32Phdr->p_memsz;
+ SegInfo->MemAddr = Elf32Phdr->p_paddr;
+ SegInfo->Alignment = Elf32Phdr->p_align;
+ return EFI_SUCCESS;
+ }
+ } else if (EiClass == ELFCLASS64) {
+ Elf64Phdr = GetElf64SegmentByIndex (ImageBase, Index);
+ if (Elf64Phdr != NULL) {
+ SegInfo->PtType = Elf64Phdr->p_type;
+ SegInfo->Offset = (UINTN)Elf64Phdr->p_offset;
+ SegInfo->Length = (UINTN)Elf64Phdr->p_filesz;
+ SegInfo->MemLen = (UINTN)Elf64Phdr->p_memsz;
+ SegInfo->MemAddr = (UINTN)Elf64Phdr->p_paddr;
+ SegInfo->Alignment = (UINTN)Elf64Phdr->p_align;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Parse the ELF image info.
+
+ On return, all fields in ElfCt are updated except ImageAddress.
+
+ @param[in] ImageBase Memory address of an image.
+ @param[out] ElfCt The EFL image context pointer.
+
+ @retval EFI_INVALID_PARAMETER Input parameters are not valid.
+ @retval EFI_UNSUPPORTED Unsupported binary type.
+ @retval EFI_LOAD_ERROR ELF binary loading error.
+ @retval EFI_SUCCESS ELF binary is loaded successfully.
+**/
+EFI_STATUS
+EFIAPI
+ParseElfImage (
+ IN VOID *ImageBase,
+ OUT ELF_IMAGE_CONTEXT *ElfCt
+ )
+{
+ Elf32_Ehdr *Elf32Hdr;
+ Elf64_Ehdr *Elf64Hdr;
+ Elf32_Shdr *Elf32Shdr;
+ Elf64_Shdr *Elf64Shdr;
+ EFI_STATUS Status;
+ UINT32 Index;
+ SEGMENT_INFO SegInfo;
+ UINTN End;
+ UINTN Base;
+
+ if (ElfCt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ZeroMem (ElfCt, sizeof(ELF_IMAGE_CONTEXT));
+
+ if (ImageBase == NULL) {
+ return (ElfCt->ParseStatus = EFI_INVALID_PARAMETER);
+ }
+
+ ElfCt->FileBase = (UINT8 *)ImageBase;
+ if (!IsElfFormat (ElfCt->FileBase)) {
+ return (ElfCt->ParseStatus = EFI_UNSUPPORTED);
+ }
+
+ Elf32Hdr = (Elf32_Ehdr *)ElfCt->FileBase;
+ ElfCt->EiClass = Elf32Hdr->e_ident[EI_CLASS];
+ if (ElfCt->EiClass == ELFCLASS32) {
+ if ((Elf32Hdr->e_type != ET_EXEC) && (Elf32Hdr->e_type != ET_DYN)) {
+ return (ElfCt->ParseStatus = EFI_UNSUPPORTED);
+ }
+ Elf32Shdr = (Elf32_Shdr *)GetElf32SectionByIndex (ElfCt->FileBase, Elf32Hdr->e_shstrndx);
+ if (Elf32Shdr == NULL) {
+ return (ElfCt->ParseStatus = EFI_UNSUPPORTED);
+ }
+ ElfCt->EntryPoint = (UINTN)Elf32Hdr->e_entry;
+ ElfCt->ShNum = Elf32Hdr->e_shnum;
+ ElfCt->PhNum = Elf32Hdr->e_phnum;
+ ElfCt->ShStrLen = Elf32Shdr->sh_size;
+ ElfCt->ShStrOff = Elf32Shdr->sh_offset;
+ } else {
+ Elf64Hdr = (Elf64_Ehdr *)Elf32Hdr;
+ if ((Elf64Hdr->e_type != ET_EXEC) && (Elf64Hdr->e_type != ET_DYN)) {
+ return (ElfCt->ParseStatus = EFI_UNSUPPORTED);
+ }
+ Elf64Shdr = (Elf64_Shdr *)GetElf64SectionByIndex (ElfCt->FileBase, Elf64Hdr->e_shstrndx);
+ if (Elf64Shdr == NULL) {
+ return (ElfCt->ParseStatus = EFI_UNSUPPORTED);
+ }
+ ElfCt->EntryPoint = (UINTN)Elf64Hdr->e_entry;
+ ElfCt->ShNum = Elf64Hdr->e_shnum;
+ ElfCt->PhNum = Elf64Hdr->e_phnum;
+ ElfCt->ShStrLen = (UINT32)Elf64Shdr->sh_size;
+ ElfCt->ShStrOff = (UINT32)Elf64Shdr->sh_offset;
+ }
+
+ //
+ // Get the preferred image base and required memory size when loaded to new location.
+ //
+ End = 0;
+ Base = MAX_UINT32;
+ ElfCt->ReloadRequired = FALSE;
+ for (Index = 0; Index < ElfCt->PhNum; Index++) {
+ Status = GetElfSegmentInfo (ElfCt->FileBase, ElfCt->EiClass, Index, &SegInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ if (SegInfo.PtType != PT_LOAD) {
+ continue;
+ }
+
+ if (SegInfo.MemLen != SegInfo.Length) {
+ //
+ // Not enough space to execute at current location.
+ //
+ ElfCt->ReloadRequired = TRUE;
+ }
+
+ if (Base > (SegInfo.MemAddr & ~(EFI_PAGE_SIZE - 1))) {
+ Base = SegInfo.MemAddr & ~(EFI_PAGE_SIZE - 1);
+ }
+ if (End < ALIGN_VALUE (SegInfo.MemAddr + SegInfo.MemLen, EFI_PAGE_SIZE) - 1) {
+ End = ALIGN_VALUE (SegInfo.MemAddr + SegInfo.MemLen, EFI_PAGE_SIZE) - 1;
+ }
+ }
+ //
+ // 0 - MAX_UINT32 + 1 equals to 0.
+ //
+ ElfCt->ImageSize = End - Base + 1;
+ ElfCt->PreferredImageAddress = (VOID *) Base;
+
+ CalculateElfFileSize (ElfCt, &ElfCt->FileSize);
+ return (ElfCt->ParseStatus = EFI_SUCCESS);;
+}
+
+/**
+ Load the ELF image to Context.ImageAddress.
+
+ Context should be initialized by ParseElfImage().
+ 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_INVALID_PARAMETER Input parameters are not valid.
+ @retval EFI_UNSUPPORTED Unsupported binary type.
+ @retval EFI_LOAD_ERROR ELF binary loading error.
+ @retval EFI_SUCCESS ELF binary is loaded successfully.
+**/
+EFI_STATUS
+EFIAPI
+LoadElfImage (
+ IN ELF_IMAGE_CONTEXT *ElfCt
+ )
+{
+ EFI_STATUS Status;
+
+ if (ElfCt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_ERROR (ElfCt->ParseStatus)) {
+ return ElfCt->ParseStatus;
+ }
+
+ if (ElfCt->ImageAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_UNSUPPORTED;
+ if (ElfCt->EiClass == ELFCLASS32) {
+ Status = LoadElf32Image (ElfCt);
+ } else if (ElfCt->EiClass == ELFCLASS64) {
+ Status = LoadElf64Image (ElfCt);
+ }
+
+ return Status;
+}
+
+
+/**
+ Get a ELF section name from its index.
+
+ @param[in] ElfCt ELF image context pointer.
+ @param[in] SectionIndex ELF section index.
+ @param[out] SectionName The pointer to the section name.
+
+ @retval EFI_INVALID_PARAMETER ElfCt or SecName is NULL.
+ @retval EFI_NOT_FOUND Could not find the section.
+ @retval EFI_SUCCESS Section name was filled successfully.
+**/
+EFI_STATUS
+EFIAPI
+GetElfSectionName (
+ IN ELF_IMAGE_CONTEXT *ElfCt,
+ IN UINT32 SectionIndex,
+ OUT CHAR8 **SectionName
+ )
+{
+ Elf32_Shdr *Elf32Shdr;
+ Elf64_Shdr *Elf64Shdr;
+ CHAR8 *Name;
+
+ if ((ElfCt == NULL) || (SectionName == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_ERROR (ElfCt->ParseStatus)) {
+ return ElfCt->ParseStatus;
+ }
+
+ Name = NULL;
+ if (ElfCt->EiClass == ELFCLASS32) {
+ Elf32Shdr = GetElf32SectionByIndex (ElfCt->FileBase, SectionIndex);
+ if ((Elf32Shdr != NULL) && (Elf32Shdr->sh_name < ElfCt->ShStrLen)) {
+ Name = (CHAR8 *)(ElfCt->FileBase + ElfCt->ShStrOff + Elf32Shdr->sh_name);
+ }
+ } else if (ElfCt->EiClass == ELFCLASS64) {
+ Elf64Shdr = GetElf64SectionByIndex (ElfCt->FileBase, SectionIndex);
+ if ((Elf64Shdr != NULL) && (Elf64Shdr->sh_name < ElfCt->ShStrLen)) {
+ Name = (CHAR8 *)(ElfCt->FileBase + ElfCt->ShStrOff + Elf64Shdr->sh_name);
+ }
+ }
+
+ if (Name == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ *SectionName = Name;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the offset and size of x-th ELF section.
+
+ @param[in] ElfCt ELF image context pointer.
+ @param[in] Index ELF section index.
+ @param[out] Offset Return the offset of the specific section.
+ @param[out] Size Return the size of the specific section.
+
+ @retval EFI_INVALID_PARAMETER ImageBase, Offset or Size is NULL.
+ @retval EFI_INVALID_PARAMETER EiClass doesn't equal to ELFCLASS32 or ELFCLASS64.
+ @retval EFI_NOT_FOUND Could not find the section.
+ @retval EFI_SUCCESS Offset and Size are returned.
+**/
+EFI_STATUS
+EFIAPI
+GetElfSectionPos (
+ IN ELF_IMAGE_CONTEXT *ElfCt,
+ IN UINT32 Index,
+ OUT UINTN *Offset,
+ OUT UINTN *Size
+ )
+{
+ Elf32_Shdr *Elf32Shdr;
+ Elf64_Shdr *Elf64Shdr;
+
+ if ((ElfCt == NULL) || (Offset == NULL) || (Size == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_ERROR (ElfCt->ParseStatus)) {
+ return ElfCt->ParseStatus;
+ }
+
+ if (ElfCt->EiClass == ELFCLASS32) {
+ Elf32Shdr = GetElf32SectionByIndex (ElfCt->FileBase, Index);
+ if (Elf32Shdr != NULL) {
+ *Offset = (UINTN)Elf32Shdr->sh_offset;
+ *Size = (UINTN)Elf32Shdr->sh_size;
+ return EFI_SUCCESS;
+ }
+ } else if (ElfCt->EiClass == ELFCLASS64) {
+ Elf64Shdr = GetElf64SectionByIndex (ElfCt->FileBase, Index);
+ if (Elf64Shdr != NULL) {
+ *Offset = (UINTN)Elf64Shdr->sh_offset;
+ *Size = (UINTN)Elf64Shdr->sh_size;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}