blob: e16716498ff90490e42bb6d69bfd29f4ae862824 [file] [log] [blame] [raw]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <efi.h>
#include <efilib.h>
#include "missing_efi.h"
#include "pe.h"
#include "util.h"
#define DOS_FILE_MAGIC "MZ"
#define PE_FILE_MAGIC "PE\0\0"
#define MAX_SECTIONS 96
#if defined(__i386__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
#elif defined(__x86_64__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
#elif defined(__aarch64__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
#elif defined(__arm__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
#elif defined(__riscv) && __riscv_xlen == 64
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
#else
#error Unknown EFI arch
#endif
struct DosFileHeader {
UINT8 Magic[2];
UINT16 LastSize;
UINT16 nBlocks;
UINT16 nReloc;
UINT16 HdrSize;
UINT16 MinAlloc;
UINT16 MaxAlloc;
UINT16 ss;
UINT16 sp;
UINT16 Checksum;
UINT16 ip;
UINT16 cs;
UINT16 RelocPos;
UINT16 nOverlay;
UINT16 reserved[4];
UINT16 OEMId;
UINT16 OEMInfo;
UINT16 reserved2[10];
UINT32 ExeHeader;
} _packed_;
struct CoffFileHeader {
UINT16 Machine;
UINT16 NumberOfSections;
UINT32 TimeDateStamp;
UINT32 PointerToSymbolTable;
UINT32 NumberOfSymbols;
UINT16 SizeOfOptionalHeader;
UINT16 Characteristics;
} _packed_;
#define OPTHDR32_MAGIC 0x10B /* PE32 OptionalHeader */
#define OPTHDR64_MAGIC 0x20B /* PE32+ OptionalHeader */
struct PeOptionalHeader {
UINT16 Magic;
UINT8 LinkerMajor;
UINT8 LinkerMinor;
UINT32 SizeOfCode;
UINT32 SizeOfInitializedData;
UINT32 SizeOfUninitializeData;
UINT32 AddressOfEntryPoint;
UINT32 BaseOfCode;
union {
struct { /* PE32 */
UINT32 BaseOfData;
UINT32 ImageBase32;
};
UINT64 ImageBase64; /* PE32+ */
};
UINT32 SectionAlignment;
UINT32 FileAlignment;
UINT16 MajorOperatingSystemVersion;
UINT16 MinorOperatingSystemVersion;
UINT16 MajorImageVersion;
UINT16 MinorImageVersion;
UINT16 MajorSubsystemVersion;
UINT16 MinorSubsystemVersion;
UINT32 Win32VersionValue;
UINT32 SizeOfImage;
UINT32 SizeOfHeaders;
UINT32 CheckSum;
UINT16 Subsystem;
UINT16 DllCharacteristics;
/* fields with different sizes for 32/64 omitted */
} _packed_;
struct PeFileHeader {
UINT8 Magic[4];
struct CoffFileHeader FileHeader;
struct PeOptionalHeader OptionalHeader;
} _packed_;
struct PeSectionHeader {
UINT8 Name[8];
UINT32 VirtualSize;
UINT32 VirtualAddress;
UINT32 SizeOfRawData;
UINT32 PointerToRawData;
UINT32 PointerToRelocations;
UINT32 PointerToLinenumbers;
UINT16 NumberOfRelocations;
UINT16 NumberOfLinenumbers;
UINT32 Characteristics;
} _packed_;
static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
assert(dos);
return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
}
static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
assert(pe);
return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
pe->FileHeader.NumberOfSections > 0 &&
pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
}
static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
assert(dos);
assert(pe);
return dos->ExeHeader + offsetof(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
}
static void locate_sections(
const struct PeSectionHeader section_table[],
UINTN n_table,
const CHAR8 **sections,
UINTN *addrs,
UINTN *offsets,
UINTN *sizes) {
assert(section_table);
assert(sections);
assert(sizes);
for (UINTN i = 0; i < n_table; i++) {
const struct PeSectionHeader *sect = section_table + i;
for (UINTN j = 0; sections[j]; j++) {
if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
addrs[j] = sect->VirtualAddress;
if (offsets)
offsets[j] = sect->PointerToRawData;
sizes[j] = sect->VirtualSize;
}
}
}
EFI_STATUS pe_alignment_info(
const void *base,
UINT32 *ret_entry_point_address,
UINT32 *ret_size_of_image,
UINT32 *ret_section_alignment) {
const struct DosFileHeader *dos;
const struct PeFileHeader *pe;
assert(base);
assert(ret_entry_point_address);
assert(ret_size_of_image);
assert(ret_section_alignment);
dos = (const struct DosFileHeader *) base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
if (!verify_pe(pe))
return EFI_LOAD_ERROR;
*ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
*ret_size_of_image = pe->OptionalHeader.SizeOfImage;
*ret_section_alignment = pe->OptionalHeader.SectionAlignment;
return EFI_SUCCESS;
}
EFI_STATUS pe_memory_locate_sections(
const CHAR8 *base,
const CHAR8 **sections,
UINTN *addrs,
UINTN *sizes) {
const struct DosFileHeader *dos;
const struct PeFileHeader *pe;
UINTN offset;
assert(base);
assert(sections);
assert(addrs);
assert(sizes);
dos = (const struct DosFileHeader*)base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
if (!verify_pe(pe))
return EFI_LOAD_ERROR;
offset = section_table_offset(dos, pe);
locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
sections, addrs, NULL, sizes);
return EFI_SUCCESS;
}
EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir,
const CHAR16 *path,
const CHAR8 **sections,
UINTN *offsets,
UINTN *sizes) {
_cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
_cleanup_(file_closep) EFI_FILE *handle = NULL;
struct DosFileHeader dos;
struct PeFileHeader pe;
UINTN len, section_table_len;
EFI_STATUS err;
assert(dir);
assert(path);
assert(sections);
assert(offsets);
assert(sizes);
err = dir->Open(dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
len = sizeof(dos);
err = handle->Read(handle, &len, &dos);
if (EFI_ERROR(err))
return err;
if (len != sizeof(dos) || !verify_dos(&dos))
return EFI_LOAD_ERROR;
err = handle->SetPosition(handle, dos.ExeHeader);
if (EFI_ERROR(err))
return err;
len = sizeof(pe);
err = handle->Read(handle, &len, &pe);
if (EFI_ERROR(err))
return err;
if (len != sizeof(pe) || !verify_pe(&pe))
return EFI_LOAD_ERROR;
section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
section_table = xallocate_pool(section_table_len);
if (!section_table)
return EFI_OUT_OF_RESOURCES;
err = handle->SetPosition(handle, section_table_offset(&dos, &pe));
if (EFI_ERROR(err))
return err;
len = section_table_len;
err = handle->Read(handle, &len, section_table);
if (EFI_ERROR(err))
return err;
if (len != section_table_len)
return EFI_LOAD_ERROR;
locate_sections(section_table, pe.FileHeader.NumberOfSections,
sections, NULL, offsets, sizes);
return EFI_SUCCESS;
}