From 0d04635ef0e4fa0850716c03163e120c63125df7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 5 Apr 2023 13:41:53 +0100 Subject: [efi] Remove redundant zero padding in PE header Hybrid bzImage and UEFI binaries (such as wimboot) require the PE header to be kept as small as possible, since the bzImage header starts at a fixed offset 0x1f1. The PE header currently includes 128 bytes of zero padding between the DOS and NT header portions. This padding has been present since commit 81d92c6 ("[efi] Add EFI image format and basic runtime environment") first added support for EFI images in iPXE, and was included on the basis of matching the observed behaviour of the Microsoft toolchain. There appears to be no requirement for this padding to exist: EDK2 binaries built with gcc include only 64 bytes of zero padding, Linux kernel binaries include 66 bytes of non-zero padding, and wimboot binaries include no padding at all. Remove the unnecessary padding between the DOS and NT header portions to minimise the overall size of the PE header. Signed-off-by: Michael Brown --- src/util/elf2efi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/util') diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index cea9abf86..159f14e76 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -191,7 +191,6 @@ struct pe_relocs { struct pe_header { EFI_IMAGE_DOS_HEADER dos; - uint8_t padding[128]; EFI_IMAGE_NT_HEADERS nt; }; -- cgit v1.2.3-55-g7522 From 1e4c3789e95393a1b87a0b2147bed82a87576673 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Apr 2023 16:44:36 +0100 Subject: [efi] Shrink size of data directory in PE header Hybrid bzImage and UEFI binaries (such as wimboot) require the PE header to be kept as small as possible, since the bzImage header starts at a fixed offset 0x1f1. The EFI_IMAGE_OPTIONAL_HEADER structures in PeImage.h define an optional header containing 16 data directory entries, of which the last eight are unused in binaries that we create. Shrink the data directory to contain only the first eight entries, to minimise the overall size of the PE header. Signed-off-by: Michael Brown --- src/util/elf2efi.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/util') diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 159f14e76..a68154e4c 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -168,6 +168,9 @@ */ #define EFI_IMAGE_ALIGN 0x1000 +/** Number of data directory entries */ +#define NUMBER_OF_DIRECTORY_ENTRIES 8 + struct elf_file { void *data; size_t len; @@ -204,7 +207,11 @@ static struct pe_header efi_pe_header = { .FileHeader = { .TimeDateStamp = 0x10d1a884, .SizeOfOptionalHeader = - sizeof ( efi_pe_header.nt.OptionalHeader ), + ( 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_EXECUTABLE_IMAGE ), @@ -217,8 +224,7 @@ static struct pe_header efi_pe_header = { .FileAlignment = EFI_FILE_ALIGN, .SizeOfImage = EFI_IMAGE_ALIGN, .SizeOfHeaders = sizeof ( efi_pe_header ), - .NumberOfRvaAndSizes = - EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, + .NumberOfRvaAndSizes = NUMBER_OF_DIRECTORY_ENTRIES, }, }, }; @@ -930,7 +936,10 @@ static void write_pe_file ( struct pe_header *pe_header, } /* Write file header */ - if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 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 ); } -- cgit v1.2.3-55-g7522 From 9fb28080d97fac1061660befacfad8caaa2bc522 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 5 Apr 2023 13:29:29 +0100 Subject: [efi] Allow elf2efi to be used for hybrid binaries Hybrid 32-bit BIOS and 64-bit UEFI binaries (such as wimboot) may include R_X86_64_32 relocation records for the 32-bit BIOS portions. These should be ignored when generating PE relocation records, since they apply only to code that cannot be executed within the context of the 64-bit UEFI binary, and creating a 4-byte relocation record is invalid in a binary that may be relocated anywhere within the 64-bit address space (see commit 907cffb "[efi] Disallow R_X86_64_32 relocations"). Add a "--hybrid" option to elf2efi, which will cause R_X86_64_32 relocation records to be silently discarded. Signed-off-by: Michael Brown --- src/util/elf2efi.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'src/util') diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index a68154e4c..61ac0e277 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -231,7 +231,10 @@ static struct pe_header efi_pe_header = { /** Command-line options */ struct options { + /** PE32+ subsystem type */ unsigned int subsystem; + /** Create hybrid BIOS/UEFI binary */ + int hybrid; }; /** @@ -678,10 +681,12 @@ static struct pe_section * process_section ( struct elf_file *elf, * @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 ) { + 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 ); @@ -744,6 +749,15 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, * loaded. */ 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 ); @@ -758,9 +772,11 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, * @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 ) { + size_t stride, struct pe_relocs **pe_reltab, + struct options *opts ) { const Elf_Shdr *symtab; const Elf_Sym *syms; const Elf_Rel *rel; @@ -778,7 +794,7 @@ static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr, 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 ); + process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab, opts ); rel = ( ( ( const void * ) rel ) + stride ); } } @@ -977,6 +993,7 @@ static void write_pe_file ( struct pe_header *pe_header, * * @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 ) { @@ -1020,13 +1037,13 @@ static void elf2pe ( const char *elf_name, const char *pe_name, /* Process .rel relocations */ process_relocs ( &elf, shdr, sizeof ( Elf_Rel ), - &pe_reltab ); + &pe_reltab, opts ); } else if ( shdr->sh_type == SHT_RELA ) { /* Process .rela relocations */ process_relocs ( &elf, shdr, sizeof ( Elf_Rela ), - &pe_reltab ); + &pe_reltab, opts ); } } @@ -1079,11 +1096,12 @@ static int parse_options ( const int argc, char **argv, 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:h", + if ( ( c = getopt_long ( argc, argv, "s:Hh", long_options, &option_index ) ) == -1 ) { break; @@ -1098,6 +1116,9 @@ static int parse_options ( const int argc, char **argv, exit ( 2 ); } break; + case 'H': + opts->hybrid = 1; + break; case 'h': print_help ( argv[0] ); exit ( 0 ); -- cgit v1.2.3-55-g7522 From bd13697446e758d5fed6afcba8f3e9883b2e2de7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Apr 2023 16:55:28 +0100 Subject: [efi] Allow for sections to be excluded from the generated PE file Hybrid bzImage and UEFI binaries (such as wimboot) include a bzImage header within a section starting at offset zero, with the PE header effectively occupying unused space within this section. This section should not appear as a named section in the resulting PE file. Allow for the existence of hidden sections that do not result in a section header being written to the PE file. Signed-off-by: Michael Brown --- src/util/elf2efi.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src/util') diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 61ac0e277..5b3e785f5 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -181,6 +181,7 @@ struct pe_section { struct pe_section *next; EFI_IMAGE_SECTION_HEADER hdr; void ( * fixup ) ( struct pe_section *section ); + int hidden; uint8_t contents[0]; }; @@ -641,10 +642,12 @@ static struct pe_section * process_section ( struct elf_file *elf, /* Update RVA limits */ start = new->hdr.VirtualAddress; end = ( start + new->hdr.Misc.VirtualSize ); - if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) - *applicable_start = start; - if ( *applicable_end < end ) - *applicable_end = end; + if ( ! new->hidden ) { + if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) + *applicable_start = start; + if ( *applicable_end < end ) + *applicable_end = end; + } if ( data_start < code_end ) data_start = code_end; if ( data_mid < data_start ) @@ -664,8 +667,11 @@ static struct pe_section * process_section ( struct elf_file *elf, ( data_end - data_mid ); /* Update remaining file header fields */ - pe_header->nt.FileHeader.NumberOfSections++; - pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); + if ( ! new->hidden ) { + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += + sizeof ( new->hdr ); + } pe_header->nt.OptionalHeader.SizeOfImage = efi_image_align ( data_end ); @@ -935,6 +941,7 @@ static void write_pe_file ( struct pe_header *pe_header, FILE *pe ) { struct pe_section *section; unsigned long fpos = 0; + unsigned int count = 0; /* Align length of headers */ fpos = pe_header->nt.OptionalHeader.SizeOfHeaders = @@ -962,12 +969,16 @@ static void write_pe_file ( struct pe_header *pe_header, /* 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 ); /* Write sections */ for ( section = pe_sections ; section ; section = section->next ) { -- cgit v1.2.3-55-g7522