[Skiboot] [PATCH v9 06/30] elf: endian conversions

Nicholas Piggin npiggin at gmail.com
Fri Nov 29 17:18:07 AEDT 2019


Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
 core/flash.c    |  36 +++++---
 core/init.c     |  92 ++++++++++---------
 include/elf.h   | 235 +++++++++++++++++++++++++++++++++---------------
 include/stack.h |   4 +
 include/types.h |   4 +
 5 files changed, 248 insertions(+), 123 deletions(-)

diff --git a/core/flash.c b/core/flash.c
index 4e10229d5..de748641f 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -523,24 +523,38 @@ const char *flash_map_resource_name(enum resource_id id)
 
 static size_t sizeof_elf_from_hdr(void *buf)
 {
-	struct elf_hdr *elf = (struct elf_hdr*) buf;
+	struct elf_hdr *elf = (struct elf_hdr *)buf;
 	size_t sz = 0;
 
 	BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf_hdr));
-	BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf64_hdr));
-	BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf32_hdr));
+	BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf64be_hdr));
+	BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf32be_hdr));
 
 	if (elf->ei_ident == ELF_IDENT) {
 		if (elf->ei_class == ELF_CLASS_64) {
-			struct elf64_hdr *elf64 = (struct elf64_hdr*) buf;
-			sz = le64_to_cpu(elf64->e_shoff) +
-				((uint32_t)le16_to_cpu(elf64->e_shentsize) *
-				 (uint32_t)le16_to_cpu(elf64->e_shnum));
+			if (elf->ei_data == ELF_DATA_LSB) {
+				struct elf64le_hdr *kh = (struct elf64le_hdr *)buf;
+				sz = le64_to_cpu(kh->e_shoff) +
+					((uint32_t)le16_to_cpu(kh->e_shentsize) *
+					 (uint32_t)le16_to_cpu(kh->e_shnum));
+			} else {
+				struct elf64be_hdr *kh = (struct elf64be_hdr *)buf;
+				sz = be64_to_cpu(kh->e_shoff) +
+					((uint32_t)be16_to_cpu(kh->e_shentsize) *
+					 (uint32_t)be16_to_cpu(kh->e_shnum));
+			}
 		} else if (elf->ei_class == ELF_CLASS_32) {
-			struct elf32_hdr *elf32 = (struct elf32_hdr*) buf;
-			sz = le32_to_cpu(elf32->e_shoff) +
-				(le16_to_cpu(elf32->e_shentsize) *
-				 le16_to_cpu(elf32->e_shnum));
+			if (elf->ei_data == ELF_DATA_LSB) {
+				struct elf32le_hdr *kh = (struct elf32le_hdr *)buf;
+				sz = le32_to_cpu(kh->e_shoff) +
+					(le16_to_cpu(kh->e_shentsize) *
+					 le16_to_cpu(kh->e_shnum));
+			} else {
+				struct elf32be_hdr *kh = (struct elf32be_hdr *)buf;
+				sz = be32_to_cpu(kh->e_shoff) +
+					(be16_to_cpu(kh->e_shentsize) *
+					 be16_to_cpu(kh->e_shnum));
+			}
 		}
 	}
 
diff --git a/core/init.c b/core/init.c
index a7083456e..d90695c16 100644
--- a/core/init.c
+++ b/core/init.c
@@ -89,9 +89,9 @@ static void checksum_romem(void);
 
 static bool try_load_elf64_le(struct elf_hdr *header)
 {
-	struct elf64_hdr *kh = (struct elf64_hdr *)header;
+	struct elf64le_hdr *kh = (struct elf64le_hdr *)header;
 	uint64_t load_base = (uint64_t)kh;
-	struct elf64_phdr *ph;
+	struct elf64le_phdr *ph;
 	unsigned int i;
 
 	printf("INIT: 64-bit LE kernel discovered\n");
@@ -103,7 +103,7 @@ static bool try_load_elf64_le(struct elf_hdr *header)
 	 * to work for the Linux Kernel because it's a fairly dumb ELF
 	 * but it will not work for any ELF binary.
 	 */
-	ph = (struct elf64_phdr *)(load_base + le64_to_cpu(kh->e_phoff));
+	ph = (struct elf64le_phdr *)(load_base + le64_to_cpu(kh->e_phoff));
 	for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) {
 		if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
 			continue;
@@ -137,23 +137,24 @@ static bool try_load_elf64_le(struct elf_hdr *header)
 
 static bool try_load_elf64(struct elf_hdr *header)
 {
-	struct elf64_hdr *kh = (struct elf64_hdr *)header;
+	struct elf64be_hdr *kh = (struct elf64be_hdr *)header;
+	struct elf64le_hdr *khle = (struct elf64le_hdr *)header;
 	uint64_t load_base = (uint64_t)kh;
-	struct elf64_phdr *ph;
-	struct elf64_shdr *sh;
+	struct elf64be_phdr *ph;
+	struct elf64be_shdr *sh;
 	unsigned int i;
 
 	/* Check it's a ppc64 LE ELF */
-	if (kh->ei_ident == ELF_IDENT		&&
-	    kh->ei_data == ELF_DATA_LSB		&&
-	    kh->e_machine == le16_to_cpu(ELF_MACH_PPC64)) {
+	if (khle->ei_ident == ELF_IDENT		&&
+	    khle->ei_data == ELF_DATA_LSB	&&
+	    le16_to_cpu(khle->e_machine) == ELF_MACH_PPC64) {
 		return try_load_elf64_le(header);
 	}
 
 	/* Check it's a ppc64 ELF */
 	if (kh->ei_ident != ELF_IDENT		||
 	    kh->ei_data != ELF_DATA_MSB		||
-	    kh->e_machine != ELF_MACH_PPC64) {
+	    be16_to_cpu(kh->e_machine) != ELF_MACH_PPC64) {
 		prerror("INIT: Kernel doesn't look like an ppc64 ELF\n");
 		return false;
 	}
@@ -165,16 +166,18 @@ static bool try_load_elf64(struct elf_hdr *header)
 	 * to work for the Linux Kernel because it's a fairly dumb ELF
 	 * but it will not work for any ELF binary.
 	 */
-	ph = (struct elf64_phdr *)(load_base + kh->e_phoff);
-	for (i = 0; i < kh->e_phnum; i++, ph++) {
-		if (ph->p_type != ELF_PTYPE_LOAD)
+	ph = (struct elf64be_phdr *)(load_base + be64_to_cpu(kh->e_phoff));
+	for (i = 0; i < be16_to_cpu(kh->e_phnum); i++, ph++) {
+		if (be32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
 			continue;
-		if (ph->p_vaddr > kh->e_entry ||
-		    (ph->p_vaddr + ph->p_memsz) < kh->e_entry)
+		if (be64_to_cpu(ph->p_vaddr) > be64_to_cpu(kh->e_entry) ||
+		    (be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_memsz)) <
+		    be64_to_cpu(kh->e_entry))
 			continue;
 
 		/* Get our entry */
-		kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset;
+		kernel_entry = be64_to_cpu(kh->e_entry) -
+			be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_offset);
 		break;
 	}
 
@@ -189,23 +192,27 @@ static bool try_load_elf64(struct elf_hdr *header)
 	 * 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)
+	sh = (struct elf64be_shdr *)(load_base + be64_to_cpu(kh->e_shoff));
+	for (i = 0; i < be16_to_cpu(kh->e_shnum); i++, sh++) {
+		if (be64_to_cpu(sh->sh_addr) <= be64_to_cpu(kh->e_entry) &&
+		    (be64_to_cpu(sh->sh_addr) + be64_to_cpu(sh->sh_size)) >
+		    be64_to_cpu(kh->e_entry))
 			break;
 	}
 
-	if (i == kh->e_shnum || !(sh->sh_flags & ELF_SFLAGS_X)) {
+	if (i == be16_to_cpu(kh->e_shnum) ||
+			!(be64_to_cpu(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 = kernel_entry -
+			be64_to_cpu(ph->p_vaddr) + be64_to_cpu(ph->p_offset);
 	}
 
 	kernel_entry += load_base;
 	kernel_32bit = false;
 
-	kernel_size = kh->e_shoff +
-		((uint32_t)kh->e_shentsize * (uint32_t)kh->e_shnum);
+	kernel_size = be64_to_cpu(kh->e_shoff) +
+		((uint32_t)be16_to_cpu(kh->e_shentsize) *
+		 (uint32_t)be16_to_cpu(kh->e_shnum));
 
 	printf("INIT: 64-bit kernel entry at 0x%llx, size 0x%lx\n",
 	       kernel_entry, kernel_size);
@@ -215,9 +222,9 @@ static bool try_load_elf64(struct elf_hdr *header)
 
 static bool try_load_elf32_le(struct elf_hdr *header)
 {
-	struct elf32_hdr *kh = (struct elf32_hdr *)header;
+	struct elf32le_hdr *kh = (struct elf32le_hdr *)header;
 	uint64_t load_base = (uint64_t)kh;
-	struct elf32_phdr *ph;
+	struct elf32le_phdr *ph;
 	unsigned int i;
 
 	printf("INIT: 32-bit LE kernel discovered\n");
@@ -229,7 +236,7 @@ static bool try_load_elf32_le(struct elf_hdr *header)
 	 * to work for the Linux Kernel because it's a fairly dumb ELF
 	 * but it will not work for any ELF binary.
 	 */
-	ph = (struct elf32_phdr *)(load_base + le32_to_cpu(kh->e_phoff));
+	ph = (struct elf32le_phdr *)(load_base + le32_to_cpu(kh->e_phoff));
 	for (i = 0; i < le16_to_cpu(kh->e_phnum); i++, ph++) {
 		if (le32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
 			continue;
@@ -259,22 +266,23 @@ static bool try_load_elf32_le(struct elf_hdr *header)
 
 static bool try_load_elf32(struct elf_hdr *header)
 {
-	struct elf32_hdr *kh = (struct elf32_hdr *)header;
+	struct elf32be_hdr *kh = (struct elf32be_hdr *)header;
+	struct elf32le_hdr *khle = (struct elf32le_hdr *)header;
 	uint64_t load_base = (uint64_t)kh;
-	struct elf32_phdr *ph;
+	struct elf32be_phdr *ph;
 	unsigned int i;
 
 	/* Check it's a ppc32 LE ELF */
-	if (header->ei_ident == ELF_IDENT		&&
-	    header->ei_data == ELF_DATA_LSB		&&
-	    header->e_machine == le16_to_cpu(ELF_MACH_PPC32)) {
+	if (khle->ei_ident == ELF_IDENT		&&
+	    khle->ei_data == ELF_DATA_LSB	&&
+	    le16_to_cpu(khle->e_machine) == ELF_MACH_PPC32) {
 		return try_load_elf32_le(header);
 	}
 
 	/* Check it's a ppc32 ELF */
-	if (header->ei_ident != ELF_IDENT		||
-	    header->ei_data != ELF_DATA_MSB		||
-	    header->e_machine != ELF_MACH_PPC32) {
+	if (kh->ei_ident != ELF_IDENT		||
+	    kh->ei_data != ELF_DATA_MSB		||
+	    be16_to_cpu(kh->e_machine) != ELF_MACH_PPC32) {
 		prerror("INIT: Kernel doesn't look like an ppc32 ELF\n");
 		return false;
 	}
@@ -286,16 +294,18 @@ static bool try_load_elf32(struct elf_hdr *header)
 	 * to work for the Linux Kernel because it's a fairly dumb ELF
 	 * but it will not work for any ELF binary.
 	 */
-	ph = (struct elf32_phdr *)(load_base + kh->e_phoff);
-	for (i = 0; i < kh->e_phnum; i++, ph++) {
-		if (ph->p_type != ELF_PTYPE_LOAD)
+	ph = (struct elf32be_phdr *)(load_base + be32_to_cpu(kh->e_phoff));
+	for (i = 0; i < be16_to_cpu(kh->e_phnum); i++, ph++) {
+		if (be32_to_cpu(ph->p_type) != ELF_PTYPE_LOAD)
 			continue;
-		if (ph->p_vaddr > kh->e_entry ||
-		    (ph->p_vaddr + ph->p_memsz) < kh->e_entry)
+		if (be32_to_cpu(ph->p_vaddr) > be32_to_cpu(kh->e_entry) ||
+		    (be32_to_cpu(ph->p_vaddr) + be32_to_cpu(ph->p_memsz)) <
+		    be32_to_cpu(kh->e_entry))
 			continue;
 
 		/* Get our entry */
-		kernel_entry = kh->e_entry - ph->p_vaddr + ph->p_offset;
+		kernel_entry = be32_to_cpu(kh->e_entry) -
+			be32_to_cpu(ph->p_vaddr) + be32_to_cpu(ph->p_offset);
 		break;
 	}
 
diff --git a/include/elf.h b/include/elf.h
index 93524bb99..f3e071de1 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -5,11 +5,16 @@
 #define __ELF_H
 
 #include <stdint.h>
+#include <types.h>
 
 /* Generic ELF header */
 struct elf_hdr {
 	uint32_t ei_ident;
+#if HAVE_BIG_ENDIAN
 #define ELF_IDENT	0x7F454C46
+#else
+#define ELF_IDENT	0x464C457F
+#endif
 	uint8_t ei_class;
 #define ELF_CLASS_32	1
 #define ELF_CLASS_64	2
@@ -18,68 +23,190 @@ struct elf_hdr {
 #define ELF_DATA_MSB	2
 	uint8_t ei_version;
 	uint8_t ei_pad[9];
-	uint16_t e_type;
-	uint16_t e_machine;
+};
+
 #define ELF_MACH_PPC32	0x14
 #define ELF_MACH_PPC64	0x15
-	uint32_t e_version;
+
+/* 64-bit ELF header */
+struct elf64be_hdr {
+	uint32_t ei_ident;
+	uint8_t ei_class;
+	uint8_t ei_data;
+	uint8_t ei_version;
+	uint8_t ei_pad[9];
+	__be16 e_type;
+	__be16 e_machine;
+	__be32 e_version;
+	__be64 e_entry;
+	__be64 e_phoff;
+	__be64 e_shoff;
+	__be32 e_flags;
+	__be16 e_ehsize;
+	__be16 e_phentsize;
+	__be16 e_phnum;
+	__be16 e_shentsize;
+	__be16 e_shnum;
+	__be16 e_shstrndx;
+};
+
+/* 64-bit ELF program header */
+struct elf64be_phdr {
+	__be32 p_type;
+#define ELF_PTYPE_LOAD	1
+	__be32 p_flags;
+#define ELF_PFLAGS_R	0x4
+#define ELF_PFLAGS_W	0x2
+#define ELF_PFLAGS_X	0x1
+	__be64 p_offset;
+	__be64 p_vaddr;
+	__be64 p_paddr;
+	__be64 p_filesz;
+	__be64 p_memsz;
+	__be64 p_align;
+};
+
+/* 64-bit ELF section header */
+struct elf64be_shdr {
+	__be32 sh_name;
+	__be32 sh_type;
+	__be64 sh_flags;
+#define ELF_SFLAGS_X	0x4
+#define ELF_SFLAGS_A	0x2
+#define ELF_SFLAGS_W	0x1
+	__be64 sh_addr;
+	__be64 sh_offset;
+	__be64 sh_size;
+	__be32 sh_link;
+	__be32 sh_info;
+	__be64 sh_addralign;
+	__be64 sh_entsize;
+};
+
+/* 32-bit ELF header */
+struct elf32be_hdr {
+	uint32_t ei_ident;
+	uint8_t ei_class;
+	uint8_t ei_data;
+	uint8_t ei_version;
+	uint8_t ei_pad[9];
+	__be16 e_type;
+	__be16 e_machine;
+	__be32 e_version;
+	__be32 e_entry;
+	__be32 e_phoff;
+	__be32 e_shoff;
+	__be32 e_flags;
+	__be16 e_ehsize;
+	__be16 e_phentsize;
+	__be16 e_phnum;
+	__be16 e_shentsize;
+	__be16 e_shnum;
+	__be16 e_shstrndx;
+};
+
+/* 32-bit ELF program header*/
+struct elf32be_phdr {
+	__be32 p_type;
+	__be32 p_offset;
+	__be32 p_vaddr;
+	__be32 p_paddr;
+	__be32 p_filesz;
+	__be32 p_memsz;
+	__be32 p_flags;
+	__be32 p_align;
 };
 
 /* 64-bit ELF header */
-struct elf64_hdr {
+struct elf64le_hdr {
 	uint32_t ei_ident;
 	uint8_t ei_class;
 	uint8_t ei_data;
 	uint8_t ei_version;
 	uint8_t ei_pad[9];
-	uint16_t e_type;
-	uint16_t e_machine;
-	uint32_t e_version;
-	uint64_t e_entry;
-	uint64_t e_phoff;
-	uint64_t e_shoff;
-	uint32_t e_flags;
-	uint16_t e_ehsize;
-	uint16_t e_phentsize;
-	uint16_t e_phnum;
-	uint16_t e_shentsize;
-	uint16_t e_shnum;
-	uint16_t e_shstrndx;
+	__le16 e_type;
+	__le16 e_machine;
+	__le32 e_version;
+	__le64 e_entry;
+	__le64 e_phoff;
+	__le64 e_shoff;
+	__le32 e_flags;
+	__le16 e_ehsize;
+	__le16 e_phentsize;
+	__le16 e_phnum;
+	__le16 e_shentsize;
+	__le16 e_shnum;
+	__le16 e_shstrndx;
 };
 
 /* 64-bit ELF program header */
-struct elf64_phdr {
-	uint32_t p_type;
+struct elf64le_phdr {
+	__le32 p_type;
 #define ELF_PTYPE_LOAD	1
-	uint32_t p_flags;
+	__le32 p_flags;
 #define ELF_PFLAGS_R	0x4
 #define ELF_PFLAGS_W	0x2
 #define ELF_PFLAGS_X	0x1
-	uint64_t p_offset;
-	uint64_t p_vaddr;
-	uint64_t p_paddr;
-	uint64_t p_filesz;
-	uint64_t p_memsz;
-	uint64_t p_align;
+	__le64 p_offset;
+	__le64 p_vaddr;
+	__le64 p_paddr;
+	__le64 p_filesz;
+	__le64 p_memsz;
+	__le64 p_align;
 };
 
 /* 64-bit ELF section header */
-struct elf64_shdr {
-	uint32_t sh_name;
-	uint32_t sh_type;
-	uint64_t sh_flags;
+struct elf64le_shdr {
+	__le32 sh_name;
+	__le32 sh_type;
+	__le64 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;
-	int64_t sh_entsize;
+	__le64 sh_addr;
+	__le64 sh_offset;
+	__le64 sh_size;
+	__le32 sh_link;
+	__le32 sh_info;
+	__le64 sh_addralign;
+	__le64 sh_entsize;
+};
+
+/* 32-bit ELF header */
+struct elf32le_hdr {
+	uint32_t ei_ident;
+	uint8_t ei_class;
+	uint8_t ei_data;
+	uint8_t ei_version;
+	uint8_t ei_pad[9];
+	__le16 e_type;
+	__le16 e_machine;
+	__le32 e_version;
+	__le32 e_entry;
+	__le32 e_phoff;
+	__le32 e_shoff;
+	__le32 e_flags;
+	__le16 e_ehsize;
+	__le16 e_phentsize;
+	__le16 e_phnum;
+	__le16 e_shentsize;
+	__le16 e_shnum;
+	__le16 e_shstrndx;
+};
+
+/* 32-bit ELF program header*/
+struct elf32le_phdr {
+	__le32 p_type;
+	__le32 p_offset;
+	__le32 p_vaddr;
+	__le32 p_paddr;
+	__le32 p_filesz;
+	__le32 p_memsz;
+	__le32 p_flags;
+	__le32 p_align;
 };
 
+
 /* Some relocation related stuff used in relocate.c */
 struct elf64_dyn {
 	int64_t	 d_tag;
@@ -101,39 +228,5 @@ struct elf64_rela {
 /* relocs we support */
 #define R_PPC64_RELATIVE	22
 
-/* 32-bit ELF header */
-struct elf32_hdr {
-	uint32_t ei_ident;
-	uint8_t ei_class;
-	uint8_t ei_data;
-	uint8_t ei_version;
-	uint8_t ei_pad[9];
-	uint16_t e_type;
-	uint16_t e_machine;
-	uint32_t e_version;
-	uint32_t e_entry;
-	uint32_t e_phoff;
-	uint32_t e_shoff;
-	uint32_t e_flags;
-	uint16_t e_ehsize;
-	uint16_t e_phentsize;
-	uint16_t e_phnum;
-	uint16_t e_shentsize;
-	uint16_t e_shnum;
-	uint16_t e_shstrndx;
-};
-
-/* 32-bit ELF program header*/
-struct elf32_phdr {
-	uint32_t p_type;
-	uint32_t p_offset;
-	uint32_t p_vaddr;
-	uint32_t p_paddr;
-	uint32_t p_filesz;
-	uint32_t p_memsz;
-	uint32_t p_flags;
-	uint32_t p_align;
-};
-
 
 #endif /* __ELF_H */
diff --git a/include/stack.h b/include/stack.h
index 09d22adb6..b0d6df17d 100644
--- a/include/stack.h
+++ b/include/stack.h
@@ -11,7 +11,11 @@
 #define STACK_ENTRY_RESET	0x0100	/* System reset */
 #define STACK_ENTRY_SOFTPATCH	0x1500	/* Soft patch (denorm emulation) */
 
+#if HAVE_BIG_ENDIAN
 #define STACK_TOC_OFFSET	40
+#else
+#define STACK_TOC_OFFSET	24
+#endif
 
 /* Safety/ABI gap at top of stack */
 #define STACK_TOP_GAP		0x100
diff --git a/include/types.h b/include/types.h
index a7a45f5cb..e7660f6b7 100644
--- a/include/types.h
+++ b/include/types.h
@@ -11,5 +11,9 @@ typedef beint16_t __be16;
 typedef beint32_t __be32;
 typedef beint64_t __be64;
 
+typedef leint16_t __le16;
+typedef leint32_t __le32;
+typedef leint64_t __le64;
+
 #endif /* __TYPES_H */
 
-- 
2.23.0



More information about the Skiboot mailing list