[U-Boot] [PATCH 03/14] efi_loader: Add PE image loader

Simon Glass
2016-01-31T15:18:21Z

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> 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+
> ---
> include/efi_loader.h | 23 ++++
> include/pe.h | 263 ++++++++++++++++++++++++++++++++++++++
> lib/efi_loader/efi_image_loader.c | 178 ++++++++++++++++++++++++++
> 3 files changed, 464 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..bf77573
> --- /dev/null
> +++ b/include/efi_loader.h
> @@ -0,0 +1,23 @@
> +/*
> + * 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);
> +
> +#define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)
> +void *efi_loader_alloc(uint64_t len);
> 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;
> +
> +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..a7788bf
> --- /dev/null
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -0,0 +1,178 @@
> +/*
> + * EFI image loader
> + *
> + * based partly on wine code
> + *
> + * Copyright (c) 2016 Alexander Graf
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <pe.h>
> +#include <efi_loader.h>

This should go above pe.h to keep alpha ordering.

> +#include <asm/global_data.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define ROUND_UP(val, round) ((val + (round - 1)) & ~(round - 1))

Can you use round_up()?

> +#define MB (1024 * 1024)
> +
> +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 efi_return_handle(void *handle, efi_guid_t *protocol,
> + void **protocol_interface, void *agent_handle,
> + void *controller_handle, uint32_t attributes)
> +{
> + *protocol_interface = handle;
> + return EFI_SUCCESS;
> +}
> +
> +/* Will be implemented in a later patch */
> +void *efi_loader_alloc(uint64_t len)
> +{
> + return NULL;
> +}
> +
> +/*
> + * 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 uint16_t *relocs;
> + const IMAGE_BASE_RELOCATION *rel;
> + const IMAGE_BASE_RELOCATION *end;
> + 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;

What does nt stand for?

> +
> +#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 = &sections[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_loader_alloc(virt_size);
> + 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_loader_alloc(virt_size);
> + 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 = &sections[i];
> + memset(efi_reloc + sec->VirtualAddress, 0,
> + sec->Misc.VirtualSize);
> + memcpy(efi_reloc + sec->VirtualAddress,
> + efi + sec->PointerToRawData,
> + sec->SizeOfRawData);
> + }
> +
> + /* Run through relocations */
> + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
> +
> + while (rel < end - 1 && rel->SizeOfBlock) {
> + 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;
> + }

How about putting the relocation code in a separate function, as this
one is getting large.

> +
> + /* Populate the loaded image interface bits */
> + loaded_image_info->image_base = efi;
> + loaded_image_info->image_size = image_size;
> +
> + return entry;
> +}
> --
> 2.1.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

Regards,
Simon