Hi Ard, This looks good.
My comments below are mostly nits, and much of the rest probably betrays my lack of familiarity with ELF. On Mon, Jan 11, 2016 at 02:19:01PM +0100, Ard Biesheuvel wrote: > This adds support for emitting PLTs at module load time for relative > branches that are out of range. This is a prerequisite for KASLR, which > may place the kernel and the modules anywhere in the vmalloc area, > making it more likely that branch target offsets exceed the maximum > range of +/- 128 MB. > > Signed-off-by: Ard Biesheuvel <[email protected]> > --- > arch/arm64/Kconfig | 9 ++ > arch/arm64/Makefile | 6 +- > arch/arm64/include/asm/module.h | 11 ++ > arch/arm64/kernel/Makefile | 1 + > arch/arm64/kernel/module-plts.c | 137 ++++++++++++++++++++ > arch/arm64/kernel/module.c | 12 ++ > arch/arm64/kernel/module.lds | 4 + > 7 files changed, 179 insertions(+), 1 deletion(-) > > diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig > index ffa3c549a4ba..778df20bf623 100644 > --- a/arch/arm64/Kconfig > +++ b/arch/arm64/Kconfig > @@ -363,6 +363,7 @@ config ARM64_ERRATUM_843419 > bool "Cortex-A53: 843419: A load or store might access an incorrect > address" > depends on MODULES > default y > + select ARM64_MODULE_CMODEL_LARGE > help > This option builds kernel modules using the large memory model in > order to avoid the use of the ADRP instruction, which can cause > @@ -702,6 +703,14 @@ config ARM64_LSE_ATOMICS > > endmenu > > +config ARM64_MODULE_CMODEL_LARGE > + bool > + > +config ARM64_MODULE_PLTS > + bool > + select ARM64_MODULE_CMODEL_LARGE > + select HAVE_MOD_ARCH_SPECIFIC > + > endmenu > > menu "Boot options" > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile > index cd822d8454c0..db462980c6be 100644 > --- a/arch/arm64/Makefile > +++ b/arch/arm64/Makefile > @@ -41,10 +41,14 @@ endif > > CHECKFLAGS += -D__aarch64__ > > -ifeq ($(CONFIG_ARM64_ERRATUM_843419), y) > +ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y) > KBUILD_CFLAGS_MODULE += -mcmodel=large > endif > > +ifeq ($(CONFIG_ARM64_MODULE_PLTS),y) > +KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/arm64/kernel/module.lds > +endif > + > # Default value > head-y := arch/arm64/kernel/head.o > > diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h > index e80e232b730e..7b8cd3dc9d8e 100644 > --- a/arch/arm64/include/asm/module.h > +++ b/arch/arm64/include/asm/module.h > @@ -20,4 +20,15 @@ > > #define MODULE_ARCH_VERMAGIC "aarch64" > > +#ifdef CONFIG_ARM64_MODULE_PLTS > +struct mod_arch_specific { > + struct elf64_shdr *core_plt; > + struct elf64_shdr *init_plt; > + int core_plt_count; > + int init_plt_count; > +}; > +#endif > + > +u64 get_module_plt(struct module *mod, void *loc, u64 val); > + > #endif /* __ASM_MODULE_H */ > diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile > index 474691f8b13a..f42b0fff607f 100644 > --- a/arch/arm64/kernel/Makefile > +++ b/arch/arm64/kernel/Makefile > @@ -30,6 +30,7 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o > signal32.o \ > ../../arm/kernel/opcodes.o > arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o > arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o > +arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o > arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o > arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o > arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o > diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c > new file mode 100644 > index 000000000000..4a8ef9ea01ee > --- /dev/null > +++ b/arch/arm64/kernel/module-plts.c > @@ -0,0 +1,137 @@ > +/* > + * Copyright (C) 2014-2015 Linaro Ltd. <[email protected]> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/elf.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > + > +struct plt_entry { > + __le32 mov0; /* movn x16, #0x.... */ > + __le32 mov1; /* movk x16, #0x...., lsl #16 */ > + __le32 mov2; /* movk x16, #0x...., lsl #32 */ > + __le32 br; /* br x16 */ > +} __aligned(8); We only need natural alignment for the instructions, so what's the alignment for? I can't see that anything else cares. It might be worth a comment regarding why why use x16 (i.e. because the AAPCS says that as IP0 it is valid for veneers/PLTs to clobber). > +static bool in_init(const struct module *mod, void *addr) > +{ > + return (u64)addr - (u64)mod->module_init < mod->init_size; > +} > + > +u64 get_module_plt(struct module *mod, void *loc, u64 val) > +{ > + struct plt_entry entry = { > + cpu_to_le32(0x92800010 | (((~val ) & 0xffff)) << 5), > + cpu_to_le32(0xf2a00010 | ((( val >> 16) & 0xffff)) << 5), > + cpu_to_le32(0xf2c00010 | ((( val >> 32) & 0xffff)) << 5), > + cpu_to_le32(0xd61f0200) > + }, *plt; It would be nice if we could un-magic this, though I see that reusing the existing insn or reloc_insn code is painful here. > + int i, *count; > + > + if (in_init(mod, loc)) { > + plt = (struct plt_entry *)mod->arch.init_plt->sh_addr; > + count = &mod->arch.init_plt_count; > + } else { > + plt = (struct plt_entry *)mod->arch.core_plt->sh_addr; > + count = &mod->arch.core_plt_count; > + } > + > + /* Look for an existing entry pointing to 'val' */ > + for (i = 0; i < *count; i++) > + if (plt[i].mov0 == entry.mov0 && > + plt[i].mov1 == entry.mov1 && > + plt[i].mov2 == entry.mov2) > + return (u64)&plt[i]; I think that at the cost of redundantly comparing the br x16, you could simplify this by comparing the whole struct, e.g. for (i = 0; i < *count; i++) if (plt[i] == entry) return (u64)&plt[i]; Which would also work if we change the veneer for some reason. > + > + i = (*count)++; given i == *count at the end of the loop, you could just increment *count here. > + plt[i] = entry; > + return (u64)&plt[i]; > +} > + > +static int duplicate_rel(Elf64_Addr base, const Elf64_Rela *rela, int num) Perhaps: static bool is_duplicate_rel > +{ > + int i; > + > + for (i = 0; i < num; i++) { > + if (rela[i].r_info == rela[num].r_info && > + rela[i].r_addend == rela[num].r_addend) > + return 1; > + } > + return 0; > +} > + > +/* Count how many PLT entries we may need */ > +static unsigned int count_plts(Elf64_Addr base, const Elf64_Rela *rela, int > num) > +{ > + unsigned int ret = 0; > + int i; > + > + /* > + * Sure, this is order(n^2), but it's usually short, and not > + * time critical > + */ > + for (i = 0; i < num; i++) > + switch (ELF64_R_TYPE(rela[i].r_info)) { > + case R_AARCH64_JUMP26: > + case R_AARCH64_CALL26: > + if (!duplicate_rel(base, rela, i)) > + ret++; > + break; > + } While braces aren't strictly required on the for loop, i think it would look better with them given the contained logic is non-trivial. > + return ret; > +} > + > +int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > + char *secstrings, struct module *mod) > +{ > + unsigned long core_plts = 0, init_plts = 0; > + Elf64_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; > + > + /* > + * To store the PLTs, we expand the .text section for core module code > + * and the .init.text section for initialization code. > + */ That comment is a bit misleading, given we don't touch .text and .init.text, but rather .core.plt and .init.plt, relying on layout_sections to group those with .text and .init.text. > + for (s = sechdrs; s < sechdrs_end; ++s) > + if (strcmp(".core.plt", secstrings + s->sh_name) == 0) > + mod->arch.core_plt = s; > + else if (strcmp(".init.plt", secstrings + s->sh_name) == 0) > + mod->arch.init_plt = s; This would be nicer with braces. > + > + if (!mod->arch.core_plt || !mod->arch.init_plt) { > + pr_err("%s: sections missing\n", mod->name); > + return -ENOEXEC; > + } > + > + for (s = sechdrs + 1; s < sechdrs_end; ++s) { Could we have a comment as to why we skip the first Shdr? I recall it's in some way special, but I can't recall why/how. > + const Elf64_Rela *rels = (void *)ehdr + s->sh_offset; > + int numrels = s->sh_size / sizeof(Elf64_Rela); > + Elf64_Shdr *dstsec = sechdrs + s->sh_info; > + > + if (s->sh_type != SHT_RELA) > + continue; We only have RELA, and no REL? Thanks, Mark. > + > + if (strstr(secstrings + s->sh_name, ".init")) > + init_plts += count_plts(dstsec->sh_addr, rels, numrels); > + else > + core_plts += count_plts(dstsec->sh_addr, rels, numrels); > + } > + > + mod->arch.core_plt->sh_type = SHT_NOBITS; > + mod->arch.core_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; > + mod->arch.core_plt->sh_addralign = L1_CACHE_BYTES; > + mod->arch.core_plt->sh_size = core_plts * sizeof(struct plt_entry); > + mod->arch.core_plt_count = 0; > + > + mod->arch.init_plt->sh_type = SHT_NOBITS; > + mod->arch.init_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; > + mod->arch.init_plt->sh_addralign = L1_CACHE_BYTES; > + mod->arch.init_plt->sh_size = init_plts * sizeof(struct plt_entry); > + mod->arch.init_plt_count = 0; > + pr_debug("%s: core.plt=%lld, init.plt=%lld\n", __func__, > + mod->arch.core_plt->sh_size, mod->arch.init_plt->sh_size); > + return 0; > +} > diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c > index 93e970231ca9..3a298b0e21bb 100644 > --- a/arch/arm64/kernel/module.c > +++ b/arch/arm64/kernel/module.c > @@ -38,6 +38,11 @@ void *module_alloc(unsigned long size) > GFP_KERNEL, PAGE_KERNEL_EXEC, 0, > NUMA_NO_NODE, __builtin_return_address(0)); > > + if (!p && IS_ENABLED(CONFIG_ARM64_MODULE_PLTS)) > + p = __vmalloc_node_range(size, MODULE_ALIGN, VMALLOC_START, > + VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_EXEC, 0, > + NUMA_NO_NODE, __builtin_return_address(0)); > + > if (p && (kasan_module_alloc(p, size) < 0)) { > vfree(p); > return NULL; > @@ -361,6 +366,13 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > case R_AARCH64_CALL26: > ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, > AARCH64_INSN_IMM_26); > + > + if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) && > + ovf == -ERANGE) { > + val = get_module_plt(me, loc, val); > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, > + 26, AARCH64_INSN_IMM_26); > + } > break; > > default: > diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds > new file mode 100644 > index 000000000000..3682fa107918 > --- /dev/null > +++ b/arch/arm64/kernel/module.lds > @@ -0,0 +1,4 @@ > +SECTIONS { > + .core.plt : { BYTE(0) } > + .init.plt : { BYTE(0) } > +} > -- > 2.5.0 >

