/** @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; | |
} |