aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2017-02-22 13:40:12 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-25 09:44:44 -0400
commit3304f5a1cb874c63fcc48f9021320510a73c03f9 (patch)
tree8ba7b9e93256ccafb5ba69f6928a45e61278177f
parentee773459557d2242ddf6a2289e4615b4eb0668ae (diff)
ARM: 8662/1: module: split core and init PLT sections
commit b7ede5a1f5905ac394cc8e61712a13e3c5cb7b8f upstream. Since commit 35fa91eed817 ("ARM: kernel: merge core and init PLTs"), the ARM module PLT code allocates all PLT entries in a single core section, since the overhead of having a separate init PLT section is not justified by the small number of PLT entries usually required for init code. However, the core and init module regions are allocated independently, and there is a corner case where the core region may be allocated from the VMALLOC region if the dedicated module region is exhausted, but the init region, being much smaller, can still be allocated from the module region. This puts the PLT entries out of reach of the relocated branch instructions, defeating the whole purpose of PLTs. So split the core and init PLT regions, and name the latter ".init.plt" so it gets allocated along with (and sufficiently close to) the .init sections that it serves. Also, given that init PLT entries may need to be emitted for branches that target the core module, modify the logic that disregards defined symbols to only disregard symbols that are defined in the same section. Fixes: 35fa91eed817 ("ARM: kernel: merge core and init PLTs") Reported-by: Angus Clark <angus@angusclark.org> Tested-by: Angus Clark <angus@angusclark.org> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/arm/include/asm/module.h9
-rw-r--r--arch/arm/kernel/module-plts.c87
-rw-r--r--arch/arm/kernel/module.lds1
3 files changed, 68 insertions, 29 deletions
diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
index 464748b9fd7d..ed2319663a1e 100644
--- a/arch/arm/include/asm/module.h
+++ b/arch/arm/include/asm/module.h
@@ -18,13 +18,18 @@ enum {
18}; 18};
19#endif 19#endif
20 20
21struct mod_plt_sec {
22 struct elf32_shdr *plt;
23 int plt_count;
24};
25
21struct mod_arch_specific { 26struct mod_arch_specific {
22#ifdef CONFIG_ARM_UNWIND 27#ifdef CONFIG_ARM_UNWIND
23 struct unwind_table *unwind[ARM_SEC_MAX]; 28 struct unwind_table *unwind[ARM_SEC_MAX];
24#endif 29#endif
25#ifdef CONFIG_ARM_MODULE_PLTS 30#ifdef CONFIG_ARM_MODULE_PLTS
26 struct elf32_shdr *plt; 31 struct mod_plt_sec core;
27 int plt_count; 32 struct mod_plt_sec init;
28#endif 33#endif
29}; 34};
30 35
diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c
index 3a5cba90c971..3d0c2e4dda1d 100644
--- a/arch/arm/kernel/module-plts.c
+++ b/arch/arm/kernel/module-plts.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> 2 * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org>
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 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 5 * it under the terms of the GNU General Public License version 2 as
@@ -31,9 +31,17 @@ struct plt_entries {
31 u32 lit[PLT_ENT_COUNT]; 31 u32 lit[PLT_ENT_COUNT];
32}; 32};
33 33
34static bool in_init(const struct module *mod, unsigned long loc)
35{
36 return loc - (u32)mod->init_layout.base < mod->init_layout.size;
37}
38
34u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) 39u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
35{ 40{
36 struct plt_entries *plt = (struct plt_entries *)mod->arch.plt->sh_addr; 41 struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
42 &mod->arch.init;
43
44 struct plt_entries *plt = (struct plt_entries *)pltsec->plt->sh_addr;
37 int idx = 0; 45 int idx = 0;
38 46
39 /* 47 /*
@@ -41,9 +49,9 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
41 * relocations are sorted, this will be the last entry we allocated. 49 * relocations are sorted, this will be the last entry we allocated.
42 * (if one exists). 50 * (if one exists).
43 */ 51 */
44 if (mod->arch.plt_count > 0) { 52 if (pltsec->plt_count > 0) {
45 plt += (mod->arch.plt_count - 1) / PLT_ENT_COUNT; 53 plt += (pltsec->plt_count - 1) / PLT_ENT_COUNT;
46 idx = (mod->arch.plt_count - 1) % PLT_ENT_COUNT; 54 idx = (pltsec->plt_count - 1) % PLT_ENT_COUNT;
47 55
48 if (plt->lit[idx] == val) 56 if (plt->lit[idx] == val)
49 return (u32)&plt->ldr[idx]; 57 return (u32)&plt->ldr[idx];
@@ -53,8 +61,8 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
53 plt++; 61 plt++;
54 } 62 }
55 63
56 mod->arch.plt_count++; 64 pltsec->plt_count++;
57 BUG_ON(mod->arch.plt_count * PLT_ENT_SIZE > mod->arch.plt->sh_size); 65 BUG_ON(pltsec->plt_count * PLT_ENT_SIZE > pltsec->plt->sh_size);
58 66
59 if (!idx) 67 if (!idx)
60 /* Populate a new set of entries */ 68 /* Populate a new set of entries */
@@ -129,7 +137,7 @@ static bool duplicate_rel(Elf32_Addr base, const Elf32_Rel *rel, int num)
129 137
130/* Count how many PLT entries we may need */ 138/* Count how many PLT entries we may need */
131static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base, 139static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base,
132 const Elf32_Rel *rel, int num) 140 const Elf32_Rel *rel, int num, Elf32_Word dstidx)
133{ 141{
134 unsigned int ret = 0; 142 unsigned int ret = 0;
135 const Elf32_Sym *s; 143 const Elf32_Sym *s;
@@ -144,13 +152,17 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base,
144 case R_ARM_THM_JUMP24: 152 case R_ARM_THM_JUMP24:
145 /* 153 /*
146 * We only have to consider branch targets that resolve 154 * We only have to consider branch targets that resolve
147 * to undefined symbols. This is not simply a heuristic, 155 * to symbols that are defined in a different section.
148 * it is a fundamental limitation, since the PLT itself 156 * This is not simply a heuristic, it is a fundamental
149 * is part of the module, and needs to be within range 157 * limitation, since there is no guaranteed way to emit
150 * as well, so modules can never grow beyond that limit. 158 * PLT entries sufficiently close to the branch if the
159 * section size exceeds the range of a branch
160 * instruction. So ignore relocations against defined
161 * symbols if they live in the same section as the
162 * relocation target.
151 */ 163 */
152 s = syms + ELF32_R_SYM(rel[i].r_info); 164 s = syms + ELF32_R_SYM(rel[i].r_info);
153 if (s->st_shndx != SHN_UNDEF) 165 if (s->st_shndx == dstidx)
154 break; 166 break;
155 167
156 /* 168 /*
@@ -161,7 +173,12 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base,
161 * So we need to support them, but there is no need to 173 * So we need to support them, but there is no need to
162 * take them into consideration when trying to optimize 174 * take them into consideration when trying to optimize
163 * this code. So let's only check for duplicates when 175 * this code. So let's only check for duplicates when
164 * the addend is zero. 176 * the addend is zero. (Note that calls into the core
177 * module via init PLT entries could involve section
178 * relative symbol references with non-zero addends, for
179 * which we may end up emitting duplicates, but the init
180 * PLT is released along with the rest of the .init
181 * region as soon as module loading completes.)
165 */ 182 */
166 if (!is_zero_addend_relocation(base, rel + i) || 183 if (!is_zero_addend_relocation(base, rel + i) ||
167 !duplicate_rel(base, rel, i)) 184 !duplicate_rel(base, rel, i))
@@ -174,7 +191,8 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base,
174int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, 191int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
175 char *secstrings, struct module *mod) 192 char *secstrings, struct module *mod)
176{ 193{
177 unsigned long plts = 0; 194 unsigned long core_plts = 0;
195 unsigned long init_plts = 0;
178 Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; 196 Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum;
179 Elf32_Sym *syms = NULL; 197 Elf32_Sym *syms = NULL;
180 198
@@ -184,13 +202,15 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
184 */ 202 */
185 for (s = sechdrs; s < sechdrs_end; ++s) { 203 for (s = sechdrs; s < sechdrs_end; ++s) {
186 if (strcmp(".plt", secstrings + s->sh_name) == 0) 204 if (strcmp(".plt", secstrings + s->sh_name) == 0)
187 mod->arch.plt = s; 205 mod->arch.core.plt = s;
206 else if (strcmp(".init.plt", secstrings + s->sh_name) == 0)
207 mod->arch.init.plt = s;
188 else if (s->sh_type == SHT_SYMTAB) 208 else if (s->sh_type == SHT_SYMTAB)
189 syms = (Elf32_Sym *)s->sh_addr; 209 syms = (Elf32_Sym *)s->sh_addr;
190 } 210 }
191 211
192 if (!mod->arch.plt) { 212 if (!mod->arch.core.plt || !mod->arch.init.plt) {
193 pr_err("%s: module PLT section missing\n", mod->name); 213 pr_err("%s: module PLT section(s) missing\n", mod->name);
194 return -ENOEXEC; 214 return -ENOEXEC;
195 } 215 }
196 if (!syms) { 216 if (!syms) {
@@ -213,16 +233,29 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
213 /* sort by type and symbol index */ 233 /* sort by type and symbol index */
214 sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL); 234 sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL);
215 235
216 plts += count_plts(syms, dstsec->sh_addr, rels, numrels); 236 if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0)
237 core_plts += count_plts(syms, dstsec->sh_addr, rels,
238 numrels, s->sh_info);
239 else
240 init_plts += count_plts(syms, dstsec->sh_addr, rels,
241 numrels, s->sh_info);
217 } 242 }
218 243
219 mod->arch.plt->sh_type = SHT_NOBITS; 244 mod->arch.core.plt->sh_type = SHT_NOBITS;
220 mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; 245 mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
221 mod->arch.plt->sh_addralign = L1_CACHE_BYTES; 246 mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES;
222 mod->arch.plt->sh_size = round_up(plts * PLT_ENT_SIZE, 247 mod->arch.core.plt->sh_size = round_up(core_plts * PLT_ENT_SIZE,
223 sizeof(struct plt_entries)); 248 sizeof(struct plt_entries));
224 mod->arch.plt_count = 0; 249 mod->arch.core.plt_count = 0;
225 250
226 pr_debug("%s: plt=%x\n", __func__, mod->arch.plt->sh_size); 251 mod->arch.init.plt->sh_type = SHT_NOBITS;
252 mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
253 mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES;
254 mod->arch.init.plt->sh_size = round_up(init_plts * PLT_ENT_SIZE,
255 sizeof(struct plt_entries));
256 mod->arch.init.plt_count = 0;
257
258 pr_debug("%s: plt=%x, init.plt=%x\n", __func__,
259 mod->arch.core.plt->sh_size, mod->arch.init.plt->sh_size);
227 return 0; 260 return 0;
228} 261}
diff --git a/arch/arm/kernel/module.lds b/arch/arm/kernel/module.lds
index 05881e2b414c..eacb5c67f61e 100644
--- a/arch/arm/kernel/module.lds
+++ b/arch/arm/kernel/module.lds
@@ -1,3 +1,4 @@
1SECTIONS { 1SECTIONS {
2 .plt : { BYTE(0) } 2 .plt : { BYTE(0) }
3 .init.plt : { BYTE(0) }
3} 4}