]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
MIPS: Use a custom elf-entry program to find kernel entry point
authorPaul Burton <paul.burton@mips.com>
Wed, 29 Aug 2018 18:01:30 +0000 (11:01 -0700)
committerPaul Burton <paul.burton@mips.com>
Thu, 30 Aug 2018 16:39:22 +0000 (09:39 -0700)
For a long time arch/mips/Makefile used nm to discover the kernel entry
point by looking for the address of the kernel_entry symbol. This
doesn't work for systems which make use of bit 0 of the PC to reflect
the ISA mode - ie. microMIPS (and MIPS16, but we don't support building
kernels that target MIPS16 anyway).

So for a while with commit 5fc9484f5e41 ("MIPS: Set ISA bit in entry-y
for microMIPS kernels") we manually modified the last nibble of the
output from nm, which worked but wasn't particularly pretty.

Commit 27c524d17430 ("MIPS: Use the entry point from the ELF file
header") then cleaned this up by using objdump to print the ELF entry
point which includes the ISA bit, rather than using nm to print the
address of the kernel_entry symbol which doesn't. That removed the ugly
replacement of the last nibble, but added its own ugliness by needing to
manually sign extend in the 32 bit case.

Unfortunately it has been pointed out that objdump's output is
localised, and therefore grepping for its "start address" output doesn't
work when the user's language settings are such that objdump doesn't
print in English.

We could simply revert commit 27c524d17430 ("MIPS: Use the entry point
from the ELF file header") and return to the manual replacement of the
last nibble of entry-y, but it seems that was found sufficiently
unpalatable to avoid. We could attempt to force the language used by
objdump by setting an environment variable such as LC_ALL, but that
seems fragile. Instead we add a small tool named elf-entry which simply
prints out the entry point of the kernel in the format we require.

Signed-off-by: Paul Burton <paul.burton@mips.com>
Reported-by: Philippe Reynes <philippe.reynes@softathome.com>
Tested-by: Philippe Reynes <philippe.reynes@softathome.com>
Fixes: 27c524d17430 ("MIPS: Use the entry point from the ELF file header")
Patchwork: https://patchwork.linux-mips.org/patch/20322/
Cc: James Hogan <jhogan@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
arch/mips/Makefile
arch/mips/tools/.gitignore [new file with mode: 0644]
arch/mips/tools/Makefile [new file with mode: 0644]
arch/mips/tools/elf-entry.c [new file with mode: 0644]

index d74b3742fa5d8d38d0b3995d988119b8c8b15cf5..053e1c314f9ee586ae77cd8e4ae0eadb6432ef1f 100644 (file)
@@ -13,6 +13,7 @@
 #
 
 archscripts: scripts_basic
+       $(Q)$(MAKE) $(build)=arch/mips/tools elf-entry
        $(Q)$(MAKE) $(build)=arch/mips/boot/tools relocs
 
 KBUILD_DEFCONFIG := 32r2el_defconfig
@@ -257,13 +258,7 @@ ifdef CONFIG_PHYSICAL_START
 load-y                                 = $(CONFIG_PHYSICAL_START)
 endif
 
-# Sign-extend the entry point to 64 bits if retrieved as a 32-bit number.
-entry-y                = $(shell $(OBJDUMP) -f vmlinux 2>/dev/null \
-                       | sed -n '/^start address / { \
-                               s/^.* //; \
-                               s/0x\([0-7].......\)$$/0x00000000\1/; \
-                               s/0x\(........\)$$/0xffffffff\1/; p }')
-
+entry-y                                = $(shell $(objtree)/arch/mips/tools/elf-entry vmlinux)
 cflags-y                       += -I$(srctree)/arch/mips/include/asm/mach-generic
 drivers-$(CONFIG_PCI)          += arch/mips/pci/
 
diff --git a/arch/mips/tools/.gitignore b/arch/mips/tools/.gitignore
new file mode 100644 (file)
index 0000000..56d34cc
--- /dev/null
@@ -0,0 +1 @@
+elf-entry
diff --git a/arch/mips/tools/Makefile b/arch/mips/tools/Makefile
new file mode 100644 (file)
index 0000000..3baee4b
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-y := elf-entry
+PHONY += elf-entry
+elf-entry: $(obj)/elf-entry
+       @:
diff --git a/arch/mips/tools/elf-entry.c b/arch/mips/tools/elf-entry.c
new file mode 100644 (file)
index 0000000..adde79c
--- /dev/null
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <byteswap.h>
+#include <elf.h>
+#include <endian.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef be32toh
+/* If libc provides [bl]e{32,64}toh() then we'll use them */
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# define be32toh(x)    bswap_32(x)
+# define le32toh(x)    (x)
+# define be64toh(x)    bswap_64(x)
+# define le64toh(x)    (x)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define be32toh(x)    (x)
+# define le32toh(x)    bswap_32(x)
+# define be64toh(x)    (x)
+# define le64toh(x)    bswap_64(x)
+#endif
+
+__attribute__((noreturn))
+static void die(const char *msg)
+{
+       fputs(msg, stderr);
+       exit(EXIT_FAILURE);
+}
+
+int main(int argc, const char *argv[])
+{
+       uint64_t entry;
+       size_t nread;
+       FILE *file;
+       union {
+               Elf32_Ehdr ehdr32;
+               Elf64_Ehdr ehdr64;
+       } hdr;
+
+       if (argc != 2)
+               die("Usage: elf-entry <elf-file>\n");
+
+       file = fopen(argv[1], "r");
+       if (!file) {
+               perror("Unable to open input file");
+               return EXIT_FAILURE;
+       }
+
+       nread = fread(&hdr, 1, sizeof(hdr), file);
+       if (nread != sizeof(hdr)) {
+               perror("Unable to read input file");
+               return EXIT_FAILURE;
+       }
+
+       if (memcmp(hdr.ehdr32.e_ident, ELFMAG, SELFMAG))
+               die("Input is not an ELF\n");
+
+       switch (hdr.ehdr32.e_ident[EI_CLASS]) {
+       case ELFCLASS32:
+               switch (hdr.ehdr32.e_ident[EI_DATA]) {
+               case ELFDATA2LSB:
+                       entry = le32toh(hdr.ehdr32.e_entry);
+                       break;
+               case ELFDATA2MSB:
+                       entry = be32toh(hdr.ehdr32.e_entry);
+                       break;
+               default:
+                       die("Invalid ELF encoding\n");
+               }
+
+               /* Sign extend to form a canonical address */
+               entry = (int64_t)(int32_t)entry;
+               break;
+
+       case ELFCLASS64:
+               switch (hdr.ehdr32.e_ident[EI_DATA]) {
+               case ELFDATA2LSB:
+                       entry = le64toh(hdr.ehdr64.e_entry);
+                       break;
+               case ELFDATA2MSB:
+                       entry = be64toh(hdr.ehdr64.e_entry);
+                       break;
+               default:
+                       die("Invalid ELF encoding\n");
+               }
+               break;
+
+       default:
+               die("Invalid ELF class\n");
+       }
+
+       printf("0x%016" PRIx64 "\n", entry);
+       return EXIT_SUCCESS;
+}