[efi] Use load memory address as file offset for PE sections

Mimic the behaviour of "objcopy -Obinary" by using the load memory
address (extracted from the ELF program headers) to determine the
physical placement of the section within the resulting PE file.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c
index 4231b1c..bacc08d 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
+
 struct elf_file {
 	void *data;
 	size_t len;
@@ -419,6 +424,14 @@
 	}
 	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 ) );
@@ -489,6 +502,39 @@
 }
 
 /**
+ * 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
@@ -581,6 +627,9 @@
 	new->hdr.Misc.VirtualSize = section_memsz;
 	new->hdr.VirtualAddress = shdr->sh_addr;
 	new->hdr.SizeOfRawData = section_filesz;
+	if ( shdr->sh_type == SHT_PROGBITS ) {
+		new->hdr.PointerToRawData = elf_lma ( elf, shdr, name );
+	}
 
 	/* Fill in section characteristics and update RVA limits */
 	if ( ( shdr->sh_type == SHT_PROGBITS ) &&
@@ -820,6 +869,7 @@
 	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 |
@@ -886,6 +936,7 @@
 	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 |
@@ -937,7 +988,11 @@
 	/* Assign raw data pointers */
 	for ( section = pe_sections ; section ; section = section->next ) {
 		if ( section->hdr.SizeOfRawData ) {
-			section->hdr.PointerToRawData = fpos;
+			if ( section->hdr.PointerToRawData == PTRD_AUTO ) {
+				section->hdr.PointerToRawData = fpos;
+			} else {
+				fpos = section->hdr.PointerToRawData;
+			}
 			fpos += section->hdr.SizeOfRawData;
 			fpos = efi_file_align ( fpos );
 		}
@@ -947,6 +1002,12 @@
 
 	/* 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",