Add support to ARM for embedding PIEs into the kernel, loading them into genalloc pools (such as SRAM) and executing them. Support for ARM means performing R_ARM_RELATIVE fixups within the .rel.dyn section. Signed-off-by: Russ Dill <Russ.Dill@xxxxxx> --- arch/arm/Kconfig | 1 + arch/arm/Makefile | 5 +++ arch/arm/include/asm/elf.h | 1 + arch/arm/kernel/.gitignore | 1 + arch/arm/kernel/Makefile | 4 ++- arch/arm/kernel/pie.c | 83 +++++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/pie.lds.S | 40 +++++++++++++++++++++ arch/arm/kernel/vmlinux.lds.S | 2 ++ arch/arm/libpie/.gitignore | 3 ++ arch/arm/libpie/Makefile | 32 +++++++++++++++++ arch/arm/libpie/empty.S | 12 +++++++ 11 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 arch/arm/kernel/pie.c create mode 100644 arch/arm/kernel/pie.lds.S create mode 100644 arch/arm/libpie/.gitignore create mode 100644 arch/arm/libpie/Makefile create mode 100644 arch/arm/libpie/empty.S diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 43594d5..de7b7603 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -49,6 +49,7 @@ config ARM select HAVE_MEMBLOCK select HAVE_OPROFILE if (HAVE_PERF_EVENTS) select HAVE_PERF_EVENTS + select HAVE_PIE select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_SYSCALL_TRACEPOINTS select HAVE_UID16 diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 6fd2cea..a673d36 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -128,6 +128,8 @@ KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/uni CHECKFLAGS += -D__arm__ +OBJCOPY_OUTPUT_FORMAT := elf32-littlearm + #Default value head-y := arch/arm/kernel/head$(MMUEXT).o textofs-y := 0x00008000 @@ -273,6 +275,9 @@ drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ libs-y := arch/arm/lib/ $(libs-y) +PIE_LDS := arch/arm/kernel/pie.lds +libpie-$(CONFIG_PIE) += arch/arm/libpie/ + # Default target when executing plain make ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := xipImage diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index 56211f2..a8d036b 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -50,6 +50,7 @@ typedef struct user_fp elf_fpregset_t; #define R_ARM_NONE 0 #define R_ARM_PC24 1 #define R_ARM_ABS32 2 +#define R_ARM_RELATIVE 23 #define R_ARM_CALL 28 #define R_ARM_JUMP24 29 #define R_ARM_V4BX 40 diff --git a/arch/arm/kernel/.gitignore b/arch/arm/kernel/.gitignore index c5f676c..a055a48 100644 --- a/arch/arm/kernel/.gitignore +++ b/arch/arm/kernel/.gitignore @@ -1 +1,2 @@ vmlinux.lds +pie.lds diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 86d10dd..652312e 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -96,4 +96,6 @@ obj-y += psci.o obj-$(CONFIG_SMP) += psci_smp.o endif -extra-y := $(head-y) vmlinux.lds +obj-$(CONFIG_PIE) += pie.o + +extra-y := $(head-y) vmlinux.lds pie.lds diff --git a/arch/arm/kernel/pie.c b/arch/arm/kernel/pie.c new file mode 100644 index 0000000..5dff5d6 --- /dev/null +++ b/arch/arm/kernel/pie.c @@ -0,0 +1,83 @@ +/* + * Copyright 2013 Texas Instruments, Inc. + * Russ Dill <russ.dill@xxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/pie.h> +#include <linux/elf.h> + +#include <asm/elf.h> + +extern char __pie_rel_dyn_start[]; +extern char __pie_rel_dyn_end[]; +extern char __pie_tail_offset[]; + +struct arm_pie_tail { + int count; + uintptr_t offset[0]; +}; + +int pie_arch_fill_tail(void *tail, void *common_start, void *common_end, + void *overlay_start, void *code_start, void *code_end) +{ + Elf32_Rel *rel; + int records; + int i; + struct arm_pie_tail *pie_tail = tail; + int count; + + rel = (Elf32_Rel *) __pie_rel_dyn_start; + records = (__pie_rel_dyn_end - __pie_rel_dyn_start) / + sizeof(*rel); + + count = 0; + for (i = 0; i < records; i++, rel++) { + void *kern_off; + if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE) + return -ENOEXEC; + + /* Adjust offset to match area in kernel */ + kern_off = common_start + rel->r_offset; + + if (kern_off >= common_start && kern_off < code_end) { + if (tail) + pie_tail->offset[count] = rel->r_offset; + count++; + } else if (kern_off >= code_start && kern_off < code_end) { + if (tail) + pie_tail->offset[count] = rel->r_offset - + (code_start - overlay_start); + count++; + } + } + + if (tail) + pie_tail->count = count; + + return count * sizeof(uintptr_t) + sizeof(*pie_tail); +} +EXPORT_SYMBOL_GPL(pie_arch_fill_tail); + +/* + * R_ARM_RELATIVE: B(S) + A + * B(S) - Addressing origin of the output segment defining the symbol S. + * A - Addend for the relocation. + */ +int pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail, + unsigned long offset) +{ + struct arm_pie_tail *pie_tail = tail; + int i; + + /* Perform relocation fixups for given offset */ + for (i = 0; i < pie_tail->count; i++) + *((uintptr_t *) (pie_tail->offset[i] + base)) += offset; + + return 0; +} +EXPORT_SYMBOL_GPL(pie_arch_fixup); diff --git a/arch/arm/kernel/pie.lds.S b/arch/arm/kernel/pie.lds.S new file mode 100644 index 0000000..4fd5ac5 --- /dev/null +++ b/arch/arm/kernel/pie.lds.S @@ -0,0 +1,40 @@ +/* + * ld script to make ARM PIEs + * taken from the ARM vmlinux.lds.S version by Russ Dill <russ.dill@xxxxxx. + */ + +#include <asm-generic/pie.lds.h> + +OUTPUT_ARCH(arm) + +SECTIONS +{ + . = 0x0; + + PIE_COMMON_START + .got.plt : { + *(.got) + *(.got.plt) + } + .text : { + PIE_TEXT_TEXT + } + PIE_COMMON_END + + PIE_OVERLAY_START + OVERLAY : NOCROSSREFS { + } + PIE_OVERLAY_SEND + + __pie_rel_dyn_start : { + VMLINUX_SYMBOL(__pie_rel_dyn_start) = .; + } + .rel.dyn : { + KEEP(*(.rel*)) + } + __pie_rel_dyn_end : { + VMLINUX_SYMBOL(__pie_rel_dyn_end) = .; + } + + PIE_DISCARDS +} diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 7bcee5c..8c11235 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -77,6 +77,8 @@ SECTIONS #ifndef CONFIG_SMP_ON_UP *(.alt.smp.init) #endif + *(.pie.*) + *(.ARM.exidx.pie.*.text) *(.discard) *(.discard.*) } diff --git a/arch/arm/libpie/.gitignore b/arch/arm/libpie/.gitignore new file mode 100644 index 0000000..02e3cd5 --- /dev/null +++ b/arch/arm/libpie/.gitignore @@ -0,0 +1,3 @@ +lib1funcs.S +ashldi3.S +string.c diff --git a/arch/arm/libpie/Makefile b/arch/arm/libpie/Makefile new file mode 100644 index 0000000..5662e99 --- /dev/null +++ b/arch/arm/libpie/Makefile @@ -0,0 +1,32 @@ +# +# linux/arch/arm/libpie/Makefile +# +ccflags-y := -fpic -mno-single-pic-base -fno-builtin + +obj-y := empty.o +obj-y += lib1funcs.o ashldi3.o string.o + +# string library code (-Os is enforced to keep it much smaller) +string = $(obj)/string.o +CFLAGS_string.o := -Os + +$(obj)/string.c: $(srctree)/arch/$(SRCARCH)/boot/compressed/string.c + $(call cmd,shipped) + +# For __aeabi_uidivmod +lib1funcs = $(obj)/lib1funcs.o + +$(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S + $(call cmd,shipped) + +# For __aeabi_llsl +ashldi3 = $(obj)/ashldi3.o + +$(obj)/ashldi3.S: $(srctree)/arch/$(SRCARCH)/lib/ashldi3.S + $(call cmd,shipped) + +$(obj)/libpie.o: $(string) $(lib1funcs) $(ashldi3) $(addprefix $(obj)/,$(OBJS)) + $(call if_changed,ld) + +# Make sure files are removed during clean +extra-y += string.c lib1funcs.S ashldi3.S diff --git a/arch/arm/libpie/empty.S b/arch/arm/libpie/empty.S new file mode 100644 index 0000000..2416862 --- /dev/null +++ b/arch/arm/libpie/empty.S @@ -0,0 +1,12 @@ +#include <linux/linkage.h> + +ENTRY(__div0) +ENTRY(__aeabi_unwind_cpp_pr0) +ENTRY(__aeabi_unwind_cpp_pr1) +ENTRY(__aeabi_unwind_cpp_pr2) + mov pc, lr +ENDPROC(__div0) +ENDPROC(__aeabi_unwind_cpp_pr0) +ENDPROC(__aeabi_unwind_cpp_pr1) +ENDPROC(__aeabi_unwind_cpp_pr2) + -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html