diff options
-rw-r--r-- | arch/arm64/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm64/Makefile | 6 | ||||
-rw-r--r-- | arch/arm64/include/asm/module.h | 11 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/module-plts.c | 201 | ||||
-rw-r--r-- | arch/arm64/kernel/module.c | 22 | ||||
-rw-r--r-- | arch/arm64/kernel/module.lds | 3 |
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 | ||
782 | config ARM64_MODULE_CMODEL_LARGE | ||
783 | bool | ||
784 | |||
785 | config ARM64_MODULE_PLTS | ||
786 | bool | ||
787 | select ARM64_MODULE_CMODEL_LARGE | ||
788 | select HAVE_MOD_ARCH_SPECIFIC | ||
789 | |||
781 | endmenu | 790 | endmenu |
782 | 791 | ||
783 | menu "Boot options" | 792 | menu "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 | ||
44 | CHECKFLAGS += -D__aarch64__ | 44 | CHECKFLAGS += -D__aarch64__ |
45 | 45 | ||
46 | ifeq ($(CONFIG_ARM64_ERRATUM_843419), y) | 46 | ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y) |
47 | KBUILD_CFLAGS_MODULE += -mcmodel=large | 47 | KBUILD_CFLAGS_MODULE += -mcmodel=large |
48 | endif | 48 | endif |
49 | 49 | ||
50 | ifeq ($(CONFIG_ARM64_MODULE_PLTS),y) | ||
51 | KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/arm64/kernel/module.lds | ||
52 | endif | ||
53 | |||
50 | # Default value | 54 | # Default value |
51 | head-y := arch/arm64/kernel/head.o | 55 | head-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 | ||
24 | struct mod_arch_specific { | ||
25 | struct elf64_shdr *plt; | ||
26 | int plt_num_entries; | ||
27 | int plt_max_entries; | ||
28 | }; | ||
29 | #endif | ||
30 | |||
31 | u64 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 |
31 | arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o | 31 | arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o |
32 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o | 32 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o |
33 | arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o | ||
33 | arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o | 34 | arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o |
34 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 35 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
35 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | 36 | 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..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 | |||
14 | struct 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 | |||
29 | u64 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 | |||
83 | static 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 | |||
97 | static 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 | |||
107 | static 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 | |||
149 | int 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 @@ | |||
1 | SECTIONS { | ||
2 | .plt (NOLOAD) : { BYTE(0) } | ||
3 | } | ||