[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