summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2023-11-22 15:57:05 +0100
committerMichael Brown2023-11-23 14:37:47 +0100
commitb30a0987e239baf4689adbf79035b7f737eef873 (patch)
tree39b01f9309f44962c618024b9dfaa901b66c20ba
parent[efi] Mark PE images as large address aware (diff)
downloadipxe-b30a0987e239baf4689adbf79035b7f737eef873.tar.gz
ipxe-b30a0987e239baf4689adbf79035b7f737eef873.tar.xz
ipxe-b30a0987e239baf4689adbf79035b7f737eef873.zip
[efi] Use load memory address as file offset for hybrid binaries
Hybrid bzImage and UEFI binaries (such as wimboot) may be loaded as a single contiguous blob without reference to the PE headers, and the placement of sections within the PE file must therefore be known at link time. Use the load memory address (extracted from the ELF program headers) to determine the physical placement of the section within the PE file when generating a hybrid binary. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/util/elf2efi.c84
1 files changed, 77 insertions, 7 deletions
diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c
index b13ff43e..682580f4 100644
--- a/src/util/elf2efi.c
+++ b/src/util/elf2efi.c
@@ -50,6 +50,7 @@
#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_Sym Elf32_Sym
#define Elf_Addr Elf32_Addr
@@ -65,6 +66,7 @@
#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_Sym Elf64_Sym
#define Elf_Addr Elf64_Addr
@@ -168,6 +170,9 @@
*/
#define EFI_IMAGE_ALIGN 0x1000
+/** Set PointerToRawData automatically */
+#define PTRD_AUTO 0xffffffffUL
+
/** Number of data directory entries */
#define NUMBER_OF_DIRECTORY_ENTRIES 8
@@ -429,6 +434,14 @@ static void read_elf_file ( const char *name, struct elf_file *elf ) {
}
elf->ehdr = ehdr;
+ /* Check program headers */
+ if ( ( elf->len < ehdr->e_phoff ) ||
+ ( ( elf->len - ehdr->e_phoff ) < ( 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 ) );
@@ -499,6 +512,39 @@ static const char * elf_string ( struct elf_file *elf, unsigned int section,
}
/**
+ * 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
@@ -540,11 +586,13 @@ static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) {
* @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 pe_header *pe_header,
+ struct options *opts ) {
struct pe_section *new;
const char *name;
size_t name_len;
@@ -591,6 +639,13 @@ static struct pe_section * process_section ( struct elf_file *elf,
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 );
+ } else {
+ new->hdr.PointerToRawData = PTRD_AUTO;
+ }
+ }
/* Fill in section characteristics and update RVA limits */
if ( ( shdr->sh_type == SHT_PROGBITS ) &&
@@ -835,6 +890,7 @@ create_reloc_section ( struct pe_header *pe_header,
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 |
@@ -901,6 +957,7 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) {
debug->hdr.Misc.VirtualSize = section_memsz;
debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage;
debug->hdr.SizeOfRawData = section_filesz;
+ debug->hdr.PointerToRawData = PTRD_AUTO;
debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
EFI_IMAGE_SCN_MEM_DISCARDABLE |
EFI_IMAGE_SCN_MEM_NOT_PAGED |
@@ -944,19 +1001,25 @@ static void write_pe_file ( struct pe_header *pe_header,
FILE *pe ) {
struct pe_section *section;
unsigned long fpos = 0;
+ unsigned long fposmax = 0;
unsigned int count = 0;
/* Align length of headers */
- fpos = pe_header->nt.OptionalHeader.SizeOfHeaders =
+ fpos = fposmax = pe_header->nt.OptionalHeader.SizeOfHeaders =
efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
/* Assign raw data pointers */
for ( section = pe_sections ; section ; section = section->next ) {
- if ( section->hdr.SizeOfRawData ) {
- section->hdr.PointerToRawData = fpos;
- fpos += section->hdr.SizeOfRawData;
- fpos = efi_file_align ( fpos );
+ 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;
if ( section->fixup )
section->fixup ( section );
}
@@ -985,6 +1048,12 @@ static void write_pe_file ( struct pe_header *pe_header,
/* Write sections */
for ( section = pe_sections ; section ; section = section->next ) {
+ if ( section->hdr.PointerToRawData & ( EFI_FILE_ALIGN - 1 ) ) {
+ 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",
@@ -1044,7 +1113,8 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
/* Create output section */
*(next_pe_section) = process_section ( &elf, shdr,
- &pe_header );
+ &pe_header,
+ opts );
next_pe_section = &(*next_pe_section)->next;
} else if ( shdr->sh_type == SHT_REL ) {