[Skiboot] [PATCH] Load ELF64 binaries correctly

Nathan Whitehorn nwhitehorn at freebsd.org
Wed Jan 28 05:00:12 AEDT 2015


On 01/27/15 09:59, Nathan Whitehorn wrote:
> 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

Sorry, missed a file in the patch. Here's the right one.
-Nathan
-------------- next part --------------
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;
 
diff --git a/include/elf.h b/include/elf.h
index 0a52f3e..c600f7f 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -76,6 +76,23 @@ struct elf64_phdr {
 	uint64_t p_align;
 };
 
+/* 64-bit ELF section header */
+struct elf64_shdr {
+	uint32_t sh_name;
+	uint32_t sh_type;
+	uint64_t sh_flags;
+#define ELF_SFLAGS_X	0x4
+#define ELF_SFLAGS_A	0x2
+#define ELF_SFLAGS_W	0x1
+	uint64_t sh_addr;
+	uint64_t sh_offset;
+	uint64_t sh_size;
+	uint32_t sh_link;
+	uint32_t sh_info;
+	uint64_t sh_addralign;
+	uint64_t sh_entsize;
+};
+
 /* Some relocation related stuff used in relocate.c */
 struct elf64_dyn {
 	int64_t	 d_tag;


More information about the Skiboot mailing list