blob: a9bf6f3223215caaacecba6103a504af9f34d305 [file] [log] [blame]
/** @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;
}