From: mark.rutland@arm.com (Mark Rutland)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 08/21] arm64: add support for module PLTs
Date: Fri, 22 Jan 2016 16:55:20 +0000 [thread overview]
Message-ID: <20160122165517.GD11645@leverpostej> (raw)
In-Reply-To: <1452518355-4606-9-git-send-email-ard.biesheuvel@linaro.org>
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 <ard.biesheuvel@linaro.org>
> ---
> 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. <ard.biesheuvel@linaro.org>
> + *
> + * 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
>
next prev parent reply other threads:[~2016-01-22 16:55 UTC|newest]
Thread overview: 69+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-11 13:18 [PATCH v3 00/21] arm64: implement support for KASLR Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 01/21] of/fdt: make memblock minimum physical address arch configurable Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 02/21] arm64: introduce KIMAGE_VADDR as the virtual base of the kernel region Ard Biesheuvel
2016-01-11 16:31 ` Mark Rutland
2016-01-11 13:18 ` [PATCH v3 03/21] arm64: pgtable: add dummy pud_index() and pmd_index() definitions Ard Biesheuvel
2016-01-11 17:40 ` Mark Rutland
2016-01-12 17:25 ` Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 04/21] arm64: decouple early fixmap init from linear mapping Ard Biesheuvel
2016-01-11 16:09 ` Mark Rutland
2016-01-11 16:15 ` Ard Biesheuvel
2016-01-11 16:27 ` Mark Rutland
2016-01-11 16:51 ` Mark Rutland
2016-01-11 17:08 ` Ard Biesheuvel
2016-01-11 17:15 ` Ard Biesheuvel
2016-01-11 17:21 ` Mark Rutland
2016-01-11 13:18 ` [PATCH v3 05/21] arm64: kvm: deal with kernel symbols outside of " Ard Biesheuvel
2016-01-12 12:36 ` Mark Rutland
2016-01-12 13:23 ` Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 06/21] arm64: pgtable: implement static [pte|pmd|pud]_offset variants Ard Biesheuvel
2016-01-11 16:24 ` Mark Rutland
2016-01-11 17:28 ` Ard Biesheuvel
2016-01-11 17:31 ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 07/21] arm64: move kernel image to base of vmalloc area Ard Biesheuvel
2016-01-12 18:14 ` Mark Rutland
2016-01-13 8:39 ` Ard Biesheuvel
2016-01-13 9:58 ` Ard Biesheuvel
2016-01-13 11:11 ` Mark Rutland
2016-01-13 11:14 ` Ard Biesheuvel
2016-01-13 13:51 ` Mark Rutland
2016-01-13 15:50 ` Ard Biesheuvel
2016-01-13 16:26 ` Mark Rutland
2016-01-14 18:57 ` Mark Rutland
2016-01-15 9:54 ` Ard Biesheuvel
2016-01-15 11:23 ` Mark Rutland
2016-01-27 14:31 ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 08/21] arm64: add support for module PLTs Ard Biesheuvel
2016-01-22 16:55 ` Mark Rutland [this message]
2016-01-22 17:06 ` Ard Biesheuvel
2016-01-22 17:19 ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 09/21] extable: add support for relative extables to search and sort routines Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 10/21] arm64: switch to relative exception tables Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 11/21] arm64: avoid R_AARCH64_ABS64 relocations for Image header fields Ard Biesheuvel
2016-01-13 18:12 ` Mark Rutland
2016-01-13 18:48 ` Ard Biesheuvel
2016-01-14 8:51 ` Ard Biesheuvel
2016-01-14 9:05 ` Ard Biesheuvel
2016-01-14 10:46 ` Mark Rutland
2016-01-14 11:22 ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 12/21] arm64: avoid dynamic relocations in early boot code Ard Biesheuvel
2016-01-14 17:09 ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 13/21] arm64: allow kernel Image to be loaded anywhere in physical memory Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 14/21] arm64: redefine SWAPPER_TABLE_SHIFT for use in asm code Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 14/21] arm64: [re]define SWAPPER_TABLE_[SHIFT|SIZE] " Ard Biesheuvel
2016-01-11 13:26 ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 15/21] arm64: split elf relocs into a separate header Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 16/21] scripts/sortextable: add support for ET_DYN binaries Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 17/21] arm64: add support for a relocatable kernel and KASLR Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 18/21] efi: stub: implement efi_get_random_bytes() based on EFI_RNG_PROTOCOL Ard Biesheuvel
2016-01-21 15:42 ` Matt Fleming
2016-01-21 16:12 ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 19/21] efi: stub: add implementation of efi_random_alloc() Ard Biesheuvel
2016-01-21 16:10 ` Matt Fleming
2016-01-21 16:16 ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 20/21] efi: stub: use high allocation for converted command line Ard Biesheuvel
2016-01-21 16:20 ` Matt Fleming
2016-01-11 13:19 ` [PATCH v3 21/21] arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness Ard Biesheuvel
2016-01-21 16:31 ` Matt Fleming
2016-01-11 22:07 ` [PATCH v3 00/21] arm64: implement support for KASLR Kees Cook
2016-01-12 7:17 ` Ard Biesheuvel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160122165517.GD11645@leverpostej \
--to=mark.rutland@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).