summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/Kconfig9
-rw-r--r--arch/arm64/Makefile6
-rw-r--r--arch/arm64/include/asm/module.h11
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/module-plts.c201
-rw-r--r--arch/arm64/kernel/module.c22
-rw-r--r--arch/arm64/kernel/module.lds3
7 files changed, 252 insertions, 1 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index cfa1cc90ebf4..c85c29d660bd 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -395,6 +395,7 @@ config ARM64_ERRATUM_843419
395 bool "Cortex-A53: 843419: A load or store might access an incorrect address" 395 bool "Cortex-A53: 843419: A load or store might access an incorrect address"
396 depends on MODULES 396 depends on MODULES
397 default y 397 default y
398 select ARM64_MODULE_CMODEL_LARGE
398 help 399 help
399 This option builds kernel modules using the large memory model in 400 This option builds kernel modules using the large memory model in
400 order to avoid the use of the ADRP instruction, which can cause 401 order to avoid the use of the ADRP instruction, which can cause
@@ -778,6 +779,14 @@ config ARM64_UAO
778 regular load/store instructions if the cpu does not implement the 779 regular load/store instructions if the cpu does not implement the
779 feature. 780 feature.
780 781
782config ARM64_MODULE_CMODEL_LARGE
783 bool
784
785config ARM64_MODULE_PLTS
786 bool
787 select ARM64_MODULE_CMODEL_LARGE
788 select HAVE_MOD_ARCH_SPECIFIC
789
781endmenu 790endmenu
782 791
783menu "Boot options" 792menu "Boot options"
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 307237cfe728..a6bba9623836 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -43,10 +43,14 @@ endif
43 43
44CHECKFLAGS += -D__aarch64__ 44CHECKFLAGS += -D__aarch64__
45 45
46ifeq ($(CONFIG_ARM64_ERRATUM_843419), y) 46ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y)
47KBUILD_CFLAGS_MODULE += -mcmodel=large 47KBUILD_CFLAGS_MODULE += -mcmodel=large
48endif 48endif
49 49
50ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
51KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/arm64/kernel/module.lds
52endif
53
50# Default value 54# Default value
51head-y := arch/arm64/kernel/head.o 55head-y := arch/arm64/kernel/head.o
52 56
diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
index e80e232b730e..8652fb613304 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -20,4 +20,15 @@
20 20
21#define MODULE_ARCH_VERMAGIC "aarch64" 21#define MODULE_ARCH_VERMAGIC "aarch64"
22 22
23#ifdef CONFIG_ARM64_MODULE_PLTS
24struct mod_arch_specific {
25 struct elf64_shdr *plt;
26 int plt_num_entries;
27 int plt_max_entries;
28};
29#endif
30
31u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
32 Elf64_Sym *sym);
33
23#endif /* __ASM_MODULE_H */ 34#endif /* __ASM_MODULE_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 8a9c65ccb636..9ca2a48ba326 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 \
30 ../../arm/kernel/opcodes.o 30 ../../arm/kernel/opcodes.o
31arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o 31arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
32arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o 32arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
33arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
33arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o 34arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
34arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o 35arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
35arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o 36arm64-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..1ce90d8450ae
--- /dev/null
+++ b/arch/arm64/kernel/module-plts.c
@@ -0,0 +1,201 @@
1/*
2 * Copyright (C) 2014-2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/elf.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/sort.h>
13
14struct plt_entry {
15 /*
16 * A program that conforms to the AArch64 Procedure Call Standard
17 * (AAPCS64) must assume that a veneer that alters IP0 (x16) and/or
18 * IP1 (x17) may be inserted at any branch instruction that is
19 * exposed to a relocation that supports long branches. Since that
20 * is exactly what we are dealing with here, we are free to use x16
21 * as a scratch register in the PLT veneers.
22 */
23 __le32 mov0; /* movn x16, #0x.... */
24 __le32 mov1; /* movk x16, #0x...., lsl #16 */
25 __le32 mov2; /* movk x16, #0x...., lsl #32 */
26 __le32 br; /* br x16 */
27};
28
29u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
30 Elf64_Sym *sym)
31{
32 struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr;
33 int i = mod->arch.plt_num_entries;
34 u64 val = sym->st_value + rela->r_addend;
35
36 /*
37 * We only emit PLT entries against undefined (SHN_UNDEF) symbols,
38 * which are listed in the ELF symtab section, but without a type
39 * or a size.
40 * So, similar to how the module loader uses the Elf64_Sym::st_value
41 * field to store the resolved addresses of undefined symbols, let's
42 * borrow the Elf64_Sym::st_size field (whose value is never used by
43 * the module loader, even for symbols that are defined) to record
44 * the address of a symbol's associated PLT entry as we emit it for a
45 * zero addend relocation (which is the only kind we have to deal with
46 * in practice). This allows us to find duplicates without having to
47 * go through the table every time.
48 */
49 if (rela->r_addend == 0 && sym->st_size != 0) {
50 BUG_ON(sym->st_size < (u64)plt || sym->st_size >= (u64)&plt[i]);
51 return sym->st_size;
52 }
53
54 mod->arch.plt_num_entries++;
55 BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries);
56
57 /*
58 * MOVK/MOVN/MOVZ opcode:
59 * +--------+------------+--------+-----------+-------------+---------+
60 * | sf[31] | opc[30:29] | 100101 | hw[22:21] | imm16[20:5] | Rd[4:0] |
61 * +--------+------------+--------+-----------+-------------+---------+
62 *
63 * Rd := 0x10 (x16)
64 * hw := 0b00 (no shift), 0b01 (lsl #16), 0b10 (lsl #32)
65 * opc := 0b11 (MOVK), 0b00 (MOVN), 0b10 (MOVZ)
66 * sf := 1 (64-bit variant)
67 */
68 plt[i] = (struct plt_entry){
69 cpu_to_le32(0x92800010 | (((~val ) & 0xffff)) << 5),
70 cpu_to_le32(0xf2a00010 | ((( val >> 16) & 0xffff)) << 5),
71 cpu_to_le32(0xf2c00010 | ((( val >> 32) & 0xffff)) << 5),
72 cpu_to_le32(0xd61f0200)
73 };
74
75 if (rela->r_addend == 0)
76 sym->st_size = (u64)&plt[i];
77
78 return (u64)&plt[i];
79}
80
81#define cmp_3way(a,b) ((a) < (b) ? -1 : (a) > (b))
82
83static int cmp_rela(const void *a, const void *b)
84{
85 const Elf64_Rela *x = a, *y = b;
86 int i;
87
88 /* sort by type, symbol index and addend */
89 i = cmp_3way(ELF64_R_TYPE(x->r_info), ELF64_R_TYPE(y->r_info));
90 if (i == 0)
91 i = cmp_3way(ELF64_R_SYM(x->r_info), ELF64_R_SYM(y->r_info));
92 if (i == 0)
93 i = cmp_3way(x->r_addend, y->r_addend);
94 return i;
95}
96
97static bool duplicate_rel(const Elf64_Rela *rela, int num)
98{
99 /*
100 * Entries are sorted by type, symbol index and addend. That means
101 * that, if a duplicate entry exists, it must be in the preceding
102 * slot.
103 */
104 return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0;
105}
106
107static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num)
108{
109 unsigned int ret = 0;
110 Elf64_Sym *s;
111 int i;
112
113 for (i = 0; i < num; i++) {
114 switch (ELF64_R_TYPE(rela[i].r_info)) {
115 case R_AARCH64_JUMP26:
116 case R_AARCH64_CALL26:
117 /*
118 * We only have to consider branch targets that resolve
119 * to undefined symbols. This is not simply a heuristic,
120 * it is a fundamental limitation, since the PLT itself
121 * is part of the module, and needs to be within 128 MB
122 * as well, so modules can never grow beyond that limit.
123 */
124 s = syms + ELF64_R_SYM(rela[i].r_info);
125 if (s->st_shndx != SHN_UNDEF)
126 break;
127
128 /*
129 * Jump relocations with non-zero addends against
130 * undefined symbols are supported by the ELF spec, but
131 * do not occur in practice (e.g., 'jump n bytes past
132 * the entry point of undefined function symbol f').
133 * So we need to support them, but there is no need to
134 * take them into consideration when trying to optimize
135 * this code. So let's only check for duplicates when
136 * the addend is zero: this allows us to record the PLT
137 * entry address in the symbol table itself, rather than
138 * having to search the list for duplicates each time we
139 * emit one.
140 */
141 if (rela[i].r_addend != 0 || !duplicate_rel(rela, i))
142 ret++;
143 break;
144 }
145 }
146 return ret;
147}
148
149int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
150 char *secstrings, struct module *mod)
151{
152 unsigned long plt_max_entries = 0;
153 Elf64_Sym *syms = NULL;
154 int i;
155
156 /*
157 * Find the empty .plt section so we can expand it to store the PLT
158 * entries. Record the symtab address as well.
159 */
160 for (i = 0; i < ehdr->e_shnum; i++) {
161 if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0)
162 mod->arch.plt = sechdrs + i;
163 else if (sechdrs[i].sh_type == SHT_SYMTAB)
164 syms = (Elf64_Sym *)sechdrs[i].sh_addr;
165 }
166
167 if (!mod->arch.plt) {
168 pr_err("%s: module PLT section missing\n", mod->name);
169 return -ENOEXEC;
170 }
171 if (!syms) {
172 pr_err("%s: module symtab section missing\n", mod->name);
173 return -ENOEXEC;
174 }
175
176 for (i = 0; i < ehdr->e_shnum; i++) {
177 Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset;
178 int numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela);
179 Elf64_Shdr *dstsec = sechdrs + sechdrs[i].sh_info;
180
181 if (sechdrs[i].sh_type != SHT_RELA)
182 continue;
183
184 /* ignore relocations that operate on non-exec sections */
185 if (!(dstsec->sh_flags & SHF_EXECINSTR))
186 continue;
187
188 /* sort by type, symbol index and addend */
189 sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL);
190
191 plt_max_entries += count_plts(syms, rels, numrels);
192 }
193
194 mod->arch.plt->sh_type = SHT_NOBITS;
195 mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
196 mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
197 mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry);
198 mod->arch.plt_num_entries = 0;
199 mod->arch.plt_max_entries = plt_max_entries;
200 return 0;
201}
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 93e970231ca9..a9dde97f5ca5 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -38,6 +38,21 @@ void *module_alloc(unsigned long size)
38 GFP_KERNEL, PAGE_KERNEL_EXEC, 0, 38 GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
39 NUMA_NO_NODE, __builtin_return_address(0)); 39 NUMA_NO_NODE, __builtin_return_address(0));
40 40
41 if (!p && IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
42 !IS_ENABLED(CONFIG_KASAN))
43 /*
44 * KASAN can only deal with module allocations being served
45 * from the reserved module region, since the remainder of
46 * the vmalloc region is already backed by zero shadow pages,
47 * and punching holes into it is non-trivial. Since the module
48 * region is not randomized when KASAN is enabled, it is even
49 * less likely that the module region gets exhausted, so we
50 * can simply omit this fallback in that case.
51 */
52 p = __vmalloc_node_range(size, MODULE_ALIGN, VMALLOC_START,
53 VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
54 NUMA_NO_NODE, __builtin_return_address(0));
55
41 if (p && (kasan_module_alloc(p, size) < 0)) { 56 if (p && (kasan_module_alloc(p, size) < 0)) {
42 vfree(p); 57 vfree(p);
43 return NULL; 58 return NULL;
@@ -361,6 +376,13 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
361 case R_AARCH64_CALL26: 376 case R_AARCH64_CALL26:
362 ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, 377 ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
363 AARCH64_INSN_IMM_26); 378 AARCH64_INSN_IMM_26);
379
380 if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
381 ovf == -ERANGE) {
382 val = module_emit_plt_entry(me, &rel[i], sym);
383 ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
384 26, AARCH64_INSN_IMM_26);
385 }
364 break; 386 break;
365 387
366 default: 388 default:
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
new file mode 100644
index 000000000000..8949f6c6f729
--- /dev/null
+++ b/arch/arm64/kernel/module.lds
@@ -0,0 +1,3 @@
1SECTIONS {
2 .plt (NOLOAD) : { BYTE(0) }
3}