tools: relocate-rela: Fix ELF decoding on big-endian hosts

The new ELF decoding logic assumed that the target binary has the same
endianness as the host, which broke building ARM64 firmware binaries on
big-endian machines.

This commit fixes the ELF64 decoding to be host-endianness-neutral, and
applies the same changes to the ELF32 decoding. It does not fix the
microblaze-specific dynamic symbol decoding.

It also corrects the functions used for byte swapping in rela_elf64()
and rela_elf32(). The result is the same, but semantically the code is
converting bytes read from a foreign-endianness file to host byte order.

Fixes: 4c9e2d643460 ("tools: relocate-rela: Read rela start/end directly from ELF")
Fixes: a1405d9cfedb ("tools: relocate-rela: Check that relocation works only for EM_AARCH64")
Signed-off-by: Samuel Holland <samuel@sholland.org>
Link: https://lore.kernel.org/r/20220715064026.54551-1-samuel@sholland.org
Signed-off-by: Michal Simek <michal.simek@amd.com>
diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c
index 090fb1a..fcf3fb2 100644
--- a/tools/relocate-rela.c
+++ b/tools/relocate-rela.c
@@ -60,7 +60,9 @@
 {
 	size_t size;
 	Elf64_Ehdr header;
-	uint64_t section_header_base, section_header_size, sh_offset, sh_size;
+	uint64_t section_header_base, section_header_size;
+	uint64_t sh_addr, sh_offset, sh_size;
+	Elf64_Half sh_index, sh_num;
 	Elf64_Shdr *sh_table; /* Elf symbol table */
 	int ret, i, machine;
 	char *sh_str;
@@ -76,7 +78,7 @@
 		return 25;
 	}
 
-	machine = header.e_machine;
+	machine = le16_to_cpu(header.e_machine);
 	debug("Machine\t%d\n", machine);
 
 	if (machine != EM_AARCH64) {
@@ -84,9 +86,10 @@
 		return 30;
 	}
 
-	text_base = header.e_entry;
-	section_header_base = header.e_shoff;
-	section_header_size = header.e_shentsize * header.e_shnum;
+	text_base = le64_to_cpu(header.e_entry);
+	section_header_base = le64_to_cpu(header.e_shoff);
+	section_header_size = le16_to_cpu(header.e_shentsize) *
+			      le16_to_cpu(header.e_shnum);
 
 	sh_table = malloc(section_header_size);
 	if (!sh_table) {
@@ -114,9 +117,9 @@
 		return 27;
 	}
 
-	sh_size = sh_table[header.e_shstrndx].sh_size;
-	debug("e_shstrndx\t0x%08x\n", header.e_shstrndx);
-	debug("sh_size\t\t0x%08lx\n", sh_size);
+	sh_index = le16_to_cpu(header.e_shstrndx);
+	sh_size = le64_to_cpu(sh_table[sh_index].sh_size);
+	debug("e_shstrndx %x, sh_size %lx\n", sh_index, sh_size);
 
 	sh_str = malloc(sh_size);
 	if (!sh_str) {
@@ -130,9 +133,8 @@
 	 * Specifies the byte offset from the beginning of the file
 	 * to the first byte in the section.
 	 */
-	sh_offset = sh_table[header.e_shstrndx].sh_offset;
-
-	debug("sh_offset\t0x%08x\n", header.e_shnum);
+	sh_offset = le64_to_cpu(sh_table[sh_index].sh_offset);
+	sh_num = le16_to_cpu(header.e_shnum);
 
 	ret = fseek(felf, sh_offset, SEEK_SET);
 	if (ret) {
@@ -153,18 +155,22 @@
 		return 30;
 	}
 
-	for (i = 0; i < header.e_shnum; i++) {
-		/* fprintf(stderr, "%s\n", sh_str + sh_table[i].sh_name); Debug only */
-		if (!strcmp(".rela.dyn", (sh_str + sh_table[i].sh_name))) {
+	for (i = 0; i < sh_num; i++) {
+		char *sh_name = sh_str + le32_to_cpu(sh_table[i].sh_name);
+
+		debug("%s\n", sh_name);
+
+		sh_addr = le64_to_cpu(sh_table[i].sh_addr);
+		sh_offset = le64_to_cpu(sh_table[i].sh_offset);
+		sh_size = le64_to_cpu(sh_table[i].sh_size);
+
+		if (!strcmp(".rela.dyn", sh_name)) {
 			debug("Found section\t\".rela_dyn\"\n");
-			debug(" at addr\t0x%08x\n",
-			      (unsigned int)sh_table[i].sh_addr);
-			debug(" at offset\t0x%08x\n",
-			      (unsigned int)sh_table[i].sh_offset);
-			debug(" of size\t0x%08x\n",
-			      (unsigned int)sh_table[i].sh_size);
-			rela_start = sh_table[i].sh_addr;
-			rela_end = rela_start + sh_table[i].sh_size;
+			debug(" at addr\t0x%08x\n", sh_addr);
+			debug(" at offset\t0x%08x\n", sh_offset);
+			debug(" of size\t0x%08x\n", sh_size);
+			rela_start = sh_addr;
+			rela_end = rela_start + sh_size;
 			break;
 		}
 	}
@@ -188,7 +194,9 @@
 {
 	size_t size;
 	Elf32_Ehdr header;
-	uint64_t section_header_base, section_header_size, sh_offset, sh_size;
+	uint64_t section_header_base, section_header_size;
+	uint32_t sh_addr, sh_offset, sh_size;
+	Elf32_Half sh_index, sh_num;
 	Elf32_Shdr *sh_table; /* Elf symbol table */
 	int ret, i, machine;
 	char *sh_str;
@@ -204,7 +212,7 @@
 		return 25;
 	}
 
-	machine = header.e_machine;
+	machine = le16_to_cpu(header.e_machine);
 	debug("Machine %d\n", machine);
 
 	if (machine != EM_MICROBLAZE) {
@@ -212,14 +220,10 @@
 		return 30;
 	}
 
-	text_base = header.e_entry;
-	section_header_base = header.e_shoff;
-
-	debug("Section header base %x\n", section_header_base);
-
-	section_header_size = header.e_shentsize * header.e_shnum;
-
-	debug("Section header size %d\n", section_header_size);
+	text_base = le32_to_cpu(header.e_entry);
+	section_header_base = le32_to_cpu(header.e_shoff);
+	section_header_size = le16_to_cpu(header.e_shentsize) *
+			      le16_to_cpu(header.e_shnum);
 
 	sh_table = malloc(section_header_size);
 	if (!sh_table) {
@@ -247,8 +251,9 @@
 		return 27;
 	}
 
-	sh_size = sh_table[header.e_shstrndx].sh_size;
-	debug("e_shstrndx %x, sh_size %lx\n", header.e_shstrndx, sh_size);
+	sh_index = le16_to_cpu(header.e_shstrndx);
+	sh_size = le32_to_cpu(sh_table[sh_index].sh_size);
+	debug("e_shstrndx %x, sh_size %lx\n", sh_index, sh_size);
 
 	sh_str = malloc(sh_size);
 	if (!sh_str) {
@@ -262,9 +267,8 @@
 	 * Specifies the byte offset from the beginning of the file
 	 * to the first byte in the section.
 	 */
-	sh_offset = sh_table[header.e_shstrndx].sh_offset;
-
-	debug("sh_offset %x\n", header.e_shnum);
+	sh_offset = le32_to_cpu(sh_table[sh_index].sh_offset);
+	sh_num = le16_to_cpu(header.e_shnum);
 
 	ret = fseek(felf, sh_offset, SEEK_SET);
 	if (ret) {
@@ -277,7 +281,7 @@
 
 	size = fread(sh_str, 1, sh_size, felf);
 	if (size != sh_size) {
-		fprintf(stderr, "%s: Can't read section: %lx/%lx\n",
+		fprintf(stderr, "%s: Can't read section: %lx/%x\n",
 			argv[0], size, sh_size);
 		free(sh_str);
 		free(sh_table);
@@ -285,22 +289,29 @@
 		return 30;
 	}
 
-	for (i = 0; i < header.e_shnum; i++) {
-		debug("%s\n", sh_str + sh_table[i].sh_name);
-		if (!strcmp(".rela.dyn", (sh_str + sh_table[i].sh_name))) {
+	for (i = 0; i < sh_num; i++) {
+		char *sh_name = sh_str + le32_to_cpu(sh_table[i].sh_name);
+
+		debug("%s\n", sh_name);
+
+		sh_addr = le64_to_cpu(sh_table[i].sh_addr);
+		sh_offset = le64_to_cpu(sh_table[i].sh_offset);
+		sh_size = le64_to_cpu(sh_table[i].sh_size);
+
+		if (!strcmp(".rela.dyn", sh_name)) {
 			debug("Found section\t\".rela_dyn\"\n");
-			debug(" at addr\t0x%08x\n", (unsigned int)sh_table[i].sh_addr);
-			debug(" at offset\t0x%08x\n", (unsigned int)sh_table[i].sh_offset);
-			debug(" of size\t0x%08x\n", (unsigned int)sh_table[i].sh_size);
-			rela_start = sh_table[i].sh_addr;
-			rela_end = rela_start + sh_table[i].sh_size;
+			debug(" at addr\t0x%08x\n", sh_addr);
+			debug(" at offset\t0x%08x\n", sh_offset);
+			debug(" of size\t0x%08x\n", sh_size);
+			rela_start = sh_addr;
+			rela_end = rela_start + sh_size;
 		}
-		if (!strcmp(".dynsym", (sh_str + sh_table[i].sh_name))) {
+		if (!strcmp(".dynsym", sh_name)) {
 			debug("Found section\t\".dynsym\"\n");
-			debug(" at addr\t0x%08x\n", (unsigned int)sh_table[i].sh_addr);
-			debug(" at offset\t0x%08x\n", (unsigned int)sh_table[i].sh_offset);
-			debug(" of size\t0x%08x\n", (unsigned int)sh_table[i].sh_size);
-			dyn_start = sh_table[i].sh_addr;
+			debug(" at addr\t0x%08x\n", sh_addr);
+			debug(" at offset\t0x%08x\n", sh_offset);
+			debug(" of size\t0x%08x\n", sh_size);
+			dyn_start = sh_addr;
 		}
 	}
 
@@ -386,9 +397,9 @@
 			return 4;
 		}
 
-		swrela.r_offset = cpu_to_le64(rela.r_offset);
-		swrela.r_info = cpu_to_le64(rela.r_info);
-		swrela.r_addend = cpu_to_le64(rela.r_addend);
+		swrela.r_offset = le64_to_cpu(rela.r_offset);
+		swrela.r_info = le64_to_cpu(rela.r_info);
+		swrela.r_addend = le64_to_cpu(rela.r_addend);
 
 		if (!supported_rela(&swrela))
 			continue;
@@ -487,9 +498,9 @@
 		      PRIu32 " r_addend:\t%" PRIx32 "\n",
 		      rela.r_offset, rela.r_info, rela.r_addend);
 
-		swrela.r_offset = cpu_to_le32(rela.r_offset);
-		swrela.r_info = cpu_to_le32(rela.r_info);
-		swrela.r_addend = cpu_to_le32(rela.r_addend);
+		swrela.r_offset = le32_to_cpu(rela.r_offset);
+		swrela.r_info = le32_to_cpu(rela.r_info);
+		swrela.r_addend = le32_to_cpu(rela.r_addend);
 
 		debug("SWRela:\toffset:\t%" PRIx32 " r_info:\t%"
 		      PRIu32 " r_addend:\t%" PRIx32 "\n",