| /** @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 + (UINTN)(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; | |
| } |