mirror of https://github.com/acidanthera/audk.git
341 lines
10 KiB
C
341 lines
10 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2014, ARM Limited. All rights reserved.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include "ArmShellCmdRunAxf.h"
|
|
#include "ElfLoader.h"
|
|
#include "elf_common.h"
|
|
#include "elf32.h"
|
|
#include "elf64.h"
|
|
|
|
|
|
// Put the functions the #ifdef. We only use the appropriate one for the platform.
|
|
// This prevents 'defined but not used' compiler warning.
|
|
#ifdef MDE_CPU_ARM
|
|
STATIC
|
|
BOOLEAN
|
|
IsArmElf (
|
|
IN CONST VOID *Buf
|
|
)
|
|
{
|
|
Elf32_Ehdr *Hdr = (Elf32_Ehdr*)Buf;
|
|
|
|
if (Hdr->e_ident[EI_CLASS] != ELFCLASS32) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_32), gRunAxfHiiHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Hdr->e_machine != EM_ARM) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_32), gRunAxfHiiHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
// We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
|
|
|
|
return TRUE;
|
|
}
|
|
#elif defined(MDE_CPU_AARCH64)
|
|
STATIC
|
|
BOOLEAN
|
|
IsAarch64Elf (
|
|
IN CONST VOID *Buf
|
|
)
|
|
{
|
|
Elf64_Ehdr *Hdr = (Elf64_Ehdr*)Buf;
|
|
|
|
if (Hdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS_64), gRunAxfHiiHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Hdr->e_machine != EM_AARCH64) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGMACH_64), gRunAxfHiiHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
// We don't currently check endianness of ELF data (hdr->e_ident[EI_DATA])
|
|
|
|
return TRUE;
|
|
}
|
|
#endif // MDE_CPU_ARM , MDE_CPU_AARCH64
|
|
|
|
|
|
/**
|
|
Support checking 32 and 64bit as the header could be valid, we might just
|
|
not support loading it.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
ElfCheckHeader (
|
|
IN CONST VOID *Buf
|
|
)
|
|
{
|
|
Elf32_Ehdr *Hdr32 = (Elf32_Ehdr*)Buf;
|
|
|
|
if (!IS_ELF (*Hdr32)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFMAGIC), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Hdr32->e_type != ET_EXEC) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOTEXEC), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Hdr32->e_ident[EI_CLASS] == ELFCLASS32) {
|
|
if ((Hdr32->e_phoff == 0) || (Hdr32->e_phentsize == 0) || (Hdr32->e_phnum == 0)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Hdr32->e_flags != 0) {
|
|
DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr32->e_entry));
|
|
DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr32->e_phoff));
|
|
DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr32->e_phentsize));
|
|
DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr32->e_phnum));
|
|
} else if (Hdr32->e_ident[EI_CLASS] == ELFCLASS64) {
|
|
Elf64_Ehdr *Hdr64 = (Elf64_Ehdr*)Buf;
|
|
|
|
if ((Hdr64->e_phoff == 0) || (Hdr64->e_phentsize == 0) || (Hdr64->e_phnum == 0)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOPROG), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Hdr64->e_flags != 0) {
|
|
DEBUG ((EFI_D_INFO, "Warning: Wrong processor-specific flags, expected 0.\n"));
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "Entry point addr: 0x%lx\n", Hdr64->e_entry));
|
|
DEBUG ((EFI_D_INFO, "Start of program headers: 0x%lx\n", Hdr64->e_phoff));
|
|
DEBUG ((EFI_D_INFO, "Size of 1 program header: %d\n", Hdr64->e_phentsize));
|
|
DEBUG ((EFI_D_INFO, "Number of program headers: %d\n", Hdr64->e_phnum));
|
|
} else {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFWRONGCLASS), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Load an ELF segment into memory.
|
|
|
|
This function assumes the ELF file is valid.
|
|
This function is meant to be called for PT_LOAD type segments only.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
ElfLoadSegment (
|
|
IN CONST VOID *ElfImage,
|
|
IN CONST VOID *PHdr,
|
|
IN LIST_ENTRY *LoadList
|
|
)
|
|
{
|
|
VOID *FileSegment;
|
|
VOID *MemSegment;
|
|
UINTN ExtraZeroes;
|
|
UINTN ExtraZeroesCount;
|
|
RUNAXF_LOAD_LIST *LoadNode;
|
|
|
|
#ifdef MDE_CPU_ARM
|
|
Elf32_Phdr *ProgramHdr;
|
|
ProgramHdr = (Elf32_Phdr *)PHdr;
|
|
#elif defined(MDE_CPU_AARCH64)
|
|
Elf64_Phdr *ProgramHdr;
|
|
ProgramHdr = (Elf64_Phdr *)PHdr;
|
|
#endif
|
|
|
|
ASSERT (ElfImage != NULL);
|
|
ASSERT (ProgramHdr != NULL);
|
|
|
|
FileSegment = (VOID *)((UINTN)ElfImage + ProgramHdr->p_offset);
|
|
MemSegment = (VOID *)ProgramHdr->p_vaddr;
|
|
|
|
// If the segment's memory size p_memsz is larger than the file size p_filesz,
|
|
// the "extra" bytes are defined to hold the value 0 and to follow the
|
|
// segment's initialised area.
|
|
// This is typically the case for the .bss segment.
|
|
// The file size may not be larger than the memory size.
|
|
if (ProgramHdr->p_filesz > ProgramHdr->p_memsz) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADFORMAT), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Load the segment in memory.
|
|
if (ProgramHdr->p_filesz != 0) {
|
|
DEBUG ((EFI_D_INFO, "Loading segment from 0x%lx to 0x%lx (size = %ld)\n",
|
|
FileSegment, MemSegment, ProgramHdr->p_filesz));
|
|
|
|
LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
|
|
if (LoadNode == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
LoadNode->MemOffset = (UINTN)MemSegment;
|
|
LoadNode->FileOffset = (UINTN)FileSegment;
|
|
LoadNode->Length = (UINTN)ProgramHdr->p_filesz;
|
|
InsertTailList (LoadList, &LoadNode->Link);
|
|
}
|
|
|
|
ExtraZeroes = ((UINTN)MemSegment + ProgramHdr->p_filesz);
|
|
ExtraZeroesCount = ProgramHdr->p_memsz - ProgramHdr->p_filesz;
|
|
DEBUG ((EFI_D_INFO, "Completing segment with %d zero bytes.\n", ExtraZeroesCount));
|
|
if (ExtraZeroesCount > 0) {
|
|
// Extra Node to add the Zeroes.
|
|
LoadNode = AllocateRuntimeZeroPool (sizeof (RUNAXF_LOAD_LIST));
|
|
if (LoadNode == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
LoadNode->MemOffset = (UINTN)ExtraZeroes;
|
|
LoadNode->Zeroes = TRUE;
|
|
LoadNode->Length = ExtraZeroesCount;
|
|
InsertTailList (LoadList, &LoadNode->Link);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Check that the ELF File Header is valid and Machine type supported.
|
|
|
|
Not all information is checked in the ELF header, only the stuff that
|
|
matters to us in our simplified ELF loader.
|
|
|
|
@param[in] ElfImage Address of the ELF file to check.
|
|
|
|
@retval EFI_SUCCESS on success.
|
|
@retval EFI_INVALID_PARAMETER if the header is invalid.
|
|
@retval EFI_UNSUPPORTED if the file type/platform is not supported.
|
|
**/
|
|
EFI_STATUS
|
|
ElfCheckFile (
|
|
IN CONST VOID *ElfImage
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ASSERT (ElfImage != NULL);
|
|
|
|
// Check that the ELF header is valid.
|
|
Status = ElfCheckHeader (ElfImage);
|
|
if (EFI_ERROR(Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFBADHEADER), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
#ifdef MDE_CPU_ARM
|
|
if (IsArmElf (ElfImage)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
#elif defined(MDE_CPU_AARCH64)
|
|
if (IsAarch64Elf (ElfImage)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_BAD_ARCH), gRunAxfHiiHandle);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
Load a ELF file.
|
|
|
|
@param[in] ElfImage Address of the ELF file in memory.
|
|
|
|
@param[out] EntryPoint Will be filled with the ELF entry point address.
|
|
|
|
@param[out] ImageSize Will be filled with the ELF size in memory. This will
|
|
effectively be equal to the sum of the segments sizes.
|
|
|
|
This functon assumes the header is valid and supported as checked with
|
|
ElfCheckFile().
|
|
|
|
@retval EFI_SUCCESS on success.
|
|
@retval EFI_INVALID_PARAMETER if the ELF file is invalid.
|
|
**/
|
|
EFI_STATUS
|
|
ElfLoadFile (
|
|
IN CONST VOID *ElfImage,
|
|
OUT VOID **EntryPoint,
|
|
OUT LIST_ENTRY *LoadList
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *ProgramHdr;
|
|
UINTN Index;
|
|
UINTN ImageSize;
|
|
|
|
#ifdef MDE_CPU_ARM
|
|
Elf32_Ehdr *ElfHdr;
|
|
Elf32_Phdr *ProgramHdrPtr;
|
|
|
|
ElfHdr = (Elf32_Ehdr*)ElfImage;
|
|
#elif defined(MDE_CPU_AARCH64)
|
|
Elf64_Ehdr *ElfHdr;
|
|
Elf64_Phdr *ProgramHdrPtr;
|
|
|
|
ElfHdr = (Elf64_Ehdr*)ElfImage;
|
|
#endif
|
|
|
|
ASSERT (ElfImage != NULL);
|
|
ASSERT (EntryPoint != NULL);
|
|
ASSERT (LoadList != NULL);
|
|
|
|
ProgramHdr = (UINT8*)ElfImage + ElfHdr->e_phoff;
|
|
DEBUG ((EFI_D_INFO, "ELF program header entry : 0x%lx\n", ProgramHdr));
|
|
|
|
ImageSize = 0;
|
|
|
|
// Load every loadable ELF segment into memory.
|
|
for (Index = 0; Index < ElfHdr->e_phnum; ++Index) {
|
|
|
|
#ifdef MDE_CPU_ARM
|
|
ProgramHdrPtr = (Elf32_Phdr*)ProgramHdr;
|
|
#elif defined(MDE_CPU_AARCH64)
|
|
ProgramHdrPtr = (Elf64_Phdr*)ProgramHdr;
|
|
#endif
|
|
|
|
// Only consider PT_LOAD type segments, ignore others.
|
|
if (ProgramHdrPtr->p_type == PT_LOAD) {
|
|
Status = ElfLoadSegment (ElfImage, (VOID *)ProgramHdrPtr, LoadList);
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFFAILSEG), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
ImageSize += ProgramHdrPtr->p_memsz;
|
|
}
|
|
ProgramHdr += ElfHdr->e_phentsize;
|
|
}
|
|
|
|
if (ImageSize == 0) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_RUNAXF_ELFNOSEG), gRunAxfHiiHandle);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Return the entry point specified in the ELF header.
|
|
*EntryPoint = (void*)ElfHdr->e_entry;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|