
On Tue, Feb 02, 2016 at 03:45:01AM +0100, Alexander Graf wrote:
EFI uses the PE binary format for its application images. Add support to EFI PE binaries as well as all necessary bits for the "EFI image loader" interfaces.
Signed-off-by: Alexander Graf agraf@suse.de
v1 -> v2:
- move memory allocation to separate patch
- limit 32/64 to hosts that support it
- check 32bit optional nt header magic
- switch to GPL2+
v2 -> v3:
- use efi_alloc
- add EFIAPI to function prototypes
- remove unused macros
- reorder header inclusion
- split relocation code into function
- flush cache after loading
include/efi_loader.h | 20 +++ include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 182 ++++++++++++++++++++++++++ 3 files changed, 465 insertions(+) create mode 100644 include/efi_loader.h create mode 100644 include/pe.h create mode 100644 lib/efi_loader/efi_image_loader.c
diff --git a/include/efi_loader.h b/include/efi_loader.h new file mode 100644 index 0000000..5618185 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,20 @@ +/*
- EFI application loader
- Copyright (c) 2016 Alexander Graf
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <part_efi.h> +#include <efi_api.h> +#include <linux/list.h>
+extern const efi_guid_t efi_guid_device_path; +extern const efi_guid_t efi_guid_loaded_image;
+efi_status_t efi_return_handle(void *handle,
efi_guid_t *protocol, void **protocol_interface,
void *agent_handle, void *controller_handle,
uint32_t attributes);
+void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info); diff --git a/include/pe.h b/include/pe.h new file mode 100644 index 0000000..6379ae1 --- /dev/null +++ b/include/pe.h @@ -0,0 +1,263 @@ +/*
- Portable Executable binary format structures
- Copyright (c) 2016 Alexander Graf
- Based on wine code
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef _PE_H +#define _PE_H
+typedef struct _IMAGE_DOS_HEADER {
- uint16_t e_magic; /* 00: MZ Header signature */
- uint16_t e_cblp; /* 02: Bytes on last page of file */
- uint16_t e_cp; /* 04: Pages in file */
- uint16_t e_crlc; /* 06: Relocations */
- uint16_t e_cparhdr; /* 08: Size of header in paragraphs */
- uint16_t e_minalloc; /* 0a: Minimum extra paragraphs needed */
- uint16_t e_maxalloc; /* 0c: Maximum extra paragraphs needed */
- uint16_t e_ss; /* 0e: Initial (relative) SS value */
- uint16_t e_sp; /* 10: Initial SP value */
- uint16_t e_csum; /* 12: Checksum */
- uint16_t e_ip; /* 14: Initial IP value */
- uint16_t e_cs; /* 16: Initial (relative) CS value */
- uint16_t e_lfarlc; /* 18: File address of relocation table */
- uint16_t e_ovno; /* 1a: Overlay number */
- uint16_t e_res[4]; /* 1c: Reserved words */
- uint16_t e_oemid; /* 24: OEM identifier (for e_oeminfo) */
- uint16_t e_oeminfo; /* 26: OEM information; e_oemid specific */
- uint16_t e_res2[10]; /* 28: Reserved words */
- uint32_t e_lfanew; /* 3c: Offset to extended header */
+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
+#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
+typedef struct _IMAGE_FILE_HEADER {
- uint16_t Machine;
- uint16_t NumberOfSections;
- uint32_t TimeDateStamp;
- uint32_t PointerToSymbolTable;
- uint32_t NumberOfSymbols;
- uint16_t SizeOfOptionalHeader;
- uint16_t Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
There is something fishy going on with tabs vs. spaces in this file, for variable name alignment. Above is one example, several more are below. No further comments on functionality.
+typedef struct _IMAGE_DATA_DIRECTORY {
- uint32_t VirtualAddress;
- uint32_t Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+typedef struct _IMAGE_OPTIONAL_HEADER64 {
- uint16_t Magic; /* 0x20b */
- uint8_t MajorLinkerVersion;
- uint8_t MinorLinkerVersion;
- uint32_t SizeOfCode;
- uint32_t SizeOfInitializedData;
- uint32_t SizeOfUninitializedData;
- uint32_t AddressOfEntryPoint;
- uint32_t BaseOfCode;
- uint64_t ImageBase;
- uint32_t SectionAlignment;
- uint32_t FileAlignment;
- uint16_t MajorOperatingSystemVersion;
- uint16_t MinorOperatingSystemVersion;
- uint16_t MajorImageVersion;
- uint16_t MinorImageVersion;
- uint16_t MajorSubsystemVersion;
- uint16_t MinorSubsystemVersion;
- uint32_t Win32VersionValue;
- uint32_t SizeOfImage;
- uint32_t SizeOfHeaders;
- uint32_t CheckSum;
- uint16_t Subsystem;
- uint16_t DllCharacteristics;
- uint64_t SizeOfStackReserve;
- uint64_t SizeOfStackCommit;
- uint64_t SizeOfHeapReserve;
- uint64_t SizeOfHeapCommit;
- uint32_t LoaderFlags;
- uint32_t NumberOfRvaAndSizes;
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
+typedef struct _IMAGE_NT_HEADERS64 {
- uint32_t Signature;
- IMAGE_FILE_HEADER FileHeader;
- IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
+typedef struct _IMAGE_OPTIONAL_HEADER {
- /* Standard fields */
- uint16_t Magic; /* 0x10b or 0x107 */ /* 0x00 */
- uint8_t MajorLinkerVersion;
- uint8_t MinorLinkerVersion;
- uint32_t SizeOfCode;
- uint32_t SizeOfInitializedData;
- uint32_t SizeOfUninitializedData;
- uint32_t AddressOfEntryPoint; /* 0x10 */
- uint32_t BaseOfCode;
- uint32_t BaseOfData;
- /* NT additional fields */
- uint32_t ImageBase;
- uint32_t SectionAlignment; /* 0x20 */
- uint32_t FileAlignment;
- uint16_t MajorOperatingSystemVersion;
- uint16_t MinorOperatingSystemVersion;
- uint16_t MajorImageVersion;
- uint16_t MinorImageVersion;
- uint16_t MajorSubsystemVersion; /* 0x30 */
- uint16_t MinorSubsystemVersion;
- uint32_t Win32VersionValue;
- uint32_t SizeOfImage;
- uint32_t SizeOfHeaders;
- uint32_t CheckSum; /* 0x40 */
- uint16_t Subsystem;
- uint16_t DllCharacteristics;
- uint32_t SizeOfStackReserve;
- uint32_t SizeOfStackCommit;
- uint32_t SizeOfHeapReserve; /* 0x50 */
- uint32_t SizeOfHeapCommit;
- uint32_t LoaderFlags;
- uint32_t NumberOfRvaAndSizes;
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
- /* 0xE0 */
+} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
+typedef struct _IMAGE_NT_HEADERS {
- uint32_t Signature; /* "PE"\0\0 */ /* 0x00 */
- IMAGE_FILE_HEADER FileHeader; /* 0x04 */
- IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */
+} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
+#define IMAGE_SIZEOF_SHORT_NAME 8
+typedef struct _IMAGE_SECTION_HEADER {
- uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
- union {
uint32_t PhysicalAddress;
uint32_t VirtualSize;
- } Misc;
- uint32_t VirtualAddress;
- uint32_t SizeOfRawData;
- uint32_t PointerToRawData;
- uint32_t PointerToRelocations;
- uint32_t PointerToLinenumbers;
- uint16_t NumberOfRelocations;
- uint16_t NumberOfLinenumbers;
- uint32_t Characteristics;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
+typedef struct _IMAGE_BASE_RELOCATION +{
uint32_t VirtualAddress;
uint32_t SizeOfBlock;
/* WORD TypeOffset[1]; */
+} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;
+typedef struct _IMAGE_RELOCATION +{
- union {
uint32_t VirtualAddress;
uint32_t RelocCount;
- } DUMMYUNIONNAME;
- uint32_t SymbolTableIndex;
- uint16_t Type;
+} IMAGE_RELOCATION, *PIMAGE_RELOCATION;
+#define IMAGE_SIZEOF_RELOCATION 10
+/* generic relocation types */ +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_ARM_MOV32A 5 /* yes, 5 too */ +#define IMAGE_REL_BASED_ARM_MOV32 5 /* yes, 5 too */ +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL 7 +#define IMAGE_REL_BASED_ARM_MOV32T 7 /* yes, 7 too */ +#define IMAGE_REL_BASED_THUMB_MOV32 7 /* yes, 7 too */ +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ +#define IMAGE_REL_BASED_DIR64 10 +#define IMAGE_REL_BASED_HIGH3ADJ 11
+/* ARM relocation types */ +#define IMAGE_REL_ARM_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM_ADDR 0x0001 +#define IMAGE_REL_ARM_ADDR32NB 0x0002 +#define IMAGE_REL_ARM_BRANCH24 0x0003 +#define IMAGE_REL_ARM_BRANCH11 0x0004 +#define IMAGE_REL_ARM_TOKEN 0x0005 +#define IMAGE_REL_ARM_GPREL12 0x0006 +#define IMAGE_REL_ARM_GPREL7 0x0007 +#define IMAGE_REL_ARM_BLX24 0x0008 +#define IMAGE_REL_ARM_BLX11 0x0009 +#define IMAGE_REL_ARM_SECTION 0x000E +#define IMAGE_REL_ARM_SECREL 0x000F +#define IMAGE_REL_ARM_MOV32A 0x0010 +#define IMAGE_REL_ARM_MOV32T 0x0011 +#define IMAGE_REL_ARM_BRANCH20T 0x0012 +#define IMAGE_REL_ARM_BRANCH24T 0x0014 +#define IMAGE_REL_ARM_BLX23T 0x0015
+/* ARM64 relocation types */ +#define IMAGE_REL_ARM64_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM64_ADDR32 0x0001 +#define IMAGE_REL_ARM64_ADDR32NB 0x0002 +#define IMAGE_REL_ARM64_BRANCH26 0x0003 +#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 +#define IMAGE_REL_ARM64_REL21 0x0005 +#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 +#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x0007 +#define IMAGE_REL_ARM64_SECREL 0x0008 +#define IMAGE_REL_ARM64_SECREL_LOW12A 0x0009 +#define IMAGE_REL_ARM64_SECREL_HIGH12A 0x000A +#define IMAGE_REL_ARM64_SECREL_LOW12L 0x000B +#define IMAGE_REL_ARM64_TOKEN 0x000C +#define IMAGE_REL_ARM64_SECTION 0x000D +#define IMAGE_REL_ARM64_ADDR64 0x000E
+/* AMD64 relocation types */ +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010
+#endif /* _PE_H */ diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c new file mode 100644 index 0000000..6e08099 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,182 @@ +/*
- EFI image loader
- based partly on wine code
- Copyright (c) 2016 Alexander Graf
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <efi_loader.h> +#include <pe.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID; +const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+efi_status_t EFIAPI efi_return_handle(void *handle, efi_guid_t *protocol,
void **protocol_interface, void *agent_handle,
void *controller_handle, uint32_t attributes)
+{
- EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
protocol_interface, agent_handle, controller_handle,
attributes);
- *protocol_interface = handle;
- return EFI_EXIT(EFI_SUCCESS);
+}
+static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
unsigned long rel_size, void *efi_reloc)
+{
- const IMAGE_BASE_RELOCATION *end;
- int i;
- end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
- while (rel < end - 1 && rel->SizeOfBlock) {
const uint16_t *relocs = (const uint16_t *)(rel + 1);
i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
while (i--) {
uint16_t offset = (*relocs & 0xfff) +
rel->VirtualAddress;
int type = *relocs >> 12;
unsigned long delta = (unsigned long)efi_reloc;
uint64_t *x64 = efi_reloc + offset;
uint32_t *x32 = efi_reloc + offset;
uint16_t *x16 = efi_reloc + offset;
switch (type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
*x16 += ((uint32_t)delta) >> 16;
break;
case IMAGE_REL_BASED_LOW:
*x16 += (uint16_t)delta;
break;
case IMAGE_REL_BASED_HIGHLOW:
*x32 += (uint32_t)delta;
break;
case IMAGE_REL_BASED_DIR64:
*x64 += (uint64_t)delta;
break;
default:
printf("Unknown Relocation off %x type %x\n",
offset, type);
}
relocs++;
}
rel = (const IMAGE_BASE_RELOCATION *)relocs;
- }
+}
+/*
- This function loads all sections from a PE binary into a newly reserved
- piece of memory. On successful load it then returns the entry point for
- the binary. Otherwise NULL.
- */
+void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) +{
- IMAGE_NT_HEADERS32 *nt;
- IMAGE_DOS_HEADER *dos;
- IMAGE_SECTION_HEADER *sections;
- int num_sections;
- void *efi_reloc;
- int i;
- const IMAGE_BASE_RELOCATION *rel;
- unsigned long rel_size;
- int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
- void *entry;
- uint64_t image_size;
- unsigned long virt_size = 0;
- bool can_run_nt64 = true;
- bool can_run_nt32 = true;
+#if defined(CONFIG_ARM64)
- can_run_nt32 = false;
+#elif defined(CONFIG_ARM)
- can_run_nt64 = false;
+#endif
- dos = efi;
- if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
return NULL;
- }
- nt = (void *) ((char *)efi + dos->e_lfanew);
- if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
return NULL;
- }
- /* Calculate upper virtual address boundary */
- num_sections = nt->FileHeader.NumberOfSections;
- sections = (void *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader;
- for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = §ions[i];
virt_size = max_t(unsigned long, virt_size,
sec->VirtualAddress + sec->Misc.VirtualSize);
- }
- /* Read 32/64bit specific header bits */
- if (can_run_nt64 &&
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
image_size = opt->SizeOfImage;
efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
if (!efi_reloc) {
printf("%s: Could not allocate %ld bytes\n",
__func__, virt_size);
return NULL;
}
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
- } else if (can_run_nt32 &&
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
image_size = opt->SizeOfImage;
efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
if (!efi_reloc) {
printf("%s: Could not allocate %ld bytes\n",
__func__, virt_size);
return NULL;
}
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
- } else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
return NULL;
- }
- /* Load sections into RAM */
- for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = §ions[i];
memset(efi_reloc + sec->VirtualAddress, 0,
sec->Misc.VirtualSize);
memcpy(efi_reloc + sec->VirtualAddress,
efi + sec->PointerToRawData,
sec->SizeOfRawData);
- }
- /* Run through relocations */
- efi_loader_relocate(rel, rel_size, efi_reloc);
- /* Flush cache */
- flush_cache((ulong)efi_reloc, virt_size);
- /* Populate the loaded image interface bits */
- loaded_image_info->image_base = efi;
- loaded_image_info->image_size = image_size;
- return entry;
+}
2.6.2