* [RFC PATCH 0/7] Relocatable Kernel
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Hi,
This patch series is a prerequisite of adding KASLR support for MIPS.
So far this supports 32/64 bit big/little endian CPUs and has been
tested in Qemu for Malta. It has not been tested on real hardware yet.
It is presented here as an early RFC for issues that people can forsee with
the many platforms, memory layouts etc that it must support.
Here is a description of how relocation is achieved:
* Kernel is compiled & statically linked as normal (no position independent code).
* The linker flag --emit-relocs is added to the linker command line, causing ld
to include additional ".rel" sections in the output elf
* A tool is used to parse the ".rel" sections and create a binary table of
relocations. Each entry in the table is 32bits, comprised of a 24bit offset
and 8bit relocation type.
* The table is appended to the kernel binary image, with some space in the
memory map reserved for it in the linker script
* At boot, the kernel memcpy()s itself elsewhere in memory, then goes through
the table performing each relocation on the new image
* If all goes well, control is passed to the entry point of the new kernel.
The net result of the relocation parsing is that the kernel as relocated is
binary identical to if it is statically linked to that address via the
CONFIG_PHYSICAL_START option.
Restrictions:
* Relocation is supported only by multiples of 64k bytes. This eliminates
the need to handle R_MIPS_LO16 relocations as the bottom 16bits will
remain the same at the relocated address.
* In 64 bit kernels, relocation is supported only within the same 4Gb memory
segment. This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
relocations as the top 32bits will remain the same at the relocated address.
What is not yet implemented:
* Cache flushing, likely to be implemented via the SYNCI instruction, but
without this the relocated kernel will likely fail on real hardware
* Support for SMP (handling percpu sections)
* Currently the relocation target address is hard coded. In future this may
be determined by command line argument or by choosing an address at random
(within platform specific constraints) to implement KASLR.
* Support for CONFIG_RAW_APPENDED_DTB. This option appends a binary DTB
to the kernel image, which will currently not appear at the expected
address. Also the size of the appended relocation table must be checked to
ensure that it does not overflow the reserved space
* Currently the kernel reserves all memory up to the _end symbol as boot memory.
While this is fine when the kernel is linked to appear near the bottom of RAM,
once it can relocate elsewhere this will have to change so as not to waste
a significant amount of memory. However, the way in which memory is reserved
is very platform specific and I forsee there will be issues here :-)
Thanks,
Matt
Matt Redfearn (7):
MIPS: tools: Add relocs tool
MIPS: tools: Build relocs tool
MIPS: Reserve space for relocation table
MIPS: Generate relocation table when CONFIG_RELOCATABLE
MIPS: Kernel: Add relocate.c
MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
MIPS: Add CONFIG_RELOCATABLE Kconfig option
arch/mips/Kconfig | 8 +
arch/mips/Makefile | 7 +
arch/mips/boot/Makefile | 19 ++
arch/mips/boot/tools/Makefile | 6 +
arch/mips/boot/tools/relocs.c | 552 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 32 +++
arch/mips/boot/tools/relocs_32.c | 17 ++
arch/mips/boot/tools/relocs_64.c | 17 ++
arch/mips/boot/tools/relocs_main.c | 74 +++++
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/head.S | 18 ++
arch/mips/kernel/relocate.c | 225 +++++++++++++++
arch/mips/kernel/vmlinux.lds.S | 9 +
13 files changed, 986 insertions(+)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c
create mode 100644 arch/mips/kernel/relocate.c
--
2.1.4
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC PATCH 0/7] Relocatable Kernel
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Hi,
This patch series is a prerequisite of adding KASLR support for MIPS.
So far this supports 32/64 bit big/little endian CPUs and has been
tested in Qemu for Malta. It has not been tested on real hardware yet.
It is presented here as an early RFC for issues that people can forsee with
the many platforms, memory layouts etc that it must support.
Here is a description of how relocation is achieved:
* Kernel is compiled & statically linked as normal (no position independent code).
* The linker flag --emit-relocs is added to the linker command line, causing ld
to include additional ".rel" sections in the output elf
* A tool is used to parse the ".rel" sections and create a binary table of
relocations. Each entry in the table is 32bits, comprised of a 24bit offset
and 8bit relocation type.
* The table is appended to the kernel binary image, with some space in the
memory map reserved for it in the linker script
* At boot, the kernel memcpy()s itself elsewhere in memory, then goes through
the table performing each relocation on the new image
* If all goes well, control is passed to the entry point of the new kernel.
The net result of the relocation parsing is that the kernel as relocated is
binary identical to if it is statically linked to that address via the
CONFIG_PHYSICAL_START option.
Restrictions:
* Relocation is supported only by multiples of 64k bytes. This eliminates
the need to handle R_MIPS_LO16 relocations as the bottom 16bits will
remain the same at the relocated address.
* In 64 bit kernels, relocation is supported only within the same 4Gb memory
segment. This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
relocations as the top 32bits will remain the same at the relocated address.
What is not yet implemented:
* Cache flushing, likely to be implemented via the SYNCI instruction, but
without this the relocated kernel will likely fail on real hardware
* Support for SMP (handling percpu sections)
* Currently the relocation target address is hard coded. In future this may
be determined by command line argument or by choosing an address at random
(within platform specific constraints) to implement KASLR.
* Support for CONFIG_RAW_APPENDED_DTB. This option appends a binary DTB
to the kernel image, which will currently not appear at the expected
address. Also the size of the appended relocation table must be checked to
ensure that it does not overflow the reserved space
* Currently the kernel reserves all memory up to the _end symbol as boot memory.
While this is fine when the kernel is linked to appear near the bottom of RAM,
once it can relocate elsewhere this will have to change so as not to waste
a significant amount of memory. However, the way in which memory is reserved
is very platform specific and I forsee there will be issues here :-)
Thanks,
Matt
Matt Redfearn (7):
MIPS: tools: Add relocs tool
MIPS: tools: Build relocs tool
MIPS: Reserve space for relocation table
MIPS: Generate relocation table when CONFIG_RELOCATABLE
MIPS: Kernel: Add relocate.c
MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
MIPS: Add CONFIG_RELOCATABLE Kconfig option
arch/mips/Kconfig | 8 +
arch/mips/Makefile | 7 +
arch/mips/boot/Makefile | 19 ++
arch/mips/boot/tools/Makefile | 6 +
arch/mips/boot/tools/relocs.c | 552 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 32 +++
arch/mips/boot/tools/relocs_32.c | 17 ++
arch/mips/boot/tools/relocs_64.c | 17 ++
arch/mips/boot/tools/relocs_main.c | 74 +++++
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/head.S | 18 ++
arch/mips/kernel/relocate.c | 225 +++++++++++++++
arch/mips/kernel/vmlinux.lds.S | 9 +
13 files changed, 986 insertions(+)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c
create mode 100644 arch/mips/kernel/relocate.c
--
2.1.4
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC PATCH 1/7] MIPS: tools: Add relocs tool
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
This tool is based on the x86/boot/tools/relocs tool.
It parses the relocations present in the vmlinux elf file
building a table of relocations which may be appended onto
vmlinux.bin for use by the code in arch/mips/kernel/relocate.c
(added later).
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
Issues still to address in this tool:
- Support CONFIG_APPENDED_DTB by padding to the expected length
- Ensure that relocation table doesn't overflow the reserved space
- Check support for .per_cpu sections with multiple CPUs
---
arch/mips/boot/tools/Makefile | 6 +
arch/mips/boot/tools/relocs.c | 552 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 32 +++
arch/mips/boot/tools/relocs_32.c | 17 ++
arch/mips/boot/tools/relocs_64.c | 17 ++
arch/mips/boot/tools/relocs_main.c | 74 +++++
6 files changed, 698 insertions(+)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c
diff --git a/arch/mips/boot/tools/Makefile b/arch/mips/boot/tools/Makefile
new file mode 100644
index 000000000000..42100bd8ad94
--- /dev/null
+++ b/arch/mips/boot/tools/Makefile
@@ -0,0 +1,6 @@
+
+hostprogs-y += relocs
+relocs-objs := relocs_main.o relocs_32.o relocs_64.o
+PHONY += relocs
+relocs: $(obj)/relocs
+ @:
diff --git a/arch/mips/boot/tools/relocs.c b/arch/mips/boot/tools/relocs.c
new file mode 100644
index 000000000000..4eaf0619b77c
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.c
@@ -0,0 +1,552 @@
+/* This is included from relocs_32/64.c */
+
+#define ElfW(type) _ElfW(ELF_BITS, type)
+#define _ElfW(bits, type) __ElfW(bits, type)
+#define __ElfW(bits, type) Elf##bits##_##type
+
+#define Elf_Addr ElfW(Addr)
+#define Elf_Ehdr ElfW(Ehdr)
+#define Elf_Phdr ElfW(Phdr)
+#define Elf_Shdr ElfW(Shdr)
+#define Elf_Sym ElfW(Sym)
+
+static Elf_Ehdr ehdr;
+
+struct relocs {
+ uint32_t *offset;
+ unsigned long count;
+ unsigned long size;
+};
+
+static struct relocs relocs;
+
+struct section {
+ Elf_Shdr shdr;
+ struct section *link;
+ Elf_Sym *symtab;
+ Elf_Rel *reltab;
+ char *strtab;
+};
+static struct section *secs;
+
+static const char *rel_type(unsigned type)
+{
+ static const char *type_name[] = {
+#define REL_TYPE(X) [X] = #X
+ REL_TYPE(R_MIPS_NONE),
+ REL_TYPE(R_MIPS_16),
+ REL_TYPE(R_MIPS_32),
+ REL_TYPE(R_MIPS_REL32),
+ REL_TYPE(R_MIPS_26),
+ REL_TYPE(R_MIPS_HI16),
+ REL_TYPE(R_MIPS_LO16),
+ REL_TYPE(R_MIPS_GPREL16),
+ REL_TYPE(R_MIPS_LITERAL),
+ REL_TYPE(R_MIPS_GOT16),
+ REL_TYPE(R_MIPS_PC16),
+ REL_TYPE(R_MIPS_CALL16),
+ REL_TYPE(R_MIPS_GPREL32),
+ REL_TYPE(R_MIPS_64),
+ REL_TYPE(R_MIPS_HIGHER),
+ REL_TYPE(R_MIPS_HIGHEST),
+#undef REL_TYPE
+ };
+ const char *name = "unknown type rel type name";
+ if (type < ARRAY_SIZE(type_name) && type_name[type]) {
+ name = type_name[type];
+ }
+ return name;
+}
+
+static const char *sec_name(unsigned shndx)
+{
+ const char *sec_strtab;
+ const char *name;
+ sec_strtab = secs[ehdr.e_shstrndx].strtab;
+ name = "<noname>";
+ if (shndx < ehdr.e_shnum) {
+ name = sec_strtab + secs[shndx].shdr.sh_name;
+ }
+ else if (shndx == SHN_ABS) {
+ name = "ABSOLUTE";
+ }
+ else if (shndx == SHN_COMMON) {
+ name = "COMMON";
+ }
+ return name;
+}
+
+static struct section *sec_lookup(const char *secname)
+{
+ int i;
+
+ for (i = 0; i < ehdr.e_shnum; i++)
+ if (strcmp(secname, sec_name(i)) == 0)
+ return &secs[i];
+
+ return NULL;
+}
+
+static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
+{
+ const char *name;
+ name = "<noname>";
+ if (sym->st_name) {
+ name = sym_strtab + sym->st_name;
+ }
+ else {
+ name = sec_name(sym->st_shndx);
+ }
+ return name;
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#define be16_to_cpu(val) bswap_16(val)
+#define be32_to_cpu(val) bswap_32(val)
+#define be64_to_cpu(val) bswap_64(val)
+
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define cpu_to_le64(val) (val)
+#define cpu_to_be16(val) bswap_16(val)
+#define cpu_to_be32(val) bswap_32(val)
+#define cpu_to_be64(val) bswap_64(val)
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#define be16_to_cpu(val) (val)
+#define be32_to_cpu(val) (val)
+#define be64_to_cpu(val) (val)
+
+#define cpu_to_le16(val) bswap_16(val)
+#define cpu_to_le32(val) bswap_32(val)
+#define cpu_to_le64(val) bswap_64(val)
+#define cpu_to_be16(val) (val)
+#define cpu_to_be32(val) (val)
+#define cpu_to_be64(val) (val)
+#endif
+
+static uint16_t elf16_to_cpu(uint16_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le16_to_cpu(val);
+ else
+ return be16_to_cpu(val);
+}
+
+static uint32_t elf32_to_cpu(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le32_to_cpu(val);
+ else
+ return be32_to_cpu(val);
+}
+
+static uint32_t cpu_to_elf32(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return cpu_to_le32(val);
+ else
+ return cpu_to_be32(val);
+}
+
+#define elf_half_to_cpu(x) elf16_to_cpu(x)
+#define elf_word_to_cpu(x) elf32_to_cpu(x)
+
+#if ELF_BITS == 64
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le64_to_cpu(val);
+ else
+ return be64_to_cpu(val);
+}
+#define elf_addr_to_cpu(x) elf64_to_cpu(x)
+#define elf_off_to_cpu(x) elf64_to_cpu(x)
+#define elf_xword_to_cpu(x) elf64_to_cpu(x)
+#else
+#define elf_addr_to_cpu(x) elf32_to_cpu(x)
+#define elf_off_to_cpu(x) elf32_to_cpu(x)
+#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+#endif
+
+static void read_ehdr(FILE *fp)
+{
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
+ die("Cannot read ELF header: %s\n",
+ strerror(errno));
+ }
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
+ die("No ELF magic\n");
+ }
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) {
+ die("Not a %d bit executable\n", ELF_BITS);
+ }
+ if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) &&
+ (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) {
+ die("Unknown ELF Endianness\n");
+ }
+ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ /* Convert the fields to native endian */
+ ehdr.e_type = elf_half_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf_word_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx);
+
+ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
+ die("Unsupported ELF header type\n");
+ }
+ if (ehdr.e_machine != ELF_MACHINE) {
+ die("Not for %s\n", ELF_MACHINE_NAME);
+ }
+ if (ehdr.e_version != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) {
+ die("Bad Elf header size\n");
+ }
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr)) {
+ die("Bad program header entry\n");
+ }
+ if (ehdr.e_shentsize != sizeof(Elf_Shdr)) {
+ die("Bad section header entry\n");
+ }
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ die("String table index out of bounds\n");
+ }
+}
+
+static void read_shdrs(FILE *fp)
+{
+ int i;
+ Elf_Shdr shdr;
+
+ secs = calloc(ehdr.e_shnum, sizeof(struct section));
+ if (!secs) {
+ die("Unable to allocate %d section headers\n",
+ ehdr.e_shnum);
+ }
+ if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ ehdr.e_shoff, strerror(errno));
+ }
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (fread(&shdr, sizeof shdr, 1, fp) != 1)
+ die("Cannot read ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+ sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize);
+ if (sec->shdr.sh_link < ehdr.e_shnum)
+ sec->link = &secs[sec->shdr.sh_link];
+ }
+}
+
+static void read_strtabs(FILE *fp)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_STRTAB) {
+ continue;
+ }
+ sec->strtab = malloc(sec->shdr.sh_size);
+ if (!sec->strtab) {
+ die("malloc of %d bytes for strtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->strtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ }
+}
+
+static void read_symtabs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_SYMTAB) {
+ continue;
+ }
+ sec->symtab = malloc(sec->shdr.sh_size);
+ if (!sec->symtab) {
+ die("malloc of %d bytes for symtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->symtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym = &sec->symtab[j];
+ sym->st_name = elf_word_to_cpu(sym->st_name);
+ sym->st_value = elf_addr_to_cpu(sym->st_value);
+ sym->st_size = elf_xword_to_cpu(sym->st_size);
+ sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
+ }
+ }
+}
+
+static void read_relocs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
+ continue;
+ }
+ sec->reltab = malloc(sec->shdr.sh_size);
+ if (!sec->reltab) {
+ die("malloc of %d bytes for relocs failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->reltab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ rel->r_offset = elf_addr_to_cpu(rel->r_offset);
+ rel->r_info = elf_xword_to_cpu(rel->r_info);
+#if (SHT_REL_TYPE == SHT_RELA)
+ rel->r_addend = elf_xword_to_cpu(rel->r_addend);
+#endif
+ }
+ }
+}
+
+static void add_reloc(struct relocs *r, uint32_t offset, unsigned type)
+{
+ static unsigned long base = 0;
+
+ if (!base) {
+ struct section *sec = sec_lookup(".text");
+ if (!sec)
+ die("Could not find .text section\n");
+
+ base = sec->shdr.sh_addr;
+ }
+
+ /* Get offset into kernel image */
+ offset -= base;
+
+ /* Relocation representation in binary table:
+ * |76543210|76543210|76543210|76543210|
+ * | Type | 24 bit offset from _text |
+ */
+ offset = (offset & 0x00FFFFFF) | ((type & 0xFF) << 24);
+
+ if (r->count == r->size) {
+ unsigned long newsize = r->size + 50000;
+ void *mem = realloc(r->offset, newsize * sizeof(r->offset[0]));
+
+ if (!mem)
+ die("realloc of %ld entries for relocs failed\n",
+ newsize);
+ r->offset = mem;
+ r->size = newsize;
+ }
+ r->offset[r->count++] = offset;
+}
+
+static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
+ Elf_Sym *sym, const char *symname))
+{
+ int i;
+ /* Walk through the relocations */
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ char *sym_strtab;
+ Elf_Sym *sh_symtab;
+ struct section *sec_applies, *sec_symtab;
+ int j;
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
+ continue;
+ }
+ sec_symtab = sec->link;
+ sec_applies = &secs[sec->shdr.sh_info];
+ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
+ continue;
+ }
+ sh_symtab = sec_symtab->symtab;
+ sym_strtab = sec_symtab->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ const char *symname = sym_name(sym_strtab, sym);
+
+ process(sec, rel, sym, symname);
+ }
+ }
+}
+
+static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF_R_TYPE(rel->r_info);
+ unsigned bind = ELF_ST_BIND(sym->st_info);
+
+ if ((bind == STB_WEAK) && (sym->st_value == 0)) {
+ /* Don't relocate weak symbols without a target */
+ return 0;
+ }
+
+ switch (r_type) {
+ case R_MIPS_NONE:
+ case R_MIPS_REL32:
+ case R_MIPS_PC16:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ case R_MIPS_HIGHEST:
+ case R_MIPS_HIGHER:
+ /* We support relocating within the same 4Gb segment only,
+ * thus leaving the top 32bits unchanged
+ */
+ case R_MIPS_LO16:
+ /* We support relocating by 64k jumps only
+ * thus leaving the bottom 16bits unchanged
+ */
+ break;
+
+ case R_MIPS_64:
+ case R_MIPS_32:
+ case R_MIPS_26:
+ case R_MIPS_HI16:
+ add_reloc(&relocs, rel->r_offset, r_type);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int write_reloc_as_bin(uint32_t v, FILE *f)
+{
+ unsigned char buf[4];
+
+ v = cpu_to_elf32(v);
+
+ memcpy(buf, &v, sizeof(uint32_t));
+ return fwrite(buf, 1, 4, f) == 4 ? 0 : -1;
+}
+
+static int write_reloc_as_text(uint32_t v, FILE *f)
+{
+ return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1;
+}
+
+static void emit_relocs(int as_text)
+{
+ int i;
+ int (*write_reloc)(uint32_t, FILE *) = write_reloc_as_bin;
+
+ /* Collect up the relocations */
+ walk_relocs(do_reloc);
+
+ /* Print the relocations */
+ if (as_text) {
+ /* Print the relocations in a form suitable that
+ * gas will like.
+ */
+ printf(".section \".data.reloc\",\"a\"\n");
+ printf(".balign 4\n");
+ write_reloc = write_reloc_as_text;
+ }
+
+ for (i = 0; i < relocs.count; i++)
+ write_reloc(relocs.offset[i], stdout);
+
+ /* Print a stop */
+ write_reloc(0, stdout);
+}
+
+/*
+ * As an aid to debugging problems with different linkers
+ * print summary information about the relocs.
+ * Since different linkers tend to emit the sections in
+ * different orders we use the section names in the output.
+ */
+static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
+ const char *symname)
+{
+ printf("%s\t%s\t%s\t%s\n",
+ sec_name(sec->shdr.sh_info),
+ rel_type(ELF_R_TYPE(rel->r_info)),
+ symname,
+ sec_name(sym->st_shndx));
+ return 0;
+}
+
+static void print_reloc_info(void)
+{
+ printf("reloc section\treloc type\tsymbol\tsymbol section\n");
+ walk_relocs(do_reloc_info);
+}
+
+#if ELF_BITS == 64
+# define process process_64
+#else
+# define process process_32
+#endif
+
+void process(FILE *fp, int as_text, int show_reloc_info)
+{
+ read_ehdr(fp);
+ read_shdrs(fp);
+ read_strtabs(fp);
+ read_symtabs(fp);
+ read_relocs(fp);
+ if (show_reloc_info) {
+ print_reloc_info();
+ return;
+ }
+ emit_relocs(as_text);
+}
diff --git a/arch/mips/boot/tools/relocs.h b/arch/mips/boot/tools/relocs.h
new file mode 100644
index 000000000000..4bc6163810d4
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.h
@@ -0,0 +1,32 @@
+#ifndef RELOCS_H
+#define RELOCS_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <regex.h>
+
+void die(char *fmt, ...);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+enum symtype {
+ S_ABS,
+ S_REL,
+ S_SEG,
+ S_LIN,
+ S_NSYMTYPES
+};
+
+void process_32(FILE *fp, int as_text, int show_reloc_info);
+void process_64(FILE *fp, int as_text, int show_reloc_info);
+#endif /* RELOCS_H */
diff --git a/arch/mips/boot/tools/relocs_32.c b/arch/mips/boot/tools/relocs_32.c
new file mode 100644
index 000000000000..915bdc07f5ed
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_32.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 32
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS"
+#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_64.c b/arch/mips/boot/tools/relocs_64.c
new file mode 100644
index 000000000000..8d674e1fc620
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_64.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 64
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) ELF64_R_SYM(val)
+#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_main.c b/arch/mips/boot/tools/relocs_main.c
new file mode 100644
index 000000000000..744786c4ce0e
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_main.c
@@ -0,0 +1,74 @@
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <elf.h>
+
+#include "relocs.h"
+
+void die(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+static void usage(void)
+{
+ die("relocs [--reloc-info|--text]" \
+ " vmlinux\n");
+}
+
+int main(int argc, char **argv)
+{
+ int show_reloc_info, as_text;
+ const char *fname;
+ FILE *fp;
+ int i;
+ unsigned char e_ident[EI_NIDENT];
+
+ show_reloc_info = 0;
+ as_text = 0;
+ fname = NULL;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (*arg == '-') {
+ if (strcmp(arg, "--reloc-info") == 0) {
+ show_reloc_info = 1;
+ continue;
+ }
+ if (strcmp(arg, "--text") == 0) {
+ as_text = 1;
+ continue;
+ }
+ }
+ else if (!fname) {
+ fname = arg;
+ continue;
+ }
+ usage();
+ }
+ if (!fname) {
+ usage();
+ }
+ fp = fopen(fname, "r");
+ if (!fp) {
+ die("Cannot open %s: %s\n", fname, strerror(errno));
+ }
+ if (fread(&e_ident, 1, EI_NIDENT, fp) != EI_NIDENT) {
+ die("Cannot read %s: %s", fname, strerror(errno));
+ }
+ rewind(fp);
+ if (e_ident[EI_CLASS] == ELFCLASS64)
+ process_64(fp, as_text, show_reloc_info);
+ else
+ process_32(fp, as_text, show_reloc_info);
+ fclose(fp);
+ return 0;
+}
\ No newline at end of file
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 1/7] MIPS: tools: Add relocs tool
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
This tool is based on the x86/boot/tools/relocs tool.
It parses the relocations present in the vmlinux elf file
building a table of relocations which may be appended onto
vmlinux.bin for use by the code in arch/mips/kernel/relocate.c
(added later).
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
Issues still to address in this tool:
- Support CONFIG_APPENDED_DTB by padding to the expected length
- Ensure that relocation table doesn't overflow the reserved space
- Check support for .per_cpu sections with multiple CPUs
---
arch/mips/boot/tools/Makefile | 6 +
arch/mips/boot/tools/relocs.c | 552 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 32 +++
arch/mips/boot/tools/relocs_32.c | 17 ++
arch/mips/boot/tools/relocs_64.c | 17 ++
arch/mips/boot/tools/relocs_main.c | 74 +++++
6 files changed, 698 insertions(+)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c
diff --git a/arch/mips/boot/tools/Makefile b/arch/mips/boot/tools/Makefile
new file mode 100644
index 000000000000..42100bd8ad94
--- /dev/null
+++ b/arch/mips/boot/tools/Makefile
@@ -0,0 +1,6 @@
+
+hostprogs-y += relocs
+relocs-objs := relocs_main.o relocs_32.o relocs_64.o
+PHONY += relocs
+relocs: $(obj)/relocs
+ @:
diff --git a/arch/mips/boot/tools/relocs.c b/arch/mips/boot/tools/relocs.c
new file mode 100644
index 000000000000..4eaf0619b77c
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.c
@@ -0,0 +1,552 @@
+/* This is included from relocs_32/64.c */
+
+#define ElfW(type) _ElfW(ELF_BITS, type)
+#define _ElfW(bits, type) __ElfW(bits, type)
+#define __ElfW(bits, type) Elf##bits##_##type
+
+#define Elf_Addr ElfW(Addr)
+#define Elf_Ehdr ElfW(Ehdr)
+#define Elf_Phdr ElfW(Phdr)
+#define Elf_Shdr ElfW(Shdr)
+#define Elf_Sym ElfW(Sym)
+
+static Elf_Ehdr ehdr;
+
+struct relocs {
+ uint32_t *offset;
+ unsigned long count;
+ unsigned long size;
+};
+
+static struct relocs relocs;
+
+struct section {
+ Elf_Shdr shdr;
+ struct section *link;
+ Elf_Sym *symtab;
+ Elf_Rel *reltab;
+ char *strtab;
+};
+static struct section *secs;
+
+static const char *rel_type(unsigned type)
+{
+ static const char *type_name[] = {
+#define REL_TYPE(X) [X] = #X
+ REL_TYPE(R_MIPS_NONE),
+ REL_TYPE(R_MIPS_16),
+ REL_TYPE(R_MIPS_32),
+ REL_TYPE(R_MIPS_REL32),
+ REL_TYPE(R_MIPS_26),
+ REL_TYPE(R_MIPS_HI16),
+ REL_TYPE(R_MIPS_LO16),
+ REL_TYPE(R_MIPS_GPREL16),
+ REL_TYPE(R_MIPS_LITERAL),
+ REL_TYPE(R_MIPS_GOT16),
+ REL_TYPE(R_MIPS_PC16),
+ REL_TYPE(R_MIPS_CALL16),
+ REL_TYPE(R_MIPS_GPREL32),
+ REL_TYPE(R_MIPS_64),
+ REL_TYPE(R_MIPS_HIGHER),
+ REL_TYPE(R_MIPS_HIGHEST),
+#undef REL_TYPE
+ };
+ const char *name = "unknown type rel type name";
+ if (type < ARRAY_SIZE(type_name) && type_name[type]) {
+ name = type_name[type];
+ }
+ return name;
+}
+
+static const char *sec_name(unsigned shndx)
+{
+ const char *sec_strtab;
+ const char *name;
+ sec_strtab = secs[ehdr.e_shstrndx].strtab;
+ name = "<noname>";
+ if (shndx < ehdr.e_shnum) {
+ name = sec_strtab + secs[shndx].shdr.sh_name;
+ }
+ else if (shndx == SHN_ABS) {
+ name = "ABSOLUTE";
+ }
+ else if (shndx == SHN_COMMON) {
+ name = "COMMON";
+ }
+ return name;
+}
+
+static struct section *sec_lookup(const char *secname)
+{
+ int i;
+
+ for (i = 0; i < ehdr.e_shnum; i++)
+ if (strcmp(secname, sec_name(i)) == 0)
+ return &secs[i];
+
+ return NULL;
+}
+
+static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
+{
+ const char *name;
+ name = "<noname>";
+ if (sym->st_name) {
+ name = sym_strtab + sym->st_name;
+ }
+ else {
+ name = sec_name(sym->st_shndx);
+ }
+ return name;
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#define be16_to_cpu(val) bswap_16(val)
+#define be32_to_cpu(val) bswap_32(val)
+#define be64_to_cpu(val) bswap_64(val)
+
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define cpu_to_le64(val) (val)
+#define cpu_to_be16(val) bswap_16(val)
+#define cpu_to_be32(val) bswap_32(val)
+#define cpu_to_be64(val) bswap_64(val)
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#define be16_to_cpu(val) (val)
+#define be32_to_cpu(val) (val)
+#define be64_to_cpu(val) (val)
+
+#define cpu_to_le16(val) bswap_16(val)
+#define cpu_to_le32(val) bswap_32(val)
+#define cpu_to_le64(val) bswap_64(val)
+#define cpu_to_be16(val) (val)
+#define cpu_to_be32(val) (val)
+#define cpu_to_be64(val) (val)
+#endif
+
+static uint16_t elf16_to_cpu(uint16_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le16_to_cpu(val);
+ else
+ return be16_to_cpu(val);
+}
+
+static uint32_t elf32_to_cpu(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le32_to_cpu(val);
+ else
+ return be32_to_cpu(val);
+}
+
+static uint32_t cpu_to_elf32(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return cpu_to_le32(val);
+ else
+ return cpu_to_be32(val);
+}
+
+#define elf_half_to_cpu(x) elf16_to_cpu(x)
+#define elf_word_to_cpu(x) elf32_to_cpu(x)
+
+#if ELF_BITS == 64
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le64_to_cpu(val);
+ else
+ return be64_to_cpu(val);
+}
+#define elf_addr_to_cpu(x) elf64_to_cpu(x)
+#define elf_off_to_cpu(x) elf64_to_cpu(x)
+#define elf_xword_to_cpu(x) elf64_to_cpu(x)
+#else
+#define elf_addr_to_cpu(x) elf32_to_cpu(x)
+#define elf_off_to_cpu(x) elf32_to_cpu(x)
+#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+#endif
+
+static void read_ehdr(FILE *fp)
+{
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
+ die("Cannot read ELF header: %s\n",
+ strerror(errno));
+ }
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
+ die("No ELF magic\n");
+ }
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) {
+ die("Not a %d bit executable\n", ELF_BITS);
+ }
+ if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) &&
+ (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) {
+ die("Unknown ELF Endianness\n");
+ }
+ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ /* Convert the fields to native endian */
+ ehdr.e_type = elf_half_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf_word_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx);
+
+ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
+ die("Unsupported ELF header type\n");
+ }
+ if (ehdr.e_machine != ELF_MACHINE) {
+ die("Not for %s\n", ELF_MACHINE_NAME);
+ }
+ if (ehdr.e_version != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) {
+ die("Bad Elf header size\n");
+ }
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr)) {
+ die("Bad program header entry\n");
+ }
+ if (ehdr.e_shentsize != sizeof(Elf_Shdr)) {
+ die("Bad section header entry\n");
+ }
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ die("String table index out of bounds\n");
+ }
+}
+
+static void read_shdrs(FILE *fp)
+{
+ int i;
+ Elf_Shdr shdr;
+
+ secs = calloc(ehdr.e_shnum, sizeof(struct section));
+ if (!secs) {
+ die("Unable to allocate %d section headers\n",
+ ehdr.e_shnum);
+ }
+ if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ ehdr.e_shoff, strerror(errno));
+ }
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (fread(&shdr, sizeof shdr, 1, fp) != 1)
+ die("Cannot read ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+ sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize);
+ if (sec->shdr.sh_link < ehdr.e_shnum)
+ sec->link = &secs[sec->shdr.sh_link];
+ }
+}
+
+static void read_strtabs(FILE *fp)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_STRTAB) {
+ continue;
+ }
+ sec->strtab = malloc(sec->shdr.sh_size);
+ if (!sec->strtab) {
+ die("malloc of %d bytes for strtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->strtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ }
+}
+
+static void read_symtabs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_SYMTAB) {
+ continue;
+ }
+ sec->symtab = malloc(sec->shdr.sh_size);
+ if (!sec->symtab) {
+ die("malloc of %d bytes for symtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->symtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym = &sec->symtab[j];
+ sym->st_name = elf_word_to_cpu(sym->st_name);
+ sym->st_value = elf_addr_to_cpu(sym->st_value);
+ sym->st_size = elf_xword_to_cpu(sym->st_size);
+ sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
+ }
+ }
+}
+
+static void read_relocs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
+ continue;
+ }
+ sec->reltab = malloc(sec->shdr.sh_size);
+ if (!sec->reltab) {
+ die("malloc of %d bytes for relocs failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->reltab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ rel->r_offset = elf_addr_to_cpu(rel->r_offset);
+ rel->r_info = elf_xword_to_cpu(rel->r_info);
+#if (SHT_REL_TYPE == SHT_RELA)
+ rel->r_addend = elf_xword_to_cpu(rel->r_addend);
+#endif
+ }
+ }
+}
+
+static void add_reloc(struct relocs *r, uint32_t offset, unsigned type)
+{
+ static unsigned long base = 0;
+
+ if (!base) {
+ struct section *sec = sec_lookup(".text");
+ if (!sec)
+ die("Could not find .text section\n");
+
+ base = sec->shdr.sh_addr;
+ }
+
+ /* Get offset into kernel image */
+ offset -= base;
+
+ /* Relocation representation in binary table:
+ * |76543210|76543210|76543210|76543210|
+ * | Type | 24 bit offset from _text |
+ */
+ offset = (offset & 0x00FFFFFF) | ((type & 0xFF) << 24);
+
+ if (r->count == r->size) {
+ unsigned long newsize = r->size + 50000;
+ void *mem = realloc(r->offset, newsize * sizeof(r->offset[0]));
+
+ if (!mem)
+ die("realloc of %ld entries for relocs failed\n",
+ newsize);
+ r->offset = mem;
+ r->size = newsize;
+ }
+ r->offset[r->count++] = offset;
+}
+
+static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
+ Elf_Sym *sym, const char *symname))
+{
+ int i;
+ /* Walk through the relocations */
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ char *sym_strtab;
+ Elf_Sym *sh_symtab;
+ struct section *sec_applies, *sec_symtab;
+ int j;
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL_TYPE) {
+ continue;
+ }
+ sec_symtab = sec->link;
+ sec_applies = &secs[sec->shdr.sh_info];
+ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
+ continue;
+ }
+ sh_symtab = sec_symtab->symtab;
+ sym_strtab = sec_symtab->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ const char *symname = sym_name(sym_strtab, sym);
+
+ process(sec, rel, sym, symname);
+ }
+ }
+}
+
+static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF_R_TYPE(rel->r_info);
+ unsigned bind = ELF_ST_BIND(sym->st_info);
+
+ if ((bind == STB_WEAK) && (sym->st_value == 0)) {
+ /* Don't relocate weak symbols without a target */
+ return 0;
+ }
+
+ switch (r_type) {
+ case R_MIPS_NONE:
+ case R_MIPS_REL32:
+ case R_MIPS_PC16:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ case R_MIPS_HIGHEST:
+ case R_MIPS_HIGHER:
+ /* We support relocating within the same 4Gb segment only,
+ * thus leaving the top 32bits unchanged
+ */
+ case R_MIPS_LO16:
+ /* We support relocating by 64k jumps only
+ * thus leaving the bottom 16bits unchanged
+ */
+ break;
+
+ case R_MIPS_64:
+ case R_MIPS_32:
+ case R_MIPS_26:
+ case R_MIPS_HI16:
+ add_reloc(&relocs, rel->r_offset, r_type);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int write_reloc_as_bin(uint32_t v, FILE *f)
+{
+ unsigned char buf[4];
+
+ v = cpu_to_elf32(v);
+
+ memcpy(buf, &v, sizeof(uint32_t));
+ return fwrite(buf, 1, 4, f) == 4 ? 0 : -1;
+}
+
+static int write_reloc_as_text(uint32_t v, FILE *f)
+{
+ return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1;
+}
+
+static void emit_relocs(int as_text)
+{
+ int i;
+ int (*write_reloc)(uint32_t, FILE *) = write_reloc_as_bin;
+
+ /* Collect up the relocations */
+ walk_relocs(do_reloc);
+
+ /* Print the relocations */
+ if (as_text) {
+ /* Print the relocations in a form suitable that
+ * gas will like.
+ */
+ printf(".section \".data.reloc\",\"a\"\n");
+ printf(".balign 4\n");
+ write_reloc = write_reloc_as_text;
+ }
+
+ for (i = 0; i < relocs.count; i++)
+ write_reloc(relocs.offset[i], stdout);
+
+ /* Print a stop */
+ write_reloc(0, stdout);
+}
+
+/*
+ * As an aid to debugging problems with different linkers
+ * print summary information about the relocs.
+ * Since different linkers tend to emit the sections in
+ * different orders we use the section names in the output.
+ */
+static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
+ const char *symname)
+{
+ printf("%s\t%s\t%s\t%s\n",
+ sec_name(sec->shdr.sh_info),
+ rel_type(ELF_R_TYPE(rel->r_info)),
+ symname,
+ sec_name(sym->st_shndx));
+ return 0;
+}
+
+static void print_reloc_info(void)
+{
+ printf("reloc section\treloc type\tsymbol\tsymbol section\n");
+ walk_relocs(do_reloc_info);
+}
+
+#if ELF_BITS == 64
+# define process process_64
+#else
+# define process process_32
+#endif
+
+void process(FILE *fp, int as_text, int show_reloc_info)
+{
+ read_ehdr(fp);
+ read_shdrs(fp);
+ read_strtabs(fp);
+ read_symtabs(fp);
+ read_relocs(fp);
+ if (show_reloc_info) {
+ print_reloc_info();
+ return;
+ }
+ emit_relocs(as_text);
+}
diff --git a/arch/mips/boot/tools/relocs.h b/arch/mips/boot/tools/relocs.h
new file mode 100644
index 000000000000..4bc6163810d4
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.h
@@ -0,0 +1,32 @@
+#ifndef RELOCS_H
+#define RELOCS_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <regex.h>
+
+void die(char *fmt, ...);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+enum symtype {
+ S_ABS,
+ S_REL,
+ S_SEG,
+ S_LIN,
+ S_NSYMTYPES
+};
+
+void process_32(FILE *fp, int as_text, int show_reloc_info);
+void process_64(FILE *fp, int as_text, int show_reloc_info);
+#endif /* RELOCS_H */
diff --git a/arch/mips/boot/tools/relocs_32.c b/arch/mips/boot/tools/relocs_32.c
new file mode 100644
index 000000000000..915bdc07f5ed
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_32.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 32
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS"
+#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_64.c b/arch/mips/boot/tools/relocs_64.c
new file mode 100644
index 000000000000..8d674e1fc620
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_64.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 64
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) ELF64_R_SYM(val)
+#define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_main.c b/arch/mips/boot/tools/relocs_main.c
new file mode 100644
index 000000000000..744786c4ce0e
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_main.c
@@ -0,0 +1,74 @@
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <elf.h>
+
+#include "relocs.h"
+
+void die(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+static void usage(void)
+{
+ die("relocs [--reloc-info|--text]" \
+ " vmlinux\n");
+}
+
+int main(int argc, char **argv)
+{
+ int show_reloc_info, as_text;
+ const char *fname;
+ FILE *fp;
+ int i;
+ unsigned char e_ident[EI_NIDENT];
+
+ show_reloc_info = 0;
+ as_text = 0;
+ fname = NULL;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (*arg == '-') {
+ if (strcmp(arg, "--reloc-info") == 0) {
+ show_reloc_info = 1;
+ continue;
+ }
+ if (strcmp(arg, "--text") == 0) {
+ as_text = 1;
+ continue;
+ }
+ }
+ else if (!fname) {
+ fname = arg;
+ continue;
+ }
+ usage();
+ }
+ if (!fname) {
+ usage();
+ }
+ fp = fopen(fname, "r");
+ if (!fp) {
+ die("Cannot open %s: %s\n", fname, strerror(errno));
+ }
+ if (fread(&e_ident, 1, EI_NIDENT, fp) != EI_NIDENT) {
+ die("Cannot read %s: %s", fname, strerror(errno));
+ }
+ rewind(fp);
+ if (e_ident[EI_CLASS] == ELFCLASS64)
+ process_64(fp, as_text, show_reloc_info);
+ else
+ process_32(fp, as_text, show_reloc_info);
+ fclose(fp);
+ return 0;
+}
\ No newline at end of file
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 2/7] MIPS: tools: Build relocs tool
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Build the relocs tool as part of the kbuild
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Makefile | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index bf4606f04193..9371ea0adb88 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -12,6 +12,9 @@
# for "archclean" cleaning up for this architecture.
#
+archscripts: scripts_basic
+ $(Q)$(MAKE) $(build)=arch/mips/boot/tools relocs
+
KBUILD_DEFCONFIG := ip22_defconfig
#
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 2/7] MIPS: tools: Build relocs tool
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Build the relocs tool as part of the kbuild
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Makefile | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index bf4606f04193..9371ea0adb88 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -12,6 +12,9 @@
# for "archclean" cleaning up for this architecture.
#
+archscripts: scripts_basic
+ $(Q)$(MAKE) $(build)=arch/mips/boot/tools relocs
+
KBUILD_DEFCONFIG := ip22_defconfig
#
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 3/7] MIPS: Reserve space for relocation table
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
When CONFIG_RELOCATABLE is enabled, reserve space in the
memory map for the relocation table to appear
once it is appended by the relocation tool.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/kernel/vmlinux.lds.S | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index 07d32a4aea60..a17551260f88 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -128,6 +128,15 @@ SECTIONS
#ifdef CONFIG_SMP
PERCPU_SECTION(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
#endif
+
+#ifdef CONFIG_RELOCATABLE
+ . = ALIGN(4);
+ _relocation_start = .;
+ /* Space for relocation table */
+ . += 0x100000;
+ _relocation_end = .;
+#endif
+
#ifdef CONFIG_MIPS_RAW_APPENDED_DTB
__appended_dtb = .;
/* leave space for appended DTB */
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 3/7] MIPS: Reserve space for relocation table
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
When CONFIG_RELOCATABLE is enabled, reserve space in the
memory map for the relocation table to appear
once it is appended by the relocation tool.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/kernel/vmlinux.lds.S | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index 07d32a4aea60..a17551260f88 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -128,6 +128,15 @@ SECTIONS
#ifdef CONFIG_SMP
PERCPU_SECTION(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
#endif
+
+#ifdef CONFIG_RELOCATABLE
+ . = ALIGN(4);
+ _relocation_start = .;
+ /* Space for relocation table */
+ . += 0x100000;
+ _relocation_end = .;
+#endif
+
#ifdef CONFIG_MIPS_RAW_APPENDED_DTB
__appended_dtb = .;
/* leave space for appended DTB */
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 4/7] MIPS: Generate relocation table when CONFIG_RELOCATABLE
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
When CONFIG_RELOCATABLE is enabled (added in later patch)
add --emit-relocs to vmlinux LDFLAGS so that the elf
contains relocation information.
Run the previously added relocation tool to append the
relocation table to the binary image.
Space for the table is reserved in the linker script
by a previous commit
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Makefile | 4 ++++
arch/mips/boot/Makefile | 19 +++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 9371ea0adb88..fd28b0965c42 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -96,6 +96,10 @@ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls
+ifeq ($(CONFIG_RELOCATABLE),y)
+LDFLAGS_vmlinux += --emit-relocs
+endif
+
#
# pass -msoft-float to GAS if it supports it. However on newer binutils
# (specifically newer than 2.24.51.20140728) we then also need to explicitly
diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile
index fb5bfaec55ac..127705bb43a8 100644
--- a/arch/mips/boot/Makefile
+++ b/arch/mips/boot/Makefile
@@ -36,10 +36,29 @@ $(obj)/vmlinux.ecoff: $(obj)/elf2ecoff $(VMLINUX) FORCE
$(call if_changed,ecoff)
targets += vmlinux.bin
+ifneq ($(CONFIG_RELOCATABLE),y)
quiet_cmd_bin = OBJCOPY $@
cmd_bin = $(OBJCOPY) -O binary $(strip-flags) $(VMLINUX) $@
$(obj)/vmlinux.bin: $(VMLINUX) FORCE
$(call if_changed,bin)
+else
+# For relocatable kernel, Generate a relocation table that is appended to the image
+
+CMD_RELOCS = arch/mips/boot/tools/relocs
+quiet_cmd_relocs = RELOCS $@
+ cmd_relocs = $(CMD_RELOCS) $< > $@
+$(obj)/vmlinux.relocs: $(VMLINUX) FORCE
+ $(call if_changed,relocs)
+
+OBJCOPYFLAGS_vmlinux.img := -O binary $(strip-flags)
+$(obj)/vmlinux.img: $(VMLINUX) FORCE
+ $(call if_changed,objcopy)
+
+quiet_cmd_bin = BIN $@
+ cmd_bin = cat $^ > $@
+$(obj)/vmlinux.bin: $(obj)/vmlinux.img $(obj)/vmlinux.relocs
+ $(call if_changed,bin)
+endif
targets += vmlinux.srec
quiet_cmd_srec = OBJCOPY $@
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 4/7] MIPS: Generate relocation table when CONFIG_RELOCATABLE
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
When CONFIG_RELOCATABLE is enabled (added in later patch)
add --emit-relocs to vmlinux LDFLAGS so that the elf
contains relocation information.
Run the previously added relocation tool to append the
relocation table to the binary image.
Space for the table is reserved in the linker script
by a previous commit
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Makefile | 4 ++++
arch/mips/boot/Makefile | 19 +++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 9371ea0adb88..fd28b0965c42 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -96,6 +96,10 @@ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls
+ifeq ($(CONFIG_RELOCATABLE),y)
+LDFLAGS_vmlinux += --emit-relocs
+endif
+
#
# pass -msoft-float to GAS if it supports it. However on newer binutils
# (specifically newer than 2.24.51.20140728) we then also need to explicitly
diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile
index fb5bfaec55ac..127705bb43a8 100644
--- a/arch/mips/boot/Makefile
+++ b/arch/mips/boot/Makefile
@@ -36,10 +36,29 @@ $(obj)/vmlinux.ecoff: $(obj)/elf2ecoff $(VMLINUX) FORCE
$(call if_changed,ecoff)
targets += vmlinux.bin
+ifneq ($(CONFIG_RELOCATABLE),y)
quiet_cmd_bin = OBJCOPY $@
cmd_bin = $(OBJCOPY) -O binary $(strip-flags) $(VMLINUX) $@
$(obj)/vmlinux.bin: $(VMLINUX) FORCE
$(call if_changed,bin)
+else
+# For relocatable kernel, Generate a relocation table that is appended to the image
+
+CMD_RELOCS = arch/mips/boot/tools/relocs
+quiet_cmd_relocs = RELOCS $@
+ cmd_relocs = $(CMD_RELOCS) $< > $@
+$(obj)/vmlinux.relocs: $(VMLINUX) FORCE
+ $(call if_changed,relocs)
+
+OBJCOPYFLAGS_vmlinux.img := -O binary $(strip-flags)
+$(obj)/vmlinux.img: $(VMLINUX) FORCE
+ $(call if_changed,objcopy)
+
+quiet_cmd_bin = BIN $@
+ cmd_bin = cat $^ > $@
+$(obj)/vmlinux.bin: $(obj)/vmlinux.img $(obj)/vmlinux.relocs
+ $(call if_changed,bin)
+endif
targets += vmlinux.srec
quiet_cmd_srec = OBJCOPY $@
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 5/7] MIPS: Kernel: Add relocate.c
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
arch/mips/kernel/relocate.c contains the functions
necessary to relocate the kernel elsewhere in memory
The kernel makes a copy of itself at the new address.
It uses the relocation table appended by the relocs tool
to fix symbol references within the new image.
If copy/relocation is sucessful then the entry point
of the new kernel is returned, otherwise fall back
to starting the kernel in place.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
Currently the destination address of the kernel is
hardcoded, but this can easily be changed to
a parameter from the command line, or a randomly
chosen address to implement KASLR.
Kernel 64bit support
---
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/relocate.c | 225 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 227 insertions(+)
create mode 100644 arch/mips/kernel/relocate.c
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index fffa1ac86740..a02170f7c6d1 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -85,6 +85,8 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
+obj-$(CONFIG_RELOCATABLE) += relocate.o
+
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c
new file mode 100644
index 000000000000..36d42f0fd198
--- /dev/null
+++ b/arch/mips/kernel/relocate.c
@@ -0,0 +1,225 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2015 Matt Redfearn (matt.redfearn@imgtec.com)
+ */
+#include <linux/start_kernel.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <asm/sections.h>
+#include <linux/sched.h>
+#include <asm/setup.h>
+#include <asm/cacheflush.h>
+
+extern long _relocation_start; /* End kernel image / start relocation table */
+extern long _relocation_end; /* End relocation table */
+
+extern long __start___ex_table; /* Start exception table */
+extern long __stop___ex_table; /* End exception table */
+
+
+#if 1
+#define PRINTK(...)
+#else
+#define PRINTK(...) printk(__VA_ARGS__)
+#endif
+
+static int __init apply_r_mips_64_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *(u64*)loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_32_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_26_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long target_addr = (*loc_orig) & 0x03ffffff;
+
+ if (offset % 4) {
+ pr_err("Dangerous R_MIPS_26 REL relocation\n");
+ return -ENOEXEC;
+ }
+
+ /* Original target address */
+ target_addr <<= 2;
+ target_addr += (unsigned long)loc_orig & ~0x03ffffff;
+
+ /* Get the new target address */
+ target_addr = (long)target_addr + offset;
+
+ if ((target_addr & 0xf0000000) != ((unsigned long)loc_new & 0xf0000000)) {
+ pr_err("R_MIPS_26 REL relocation overflow\n");
+ return -ENOEXEC;
+ }
+
+ target_addr -= (unsigned long)loc_new & ~0x03ffffff;
+ target_addr >>= 2;
+
+ *loc_new = (*loc_new & ~0x03ffffff) | (target_addr & 0x03ffffff);
+
+ return 0;
+}
+
+
+static int __init apply_r_mips_hi16_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long insn = *loc_orig;
+ unsigned long target = (insn & 0xFFFF) << 16; /* high 16bits of target */
+
+ target += offset;
+ if (target > 0xFFFFFFFF)
+ /* Relocation would overflow */
+ return -ENOEXEC;
+
+ *loc_new = (insn & ~0xFFFF) | ((target >> 16) & 0xFFFF);
+ return 0;
+}
+
+static int (*reloc_handlers_rel[]) (u32 *, u32 *, long) __initdata = {
+ [R_MIPS_64] = apply_r_mips_64_rel,
+ [R_MIPS_32] = apply_r_mips_32_rel,
+ [R_MIPS_26] = apply_r_mips_26_rel,
+ [R_MIPS_HI16] = apply_r_mips_hi16_rel,
+};
+
+int __init do_relocations(void *kbase_old, void *kbase_new, long offset)
+{
+ u32 *r;
+ u32 *loc_orig;
+ u32 *loc_new;
+ int type;
+ int res;
+
+ PRINTK(KERN_INFO "%s 0x%p -> 0x%p\n", __FUNCTION__, kbase_old, kbase_new);
+
+ for (r = (u32*)&_relocation_start; r < (u32*)&_relocation_end; r++) {
+ /* Sentinel for last relocation */
+ if (*r == 0)
+ break;
+
+ type = ((*r) >> 24) & 0xFF;
+ loc_orig = (void*)(kbase_old + ((*r) & 0x00FFFFFF));
+ loc_new = (void*)((unsigned long)loc_orig + offset);
+
+ PRINTK(KERN_INFO "relocation %d @0x%p -> 0x%p\n",
+ type, loc_orig, loc_new);
+
+ if (reloc_handlers_rel[type] == NULL)
+ /* Unsupported relocation */
+ return -ENOEXEC;
+
+ res = reloc_handlers_rel[type](loc_orig, loc_new, offset);
+ if (res)
+ return res;
+ }
+
+ /* New kernel has been written - flush the caches ready for execution */
+ //flush_cache_all();
+ return 0;
+}
+
+static int relocate_exception_table(long offset)
+{
+#ifdef CONFIG_64BIT
+ u64* etable_start, *etable_end, *e;
+#else
+ u32* etable_start, *etable_end, *e;
+#endif
+
+ etable_start = (void*)((unsigned long)&__start___ex_table + offset);
+ etable_end = (void*)((unsigned long)&__stop___ex_table + offset);
+
+ for (e = etable_start; e < etable_end; e++) {
+ *e += offset;
+ }
+ return 0;
+}
+
+static inline void __init *determine_relocation_address(void)
+{
+ /* Choose a new address for the kernel */
+ /* For now we'll hard code the destination */
+ return (void*)0xFFFFFFFF81000000;
+}
+
+static inline int __init relocation_addr_valid(void *loc_new)
+{
+ if ((unsigned long)loc_new & 0x0000FFFF)
+ return 0; /* Inappropriately aligned new location */
+ if ((unsigned long)loc_new < (unsigned long)&_end)
+ return 0; /* New location overlaps original kernel */
+ return 1;
+}
+
+void __init *relocate_kernel(void)
+{
+ void *loc_new;
+ unsigned long kernel_length = (long)(&_relocation_start)-(long)(&_text);
+ long offset = 0;
+ int res = 1;
+
+ loc_new = determine_relocation_address();
+ PRINTK(KERN_INFO "%s to 0x%p\n", __FUNCTION__, loc_new);
+
+ /* Sanity check relocation address */
+ if (relocation_addr_valid(loc_new))
+ offset = (unsigned long)loc_new - (unsigned long)(&_text);
+
+ if (offset) {
+ PRINTK(KERN_INFO "%s Copy %ld 0x%p -> %p\n",
+ __FUNCTION__, kernel_length, (&_text), loc_new);
+
+ /* Copy the kernel to it's new location */
+ memcpy(loc_new, &_text, kernel_length);
+
+ PRINTK(KERN_INFO "%s kernel copied\n", __FUNCTION__);
+
+ /* Perform relocations on the new kernel */
+ res = do_relocations(&_text, loc_new, offset);
+
+ if (res == 0)
+ res = relocate_exception_table(offset);
+ }
+
+ if (res == 0) {
+ void *bss_new = (void*)((long)&__bss_start + offset);
+ long bss_length = (long)&__bss_stop - (long)&__bss_start;
+
+ /* The original .bss has already been cleared, and
+ * some variables such as command line parameters
+ * stored to it so make a copy in the new location.
+ */
+ memcpy(bss_new, &__bss_start, bss_length);
+
+ /* The current thread is now within the relocated image */
+ __current_thread_info = (void*)((long)&init_thread_union + offset);
+
+ /* Return the new kernel's entry point */
+ return(void*)((long)start_kernel + offset);
+ }
+ else {
+ /* Something went wrong in the relocation process
+ * Just boot the original kernel */
+ return start_kernel;
+ }
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 5/7] MIPS: Kernel: Add relocate.c
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
arch/mips/kernel/relocate.c contains the functions
necessary to relocate the kernel elsewhere in memory
The kernel makes a copy of itself at the new address.
It uses the relocation table appended by the relocs tool
to fix symbol references within the new image.
If copy/relocation is sucessful then the entry point
of the new kernel is returned, otherwise fall back
to starting the kernel in place.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
Currently the destination address of the kernel is
hardcoded, but this can easily be changed to
a parameter from the command line, or a randomly
chosen address to implement KASLR.
Kernel 64bit support
---
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/relocate.c | 225 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 227 insertions(+)
create mode 100644 arch/mips/kernel/relocate.c
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index fffa1ac86740..a02170f7c6d1 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -85,6 +85,8 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
+obj-$(CONFIG_RELOCATABLE) += relocate.o
+
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c
new file mode 100644
index 000000000000..36d42f0fd198
--- /dev/null
+++ b/arch/mips/kernel/relocate.c
@@ -0,0 +1,225 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2015 Matt Redfearn (matt.redfearn@imgtec.com)
+ */
+#include <linux/start_kernel.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <asm/sections.h>
+#include <linux/sched.h>
+#include <asm/setup.h>
+#include <asm/cacheflush.h>
+
+extern long _relocation_start; /* End kernel image / start relocation table */
+extern long _relocation_end; /* End relocation table */
+
+extern long __start___ex_table; /* Start exception table */
+extern long __stop___ex_table; /* End exception table */
+
+
+#if 1
+#define PRINTK(...)
+#else
+#define PRINTK(...) printk(__VA_ARGS__)
+#endif
+
+static int __init apply_r_mips_64_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *(u64*)loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_32_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_26_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long target_addr = (*loc_orig) & 0x03ffffff;
+
+ if (offset % 4) {
+ pr_err("Dangerous R_MIPS_26 REL relocation\n");
+ return -ENOEXEC;
+ }
+
+ /* Original target address */
+ target_addr <<= 2;
+ target_addr += (unsigned long)loc_orig & ~0x03ffffff;
+
+ /* Get the new target address */
+ target_addr = (long)target_addr + offset;
+
+ if ((target_addr & 0xf0000000) != ((unsigned long)loc_new & 0xf0000000)) {
+ pr_err("R_MIPS_26 REL relocation overflow\n");
+ return -ENOEXEC;
+ }
+
+ target_addr -= (unsigned long)loc_new & ~0x03ffffff;
+ target_addr >>= 2;
+
+ *loc_new = (*loc_new & ~0x03ffffff) | (target_addr & 0x03ffffff);
+
+ return 0;
+}
+
+
+static int __init apply_r_mips_hi16_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long insn = *loc_orig;
+ unsigned long target = (insn & 0xFFFF) << 16; /* high 16bits of target */
+
+ target += offset;
+ if (target > 0xFFFFFFFF)
+ /* Relocation would overflow */
+ return -ENOEXEC;
+
+ *loc_new = (insn & ~0xFFFF) | ((target >> 16) & 0xFFFF);
+ return 0;
+}
+
+static int (*reloc_handlers_rel[]) (u32 *, u32 *, long) __initdata = {
+ [R_MIPS_64] = apply_r_mips_64_rel,
+ [R_MIPS_32] = apply_r_mips_32_rel,
+ [R_MIPS_26] = apply_r_mips_26_rel,
+ [R_MIPS_HI16] = apply_r_mips_hi16_rel,
+};
+
+int __init do_relocations(void *kbase_old, void *kbase_new, long offset)
+{
+ u32 *r;
+ u32 *loc_orig;
+ u32 *loc_new;
+ int type;
+ int res;
+
+ PRINTK(KERN_INFO "%s 0x%p -> 0x%p\n", __FUNCTION__, kbase_old, kbase_new);
+
+ for (r = (u32*)&_relocation_start; r < (u32*)&_relocation_end; r++) {
+ /* Sentinel for last relocation */
+ if (*r == 0)
+ break;
+
+ type = ((*r) >> 24) & 0xFF;
+ loc_orig = (void*)(kbase_old + ((*r) & 0x00FFFFFF));
+ loc_new = (void*)((unsigned long)loc_orig + offset);
+
+ PRINTK(KERN_INFO "relocation %d @0x%p -> 0x%p\n",
+ type, loc_orig, loc_new);
+
+ if (reloc_handlers_rel[type] == NULL)
+ /* Unsupported relocation */
+ return -ENOEXEC;
+
+ res = reloc_handlers_rel[type](loc_orig, loc_new, offset);
+ if (res)
+ return res;
+ }
+
+ /* New kernel has been written - flush the caches ready for execution */
+ //flush_cache_all();
+ return 0;
+}
+
+static int relocate_exception_table(long offset)
+{
+#ifdef CONFIG_64BIT
+ u64* etable_start, *etable_end, *e;
+#else
+ u32* etable_start, *etable_end, *e;
+#endif
+
+ etable_start = (void*)((unsigned long)&__start___ex_table + offset);
+ etable_end = (void*)((unsigned long)&__stop___ex_table + offset);
+
+ for (e = etable_start; e < etable_end; e++) {
+ *e += offset;
+ }
+ return 0;
+}
+
+static inline void __init *determine_relocation_address(void)
+{
+ /* Choose a new address for the kernel */
+ /* For now we'll hard code the destination */
+ return (void*)0xFFFFFFFF81000000;
+}
+
+static inline int __init relocation_addr_valid(void *loc_new)
+{
+ if ((unsigned long)loc_new & 0x0000FFFF)
+ return 0; /* Inappropriately aligned new location */
+ if ((unsigned long)loc_new < (unsigned long)&_end)
+ return 0; /* New location overlaps original kernel */
+ return 1;
+}
+
+void __init *relocate_kernel(void)
+{
+ void *loc_new;
+ unsigned long kernel_length = (long)(&_relocation_start)-(long)(&_text);
+ long offset = 0;
+ int res = 1;
+
+ loc_new = determine_relocation_address();
+ PRINTK(KERN_INFO "%s to 0x%p\n", __FUNCTION__, loc_new);
+
+ /* Sanity check relocation address */
+ if (relocation_addr_valid(loc_new))
+ offset = (unsigned long)loc_new - (unsigned long)(&_text);
+
+ if (offset) {
+ PRINTK(KERN_INFO "%s Copy %ld 0x%p -> %p\n",
+ __FUNCTION__, kernel_length, (&_text), loc_new);
+
+ /* Copy the kernel to it's new location */
+ memcpy(loc_new, &_text, kernel_length);
+
+ PRINTK(KERN_INFO "%s kernel copied\n", __FUNCTION__);
+
+ /* Perform relocations on the new kernel */
+ res = do_relocations(&_text, loc_new, offset);
+
+ if (res == 0)
+ res = relocate_exception_table(offset);
+ }
+
+ if (res == 0) {
+ void *bss_new = (void*)((long)&__bss_start + offset);
+ long bss_length = (long)&__bss_stop - (long)&__bss_start;
+
+ /* The original .bss has already been cleared, and
+ * some variables such as command line parameters
+ * stored to it so make a copy in the new location.
+ */
+ memcpy(bss_new, &__bss_start, bss_length);
+
+ /* The current thread is now within the relocated image */
+ __current_thread_info = (void*)((long)&init_thread_union + offset);
+
+ /* Return the new kernel's entry point */
+ return(void*)((long)start_kernel + offset);
+ }
+ else {
+ /* Something went wrong in the relocation process
+ * Just boot the original kernel */
+ return start_kernel;
+ }
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 6/7] MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
If CONFIG_RELOCATABLE is enabled, jump to relocate_kernel.
This function will return the entry point of the
relocated kernel if copy/relocate is sucessful or the
original entry point if not.
The stack pointer must then be pointed into the new image.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/kernel/head.S | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 4e4cc5b9a771..109d6a0aeab0 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -132,7 +132,25 @@ not_found:
set_saved_sp sp, t0, t1
PTR_SUBU sp, 4 * SZREG # init stack pointer
+#ifdef CONFIG_RELOCATABLE
+ /* Copy kernel and apply the relocations */
+ jal relocate_kernel
+
+ /* Repoint the sp into the new kernel image */
+ PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
+ PTR_ADDU sp, $28
+ back_to_back_c0_hazard
+ set_saved_sp sp, t0, t1
+ PTR_SUBU sp, 4 * SZREG # init stack pointer
+
+ /* relocate_kernel returns the entry point either
+ * in the relocated kernel or the original if for
+ * some reason relocation failed - jump there now
+ */
+ jr v0
+#else
j start_kernel
+#endif
END(kernel_entry)
#ifdef CONFIG_SMP
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 6/7] MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
If CONFIG_RELOCATABLE is enabled, jump to relocate_kernel.
This function will return the entry point of the
relocated kernel if copy/relocate is sucessful or the
original entry point if not.
The stack pointer must then be pointed into the new image.
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/kernel/head.S | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 4e4cc5b9a771..109d6a0aeab0 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -132,7 +132,25 @@ not_found:
set_saved_sp sp, t0, t1
PTR_SUBU sp, 4 * SZREG # init stack pointer
+#ifdef CONFIG_RELOCATABLE
+ /* Copy kernel and apply the relocations */
+ jal relocate_kernel
+
+ /* Repoint the sp into the new kernel image */
+ PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
+ PTR_ADDU sp, $28
+ back_to_back_c0_hazard
+ set_saved_sp sp, t0, t1
+ PTR_SUBU sp, 4 * SZREG # init stack pointer
+
+ /* relocate_kernel returns the entry point either
+ * in the relocated kernel or the original if for
+ * some reason relocation failed - jump there now
+ */
+ jr v0
+#else
j start_kernel
+#endif
END(kernel_entry)
#ifdef CONFIG_SMP
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 7/7] MIPS: Add CONFIG_RELOCATABLE Kconfig option
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Add option to KConfig to enable the kernel to relocate
itself at runtime
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Kconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 6a69bdb07f18..beef7dfabd58 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2472,6 +2472,14 @@ config NUMA
config SYS_SUPPORTS_NUMA
bool
+config RELOCATABLE
+ bool "Relocatable kernel"
+ help
+ This builds a kernel image that retains relocation information
+ so it can be loaded someplace besides the default 1MB.
+ The relocations make the kernel binary about 15% larger,
+ but are discarded at runtime
+
config NODES_SHIFT
int
default "6"
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 7/7] MIPS: Add CONFIG_RELOCATABLE Kconfig option
@ 2015-10-05 9:52 ` Matt Redfearn
0 siblings, 0 replies; 19+ messages in thread
From: Matt Redfearn @ 2015-10-05 9:52 UTC (permalink / raw)
To: linux-mips; +Cc: Matt Redfearn
Add option to KConfig to enable the kernel to relocate
itself at runtime
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
---
arch/mips/Kconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 6a69bdb07f18..beef7dfabd58 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2472,6 +2472,14 @@ config NUMA
config SYS_SUPPORTS_NUMA
bool
+config RELOCATABLE
+ bool "Relocatable kernel"
+ help
+ This builds a kernel image that retains relocation information
+ so it can be loaded someplace besides the default 1MB.
+ The relocations make the kernel binary about 15% larger,
+ but are discarded at runtime
+
config NODES_SHIFT
int
default "6"
--
2.1.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 0/7] Relocatable Kernel
2015-10-05 9:52 ` Matt Redfearn
` (7 preceding siblings ...)
(?)
@ 2015-10-07 5:51 ` Silesh C V
2015-10-07 6:39 ` Ralf Baechle
2015-10-07 6:42 ` Ralf Baechle
-1 siblings, 2 replies; 19+ messages in thread
From: Silesh C V @ 2015-10-07 5:51 UTC (permalink / raw)
To: Matt Redfearn; +Cc: linux-mips
Hello Matt,
On Mon, Oct 5, 2015 at 3:22 PM, Matt Redfearn <matt.redfearn@imgtec.com> wrote:
> Hi,
> This patch series is a prerequisite of adding KASLR support for MIPS.
> So far this supports 32/64 bit big/little endian CPUs and has been
> tested in Qemu for Malta. It has not been tested on real hardware yet.
> It is presented here as an early RFC for issues that people can forsee with
> the many platforms, memory layouts etc that it must support.
>
> Here is a description of how relocation is achieved:
> * Kernel is compiled & statically linked as normal (no position independent code).
> * The linker flag --emit-relocs is added to the linker command line, causing ld
> to include additional ".rel" sections in the output elf
> * A tool is used to parse the ".rel" sections and create a binary table of
> relocations. Each entry in the table is 32bits, comprised of a 24bit offset
> and 8bit relocation type.
> * The table is appended to the kernel binary image, with some space in the
> memory map reserved for it in the linker script
> * At boot, the kernel memcpy()s itself elsewhere in memory, then goes through
> the table performing each relocation on the new image
> * If all goes well, control is passed to the entry point of the new kernel.
>
We (at MontaVista) also have a working KASLR implementation (albeit on
3.10.x kernel on which our distribution is based) which has been/is
being tested only on Cavium Octeon2. We used the following approach
based on a compressed image(like what x86_64 does). This makes the
whole thing dependent on SYS_SUPPORTS_ZBOOT though.
During build:
1. Statically link the kernel but generate relocations with
--emit-relocs (As you have done).
2. Use the relocs hostprog (Again, derived from x86 relocs tool) to
parse and optimize the relocations generated after vmlinux is built.
The final relocation entries are of the form (64 bit rel_offset,8bit
rel_type). (I see you record only the offset from start of .text in
the offset field, Nice way to save some space :) ).
3. The final relocation information/table is compressed together with
vmlinux.bin to generate vmlinuz.
4. We have some logic in the relocs tool to record the size of
vmlinux.bin so that the decompressor routine can find the start of
relocation table at runtime after decompression.
At runtime:
4. The decompressor stub decompresses the vmlinux(along with the
relocation table) into a random location rather than to
VMLINUX_LOAD_ADDRESS_ULL.
5. The entropy is generated using LSBs of CP0 count register.
6. After decompression, a routine handle_relocations finds the start
of the relocation table in the decompression buffer (see 4 above) and
processes the relocations based on the link-load offset. (x86_64 goes
to the end of the buffer and works backwards using 0 as a seperator
between the relocation table and uncompressed vmlinux ).
7. Return the load offset to arch/mips/boot/compressed/head.S.
8. Based on this load offset, fixup KERNEL_ENTRY in compressed/head.S
and jump to that address.
With this approach, the only places where we needed to touch the
kernel proper code was
1. We do not relocate the entries in ex_table at bootup but resort to
fixing them up runtime (I am not proud of that code :)). My initial
plan was to use relative entries in the ex_table (Like what x86_64
has). This will make the entries not to be relocated at bootup(will
also make them half the size). But the compiler would not let me do
that because "operation combines symbols in different segments".
2. The entries in kcrctab gets relocated during bootup so that during
module insertion a crc mismatch will arise and the module will not get
installed. Fixed this the PowerPC way using ARCH_RELOCATES_KCRCTAB and
using _text - VMLINUX_LOAD_ADDRESS as reloc_start. Maybe the relocs
tool could take care of this. (I see that you have not taken care of
this or maybe you have and I missed it).
There is one issue with our approach. As our objective was only KASLR,
the vmlinuz is linked at a constant address (and I have not made the
decompressor stub -fPIC) ignoring calc_vmlinuz_load_addr . To have a
RELOCATABLE binary, I think we will need to have a -fPIC decompressor
stub.
I have not forward ported/tested my changes on newer kernels, so I do
not have a RFC patch set yet.
Thanks,
Silesh.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 0/7] Relocatable Kernel
2015-10-07 5:51 ` [RFC PATCH 0/7] Relocatable Kernel Silesh C V
@ 2015-10-07 6:39 ` Ralf Baechle
2015-10-07 6:42 ` Ralf Baechle
1 sibling, 0 replies; 19+ messages in thread
From: Ralf Baechle @ 2015-10-07 6:39 UTC (permalink / raw)
To: Silesh C V; +Cc: Matt Redfearn, linux-mips
On Wed, Oct 07, 2015 at 11:21:07AM +0530, Silesh C V wrote:
> Hello Matt,
>
> On Mon, Oct 5, 2015 at 3:22 PM, Matt Redfearn <matt.redfearn@imgtec.com> wrote:
> > Hi,
> > This patch series is a prerequisite of adding KASLR support for MIPS.
> > So far this supports 32/64 bit big/little endian CPUs and has been
> > tested in Qemu for Malta. It has not been tested on real hardware yet.
> > It is presented here as an early RFC for issues that people can forsee with
> > the many platforms, memory layouts etc that it must support.
> >
> > Here is a description of how relocation is achieved:
> > * Kernel is compiled & statically linked as normal (no position independent code).
> > * The linker flag --emit-relocs is added to the linker command line, causing ld
> > to include additional ".rel" sections in the output elf
> > * A tool is used to parse the ".rel" sections and create a binary table of
> > relocations. Each entry in the table is 32bits, comprised of a 24bit offset
> > and 8bit relocation type.
> > * The table is appended to the kernel binary image, with some space in the
> > memory map reserved for it in the linker script
> > * At boot, the kernel memcpy()s itself elsewhere in memory, then goes through
> > the table performing each relocation on the new image
> > * If all goes well, control is passed to the entry point of the new kernel.
Due to the hefty size of the generated relocs we've decided to drop some
of the relocation information. For one, due to a number of objects in
the kernel that need large alignment, such as init_thread_union, the
kernel needs at least 8k alignment anyway and that could be as large as
64k. So if we allow relocation to addresses that are a multiple of 64
the KASLR address will lose a bit of entroy at the benefit of a
significantly smaller kernel.
Also for 64 bit objects the R_MIPS_HIGHER and R_MIPS_HIGHEST addends
will be zero if we only allow relocation within a 4GB segment. Which
for many embedded systems may actually not be a limitation at all due
to physical memory limitations.
The highly bloated NABI relocation format that combines three relocs
into a single relocation record of which only one only ever seems to
get used allows for further space saving so so do the R_MIPS_NONE
records.
I say we because I was talking a lot with Matt about the implementation
but the code is 100% Matt's.
> We (at MontaVista) also have a working KASLR implementation (albeit on
> 3.10.x kernel on which our distribution is based) which has been/is
> being tested only on Cavium Octeon2. We used the following approach
> based on a compressed image(like what x86_64 does). This makes the
> whole thing dependent on SYS_SUPPORTS_ZBOOT though.
>
> During build:
>
> 1. Statically link the kernel but generate relocations with
> --emit-relocs (As you have done).
> 2. Use the relocs hostprog (Again, derived from x86 relocs tool) to
> parse and optimize the relocations generated after vmlinux is built.
> The final relocation entries are of the form (64 bit rel_offset,8bit
> rel_type). (I see you record only the offset from start of .text in
> the offset field, Nice way to save some space :) ).
> 3. The final relocation information/table is compressed together with
> vmlinux.bin to generate vmlinuz.
> 4. We have some logic in the relocs tool to record the size of
> vmlinux.bin so that the decompressor routine can find the start of
> relocation table at runtime after decompression.
>
> At runtime:
>
> 4. The decompressor stub decompresses the vmlinux(along with the
> relocation table) into a random location rather than to
> VMLINUX_LOAD_ADDRESS_ULL.
> 5. The entropy is generated using LSBs of CP0 count register.
> 6. After decompression, a routine handle_relocations finds the start
> of the relocation table in the decompression buffer (see 4 above) and
> processes the relocations based on the link-load offset. (x86_64 goes
> to the end of the buffer and works backwards using 0 as a seperator
> between the relocation table and uncompressed vmlinux ).
> 7. Return the load offset to arch/mips/boot/compressed/head.S.
> 8. Based on this load offset, fixup KERNEL_ENTRY in compressed/head.S
> and jump to that address.
>
> With this approach, the only places where we needed to touch the
> kernel proper code was
>
> 1. We do not relocate the entries in ex_table at bootup but resort to
> fixing them up runtime (I am not proud of that code :)). My initial
> plan was to use relative entries in the ex_table (Like what x86_64
> has). This will make the entries not to be relocated at bootup(will
> also make them half the size). But the compiler would not let me do
> that because "operation combines symbols in different segments".
>
> 2. The entries in kcrctab gets relocated during bootup so that during
> module insertion a crc mismatch will arise and the module will not get
> installed. Fixed this the PowerPC way using ARCH_RELOCATES_KCRCTAB and
> using _text - VMLINUX_LOAD_ADDRESS as reloc_start. Maybe the relocs
> tool could take care of this. (I see that you have not taken care of
> this or maybe you have and I missed it).
>
> There is one issue with our approach. As our objective was only KASLR,
> the vmlinuz is linked at a constant address (and I have not made the
> decompressor stub -fPIC) ignoring calc_vmlinuz_load_addr . To have a
> RELOCATABLE binary, I think we will need to have a -fPIC decompressor
> stub.
>
> I have not forward ported/tested my changes on newer kernels, so I do
> not have a RFC patch set yet.
I love competing implementations. Let the better win :-)
Ralf
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 0/7] Relocatable Kernel
2015-10-07 5:51 ` [RFC PATCH 0/7] Relocatable Kernel Silesh C V
2015-10-07 6:39 ` Ralf Baechle
@ 2015-10-07 6:42 ` Ralf Baechle
1 sibling, 0 replies; 19+ messages in thread
From: Ralf Baechle @ 2015-10-07 6:42 UTC (permalink / raw)
To: Silesh C V; +Cc: Matt Redfearn, linux-mips
On Wed, Oct 07, 2015 at 11:21:07AM +0530, Silesh C V wrote:
> Hello Matt,
>
> On Mon, Oct 5, 2015 at 3:22 PM, Matt Redfearn <matt.redfearn@imgtec.com> wrote:
> > Hi,
> > This patch series is a prerequisite of adding KASLR support for MIPS.
> > So far this supports 32/64 bit big/little endian CPUs and has been
> > tested in Qemu for Malta. It has not been tested on real hardware yet.
> > It is presented here as an early RFC for issues that people can forsee with
> > the many platforms, memory layouts etc that it must support.
> >
> > Here is a description of how relocation is achieved:
> > * Kernel is compiled & statically linked as normal (no position independent code).
> > * The linker flag --emit-relocs is added to the linker command line, causing ld
> > to include additional ".rel" sections in the output elf
> > * A tool is used to parse the ".rel" sections and create a binary table of
> > relocations. Each entry in the table is 32bits, comprised of a 24bit offset
> > and 8bit relocation type.
> > * The table is appended to the kernel binary image, with some space in the
> > memory map reserved for it in the linker script
> > * At boot, the kernel memcpy()s itself elsewhere in memory, then goes through
> > the table performing each relocation on the new image
> > * If all goes well, control is passed to the entry point of the new kernel.
Due to the hefty size of the generated relocs we've decided to drop some
of the relocation information. For one, due to a number of objects in
the kernel that need large alignment, such as init_thread_union, the
kernel needs at least 8k alignment anyway and that could be as large as
64k. So if we allow relocation to addresses that are a multiple of 64
the KASLR address will lose a bit of entroy at the benefit of a
significantly smaller kernel.
Also for 64 bit objects the R_MIPS_HIGHER and R_MIPS_HIGHEST addends
will be zero if we only allow relocation within a 4GB segment. Which
for many embedded systems may actually not be a limitation at all due
to physical memory limitations.
The highly bloated NABI relocation format that combines three relocs
into a single relocation record of which only one only ever seems to
get used allows for further space saving so so do the R_MIPS_NONE
records.
I say we because I was talking a lot with Matt about the implementation
but the code is 100% Matt's.
> We (at MontaVista) also have a working KASLR implementation (albeit on
> 3.10.x kernel on which our distribution is based) which has been/is
> being tested only on Cavium Octeon2. We used the following approach
> based on a compressed image(like what x86_64 does). This makes the
> whole thing dependent on SYS_SUPPORTS_ZBOOT though.
>
> During build:
>
> 1. Statically link the kernel but generate relocations with
> --emit-relocs (As you have done).
> 2. Use the relocs hostprog (Again, derived from x86 relocs tool) to
> parse and optimize the relocations generated after vmlinux is built.
> The final relocation entries are of the form (64 bit rel_offset,8bit
> rel_type). (I see you record only the offset from start of .text in
> the offset field, Nice way to save some space :) ).
> 3. The final relocation information/table is compressed together with
> vmlinux.bin to generate vmlinuz.
> 4. We have some logic in the relocs tool to record the size of
> vmlinux.bin so that the decompressor routine can find the start of
> relocation table at runtime after decompression.
>
> At runtime:
>
> 4. The decompressor stub decompresses the vmlinux(along with the
> relocation table) into a random location rather than to
> VMLINUX_LOAD_ADDRESS_ULL.
> 5. The entropy is generated using LSBs of CP0 count register.
> 6. After decompression, a routine handle_relocations finds the start
> of the relocation table in the decompression buffer (see 4 above) and
> processes the relocations based on the link-load offset. (x86_64 goes
> to the end of the buffer and works backwards using 0 as a seperator
> between the relocation table and uncompressed vmlinux ).
> 7. Return the load offset to arch/mips/boot/compressed/head.S.
> 8. Based on this load offset, fixup KERNEL_ENTRY in compressed/head.S
> and jump to that address.
>
> With this approach, the only places where we needed to touch the
> kernel proper code was
>
> 1. We do not relocate the entries in ex_table at bootup but resort to
> fixing them up runtime (I am not proud of that code :)). My initial
> plan was to use relative entries in the ex_table (Like what x86_64
> has). This will make the entries not to be relocated at bootup(will
> also make them half the size). But the compiler would not let me do
> that because "operation combines symbols in different segments".
>
> 2. The entries in kcrctab gets relocated during bootup so that during
> module insertion a crc mismatch will arise and the module will not get
> installed. Fixed this the PowerPC way using ARCH_RELOCATES_KCRCTAB and
> using _text - VMLINUX_LOAD_ADDRESS as reloc_start. Maybe the relocs
> tool could take care of this. (I see that you have not taken care of
> this or maybe you have and I missed it).
>
> There is one issue with our approach. As our objective was only KASLR,
> the vmlinuz is linked at a constant address (and I have not made the
> decompressor stub -fPIC) ignoring calc_vmlinuz_load_addr . To have a
> RELOCATABLE binary, I think we will need to have a -fPIC decompressor
> stub.
>
> I have not forward ported/tested my changes on newer kernels, so I do
> not have a RFC patch set yet.
I love competing implementations. Let the better win :-)
Ralf
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2015-10-07 7:20 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-05 9:52 [RFC PATCH 0/7] Relocatable Kernel Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 1/7] MIPS: tools: Add relocs tool Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 2/7] MIPS: tools: Build " Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 3/7] MIPS: Reserve space for relocation table Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 4/7] MIPS: Generate relocation table when CONFIG_RELOCATABLE Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 5/7] MIPS: Kernel: Add relocate.c Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 6/7] MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-05 9:52 ` [RFC PATCH 7/7] MIPS: Add CONFIG_RELOCATABLE Kconfig option Matt Redfearn
2015-10-05 9:52 ` Matt Redfearn
2015-10-07 5:51 ` [RFC PATCH 0/7] Relocatable Kernel Silesh C V
2015-10-07 6:39 ` Ralf Baechle
2015-10-07 6:42 ` Ralf Baechle
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.