mirror of
https://github.com/ipxe/ipxe
synced 2026-05-14 20:00:42 +03:00
646d4b0a09
For UEFI, the USB disk image is constructed from the built EFI binary (e.g. bin-x86_64-efi/ipxe.efi) by genfsimg, which does not itself have any way to access the build configuration. We therefore need a way to annotate the binary such that genfsimg can determine whether or not to include a log partition within the USB disk image. The "OEM ID" and "OEM information" fields within the PE header can be used for this, since they are easily accessed and serve no other purpose. We define bit 0 of "OEM information" as a flag indicating that a log partition should be included. If this bit is set, genfsimg will create a log partition with a layout matching that of the BIOS build (i.e. using partition 3 and at an offset of 16kB from the start of the disk). The PE header is constructed by elf2efi.c, which takes as an input the linked ELF form of the binary. We use an ELF .note section to allow any linked-in object to communicate the log partition request through to elf2efi.c, which then populates the OEM information field accordingly. We choose to use the same field locations within the BIOS bzImage header, since this allows genfsimg to use the same logic for both BIOS and UEFI binaries. In a BIOS build, there is no external processing equivalent to elf2efi.c, and so we construct the field value directly using absolute symbols and explicit relocation records. (Note that the bzImage header is relevant only when using genfsimg to construct a combined BIOS/UEFI image. In the common case of building a BIOS-only image such as bin/ipxe.usb, the partition table is manually constructed by usbdisk.S and genfsimg is not involved.) Signed-off-by: Michael Brown <mcb30@ipxe.org>
1457 lines
39 KiB
C
1457 lines
39 KiB
C
/*
|
|
* Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#define FILE_LICENCE(...) extern void __file_licence ( void )
|
|
#define FILE_SECBOOT(...) extern void __file_secboot ( void )
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <getopt.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <elf.h>
|
|
#include <libgen.h>
|
|
|
|
#define EFI_HOSTONLY
|
|
#include <ipxe/efi/Uefi.h>
|
|
#include <ipxe/efi/IndustryStandard/PeImage.h>
|
|
|
|
/* Provide constants spuriously deleted from EDK2 headers */
|
|
#define EFI_IMAGE_MACHINE_ARMTHUMB_MIXED 0x01c2
|
|
|
|
#define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
|
|
|
|
#undef ELF_R_TYPE
|
|
#undef ELF_R_SYM
|
|
|
|
#ifdef EFI_TARGET32
|
|
|
|
#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS32
|
|
#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
|
#define EFI_IMAGE_FILE_MACHINE EFI_IMAGE_FILE_32BIT_MACHINE
|
|
#define ELFCLASS ELFCLASS32
|
|
#define Elf_Ehdr Elf32_Ehdr
|
|
#define Elf_Phdr Elf32_Phdr
|
|
#define Elf_Shdr Elf32_Shdr
|
|
#define Elf_Nhdr Elf32_Nhdr
|
|
#define Elf_Sym Elf32_Sym
|
|
#define Elf_Addr Elf32_Addr
|
|
#define Elf_Rel Elf32_Rel
|
|
#define Elf_Rela Elf32_Rela
|
|
#define ELF_R_TYPE ELF32_R_TYPE
|
|
#define ELF_R_SYM ELF32_R_SYM
|
|
|
|
#elif defined(EFI_TARGET64)
|
|
|
|
#define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS64
|
|
#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
|
#define EFI_IMAGE_FILE_MACHINE 0
|
|
#define ELFCLASS ELFCLASS64
|
|
#define Elf_Ehdr Elf64_Ehdr
|
|
#define Elf_Phdr Elf64_Phdr
|
|
#define Elf_Shdr Elf64_Shdr
|
|
#define Elf_Nhdr Elf64_Nhdr
|
|
#define Elf_Sym Elf64_Sym
|
|
#define Elf_Addr Elf64_Addr
|
|
#define Elf_Rel Elf64_Rel
|
|
#define Elf_Rela Elf64_Rela
|
|
#define ELF_R_TYPE ELF64_R_TYPE
|
|
#define ELF_R_SYM ELF64_R_SYM
|
|
|
|
#endif
|
|
|
|
#define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) )
|
|
|
|
/* Provide constants missing on some platforms */
|
|
#ifndef EM_AARCH64
|
|
#define EM_AARCH64 183
|
|
#endif
|
|
#ifndef EM_RISCV
|
|
#define EM_RISCV 243
|
|
#endif
|
|
#ifndef EM_LOONGARCH
|
|
#define EM_LOONGARCH 258
|
|
#endif
|
|
#ifndef R_AARCH64_NONE
|
|
#define R_AARCH64_NONE 0
|
|
#endif
|
|
#ifndef R_AARCH64_NULL
|
|
#define R_AARCH64_NULL 256
|
|
#endif
|
|
#ifndef R_AARCH64_ABS64
|
|
#define R_AARCH64_ABS64 257
|
|
#endif
|
|
#ifndef R_AARCH64_CALL26
|
|
#define R_AARCH64_CALL26 283
|
|
#endif
|
|
#ifndef R_AARCH64_JUMP26
|
|
#define R_AARCH64_JUMP26 282
|
|
#endif
|
|
#ifndef R_AARCH64_ADR_PREL_LO21
|
|
#define R_AARCH64_ADR_PREL_LO21 274
|
|
#endif
|
|
#ifndef R_AARCH64_ADR_PREL_PG_HI21
|
|
#define R_AARCH64_ADR_PREL_PG_HI21 275
|
|
#endif
|
|
#ifndef R_AARCH64_ADD_ABS_LO12_NC
|
|
#define R_AARCH64_ADD_ABS_LO12_NC 277
|
|
#endif
|
|
#ifndef R_AARCH64_LDST8_ABS_LO12_NC
|
|
#define R_AARCH64_LDST8_ABS_LO12_NC 278
|
|
#endif
|
|
#ifndef R_AARCH64_LDST16_ABS_LO12_NC
|
|
#define R_AARCH64_LDST16_ABS_LO12_NC 284
|
|
#endif
|
|
#ifndef R_AARCH64_LDST32_ABS_LO12_NC
|
|
#define R_AARCH64_LDST32_ABS_LO12_NC 285
|
|
#endif
|
|
#ifndef R_AARCH64_LDST64_ABS_LO12_NC
|
|
#define R_AARCH64_LDST64_ABS_LO12_NC 286
|
|
#endif
|
|
#ifndef R_AARCH64_LDST128_ABS_LO12_NC
|
|
#define R_AARCH64_LDST128_ABS_LO12_NC 299
|
|
#endif
|
|
#ifndef R_ARM_CALL
|
|
#define R_ARM_CALL 28
|
|
#endif
|
|
#ifndef R_ARM_THM_JUMP24
|
|
#define R_ARM_THM_JUMP24 30
|
|
#endif
|
|
#ifndef R_ARM_V4BX
|
|
#define R_ARM_V4BX 40
|
|
#endif
|
|
#ifndef R_LARCH_NONE
|
|
#define R_LARCH_NONE 0
|
|
#endif
|
|
#ifndef R_LARCH_64
|
|
#define R_LARCH_64 2
|
|
#endif
|
|
#ifndef R_LARCH_B16
|
|
#define R_LARCH_B16 64
|
|
#endif
|
|
#ifndef R_LARCH_B21
|
|
#define R_LARCH_B21 65
|
|
#endif
|
|
#ifndef R_LARCH_B26
|
|
#define R_LARCH_B26 66
|
|
#endif
|
|
#ifndef R_LARCH_PCALA_HI20
|
|
#define R_LARCH_PCALA_HI20 71
|
|
#endif
|
|
#ifndef R_LARCH_PCALA_LO12
|
|
#define R_LARCH_PCALA_LO12 72
|
|
#endif
|
|
#ifndef R_LARCH_GOT_PC_HI20
|
|
#define R_LARCH_GOT_PC_HI20 75
|
|
#endif
|
|
#ifndef R_LARCH_GOT_PC_LO12
|
|
#define R_LARCH_GOT_PC_LO12 76
|
|
#endif
|
|
#ifndef R_LARCH_RELAX
|
|
#define R_LARCH_RELAX 100
|
|
#endif
|
|
#ifndef R_LARCH_PCREL20_S2
|
|
#define R_LARCH_PCREL20_S2 103
|
|
#endif
|
|
#ifndef R_RISCV_NONE
|
|
#define R_RISCV_NONE 0
|
|
#endif
|
|
#ifndef R_RISCV_32
|
|
#define R_RISCV_32 1
|
|
#endif
|
|
#ifndef R_RISCV_64
|
|
#define R_RISCV_64 2
|
|
#endif
|
|
#ifndef R_RISCV_BRANCH
|
|
#define R_RISCV_BRANCH 16
|
|
#endif
|
|
#ifndef R_RISCV_JAL
|
|
#define R_RISCV_JAL 17
|
|
#endif
|
|
#ifndef R_RISCV_PCREL_HI20
|
|
#define R_RISCV_PCREL_HI20 23
|
|
#endif
|
|
#ifndef R_RISCV_PCREL_LO12_I
|
|
#define R_RISCV_PCREL_LO12_I 24
|
|
#endif
|
|
#ifndef R_RISCV_PCREL_LO12_S
|
|
#define R_RISCV_PCREL_LO12_S 25
|
|
#endif
|
|
#ifndef R_RISCV_ADD32
|
|
#define R_RISCV_ADD32 35
|
|
#endif
|
|
#ifndef R_RISCV_SUB32
|
|
#define R_RISCV_SUB32 39
|
|
#endif
|
|
#ifndef R_RISCV_RVC_BRANCH
|
|
#define R_RISCV_RVC_BRANCH 44
|
|
#endif
|
|
#ifndef R_RISCV_RVC_JUMP
|
|
#define R_RISCV_RVC_JUMP 45
|
|
#endif
|
|
#ifndef R_RISCV_RELAX
|
|
#define R_RISCV_RELAX 51
|
|
#endif
|
|
#ifndef R_X86_64_GOTPCRELX
|
|
#define R_X86_64_GOTPCRELX 41
|
|
#endif
|
|
#ifndef R_X86_64_REX_GOTPCRELX
|
|
#define R_X86_64_REX_GOTPCRELX 42
|
|
#endif
|
|
|
|
/**
|
|
* Alignment of raw data of sections in the image file
|
|
*
|
|
* Some versions of signtool.exe will spuriously complain if this
|
|
* value is less than 512.
|
|
*/
|
|
#define EFI_FILE_ALIGN 0x200
|
|
|
|
/**
|
|
* Alignment of sections when loaded into memory
|
|
*
|
|
* This must equal the architecture page size, in order to allow for
|
|
* the possibility of the firmware using page-level protection to
|
|
* enforce section attributes at runtime.
|
|
*/
|
|
#define EFI_IMAGE_ALIGN 0x1000
|
|
|
|
/** Set PointerToRawData automatically */
|
|
#define PTRD_AUTO 0xffffffffUL
|
|
|
|
/** Number of data directory entries */
|
|
#define NUMBER_OF_DIRECTORY_ENTRIES 8
|
|
|
|
/** Build requested a console log partition */
|
|
#define IPXE_NOTE_DISKLOG 0x18aed109
|
|
|
|
/** OEM identifier used for iPXE */
|
|
#define IPXE_OEM_ID 0x18ae
|
|
|
|
/** OEM information: console log partition requested */
|
|
#define IPXE_OEM_INFO_DISKLOG 0x0001
|
|
|
|
struct elf_file {
|
|
void *data;
|
|
size_t len;
|
|
const Elf_Ehdr *ehdr;
|
|
};
|
|
|
|
struct pe_section {
|
|
struct pe_section *next;
|
|
EFI_IMAGE_SECTION_HEADER hdr;
|
|
int hidden;
|
|
uint8_t contents[0];
|
|
};
|
|
|
|
struct pe_relocs {
|
|
struct pe_relocs *next;
|
|
unsigned long start_rva;
|
|
unsigned int used_relocs;
|
|
unsigned int total_relocs;
|
|
uint16_t *relocs;
|
|
};
|
|
|
|
struct pe_header {
|
|
EFI_IMAGE_DOS_HEADER dos;
|
|
EFI_IMAGE_NT_HEADERS nt;
|
|
};
|
|
|
|
static struct pe_header efi_pe_header = {
|
|
.dos = {
|
|
.e_magic = EFI_IMAGE_DOS_SIGNATURE,
|
|
.e_oemid = IPXE_OEM_ID,
|
|
.e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ),
|
|
},
|
|
.nt = {
|
|
.Signature = EFI_IMAGE_NT_SIGNATURE,
|
|
.FileHeader = {
|
|
.TimeDateStamp = 0x10d1a884,
|
|
.SizeOfOptionalHeader =
|
|
( sizeof ( efi_pe_header.nt.OptionalHeader ) -
|
|
( ( EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES -
|
|
NUMBER_OF_DIRECTORY_ENTRIES ) *
|
|
sizeof ( efi_pe_header.nt.OptionalHeader.
|
|
DataDirectory[0] ) ) ),
|
|
.Characteristics = ( EFI_IMAGE_FILE_DLL |
|
|
EFI_IMAGE_FILE_MACHINE |
|
|
EFI_IMAGE_FILE_LARGE_ADDRESS_AWARE|
|
|
EFI_IMAGE_FILE_EXECUTABLE_IMAGE ),
|
|
},
|
|
.OptionalHeader = {
|
|
.Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC,
|
|
.MajorLinkerVersion = 42,
|
|
.MinorLinkerVersion = 42,
|
|
.SectionAlignment = EFI_IMAGE_ALIGN,
|
|
.FileAlignment = EFI_FILE_ALIGN,
|
|
.SizeOfImage = EFI_IMAGE_ALIGN,
|
|
.SizeOfHeaders =
|
|
( sizeof ( efi_pe_header ) -
|
|
( ( EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES -
|
|
NUMBER_OF_DIRECTORY_ENTRIES ) *
|
|
sizeof ( efi_pe_header.nt.OptionalHeader.
|
|
DataDirectory[0] ) ) ),
|
|
.DllCharacteristics =
|
|
IMAGE_DLLCHARACTERISTICS_NX_COMPAT,
|
|
.NumberOfRvaAndSizes = NUMBER_OF_DIRECTORY_ENTRIES,
|
|
},
|
|
},
|
|
};
|
|
|
|
/** Command-line options */
|
|
struct options {
|
|
/** PE32+ subsystem type */
|
|
unsigned int subsystem;
|
|
/** Create hybrid BIOS/UEFI binary */
|
|
int hybrid;
|
|
/** Use disk-based console log */
|
|
int disklog;
|
|
};
|
|
|
|
/**
|
|
* Allocate memory
|
|
*
|
|
* @v len Length of memory to allocate
|
|
* @ret ptr Pointer to allocated memory
|
|
*/
|
|
static void * xmalloc ( size_t len ) {
|
|
void *ptr;
|
|
|
|
ptr = malloc ( len );
|
|
if ( ! ptr ) {
|
|
eprintf ( "Could not allocate %zd bytes\n", len );
|
|
exit ( 1 );
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/**
|
|
* Align section within PE file
|
|
*
|
|
* @v offset Unaligned offset
|
|
* @ret aligned_offset Aligned offset
|
|
*/
|
|
static unsigned long efi_file_align ( unsigned long offset ) {
|
|
return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) );
|
|
}
|
|
|
|
/**
|
|
* Align section within PE image
|
|
*
|
|
* @v offset Unaligned offset
|
|
* @ret aligned_offset Aligned offset
|
|
*/
|
|
static unsigned long efi_image_align ( unsigned long offset ) {
|
|
return ( ( offset + EFI_IMAGE_ALIGN - 1 ) & ~( EFI_IMAGE_ALIGN - 1 ) );
|
|
}
|
|
|
|
/**
|
|
* Generate entry in PE relocation table
|
|
*
|
|
* @v pe_reltab PE relocation table
|
|
* @v rva RVA
|
|
* @v size Size of relocation entry
|
|
*/
|
|
static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
|
|
unsigned long rva, size_t size ) {
|
|
unsigned long start_rva;
|
|
uint16_t reloc;
|
|
struct pe_relocs *pe_rel;
|
|
uint16_t *relocs;
|
|
|
|
/* Construct */
|
|
start_rva = ( rva & ~0xfff );
|
|
reloc = ( rva & 0xfff );
|
|
switch ( size ) {
|
|
case 8:
|
|
reloc |= 0xa000;
|
|
break;
|
|
case 4:
|
|
reloc |= 0x3000;
|
|
break;
|
|
case 2:
|
|
reloc |= 0x2000;
|
|
break;
|
|
default:
|
|
eprintf ( "Unsupported relocation size %zd\n", size );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Locate or create PE relocation table */
|
|
for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
|
|
if ( pe_rel->start_rva == start_rva )
|
|
break;
|
|
}
|
|
if ( ! pe_rel ) {
|
|
pe_rel = xmalloc ( sizeof ( *pe_rel ) );
|
|
memset ( pe_rel, 0, sizeof ( *pe_rel ) );
|
|
pe_rel->next = *pe_reltab;
|
|
*pe_reltab = pe_rel;
|
|
pe_rel->start_rva = start_rva;
|
|
}
|
|
|
|
/* Expand relocation list if necessary */
|
|
if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
|
|
relocs = pe_rel->relocs;
|
|
} else {
|
|
pe_rel->total_relocs = ( pe_rel->total_relocs ?
|
|
( pe_rel->total_relocs * 2 ) : 256 );
|
|
relocs = xmalloc ( pe_rel->total_relocs *
|
|
sizeof ( pe_rel->relocs[0] ) );
|
|
memset ( relocs, 0,
|
|
pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) );
|
|
memcpy ( relocs, pe_rel->relocs,
|
|
pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) );
|
|
free ( pe_rel->relocs );
|
|
pe_rel->relocs = relocs;
|
|
}
|
|
|
|
/* Store relocation */
|
|
pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
|
|
}
|
|
|
|
/**
|
|
* Calculate size of binary PE relocation table
|
|
*
|
|
* @v pe_reltab PE relocation table
|
|
* @v buffer Buffer to contain binary table, or NULL
|
|
* @ret size Size of binary table
|
|
*/
|
|
static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
|
|
void *buffer ) {
|
|
struct pe_relocs *pe_rel;
|
|
unsigned int num_relocs;
|
|
size_t size;
|
|
size_t total_size = 0;
|
|
|
|
for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
|
|
num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 );
|
|
size = ( sizeof ( uint32_t ) /* VirtualAddress */ +
|
|
sizeof ( uint32_t ) /* SizeOfBlock */ +
|
|
( num_relocs * sizeof ( uint16_t ) ) );
|
|
if ( buffer ) {
|
|
*( (uint32_t *) ( buffer + total_size + 0 ) )
|
|
= pe_rel->start_rva;
|
|
*( (uint32_t *) ( buffer + total_size + 4 ) ) = size;
|
|
memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs,
|
|
( num_relocs * sizeof ( uint16_t ) ) );
|
|
}
|
|
total_size += size;
|
|
}
|
|
|
|
return total_size;
|
|
}
|
|
|
|
/**
|
|
* Read input ELF file
|
|
*
|
|
* @v name File name
|
|
* @v elf ELF file
|
|
*/
|
|
static void read_elf_file ( const char *name, struct elf_file *elf ) {
|
|
static const unsigned char ident[] = {
|
|
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS, ELFDATA2LSB
|
|
};
|
|
struct stat stat;
|
|
const Elf_Ehdr *ehdr;
|
|
const Elf_Shdr *shdr;
|
|
void *data;
|
|
size_t offset;
|
|
unsigned int i;
|
|
int fd;
|
|
|
|
/* Open file */
|
|
fd = open ( name, O_RDONLY );
|
|
if ( fd < 0 ) {
|
|
eprintf ( "Could not open %s: %s\n", name, strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Get file size */
|
|
if ( fstat ( fd, &stat ) < 0 ) {
|
|
eprintf ( "Could not get size of %s: %s\n",
|
|
name, strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
elf->len = stat.st_size;
|
|
|
|
/* Map file */
|
|
data = mmap ( NULL, elf->len, PROT_READ, MAP_SHARED, fd, 0 );
|
|
if ( data == MAP_FAILED ) {
|
|
eprintf ( "Could not map %s: %s\n", name, strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
elf->data = data;
|
|
|
|
/* Close file */
|
|
close ( fd );
|
|
|
|
/* Check header */
|
|
ehdr = elf->data;
|
|
if ( ( elf->len < sizeof ( *ehdr ) ) ||
|
|
( memcmp ( ident, ehdr->e_ident, sizeof ( ident ) ) != 0 ) ) {
|
|
eprintf ( "Invalid ELF header in %s\n", name );
|
|
exit ( 1 );
|
|
}
|
|
elf->ehdr = ehdr;
|
|
|
|
/* Check program headers */
|
|
if ( ( elf->len < ehdr->e_phoff ) ||
|
|
( ( elf->len - ehdr->e_phoff ) <
|
|
( ( ( unsigned int ) ehdr->e_phnum ) * ehdr->e_phentsize ) ) ) {
|
|
eprintf ( "ELF program headers outside file in %s\n", name );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Check section headers */
|
|
for ( i = 0 ; i < ehdr->e_shnum ; i++ ) {
|
|
offset = ( ehdr->e_shoff + ( i * ehdr->e_shentsize ) );
|
|
if ( elf->len < ( offset + sizeof ( *shdr ) ) ) {
|
|
eprintf ( "ELF section header outside file in %s\n",
|
|
name );
|
|
exit ( 1 );
|
|
}
|
|
shdr = ( data + offset );
|
|
if ( ( shdr->sh_type != SHT_NOBITS ) &&
|
|
( ( elf->len < shdr->sh_offset ) ||
|
|
( ( ( elf->len - shdr->sh_offset ) < shdr->sh_size ) ))){
|
|
eprintf ( "ELF section %d outside file in %s\n",
|
|
i, name );
|
|
exit ( 1 );
|
|
}
|
|
if ( shdr->sh_link >= ehdr->e_shnum ) {
|
|
eprintf ( "ELF section %d link section %d out of "
|
|
"range\n", i, shdr->sh_link );
|
|
exit ( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get ELF string
|
|
*
|
|
* @v elf ELF file
|
|
* @v section String table section number
|
|
* @v offset String table offset
|
|
* @ret string ELF string
|
|
*/
|
|
static const char * elf_string ( struct elf_file *elf, unsigned int section,
|
|
size_t offset ) {
|
|
const Elf_Ehdr *ehdr = elf->ehdr;
|
|
const Elf_Shdr *shdr;
|
|
char *string;
|
|
char *last;
|
|
|
|
/* Locate section header */
|
|
if ( section >= ehdr->e_shnum ) {
|
|
eprintf ( "Invalid ELF string section %d\n", section );
|
|
exit ( 1 );
|
|
}
|
|
shdr = ( elf->data + ehdr->e_shoff + ( section * ehdr->e_shentsize ) );
|
|
|
|
/* Sanity check section */
|
|
if ( shdr->sh_type != SHT_STRTAB ) {
|
|
eprintf ( "ELF section %d (type %d) is not a string table\n",
|
|
section, shdr->sh_type );
|
|
exit ( 1 );
|
|
}
|
|
last = ( elf->data + shdr->sh_offset + shdr->sh_size - 1 );
|
|
if ( *last != '\0' ) {
|
|
eprintf ( "ELF section %d is not NUL-terminated\n", section );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Locate string */
|
|
if ( offset >= shdr->sh_size ) {
|
|
eprintf ( "Invalid ELF string offset %zd in section %d\n",
|
|
offset, section );
|
|
exit ( 1 );
|
|
}
|
|
string = ( elf->data + shdr->sh_offset + offset );
|
|
|
|
return string;
|
|
}
|
|
|
|
/**
|
|
* Get section load memory address
|
|
*
|
|
* @v elf ELF file
|
|
* @v shdr ELF section header
|
|
* @v name ELF section name
|
|
* @ret lma Load memory address
|
|
*/
|
|
static unsigned long elf_lma ( struct elf_file *elf, const Elf_Shdr *shdr,
|
|
const char *name ) {
|
|
const Elf_Ehdr *ehdr = elf->ehdr;
|
|
const Elf_Phdr *phdr;
|
|
size_t offset;
|
|
unsigned int i;
|
|
|
|
/* Find containing segment */
|
|
for ( i = 0 ; i < ehdr->e_phnum ; i++ ) {
|
|
offset = ( ehdr->e_phoff + ( i * ehdr->e_phentsize ) );
|
|
phdr = ( elf->data + offset );
|
|
if ( ( phdr->p_type == PT_LOAD ) &&
|
|
( phdr->p_vaddr <= shdr->sh_addr ) &&
|
|
( ( shdr->sh_addr - phdr->p_vaddr + shdr->sh_size ) <=
|
|
phdr->p_memsz ) ) {
|
|
/* Found matching segment */
|
|
return ( phdr->p_paddr +
|
|
( shdr->sh_addr - phdr->p_vaddr ) );
|
|
}
|
|
}
|
|
|
|
eprintf ( "No containing segment for section %s\n", name );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/**
|
|
* Set machine architecture
|
|
*
|
|
* @v elf ELF file
|
|
* @v pe_header PE file header
|
|
*/
|
|
static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) {
|
|
const Elf_Ehdr *ehdr = elf->ehdr;
|
|
uint16_t machine;
|
|
|
|
/* Identify machine architecture */
|
|
switch ( ehdr->e_machine ) {
|
|
case EM_386:
|
|
machine = EFI_IMAGE_MACHINE_IA32;
|
|
break;
|
|
case EM_X86_64:
|
|
machine = EFI_IMAGE_MACHINE_X64;
|
|
break;
|
|
case EM_ARM:
|
|
machine = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED;
|
|
break;
|
|
case EM_AARCH64:
|
|
machine = EFI_IMAGE_MACHINE_AARCH64;
|
|
break;
|
|
case EM_LOONGARCH:
|
|
machine = EFI_IMAGE_MACHINE_LOONGARCH64;
|
|
break;
|
|
case EM_RISCV:
|
|
machine = ( ( ELFCLASS == ELFCLASS64 ) ?
|
|
EFI_IMAGE_MACHINE_RISCV64 :
|
|
EFI_IMAGE_MACHINE_RISCV32 );
|
|
break;
|
|
default:
|
|
eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Set machine architecture */
|
|
pe_header->nt.FileHeader.Machine = machine;
|
|
}
|
|
|
|
/**
|
|
* Process section
|
|
*
|
|
* @v elf ELF file
|
|
* @v shdr ELF section header
|
|
* @v pe_header PE file header
|
|
* @v opts Options
|
|
* @ret new New PE section
|
|
*/
|
|
static struct pe_section * process_section ( struct elf_file *elf,
|
|
const Elf_Shdr *shdr,
|
|
struct pe_header *pe_header,
|
|
struct options *opts ) {
|
|
struct pe_section *new;
|
|
const char *name;
|
|
size_t name_len;
|
|
size_t section_memsz;
|
|
size_t section_filesz;
|
|
uint32_t start;
|
|
uint32_t end;
|
|
uint32_t *code_start;
|
|
uint32_t *data_start;
|
|
uint32_t *code_size;
|
|
uint32_t *data_size;
|
|
uint32_t *bss_size;
|
|
uint32_t *applicable_start;
|
|
uint32_t *applicable_size;
|
|
|
|
/* Get section name */
|
|
name = elf_string ( elf, elf->ehdr->e_shstrndx, shdr->sh_name );
|
|
|
|
/* Identify start and size limit fields from file header */
|
|
code_start = &pe_header->nt.OptionalHeader.BaseOfCode;
|
|
code_size = &pe_header->nt.OptionalHeader.SizeOfCode;
|
|
#if defined(EFI_TARGET32)
|
|
data_start = &pe_header->nt.OptionalHeader.BaseOfData;
|
|
#elif defined(EFI_TARGET64)
|
|
data_start = NULL;
|
|
#endif
|
|
data_size = &pe_header->nt.OptionalHeader.SizeOfInitializedData;
|
|
bss_size = &pe_header->nt.OptionalHeader.SizeOfUninitializedData;
|
|
|
|
/* Allocate PE section */
|
|
section_memsz = shdr->sh_size;
|
|
section_filesz = ( ( shdr->sh_type == SHT_PROGBITS ) ?
|
|
efi_file_align ( section_memsz ) : 0 );
|
|
new = xmalloc ( sizeof ( *new ) + section_filesz );
|
|
memset ( new, 0, sizeof ( *new ) + section_filesz );
|
|
|
|
/* Fill in section header details */
|
|
name_len = strlen ( name );
|
|
if ( name_len > sizeof ( new->hdr.Name ) )
|
|
name_len = sizeof ( new->hdr.Name );
|
|
memcpy ( new->hdr.Name, name, name_len );
|
|
new->hdr.Misc.VirtualSize = section_memsz;
|
|
new->hdr.VirtualAddress = shdr->sh_addr;
|
|
new->hdr.SizeOfRawData = section_filesz;
|
|
if ( shdr->sh_type == SHT_PROGBITS ) {
|
|
if ( opts->hybrid ) {
|
|
new->hdr.PointerToRawData = elf_lma ( elf, shdr, name );
|
|
if ( new->hdr.PointerToRawData == 0 )
|
|
new->hidden = 1;
|
|
} else {
|
|
new->hdr.PointerToRawData = PTRD_AUTO;
|
|
}
|
|
}
|
|
|
|
/* Treat 16-bit sections as hidden in hybrid binaries */
|
|
if ( opts->hybrid && ( strlen ( name ) > 2 ) &&
|
|
( strcmp ( &name[ strlen ( name ) - 2 ], "16" ) == 0 ) ) {
|
|
new->hidden = 1;
|
|
}
|
|
|
|
/* Fill in section characteristics and identify applicable limits */
|
|
if ( ( shdr->sh_type == SHT_PROGBITS ) &&
|
|
( shdr->sh_flags & SHF_WRITE ) ) {
|
|
/* .data-type section */
|
|
new->hdr.Characteristics =
|
|
( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
|
|
EFI_IMAGE_SCN_MEM_NOT_PAGED |
|
|
EFI_IMAGE_SCN_MEM_READ |
|
|
EFI_IMAGE_SCN_MEM_WRITE );
|
|
applicable_start = data_start;
|
|
applicable_size = data_size;
|
|
} else if ( ( shdr->sh_type == SHT_PROGBITS ) &&
|
|
( shdr->sh_flags & SHF_EXECINSTR ) ) {
|
|
/* .text-type section */
|
|
new->hdr.Characteristics =
|
|
( EFI_IMAGE_SCN_CNT_CODE |
|
|
EFI_IMAGE_SCN_MEM_NOT_PAGED |
|
|
EFI_IMAGE_SCN_MEM_EXECUTE |
|
|
EFI_IMAGE_SCN_MEM_READ );
|
|
applicable_start = code_start;
|
|
applicable_size = code_size;
|
|
} else if ( shdr->sh_type == SHT_PROGBITS ) {
|
|
/* .rodata-type section */
|
|
new->hdr.Characteristics =
|
|
( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
|
|
EFI_IMAGE_SCN_MEM_NOT_PAGED |
|
|
EFI_IMAGE_SCN_MEM_READ );
|
|
applicable_start = data_start;
|
|
applicable_size = data_size;
|
|
} else if ( shdr->sh_type == SHT_NOBITS ) {
|
|
/* .bss-type section */
|
|
new->hdr.Characteristics =
|
|
( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA |
|
|
EFI_IMAGE_SCN_MEM_NOT_PAGED |
|
|
EFI_IMAGE_SCN_MEM_READ |
|
|
EFI_IMAGE_SCN_MEM_WRITE );
|
|
applicable_start = data_start;
|
|
applicable_size = bss_size;
|
|
} else {
|
|
eprintf ( "Unrecognised characteristics for section %s\n",
|
|
name );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Copy in section contents */
|
|
if ( shdr->sh_type == SHT_PROGBITS ) {
|
|
memcpy ( new->contents, ( elf->data + shdr->sh_offset ),
|
|
shdr->sh_size );
|
|
}
|
|
|
|
/* Update file header fields */
|
|
start = new->hdr.VirtualAddress;
|
|
if ( ! new->hidden ) {
|
|
pe_header->nt.FileHeader.NumberOfSections++;
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders +=
|
|
sizeof ( new->hdr );
|
|
if ( applicable_start && ( ( *applicable_start == 0 ) ||
|
|
( start < *applicable_start ) ) ) {
|
|
*applicable_start = start;
|
|
}
|
|
if ( applicable_size ) {
|
|
*applicable_size += section_memsz;
|
|
}
|
|
}
|
|
end = efi_image_align ( start + section_memsz );
|
|
if ( end > pe_header->nt.OptionalHeader.SizeOfImage ) {
|
|
pe_header->nt.OptionalHeader.SizeOfImage = end;
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
/**
|
|
* Update image base address
|
|
*
|
|
* @v pe_header PE file header
|
|
* @v pe_sections List of PE sections
|
|
* @v pe_reltab PE relocation table
|
|
*/
|
|
static void update_image_base ( struct pe_header *pe_header,
|
|
struct pe_section *pe_sections,
|
|
struct pe_relocs *pe_reltab ) {
|
|
struct pe_section *section;
|
|
struct pe_relocs *pe_rel;
|
|
unsigned long base = -1UL;
|
|
|
|
/* Set ImageBase to the highest possible value, leaving space
|
|
* for the PE header itself.
|
|
*/
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
if ( ! section->hidden ) {
|
|
if ( base > section->hdr.VirtualAddress )
|
|
base = section->hdr.VirtualAddress;
|
|
}
|
|
}
|
|
base -= EFI_IMAGE_ALIGN;
|
|
pe_header->nt.OptionalHeader.ImageBase = base;
|
|
|
|
/* Adjust RVAs to match ImageBase */
|
|
pe_header->nt.OptionalHeader.AddressOfEntryPoint -= base;
|
|
pe_header->nt.OptionalHeader.BaseOfCode -= base;
|
|
#if defined(EFI_TARGET32)
|
|
pe_header->nt.OptionalHeader.BaseOfData -= base;
|
|
#endif
|
|
pe_header->nt.OptionalHeader.SizeOfImage -= base;
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
section->hdr.VirtualAddress -= base;
|
|
}
|
|
for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
|
|
pe_rel->start_rva -= base;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process relocation record
|
|
*
|
|
* @v elf ELF file
|
|
* @v shdr ELF section header
|
|
* @v syms Symbol table
|
|
* @v nsyms Number of symbol table entries
|
|
* @v rel Relocation record
|
|
* @v pe_reltab PE relocation table to fill in
|
|
* @v opts Options
|
|
*/
|
|
static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
|
|
const Elf_Sym *syms, unsigned int nsyms,
|
|
const Elf_Rel *rel, struct pe_relocs **pe_reltab,
|
|
struct options *opts ) {
|
|
unsigned int type = ELF_R_TYPE ( rel->r_info );
|
|
unsigned int sym = ELF_R_SYM ( rel->r_info );
|
|
unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type );
|
|
size_t offset = ( shdr->sh_addr + rel->r_offset );
|
|
|
|
/* Look up symbol and process relocation */
|
|
if ( sym >= nsyms ) {
|
|
eprintf ( "Symbol out of range\n" );
|
|
exit ( 1 );
|
|
}
|
|
if ( syms[sym].st_shndx == SHN_ABS ) {
|
|
/* Skip absolute symbols; the symbol value won't
|
|
* change when the object is loaded.
|
|
*/
|
|
} else {
|
|
switch ( mrel ) {
|
|
case ELF_MREL ( EM_386, R_386_NONE ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_NONE ) :
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_NONE ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_NONE ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_NULL ) :
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_NONE ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_NONE ) :
|
|
/* Ignore dummy relocations used by REQUIRE_SYMBOL() */
|
|
break;
|
|
case ELF_MREL ( EM_386, R_386_32 ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_ABS32 ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_32 ) :
|
|
/* Generate a 4-byte PE relocation */
|
|
generate_pe_reloc ( pe_reltab, offset, 4 );
|
|
break;
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_64 ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_ABS64 ) :
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_64 ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_64 ) :
|
|
/* Generate an 8-byte PE relocation */
|
|
generate_pe_reloc ( pe_reltab, offset, 8 );
|
|
break;
|
|
case ELF_MREL ( EM_386, R_386_PC32 ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_CALL ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_REL32 ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) :
|
|
case ELF_MREL ( EM_ARM, R_ARM_V4BX ):
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) :
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_PLT32 ) :
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_GOTPCRELX ) :
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_REX_GOTPCRELX ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_CALL26 ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_JUMP26 ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_LO21 ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_ADR_PREL_PG_HI21 ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_ADD_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST8_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST16_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST32_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST64_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_AARCH64, R_AARCH64_LDST128_ABS_LO12_NC ) :
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_B16):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_B21):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_B26):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_PCALA_HI20 ):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_PCALA_LO12 ):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_GOT_PC_HI20 ):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_GOT_PC_LO12 ):
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_PCREL20_S2 ):
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_BRANCH ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_JAL ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_PCREL_HI20 ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_PCREL_LO12_I ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_PCREL_LO12_S ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_RVC_BRANCH ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_RVC_JUMP ) :
|
|
/* Skip PC-relative relocations; all relative
|
|
* offsets remain unaltered when the object is
|
|
* loaded.
|
|
*/
|
|
break;
|
|
case ELF_MREL ( EM_LOONGARCH, R_LARCH_RELAX ):
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_RELAX ) :
|
|
/* Relocation can be relaxed (optimized out).
|
|
* Ignore it for now.
|
|
*/
|
|
break;
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_ADD32 ) :
|
|
case ELF_MREL ( EM_RISCV, R_RISCV_SUB32 ) :
|
|
/* Ignore label difference relocations since
|
|
* we do not perform any relocations that can
|
|
* result in altered label differences.
|
|
*/
|
|
break;
|
|
case ELF_MREL ( EM_X86_64, R_X86_64_32 ) :
|
|
/* Ignore 32-bit relocations in a hybrid
|
|
* 32-bit BIOS and 64-bit UEFI binary,
|
|
* otherwise fall through to treat as an
|
|
* unknown type.
|
|
*/
|
|
if ( opts->hybrid )
|
|
break;
|
|
/* fallthrough */
|
|
default:
|
|
eprintf ( "Unrecognised relocation type %d\n", type );
|
|
exit ( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process relocation records
|
|
*
|
|
* @v elf ELF file
|
|
* @v shdr ELF section header
|
|
* @v stride Relocation record size
|
|
* @v pe_reltab PE relocation table to fill in
|
|
* @v opts Options
|
|
*/
|
|
static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
|
|
size_t stride, struct pe_relocs **pe_reltab,
|
|
struct options *opts ) {
|
|
const Elf_Shdr *symtab;
|
|
const Elf_Sym *syms;
|
|
const Elf_Rel *rel;
|
|
unsigned int nsyms;
|
|
unsigned int nrels;
|
|
unsigned int i;
|
|
|
|
/* Identify symbol table */
|
|
symtab = ( elf->data + elf->ehdr->e_shoff +
|
|
( shdr->sh_link * elf->ehdr->e_shentsize ) );
|
|
syms = ( elf->data + symtab->sh_offset );
|
|
nsyms = ( symtab->sh_size / sizeof ( syms[0] ) );
|
|
|
|
/* Process each relocation */
|
|
rel = ( elf->data + shdr->sh_offset );
|
|
nrels = ( shdr->sh_size / stride );
|
|
for ( i = 0 ; i < nrels ; i++ ) {
|
|
process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab, opts );
|
|
rel = ( ( ( const void * ) rel ) + stride );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process note records
|
|
*
|
|
* @v elf ELF file
|
|
* @v shdr ELF section header
|
|
* @v opts Options
|
|
*/
|
|
static void process_notes ( struct elf_file *elf, const Elf_Shdr *shdr,
|
|
struct options *opts ) {
|
|
const struct {
|
|
Elf_Nhdr hdr;
|
|
char name[4];
|
|
} __attribute__ (( packed )) *note;
|
|
static const char name[4] = { 'i', 'P', 'X', 'E' };
|
|
size_t remaining;
|
|
|
|
/* Process each note */
|
|
note = ( elf->data + shdr->sh_offset );
|
|
remaining = shdr->sh_size;
|
|
while ( remaining ) {
|
|
|
|
/* Sanity check */
|
|
if ( ( remaining < sizeof ( *note ) ) ||
|
|
( note->hdr.n_namesz != sizeof ( name ) ) ||
|
|
( note->hdr.n_descsz != 0 ) ||
|
|
( memcmp ( note->name, name, sizeof ( name ) ) != 0 ) ) {
|
|
eprintf ( "Invalid iPXE note\n" );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Handle note type */
|
|
switch ( note->hdr.n_type ) {
|
|
case IPXE_NOTE_DISKLOG:
|
|
opts->disklog = 1;
|
|
break;
|
|
default:
|
|
eprintf ( "Unrecognised iPXE note type %#x\n",
|
|
note->hdr.n_type );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Move to next note */
|
|
note = ( ( ( void * ) note ) + sizeof ( *note ) );
|
|
remaining -= sizeof ( *note );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create relocations section
|
|
*
|
|
* @v pe_header PE file header
|
|
* @v pe_reltab PE relocation table
|
|
* @ret section Relocation section
|
|
*/
|
|
static struct pe_section *
|
|
create_reloc_section ( struct pe_header *pe_header,
|
|
struct pe_relocs *pe_reltab ) {
|
|
struct pe_section *reloc;
|
|
size_t section_memsz;
|
|
size_t section_filesz;
|
|
EFI_IMAGE_DATA_DIRECTORY *relocdir;
|
|
|
|
/* Allocate PE section */
|
|
section_memsz = output_pe_reltab ( pe_reltab, NULL );
|
|
section_filesz = efi_file_align ( section_memsz );
|
|
reloc = xmalloc ( sizeof ( *reloc ) + section_filesz );
|
|
memset ( reloc, 0, sizeof ( *reloc ) + section_filesz );
|
|
|
|
/* Fill in section header details */
|
|
strncpy ( ( char * ) reloc->hdr.Name, ".reloc",
|
|
sizeof ( reloc->hdr.Name ) );
|
|
reloc->hdr.Misc.VirtualSize = section_memsz;
|
|
reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage;
|
|
reloc->hdr.SizeOfRawData = section_filesz;
|
|
reloc->hdr.PointerToRawData = PTRD_AUTO;
|
|
reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
|
|
EFI_IMAGE_SCN_MEM_DISCARDABLE |
|
|
EFI_IMAGE_SCN_MEM_NOT_PAGED |
|
|
EFI_IMAGE_SCN_MEM_READ );
|
|
|
|
/* Copy in section contents */
|
|
output_pe_reltab ( pe_reltab, reloc->contents );
|
|
|
|
/* Update file header details */
|
|
pe_header->nt.FileHeader.NumberOfSections++;
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr );
|
|
pe_header->nt.OptionalHeader.SizeOfImage +=
|
|
efi_image_align ( section_memsz );
|
|
relocdir = &(pe_header->nt.OptionalHeader.DataDirectory
|
|
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]);
|
|
relocdir->VirtualAddress = reloc->hdr.VirtualAddress;
|
|
relocdir->Size = section_memsz;
|
|
|
|
return reloc;
|
|
}
|
|
|
|
/**
|
|
* Create debug section
|
|
*
|
|
* @v pe_header PE file header
|
|
* @ret section Debug section
|
|
*/
|
|
static struct pe_section *
|
|
create_debug_section ( struct pe_header *pe_header, const char *filename ) {
|
|
struct pe_section *debug;
|
|
size_t section_memsz;
|
|
size_t section_filesz;
|
|
EFI_IMAGE_DATA_DIRECTORY *debugdir;
|
|
struct {
|
|
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug;
|
|
EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds;
|
|
char name[32];
|
|
} *contents;
|
|
|
|
/* Allocate PE section */
|
|
section_filesz = section_memsz = sizeof ( *contents );
|
|
debug = xmalloc ( sizeof ( *debug ) + section_filesz );
|
|
memset ( debug, 0, sizeof ( *debug ) + section_filesz );
|
|
contents = ( void * ) debug->contents;
|
|
|
|
/* Place at end of headers */
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( *contents );
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders =
|
|
efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders -= sizeof ( *contents );
|
|
|
|
/* Fill in section header details */
|
|
strncpy ( ( char * ) debug->hdr.Name, ".debug",
|
|
sizeof ( debug->hdr.Name ) );
|
|
debug->hdr.Misc.VirtualSize = section_memsz;
|
|
debug->hdr.VirtualAddress =
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders;
|
|
debug->hdr.SizeOfRawData = section_filesz;
|
|
debug->hdr.PointerToRawData =
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders;
|
|
debug->hidden = 1;
|
|
|
|
/* Create section contents */
|
|
contents->debug.TimeDateStamp = 0x10d1a884;
|
|
contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW;
|
|
contents->debug.SizeOfData =
|
|
( sizeof ( *contents ) - sizeof ( contents->debug ) );
|
|
contents->debug.RVA = ( debug->hdr.VirtualAddress +
|
|
offsetof ( typeof ( *contents ), rsds ) );
|
|
contents->debug.FileOffset = contents->debug.RVA;
|
|
contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS;
|
|
snprintf ( contents->name, sizeof ( contents->name ), "%s",
|
|
filename );
|
|
|
|
/* Update file header details */
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( *contents );
|
|
debugdir = &(pe_header->nt.OptionalHeader.DataDirectory
|
|
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
|
|
debugdir->VirtualAddress = debug->hdr.VirtualAddress;
|
|
debugdir->Size = sizeof ( contents->debug );
|
|
|
|
return debug;
|
|
}
|
|
|
|
/**
|
|
* Write out PE file
|
|
*
|
|
* @v pe_header PE file header
|
|
* @v pe_sections List of PE sections
|
|
* @v pe Output file
|
|
*/
|
|
static void write_pe_file ( struct pe_header *pe_header,
|
|
struct pe_section *pe_sections,
|
|
FILE *pe ) {
|
|
struct pe_section *section;
|
|
unsigned long hdrmax;
|
|
unsigned long fpos;
|
|
unsigned long fposmax;
|
|
unsigned int count = 0;
|
|
|
|
/* Extend header length to reach first explicitly placed section */
|
|
hdrmax = -1UL;
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
if ( ( section->hdr.PointerToRawData != PTRD_AUTO ) &&
|
|
( section->hdr.SizeOfRawData > 0 ) &&
|
|
( ! section->hidden ) &&
|
|
( hdrmax > section->hdr.PointerToRawData ) ) {
|
|
hdrmax = section->hdr.PointerToRawData;
|
|
}
|
|
}
|
|
if ( ( hdrmax != -1UL ) &&
|
|
( pe_header->nt.OptionalHeader.SizeOfHeaders < hdrmax ) ) {
|
|
pe_header->nt.OptionalHeader.SizeOfHeaders = hdrmax;
|
|
}
|
|
|
|
/* Align length of headers */
|
|
fpos = fposmax = pe_header->nt.OptionalHeader.SizeOfHeaders =
|
|
efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
|
|
if ( fpos > hdrmax ) {
|
|
eprintf ( "Cannot fit %lx bytes of headers before section at "
|
|
"file offset %lx\n", fpos, hdrmax );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Assign raw data pointers */
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
if ( section->hdr.PointerToRawData == PTRD_AUTO ) {
|
|
fpos = fposmax;
|
|
} else {
|
|
fpos = section->hdr.PointerToRawData;
|
|
}
|
|
section->hdr.PointerToRawData = fpos;
|
|
fpos += section->hdr.SizeOfRawData;
|
|
fpos = efi_file_align ( fpos );
|
|
if ( fpos > fposmax )
|
|
fposmax = fpos;
|
|
}
|
|
|
|
/* Write sections */
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
if ( ( section->hdr.PointerToRawData & ( EFI_FILE_ALIGN - 1 ) )
|
|
&& ( ! section->hidden ) ) {
|
|
eprintf ( "Section %.8s file offset %x is "
|
|
"misaligned\n", section->hdr.Name,
|
|
section->hdr.PointerToRawData );
|
|
exit ( 1 );
|
|
}
|
|
if ( fseek ( pe, section->hdr.PointerToRawData,
|
|
SEEK_SET ) != 0 ) {
|
|
eprintf ( "Could not seek to %x: %s\n",
|
|
section->hdr.PointerToRawData,
|
|
strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
if ( section->hdr.SizeOfRawData &&
|
|
( fwrite ( section->contents, section->hdr.SizeOfRawData,
|
|
1, pe ) != 1 ) ) {
|
|
eprintf ( "Could not write section %.8s: %s\n",
|
|
section->hdr.Name, strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
}
|
|
|
|
/* Write file header */
|
|
if ( fseek ( pe, 0, SEEK_SET ) != 0 ) {
|
|
eprintf ( "Could not rewind: %s\n", strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
if ( fwrite ( pe_header,
|
|
( offsetof ( typeof ( *pe_header ), nt.OptionalHeader ) +
|
|
pe_header->nt.FileHeader.SizeOfOptionalHeader ),
|
|
1, pe ) != 1 ) {
|
|
perror ( "Could not write PE header" );
|
|
exit ( 1 );
|
|
}
|
|
|
|
/* Write section headers */
|
|
for ( section = pe_sections ; section ; section = section->next ) {
|
|
if ( section->hidden )
|
|
continue;
|
|
if ( fwrite ( §ion->hdr, sizeof ( section->hdr ),
|
|
1, pe ) != 1 ) {
|
|
perror ( "Could not write section header" );
|
|
exit ( 1 );
|
|
}
|
|
count++;
|
|
}
|
|
assert ( count == pe_header->nt.FileHeader.NumberOfSections );
|
|
}
|
|
|
|
/**
|
|
* Convert ELF to PE
|
|
*
|
|
* @v elf_name ELF file name
|
|
* @v pe_name PE file name
|
|
* @v opts Options
|
|
*/
|
|
static void elf2pe ( const char *elf_name, const char *pe_name,
|
|
struct options *opts ) {
|
|
char pe_name_tmp[ strlen ( pe_name ) + 1 ];
|
|
struct pe_relocs *pe_reltab = NULL;
|
|
struct pe_section *pe_sections = NULL;
|
|
struct pe_section **next_pe_section = &pe_sections;
|
|
struct pe_header pe_header;
|
|
struct elf_file elf;
|
|
const Elf_Shdr *shdr;
|
|
size_t offset;
|
|
unsigned int i;
|
|
FILE *pe;
|
|
|
|
/* Create a modifiable copy of the PE name */
|
|
memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) );
|
|
|
|
/* Read ELF file */
|
|
read_elf_file ( elf_name, &elf );
|
|
|
|
/* Initialise the PE header */
|
|
memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) );
|
|
set_machine ( &elf, &pe_header );
|
|
pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry;
|
|
pe_header.nt.OptionalHeader.Subsystem = opts->subsystem;
|
|
|
|
/* Process input sections */
|
|
for ( i = 0 ; i < elf.ehdr->e_shnum ; i++ ) {
|
|
offset = ( elf.ehdr->e_shoff + ( i * elf.ehdr->e_shentsize ) );
|
|
shdr = ( elf.data + offset );
|
|
|
|
/* Process section */
|
|
if ( shdr->sh_flags & SHF_ALLOC ) {
|
|
|
|
/* Create output section */
|
|
*(next_pe_section) = process_section ( &elf, shdr,
|
|
&pe_header,
|
|
opts );
|
|
next_pe_section = &(*next_pe_section)->next;
|
|
|
|
} else if ( shdr->sh_type == SHT_REL ) {
|
|
|
|
/* Process .rel relocations */
|
|
process_relocs ( &elf, shdr, sizeof ( Elf_Rel ),
|
|
&pe_reltab, opts );
|
|
|
|
} else if ( shdr->sh_type == SHT_RELA ) {
|
|
|
|
/* Process .rela relocations */
|
|
process_relocs ( &elf, shdr, sizeof ( Elf_Rela ),
|
|
&pe_reltab, opts );
|
|
|
|
} else if ( shdr->sh_type == SHT_NOTE ) {
|
|
|
|
/* Process .note records */
|
|
process_notes ( &elf, shdr, opts );
|
|
}
|
|
}
|
|
|
|
/* Update image base address */
|
|
update_image_base ( &pe_header, pe_sections, pe_reltab );
|
|
|
|
/* Update OEM information */
|
|
if ( opts->disklog )
|
|
pe_header.dos.e_oeminfo |= IPXE_OEM_INFO_DISKLOG;
|
|
|
|
/* Create the .reloc section */
|
|
*(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab );
|
|
next_pe_section = &(*next_pe_section)->next;
|
|
|
|
/* Create the .debug section */
|
|
*(next_pe_section) = create_debug_section ( &pe_header,
|
|
basename ( pe_name_tmp ) );
|
|
next_pe_section = &(*next_pe_section)->next;
|
|
|
|
/* Write out PE file */
|
|
pe = fopen ( pe_name, "w" );
|
|
if ( ! pe ) {
|
|
eprintf ( "Could not open %s for writing: %s\n",
|
|
pe_name, strerror ( errno ) );
|
|
exit ( 1 );
|
|
}
|
|
write_pe_file ( &pe_header, pe_sections, pe );
|
|
fclose ( pe );
|
|
|
|
/* Unmap ELF file */
|
|
munmap ( elf.data, elf.len );
|
|
}
|
|
|
|
/**
|
|
* Print help
|
|
*
|
|
* @v program_name Program name
|
|
*/
|
|
static void print_help ( const char *program_name ) {
|
|
eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n",
|
|
program_name );
|
|
}
|
|
|
|
/**
|
|
* Parse command-line options
|
|
*
|
|
* @v argc Argument count
|
|
* @v argv Argument list
|
|
* @v opts Options structure to populate
|
|
*/
|
|
static int parse_options ( const int argc, char **argv,
|
|
struct options *opts ) {
|
|
char *end;
|
|
int c;
|
|
|
|
while (1) {
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{ "subsystem", required_argument, NULL, 's' },
|
|
{ "hybrid", no_argument, NULL, 'H' },
|
|
{ "help", 0, NULL, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
if ( ( c = getopt_long ( argc, argv, "s:Hh",
|
|
long_options,
|
|
&option_index ) ) == -1 ) {
|
|
break;
|
|
}
|
|
|
|
switch ( c ) {
|
|
case 's':
|
|
opts->subsystem = strtoul ( optarg, &end, 0 );
|
|
if ( *end || ( ! *optarg ) ) {
|
|
eprintf ( "Invalid subsytem \"%s\"\n",
|
|
optarg );
|
|
exit ( 2 );
|
|
}
|
|
break;
|
|
case 'H':
|
|
opts->hybrid = 1;
|
|
break;
|
|
case 'h':
|
|
print_help ( argv[0] );
|
|
exit ( 0 );
|
|
case '?':
|
|
default:
|
|
exit ( 2 );
|
|
}
|
|
}
|
|
return optind;
|
|
}
|
|
|
|
int main ( int argc, char **argv ) {
|
|
struct options opts = {
|
|
.subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
|
|
};
|
|
int infile_index;
|
|
const char *infile;
|
|
const char *outfile;
|
|
|
|
/* Parse command-line arguments */
|
|
infile_index = parse_options ( argc, argv, &opts );
|
|
if ( argc != ( infile_index + 2 ) ) {
|
|
print_help ( argv[0] );
|
|
exit ( 2 );
|
|
}
|
|
infile = argv[infile_index];
|
|
outfile = argv[infile_index + 1];
|
|
|
|
/* Convert file */
|
|
elf2pe ( infile, outfile, &opts );
|
|
|
|
return 0;
|
|
}
|