* [RFT PATCH 0/2] ARM/arm64: module-plt: split core and init PLT sections @ 2017-02-20 22:00 Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 1/2] ARM: module: " Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 2/2] arm64: " Ard Biesheuvel 0 siblings, 2 replies; 6+ messages in thread From: Ard Biesheuvel @ 2017-02-20 22:00 UTC (permalink / raw) To: linux-arm-kernel This fixes a thinko on my part in both the ARM and the arm64 implementations of module PLTs. What I failed to realise is that the core module sections and the init sections are allocated independently, which means they could end up further away from each other than the range of a branch instruction. This implies that they cannot share a single array of PLT entries, and so this series splits them into core and init PLT sections. For ARM, this is actually a revert of commit 35fa91eed817 ("ARM: kernel: merge core and init PLTs") [although the patch in this series is not a straight revert.] This means that the issue is a regression, and the patch should probably go to -stable. For arm64, the likelihood of this issue ever occurring is very small, due to the 128 MB range of its branch instructions. Also, the arm64 version of the code was never correct, so it is not a regression. But for correctness, it is fixed in the same way as for ARM. For now, these are build tested only. I was hoping Angus could check whether 1/2 makes his problems go away. Ard Biesheuvel (2): ARM: module: split core and init PLT sections arm64: module: split core and init PLT sections arch/arm/include/asm/module.h | 9 ++- arch/arm/kernel/module-plts.c | 62 ++++++++++++++------ arch/arm/kernel/module.lds | 1 + arch/arm64/include/asm/module.h | 9 ++- arch/arm64/kernel/module-plts.c | 56 ++++++++++++------ arch/arm64/kernel/module.c | 2 +- arch/arm64/kernel/module.lds | 1 + 7 files changed, 99 insertions(+), 41 deletions(-) -- 2.7.4 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFT PATCH 1/2] ARM: module: split core and init PLT sections 2017-02-20 22:00 [RFT PATCH 0/2] ARM/arm64: module-plt: split core and init PLT sections Ard Biesheuvel @ 2017-02-20 22:00 ` Ard Biesheuvel 2017-02-21 14:26 ` Angus Clark 2017-02-23 15:45 ` Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 2/2] arm64: " Ard Biesheuvel 1 sibling, 2 replies; 6+ messages in thread From: Ard Biesheuvel @ 2017-02-20 22:00 UTC (permalink / raw) To: linux-arm-kernel 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. This is not a straight revert, given that the PLT code was heavily modified since the commit in question was merged. Fixes: 35fa91eed817 ("ARM: kernel: merge core and init PLTs") Reported-by: Angus Clark <angus@angusclark.org> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> --- arch/arm/include/asm/module.h | 9 ++- arch/arm/kernel/module-plts.c | 62 ++++++++++++++------ arch/arm/kernel/module.lds | 1 + 3 files changed, 51 insertions(+), 21 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 { }; #endif +struct mod_plt_sec { + struct elf32_shdr *plt; + int plt_count; +}; + struct mod_arch_specific { #ifdef CONFIG_ARM_UNWIND struct unwind_table *unwind[ARM_SEC_MAX]; #endif #ifdef CONFIG_ARM_MODULE_PLTS - struct elf32_shdr *plt; - int plt_count; + struct mod_plt_sec core; + struct mod_plt_sec init; #endif }; diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c index 3a5cba90c971..1a79f2789325 100644 --- a/arch/arm/kernel/module-plts.c +++ b/arch/arm/kernel/module-plts.c @@ -31,9 +31,17 @@ struct plt_entries { u32 lit[PLT_ENT_COUNT]; }; +static bool in_init(const struct module *mod, void *loc) +{ + return (u32)loc - (u32)mod->init_layout.base < mod->init_layout.size; +} + u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) { - struct plt_entries *plt = (struct plt_entries *)mod->arch.plt->sh_addr; + struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core : + &mod->arch.init; + + struct plt_entries *plt = (struct plt_entries *)pltsec->plt->sh_addr; int idx = 0; /* @@ -41,9 +49,9 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) * relocations are sorted, this will be the last entry we allocated. * (if one exists). */ - if (mod->arch.plt_count > 0) { - plt += (mod->arch.plt_count - 1) / PLT_ENT_COUNT; - idx = (mod->arch.plt_count - 1) % PLT_ENT_COUNT; + if (pltsec->plt_count > 0) { + plt += (pltsec->plt_count - 1) / PLT_ENT_COUNT; + idx = (pltsec->plt_count - 1) % PLT_ENT_COUNT; if (plt->lit[idx] == val) return (u32)&plt->ldr[idx]; @@ -53,8 +61,8 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) plt++; } - mod->arch.plt_count++; - BUG_ON(mod->arch.plt_count * PLT_ENT_SIZE > mod->arch.plt->sh_size); + pltsec->plt_count++; + BUG_ON(pltsec->plt_count * PLT_ENT_SIZE > pltsec->plt->sh_size); if (!idx) /* Populate a new set of entries */ @@ -174,7 +182,8 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base, int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { - unsigned long plts = 0; + unsigned long core_plts = 0; + unsigned long init_plts = 0; Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; Elf32_Sym *syms = NULL; @@ -184,13 +193,15 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, */ for (s = sechdrs; s < sechdrs_end; ++s) { if (strcmp(".plt", secstrings + s->sh_name) == 0) - mod->arch.plt = s; + mod->arch.core.plt = s; + else if (strcmp(".init.plt", secstrings + s->sh_name) == 0) + mod->arch.init.plt = s; else if (s->sh_type == SHT_SYMTAB) syms = (Elf32_Sym *)s->sh_addr; } - if (!mod->arch.plt) { - pr_err("%s: module PLT section missing\n", mod->name); + if (!mod->arch.core.plt || !mod->arch.init.plt) { + pr_err("%s: module PLT section(s) missing\n", mod->name); return -ENOEXEC; } if (!syms) { @@ -213,16 +224,29 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, /* sort by type and symbol index */ sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL); - plts += count_plts(syms, dstsec->sh_addr, rels, numrels); + if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0) + core_plts += count_plts(syms, dstsec->sh_addr, rels, + numrels); + else + init_plts += count_plts(syms, dstsec->sh_addr, rels, + numrels); } - mod->arch.plt->sh_type = SHT_NOBITS; - mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; - mod->arch.plt->sh_addralign = L1_CACHE_BYTES; - mod->arch.plt->sh_size = round_up(plts * PLT_ENT_SIZE, - sizeof(struct plt_entries)); - mod->arch.plt_count = 0; - - pr_debug("%s: plt=%x\n", __func__, mod->arch.plt->sh_size); + 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 = round_up(core_plts * PLT_ENT_SIZE, + sizeof(struct plt_entries)); + 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 = round_up(init_plts * PLT_ENT_SIZE, + sizeof(struct plt_entries)); + mod->arch.init.plt_count = 0; + + pr_debug("%s: plt=%x, init.plt=%x\n", __func__, + mod->arch.core.plt->sh_size, mod->arch.init.plt->sh_size); return 0; } 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 @@ SECTIONS { .plt : { BYTE(0) } + .init.plt : { BYTE(0) } } -- 2.7.4 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [RFT PATCH 1/2] ARM: module: split core and init PLT sections 2017-02-20 22:00 ` [RFT PATCH 1/2] ARM: module: " Ard Biesheuvel @ 2017-02-21 14:26 ` Angus Clark 2017-02-21 17:35 ` Ard Biesheuvel 2017-02-23 15:45 ` Ard Biesheuvel 1 sibling, 1 reply; 6+ messages in thread From: Angus Clark @ 2017-02-21 14:26 UTC (permalink / raw) To: linux-arm-kernel Hi Ard, I have tested the patch and I think there is still a small issue. At present, count_plts() assumes a PLT entry would only be required for undefined symbols. However, if 'init' and 'core' are located far apart, then we may need an entry in the init PLT if some init code makes a call to a defined symbol in the core section. As a quick test, I added an 'include_defined' argument to count_plts() which gets set for init relocs. With this in place, the module loads fine and appears to function as expected. Happy to test any further versions :-) Cheers, Angus On 20 February 2017 at 22:00, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote: > 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. This is not a straight revert, given that the > PLT code was heavily modified since the commit in question was merged. > > Fixes: 35fa91eed817 ("ARM: kernel: merge core and init PLTs") > Reported-by: Angus Clark <angus@angusclark.org> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> > --- > arch/arm/include/asm/module.h | 9 ++- > arch/arm/kernel/module-plts.c | 62 ++++++++++++++------ > arch/arm/kernel/module.lds | 1 + > 3 files changed, 51 insertions(+), 21 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 { > }; > #endif > > +struct mod_plt_sec { > + struct elf32_shdr *plt; > + int plt_count; > +}; > + > struct mod_arch_specific { > #ifdef CONFIG_ARM_UNWIND > struct unwind_table *unwind[ARM_SEC_MAX]; > #endif > #ifdef CONFIG_ARM_MODULE_PLTS > - struct elf32_shdr *plt; > - int plt_count; > + struct mod_plt_sec core; > + struct mod_plt_sec init; > #endif > }; > > diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c > index 3a5cba90c971..1a79f2789325 100644 > --- a/arch/arm/kernel/module-plts.c > +++ b/arch/arm/kernel/module-plts.c > @@ -31,9 +31,17 @@ struct plt_entries { > u32 lit[PLT_ENT_COUNT]; > }; > > +static bool in_init(const struct module *mod, void *loc) > +{ > + return (u32)loc - (u32)mod->init_layout.base < mod->init_layout.size; > +} > + > u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > { > - struct plt_entries *plt = (struct plt_entries *)mod->arch.plt->sh_addr; > + struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core : > + &mod->arch.init; > + > + struct plt_entries *plt = (struct plt_entries *)pltsec->plt->sh_addr; > int idx = 0; > > /* > @@ -41,9 +49,9 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > * relocations are sorted, this will be the last entry we allocated. > * (if one exists). > */ > - if (mod->arch.plt_count > 0) { > - plt += (mod->arch.plt_count - 1) / PLT_ENT_COUNT; > - idx = (mod->arch.plt_count - 1) % PLT_ENT_COUNT; > + if (pltsec->plt_count > 0) { > + plt += (pltsec->plt_count - 1) / PLT_ENT_COUNT; > + idx = (pltsec->plt_count - 1) % PLT_ENT_COUNT; > > if (plt->lit[idx] == val) > return (u32)&plt->ldr[idx]; > @@ -53,8 +61,8 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > plt++; > } > > - mod->arch.plt_count++; > - BUG_ON(mod->arch.plt_count * PLT_ENT_SIZE > mod->arch.plt->sh_size); > + pltsec->plt_count++; > + BUG_ON(pltsec->plt_count * PLT_ENT_SIZE > pltsec->plt->sh_size); > > if (!idx) > /* Populate a new set of entries */ > @@ -174,7 +182,8 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base, > int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > char *secstrings, struct module *mod) > { > - unsigned long plts = 0; > + unsigned long core_plts = 0; > + unsigned long init_plts = 0; > Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; > Elf32_Sym *syms = NULL; > > @@ -184,13 +193,15 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > */ > for (s = sechdrs; s < sechdrs_end; ++s) { > if (strcmp(".plt", secstrings + s->sh_name) == 0) > - mod->arch.plt = s; > + mod->arch.core.plt = s; > + else if (strcmp(".init.plt", secstrings + s->sh_name) == 0) > + mod->arch.init.plt = s; > else if (s->sh_type == SHT_SYMTAB) > syms = (Elf32_Sym *)s->sh_addr; > } > > - if (!mod->arch.plt) { > - pr_err("%s: module PLT section missing\n", mod->name); > + if (!mod->arch.core.plt || !mod->arch.init.plt) { > + pr_err("%s: module PLT section(s) missing\n", mod->name); > return -ENOEXEC; > } > if (!syms) { > @@ -213,16 +224,29 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > /* sort by type and symbol index */ > sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL); > > - plts += count_plts(syms, dstsec->sh_addr, rels, numrels); > + if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0) > + core_plts += count_plts(syms, dstsec->sh_addr, rels, > + numrels); > + else > + init_plts += count_plts(syms, dstsec->sh_addr, rels, > + numrels); > } > > - mod->arch.plt->sh_type = SHT_NOBITS; > - mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; > - mod->arch.plt->sh_addralign = L1_CACHE_BYTES; > - mod->arch.plt->sh_size = round_up(plts * PLT_ENT_SIZE, > - sizeof(struct plt_entries)); > - mod->arch.plt_count = 0; > - > - pr_debug("%s: plt=%x\n", __func__, mod->arch.plt->sh_size); > + 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 = round_up(core_plts * PLT_ENT_SIZE, > + sizeof(struct plt_entries)); > + 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 = round_up(init_plts * PLT_ENT_SIZE, > + sizeof(struct plt_entries)); > + mod->arch.init.plt_count = 0; > + > + pr_debug("%s: plt=%x, init.plt=%x\n", __func__, > + mod->arch.core.plt->sh_size, mod->arch.init.plt->sh_size); > return 0; > } > 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 @@ > SECTIONS { > .plt : { BYTE(0) } > + .init.plt : { BYTE(0) } > } > -- > 2.7.4 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFT PATCH 1/2] ARM: module: split core and init PLT sections 2017-02-21 14:26 ` Angus Clark @ 2017-02-21 17:35 ` Ard Biesheuvel 0 siblings, 0 replies; 6+ messages in thread From: Ard Biesheuvel @ 2017-02-21 17:35 UTC (permalink / raw) To: linux-arm-kernel On 21 February 2017 at 14:26, Angus Clark <angus@angusclark.org> wrote: > Hi Ard, > > I have tested the patch and I think there is still a small issue. At > present, count_plts() assumes a PLT entry would only be required for > undefined symbols. However, if 'init' and 'core' are located far > apart, then we may need an entry in the init PLT if some init code > makes a call to a defined symbol in the core section. > Argh. You're absolutely right, another complication I did not anticipate, unfortunately. > As a quick test, I added an 'include_defined' argument to count_plts() > which gets set for init relocs. With this in place, the module loads > fine and appears to function as expected. > > Happy to test any further versions :-) > Thanks, v2 coming up ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFT PATCH 1/2] ARM: module: split core and init PLT sections 2017-02-20 22:00 ` [RFT PATCH 1/2] ARM: module: " Ard Biesheuvel 2017-02-21 14:26 ` Angus Clark @ 2017-02-23 15:45 ` Ard Biesheuvel 1 sibling, 0 replies; 6+ messages in thread From: Ard Biesheuvel @ 2017-02-23 15:45 UTC (permalink / raw) To: linux-arm-kernel On 20 February 2017 at 22:00, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote: > 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. This is not a straight revert, given that the > PLT code was heavily modified since the commit in question was merged. > > Fixes: 35fa91eed817 ("ARM: kernel: merge core and init PLTs") > Reported-by: Angus Clark <angus@angusclark.org> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> I have dropped this into the patch system as #8662 > --- > arch/arm/include/asm/module.h | 9 ++- > arch/arm/kernel/module-plts.c | 62 ++++++++++++++------ > arch/arm/kernel/module.lds | 1 + > 3 files changed, 51 insertions(+), 21 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 { > }; > #endif > > +struct mod_plt_sec { > + struct elf32_shdr *plt; > + int plt_count; > +}; > + > struct mod_arch_specific { > #ifdef CONFIG_ARM_UNWIND > struct unwind_table *unwind[ARM_SEC_MAX]; > #endif > #ifdef CONFIG_ARM_MODULE_PLTS > - struct elf32_shdr *plt; > - int plt_count; > + struct mod_plt_sec core; > + struct mod_plt_sec init; > #endif > }; > > diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c > index 3a5cba90c971..1a79f2789325 100644 > --- a/arch/arm/kernel/module-plts.c > +++ b/arch/arm/kernel/module-plts.c > @@ -31,9 +31,17 @@ struct plt_entries { > u32 lit[PLT_ENT_COUNT]; > }; > > +static bool in_init(const struct module *mod, void *loc) > +{ > + return (u32)loc - (u32)mod->init_layout.base < mod->init_layout.size; > +} > + > u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > { > - struct plt_entries *plt = (struct plt_entries *)mod->arch.plt->sh_addr; > + struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core : > + &mod->arch.init; > + > + struct plt_entries *plt = (struct plt_entries *)pltsec->plt->sh_addr; > int idx = 0; > > /* > @@ -41,9 +49,9 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > * relocations are sorted, this will be the last entry we allocated. > * (if one exists). > */ > - if (mod->arch.plt_count > 0) { > - plt += (mod->arch.plt_count - 1) / PLT_ENT_COUNT; > - idx = (mod->arch.plt_count - 1) % PLT_ENT_COUNT; > + if (pltsec->plt_count > 0) { > + plt += (pltsec->plt_count - 1) / PLT_ENT_COUNT; > + idx = (pltsec->plt_count - 1) % PLT_ENT_COUNT; > > if (plt->lit[idx] == val) > return (u32)&plt->ldr[idx]; > @@ -53,8 +61,8 @@ u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) > plt++; > } > > - mod->arch.plt_count++; > - BUG_ON(mod->arch.plt_count * PLT_ENT_SIZE > mod->arch.plt->sh_size); > + pltsec->plt_count++; > + BUG_ON(pltsec->plt_count * PLT_ENT_SIZE > pltsec->plt->sh_size); > > if (!idx) > /* Populate a new set of entries */ > @@ -174,7 +182,8 @@ static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base, > int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > char *secstrings, struct module *mod) > { > - unsigned long plts = 0; > + unsigned long core_plts = 0; > + unsigned long init_plts = 0; > Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; > Elf32_Sym *syms = NULL; > > @@ -184,13 +193,15 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > */ > for (s = sechdrs; s < sechdrs_end; ++s) { > if (strcmp(".plt", secstrings + s->sh_name) == 0) > - mod->arch.plt = s; > + mod->arch.core.plt = s; > + else if (strcmp(".init.plt", secstrings + s->sh_name) == 0) > + mod->arch.init.plt = s; > else if (s->sh_type == SHT_SYMTAB) > syms = (Elf32_Sym *)s->sh_addr; > } > > - if (!mod->arch.plt) { > - pr_err("%s: module PLT section missing\n", mod->name); > + if (!mod->arch.core.plt || !mod->arch.init.plt) { > + pr_err("%s: module PLT section(s) missing\n", mod->name); > return -ENOEXEC; > } > if (!syms) { > @@ -213,16 +224,29 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, > /* sort by type and symbol index */ > sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL); > > - plts += count_plts(syms, dstsec->sh_addr, rels, numrels); > + if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0) > + core_plts += count_plts(syms, dstsec->sh_addr, rels, > + numrels); > + else > + init_plts += count_plts(syms, dstsec->sh_addr, rels, > + numrels); > } > > - mod->arch.plt->sh_type = SHT_NOBITS; > - mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; > - mod->arch.plt->sh_addralign = L1_CACHE_BYTES; > - mod->arch.plt->sh_size = round_up(plts * PLT_ENT_SIZE, > - sizeof(struct plt_entries)); > - mod->arch.plt_count = 0; > - > - pr_debug("%s: plt=%x\n", __func__, mod->arch.plt->sh_size); > + 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 = round_up(core_plts * PLT_ENT_SIZE, > + sizeof(struct plt_entries)); > + 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 = round_up(init_plts * PLT_ENT_SIZE, > + sizeof(struct plt_entries)); > + mod->arch.init.plt_count = 0; > + > + pr_debug("%s: plt=%x, init.plt=%x\n", __func__, > + mod->arch.core.plt->sh_size, mod->arch.init.plt->sh_size); > return 0; > } > 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 @@ > SECTIONS { > .plt : { BYTE(0) } > + .init.plt : { BYTE(0) } > } > -- > 2.7.4 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFT PATCH 2/2] arm64: module: split core and init PLT sections 2017-02-20 22:00 [RFT PATCH 0/2] ARM/arm64: module-plt: split core and init PLT sections Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 1/2] ARM: module: " Ard Biesheuvel @ 2017-02-20 22:00 ` Ard Biesheuvel 1 sibling, 0 replies; 6+ messages in thread From: Ard Biesheuvel @ 2017-02-20 22:00 UTC (permalink / raw) To: linux-arm-kernel The arm64 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 leads to relocation failures if the distance between those regions exceeds 128 MB. (In fact, this corner case is highly unlikely to occur on arm64, but the issue has been observed on ARM, whose module region is much smaller). 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. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> --- arch/arm64/include/asm/module.h | 9 +++- arch/arm64/kernel/module-plts.c | 56 ++++++++++++++------ arch/arm64/kernel/module.c | 2 +- arch/arm64/kernel/module.lds | 1 + 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h index 06ff7fd9e81f..b6c6fa29fe56 100644 --- a/arch/arm64/include/asm/module.h +++ b/arch/arm64/include/asm/module.h @@ -22,14 +22,19 @@ #define MODULE_ARCH_VERMAGIC "aarch64" #ifdef CONFIG_ARM64_MODULE_PLTS -struct mod_arch_specific { +struct mod_plt_sec { struct elf64_shdr *plt; int plt_num_entries; int plt_max_entries; }; + +struct mod_arch_specific { + struct mod_plt_sec core; + struct mod_plt_sec init; +}; #endif -u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela, +u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela, Elf64_Sym *sym); #ifdef CONFIG_RANDOMIZE_BASE diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c index 1ce90d8450ae..0d396ffd3d08 100644 --- a/arch/arm64/kernel/module-plts.c +++ b/arch/arm64/kernel/module-plts.c @@ -26,11 +26,19 @@ struct plt_entry { __le32 br; /* br x16 */ }; -u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela, +static bool in_init(const struct module *mod, void *loc) +{ + return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size; +} + +u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela, Elf64_Sym *sym) { - struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr; - int i = mod->arch.plt_num_entries; + struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core : + &mod->arch.init; + + struct plt_entry *plt = (struct plt_entry *)pltsec->plt->sh_addr; + int i = pltsec->plt_num_entries; u64 val = sym->st_value + rela->r_addend; /* @@ -51,8 +59,8 @@ u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela, return sym->st_size; } - mod->arch.plt_num_entries++; - BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries); + pltsec->plt_num_entries++; + BUG_ON(pltsec->plt_num_entries > pltsec->plt_max_entries); /* * MOVK/MOVN/MOVZ opcode: @@ -149,7 +157,8 @@ static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num) int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { - unsigned long plt_max_entries = 0; + unsigned long plt_max_core = 0; + unsigned long plt_max_init = 0; Elf64_Sym *syms = NULL; int i; @@ -158,14 +167,16 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, * entries. Record the symtab address as well. */ for (i = 0; i < ehdr->e_shnum; i++) { - if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0) - mod->arch.plt = sechdrs + i; + if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) + mod->arch.core.plt = sechdrs + i; + else if (!strcmp(secstrings + sechdrs[i].sh_name, ".init.plt")) + mod->arch.init.plt = sechdrs + i; else if (sechdrs[i].sh_type == SHT_SYMTAB) syms = (Elf64_Sym *)sechdrs[i].sh_addr; } - if (!mod->arch.plt) { - pr_err("%s: module PLT section missing\n", mod->name); + if (!mod->arch.core.plt || !mod->arch.init.plt) { + pr_err("%s: module PLT section(s) missing\n", mod->name); return -ENOEXEC; } if (!syms) { @@ -188,14 +199,25 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, /* sort by type, symbol index and addend */ sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL); - plt_max_entries += count_plts(syms, rels, numrels); + if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0) + plt_max_core += count_plts(syms, rels, numrels); + else + plt_max_init += count_plts(syms, rels, numrels); } - mod->arch.plt->sh_type = SHT_NOBITS; - mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; - mod->arch.plt->sh_addralign = L1_CACHE_BYTES; - mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry); - mod->arch.plt_num_entries = 0; - mod->arch.plt_max_entries = plt_max_entries; + 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 = plt_max_core * sizeof(struct plt_entry); + mod->arch.core.plt_num_entries = 0; + mod->arch.core.plt_max_entries = plt_max_core; + + 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 = plt_max_init * sizeof(struct plt_entry); + mod->arch.init.plt_num_entries = 0; + mod->arch.init.plt_max_entries = plt_max_init; + return 0; } diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index 7f316982ce00..c9a2ab446dc6 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -380,7 +380,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) && ovf == -ERANGE) { - val = module_emit_plt_entry(me, &rel[i], sym); + val = module_emit_plt_entry(me, loc, &rel[i], sym); ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, AARCH64_INSN_IMM_26); } diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds index 8949f6c6f729..f7c9781a9d48 100644 --- a/arch/arm64/kernel/module.lds +++ b/arch/arm64/kernel/module.lds @@ -1,3 +1,4 @@ SECTIONS { .plt (NOLOAD) : { BYTE(0) } + .init.plt (NOLOAD) : { BYTE(0) } } -- 2.7.4 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2017-02-23 15:45 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-02-20 22:00 [RFT PATCH 0/2] ARM/arm64: module-plt: split core and init PLT sections Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 1/2] ARM: module: " Ard Biesheuvel 2017-02-21 14:26 ` Angus Clark 2017-02-21 17:35 ` Ard Biesheuvel 2017-02-23 15:45 ` Ard Biesheuvel 2017-02-20 22:00 ` [RFT PATCH 2/2] arm64: " Ard Biesheuvel
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).