diff mbox

Load ELF64 binaries correctly

Message ID 54C7D1FC.6090902@freebsd.org
State Superseded
Headers show

Commit Message

Nathan Whitehorn Jan. 27, 2015, 5:59 p.m. UTC
On 01/22/15 09:02, Benjamin Herrenschmidt wrote:
> On Wed, 2015-01-21 at 08:45 -0800, Nathan Whitehorn wrote:
>> The attached patch fixes the big-endian ELF64 loader in skiboot to
>> handle the fact that the ELF entry point is specified to point to a
>> function descriptor describing the entry point rather than the entry
>> point itself. (I haven't set it up to load the TOC base pointer though)
>> This is required to load the FreeBSD kernel as a skiboot payload. The
>> patch does not touch the little-endian loader since I'm not sure if the
>> ELFv2 spec still has function descriptors or not.
> An additional problem is that the Linux 64-bit BE kernel vmlinux doesn't
> have a correct function descriptor as an entry point :-( So that patch
> breaks loading a raw BE vmlinux... We probably need a quirk to recognize
> such an image, possibly via the fact that the value that would be in the
> function descriptor cannot possibly be a valid entry point as part of
> the image.

Here's a second version of the patch that can load both Linux and 
FreeBSD. It iterates through the ELF section table and, if the entry 
point points to an executable section, treats the entry point as the 
first instruction to run. Otherwise, it treats it as a function descriptor.
-Nathan

>
> As for LE, there are no descriptors.
>
> Cheers,
> Ben.
>
>> -Nathan
>> _______________________________________________
>> Skiboot mailing list
>> Skiboot@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/skiboot
>
diff mbox

Patch

diff --git a/core/init.c b/core/init.c
index 2c7e30c..e75b5a3 100644
--- a/core/init.c
+++ b/core/init.c
@@ -111,6 +111,7 @@  static bool try_load_elf64(struct elf_hdr *header)
 	struct elf64_hdr *kh = (struct elf64_hdr *)header;
 	uint64_t load_base = (uint64_t)kh;
 	struct elf64_phdr *ph;
+	struct elf64_shdr *sh;
 	unsigned int i;
 
 	/* Check it's a ppc64 LE ELF */
@@ -152,6 +153,27 @@  static bool try_load_elf64(struct elf_hdr *header)
 		prerror("INIT: Failed to find kernel entry !\n");
 		return false;
 	}
+
+	/* For the normal big-endian ELF ABI, the kernel entry points
+	 * to a function descriptor in the data section. Linux instead
+	 * has it point directly to code. Test whether it is pointing
+	 * into an executable section or not to figure this out. Default
+	 * to assuming it obeys the ABI.
+	 */
+	sh = (struct elf64_shdr *)(load_base + kh->e_shoff);
+	for (i = 0; i < kh->e_shnum; i++, sh++) {
+		if (sh->sh_addr > kh->e_entry ||
+		    (sh->sh_addr + sh->sh_size) <= kh->e_entry)
+			continue;
+
+		break;
+	}
+
+	if (i == kh->e_shnum || !(sh->sh_flags & ELF_SFLAGS_X)) {
+		kernel_entry = *(uint64_t *)(kernel_entry + load_base);
+		kernel_entry = kernel_entry - ph->p_vaddr + ph->p_offset;
+	}
+
 	kernel_entry += load_base;
 	kernel_32bit = false;