]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Allow elf2efi to be used for hybrid binaries
authorMichael Brown <mcb30@ipxe.org>
Wed, 5 Apr 2023 12:29:29 +0000 (13:29 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 10 Apr 2023 15:51:51 +0000 (16:51 +0100)
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 <mcb30@ipxe.org>
src/util/elf2efi.c

index a68154e4c545d8401b566953a138409c0b64b383..61ac0e277fde03b5ffb3fda018a8238deca35051 100644 (file)
@@ -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 );