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