linux/arch/powerpc/kernel/module_32.c

341 lines
9.4 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/* Kernel module help for PPC.
Copyright (C) 2001 Rusty Russell.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleloader.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ftrace.h>
#include <linux/cache.h>
#include <linux/bug.h>
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
#include <linux/sort.h>
#include <asm/setup.h>
#include <asm/code-patching.h>
/* Count how many different relocations (different symbol, different
addend) */
static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
{
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
unsigned int i, r_info, r_addend, _count_relocs;
_count_relocs = 0;
r_info = 0;
r_addend = 0;
for (i = 0; i < num; i++)
/* Only count 24-bit relocs, others don't need stubs */
if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
(r_info != ELF32_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF32_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
#ifdef CONFIG_DYNAMIC_FTRACE
_count_relocs++; /* add one for ftrace_caller */
#endif
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
return _count_relocs;
}
static int relacmp(const void *_x, const void *_y)
{
const Elf32_Rela *x, *y;
y = (Elf32_Rela *)_x;
x = (Elf32_Rela *)_y;
/* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to
* make the comparison cheaper/faster. It won't affect the sorting or
* the counting algorithms' performance
*/
if (x->r_info < y->r_info)
return -1;
else if (x->r_info > y->r_info)
return 1;
else if (x->r_addend < y->r_addend)
return -1;
else if (x->r_addend > y->r_addend)
return 1;
else
return 0;
}
/* Get the potential trampolines size required of the init and
non-init sections */
static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
int is_init)
{
unsigned long ret = 0;
unsigned i;
/* Everything marked ALLOC (this includes the exported
symbols) */
for (i = 1; i < hdr->e_shnum; i++) {
/* If it's called *.init*, and we're not init, we're
not interested */
powerpc/sparse: Fix plain integer as NULL pointer warning Trivial fix to remove the following sparse warnings: arch/powerpc/kernel/module_32.c:112:74: warning: Using plain integer as NULL pointer arch/powerpc/kernel/module_32.c:117:74: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1155:28: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1230:20: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1385:36: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1752:23: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2084:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2110:32: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2167:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2183:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:277:20: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:155:67: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:247:27: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:249:27: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:252:37: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:127:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:148:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:44:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:57:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:87:21: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:160:31: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:167:22: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:274:21: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:285:31: warning: Using plain integer as NULL pointer arch/powerpc/include/asm/hugetlb.h:204:16: warning: Using plain integer as NULL pointer arch/powerpc/mm/ppc_mmu_32.c:170:21: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/pci.c:1227:23: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/pci.c:65:24: warning: Using plain integer as NULL pointer Also use `--fix` command line option from `script/checkpatch --strict` to remove the following: CHECK: Comparison to NULL could be written "!dispDeviceBase" #72: FILE: arch/powerpc/kernel/btext.c:160: + if (dispDeviceBase == NULL) CHECK: Comparison to NULL could be written "!vbase" #80: FILE: arch/powerpc/kernel/btext.c:167: + if (vbase == NULL) CHECK: Comparison to NULL could be written "!base" #89: FILE: arch/powerpc/kernel/btext.c:274: + if (base == NULL) CHECK: Comparison to NULL could be written "!dispDeviceBase" #98: FILE: arch/powerpc/kernel/btext.c:285: + if (dispDeviceBase == NULL) CHECK: Comparison to NULL could be written "strstr" #117: FILE: arch/powerpc/kernel/module_32.c:117: + if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != NULL) CHECK: Comparison to NULL could be written "!Hash" #130: FILE: arch/powerpc/mm/ppc_mmu_32.c:170: + if (Hash == NULL) CHECK: Comparison to NULL could be written "Hash" #143: FILE: arch/powerpc/mm/tlb_hash32.c:44: + if (Hash != NULL) { CHECK: Comparison to NULL could be written "!Hash" #152: FILE: arch/powerpc/mm/tlb_hash32.c:57: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #161: FILE: arch/powerpc/mm/tlb_hash32.c:87: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #170: FILE: arch/powerpc/mm/tlb_hash32.c:127: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #179: FILE: arch/powerpc/mm/tlb_hash32.c:148: + if (Hash == NULL) { ERROR: space required after that ';' (ctx:VxV) #192: FILE: arch/powerpc/platforms/powermac/pci.c:65: + for (; node != NULL;node = node->sibling) { CHECK: Comparison to NULL could be written "node" #192: FILE: arch/powerpc/platforms/powermac/pci.c:65: + for (; node != NULL;node = node->sibling) { CHECK: Comparison to NULL could be written "!region" #201: FILE: arch/powerpc/platforms/powermac/pci.c:1227: + if (region == NULL) CHECK: Comparison to NULL could be written "of_get_property" #214: FILE: arch/powerpc/platforms/powermac/setup.c:155: + if (of_get_property(np, "cache-unified", NULL) != NULL && dc) { CHECK: Comparison to NULL could be written "!np" #223: FILE: arch/powerpc/platforms/powermac/setup.c:247: + if (np == NULL) CHECK: Comparison to NULL could be written "np" #226: FILE: arch/powerpc/platforms/powermac/setup.c:249: + if (np != NULL) { CHECK: Comparison to NULL could be written "l2cr" #230: FILE: arch/powerpc/platforms/powermac/setup.c:252: + if (l2cr != NULL) { CHECK: Comparison to NULL could be written "via" #243: FILE: drivers/macintosh/via-pmu.c:277: + if (via != NULL) CHECK: Comparison to NULL could be written "current_req" #252: FILE: drivers/macintosh/via-pmu.c:1155: + if (current_req != NULL) { CHECK: Comparison to NULL could be written "!req" #261: FILE: drivers/macintosh/via-pmu.c:1230: + if (req == NULL || pmu_state != idle CHECK: Comparison to NULL could be written "!req" #270: FILE: drivers/macintosh/via-pmu.c:1385: + if (req == NULL) { CHECK: Comparison to NULL could be written "!pp" #288: FILE: drivers/macintosh/via-pmu.c:2084: + if (pp == NULL) CHECK: Comparison to NULL could be written "!pp" #297: FILE: drivers/macintosh/via-pmu.c:2110: + if (count < 1 || pp == NULL) CHECK: Comparison to NULL could be written "!pp" #306: FILE: drivers/macintosh/via-pmu.c:2167: + if (pp == NULL) CHECK: Comparison to NULL could be written "pp" #315: FILE: drivers/macintosh/via-pmu.c:2183: + if (pp != NULL) { Link: https://github.com/linuxppc/linux/issues/37 Signed-off-by: Mathieu Malaterre <malat@debian.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2018-04-13 20:41:43 +02:00
if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != NULL)
!= is_init)
continue;
/* We don't want to look at debug sections. */
powerpc/sparse: Fix plain integer as NULL pointer warning Trivial fix to remove the following sparse warnings: arch/powerpc/kernel/module_32.c:112:74: warning: Using plain integer as NULL pointer arch/powerpc/kernel/module_32.c:117:74: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1155:28: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1230:20: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1385:36: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:1752:23: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2084:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2110:32: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2167:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:2183:19: warning: Using plain integer as NULL pointer drivers/macintosh/via-pmu.c:277:20: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:155:67: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:247:27: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:249:27: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/setup.c:252:37: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:127:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:148:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:44:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:57:21: warning: Using plain integer as NULL pointer arch/powerpc/mm/tlb_hash32.c:87:21: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:160:31: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:167:22: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:274:21: warning: Using plain integer as NULL pointer arch/powerpc/kernel/btext.c:285:31: warning: Using plain integer as NULL pointer arch/powerpc/include/asm/hugetlb.h:204:16: warning: Using plain integer as NULL pointer arch/powerpc/mm/ppc_mmu_32.c:170:21: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/pci.c:1227:23: warning: Using plain integer as NULL pointer arch/powerpc/platforms/powermac/pci.c:65:24: warning: Using plain integer as NULL pointer Also use `--fix` command line option from `script/checkpatch --strict` to remove the following: CHECK: Comparison to NULL could be written "!dispDeviceBase" #72: FILE: arch/powerpc/kernel/btext.c:160: + if (dispDeviceBase == NULL) CHECK: Comparison to NULL could be written "!vbase" #80: FILE: arch/powerpc/kernel/btext.c:167: + if (vbase == NULL) CHECK: Comparison to NULL could be written "!base" #89: FILE: arch/powerpc/kernel/btext.c:274: + if (base == NULL) CHECK: Comparison to NULL could be written "!dispDeviceBase" #98: FILE: arch/powerpc/kernel/btext.c:285: + if (dispDeviceBase == NULL) CHECK: Comparison to NULL could be written "strstr" #117: FILE: arch/powerpc/kernel/module_32.c:117: + if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != NULL) CHECK: Comparison to NULL could be written "!Hash" #130: FILE: arch/powerpc/mm/ppc_mmu_32.c:170: + if (Hash == NULL) CHECK: Comparison to NULL could be written "Hash" #143: FILE: arch/powerpc/mm/tlb_hash32.c:44: + if (Hash != NULL) { CHECK: Comparison to NULL could be written "!Hash" #152: FILE: arch/powerpc/mm/tlb_hash32.c:57: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #161: FILE: arch/powerpc/mm/tlb_hash32.c:87: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #170: FILE: arch/powerpc/mm/tlb_hash32.c:127: + if (Hash == NULL) { CHECK: Comparison to NULL could be written "!Hash" #179: FILE: arch/powerpc/mm/tlb_hash32.c:148: + if (Hash == NULL) { ERROR: space required after that ';' (ctx:VxV) #192: FILE: arch/powerpc/platforms/powermac/pci.c:65: + for (; node != NULL;node = node->sibling) { CHECK: Comparison to NULL could be written "node" #192: FILE: arch/powerpc/platforms/powermac/pci.c:65: + for (; node != NULL;node = node->sibling) { CHECK: Comparison to NULL could be written "!region" #201: FILE: arch/powerpc/platforms/powermac/pci.c:1227: + if (region == NULL) CHECK: Comparison to NULL could be written "of_get_property" #214: FILE: arch/powerpc/platforms/powermac/setup.c:155: + if (of_get_property(np, "cache-unified", NULL) != NULL && dc) { CHECK: Comparison to NULL could be written "!np" #223: FILE: arch/powerpc/platforms/powermac/setup.c:247: + if (np == NULL) CHECK: Comparison to NULL could be written "np" #226: FILE: arch/powerpc/platforms/powermac/setup.c:249: + if (np != NULL) { CHECK: Comparison to NULL could be written "l2cr" #230: FILE: arch/powerpc/platforms/powermac/setup.c:252: + if (l2cr != NULL) { CHECK: Comparison to NULL could be written "via" #243: FILE: drivers/macintosh/via-pmu.c:277: + if (via != NULL) CHECK: Comparison to NULL could be written "current_req" #252: FILE: drivers/macintosh/via-pmu.c:1155: + if (current_req != NULL) { CHECK: Comparison to NULL could be written "!req" #261: FILE: drivers/macintosh/via-pmu.c:1230: + if (req == NULL || pmu_state != idle CHECK: Comparison to NULL could be written "!req" #270: FILE: drivers/macintosh/via-pmu.c:1385: + if (req == NULL) { CHECK: Comparison to NULL could be written "!pp" #288: FILE: drivers/macintosh/via-pmu.c:2084: + if (pp == NULL) CHECK: Comparison to NULL could be written "!pp" #297: FILE: drivers/macintosh/via-pmu.c:2110: + if (count < 1 || pp == NULL) CHECK: Comparison to NULL could be written "!pp" #306: FILE: drivers/macintosh/via-pmu.c:2167: + if (pp == NULL) CHECK: Comparison to NULL could be written "pp" #315: FILE: drivers/macintosh/via-pmu.c:2183: + if (pp != NULL) { Link: https://github.com/linuxppc/linux/issues/37 Signed-off-by: Mathieu Malaterre <malat@debian.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2018-04-13 20:41:43 +02:00
if (strstr(secstrings + sechdrs[i].sh_name, ".debug"))
continue;
if (sechdrs[i].sh_type == SHT_RELA) {
pr_debug("Found relocations in section %u\n", i);
pr_debug("Ptr: %p. Number: %u\n",
(void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size / sizeof(Elf32_Rela));
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
/* Sort the relocation information based on a symbol and
* addend key. This is a stable O(n*log n) complexity
* algorithm but it will reduce the complexity of
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
* count_relocs() to linear complexity O(n)
*/
sort((void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size / sizeof(Elf32_Rela),
sizeof(Elf32_Rela), relacmp, NULL);
[POWERPC] Optimize counting distinct entries in the relocation sections When a module has relocation sections with tens of thousands of entries, counting the distinct/unique entries only (i.e. no duplicates) at load time can take tens of seconds and up to minutes. The sore point is the count_relocs() function which is called as part of the architecture specific module loading processing path: -> load_module() generic -> module_frob_arch_sections() arch specific -> get_plt_size() 32-bit -> get_stubs_size() 64-bit -> count_relocs() Here count_relocs is being called to find out how many distinct targets of R_PPC_REL24 relocations there are, since each distinct target needs a PLT entry or a stub created for it. The previous counting algorithm has O(n^2) complexity. Basically two solutions were proposed on the e-mail list: a hash based approach and a sort based approach. The hash based approach is the fastest (O(n)) but the has it needs additional memory and for certain corner cases it could take lots of memory due to the degeneration of the hash. One such proposal was submitted here: http://ozlabs.org/pipermail/linuxppc-dev/2007-June/037641.html The sort based approach is slower (O(n * log n + n)) but if the sorting is done "in place" it doesn't need additional memory. This has O(n + n * log n) complexity with no additional memory requirements. This commit implements the in-place sort option. Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
2007-11-14 03:24:04 +11:00
ret += count_relocs((void *)hdr
+ sechdrs[i].sh_offset,
sechdrs[i].sh_size
/ sizeof(Elf32_Rela))
* sizeof(struct ppc_plt_entry);
}
}
return ret;
}
int module_frob_arch_sections(Elf32_Ehdr *hdr,
Elf32_Shdr *sechdrs,
char *secstrings,
struct module *me)
{
unsigned int i;
/* Find .plt and .init.plt sections */
for (i = 0; i < hdr->e_shnum; i++) {
if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0)
me->arch.init_plt_section = i;
else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
me->arch.core_plt_section = i;
}
if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
pr_err("Module doesn't contain .plt or .init.plt sections.\n");
return -ENOEXEC;
}
/* Override their sizes */
sechdrs[me->arch.core_plt_section].sh_size
= get_plt_size(hdr, sechdrs, secstrings, 0);
sechdrs[me->arch.init_plt_section].sh_size
= get_plt_size(hdr, sechdrs, secstrings, 1);
return 0;
}
static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val)
{
if (entry->jump[0] != PPC_RAW_LIS(_R12, PPC_HA(val)))
return 0;
if (entry->jump[1] != PPC_RAW_ADDI(_R12, _R12, PPC_LO(val)))
return 0;
return 1;
}
/* Set up a trampoline in the PLT to bounce us to the distant function */
static uint32_t do_plt_call(void *location,
Elf32_Addr val,
const Elf32_Shdr *sechdrs,
struct module *mod)
{
struct ppc_plt_entry *entry;
pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
/* Init, or core PLT? */
module: replace module_layout with module_memory module_layout manages different types of memory (text, data, rodata, etc.) in one allocation, which is problematic for some reasons: 1. It is hard to enable CONFIG_STRICT_MODULE_RWX. 2. It is hard to use huge pages in modules (and not break strict rwx). 3. Many archs uses module_layout for arch-specific data, but it is not obvious how these data are used (are they RO, RX, or RW?) Improve the scenario by replacing 2 (or 3) module_layout per module with up to 7 module_memory per module: MOD_TEXT, MOD_DATA, MOD_RODATA, MOD_RO_AFTER_INIT, MOD_INIT_TEXT, MOD_INIT_DATA, MOD_INIT_RODATA, and allocating them separately. This adds slightly more entries to mod_tree (from up to 3 entries per module, to up to 7 entries per module). However, this at most adds a small constant overhead to __module_address(), which is expected to be fast. Various archs use module_layout for different data. These data are put into different module_memory based on their location in module_layout. IOW, data that used to go with text is allocated with MOD_MEM_TYPE_TEXT; data that used to go with data is allocated with MOD_MEM_TYPE_DATA, etc. module_memory simplifies quite some of the module code. For example, ARCH_WANTS_MODULES_DATA_IN_VMALLOC is a lot cleaner, as it just uses a different allocator for the data. kernel/module/strict_rwx.c is also much cleaner with module_memory. Signed-off-by: Song Liu <song@kernel.org> Cc: Luis Chamberlain <mcgrof@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Guenter Roeck <linux@roeck-us.net> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
2023-02-06 16:28:02 -08:00
if (within_module_core((unsigned long)location, mod))
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
/* Find this entry, or if that fails, the next avail. entry */
while (entry->jump[0]) {
if (entry_matches(entry, val)) return (uint32_t)entry;
entry++;
}
if (patch_instruction(&entry->jump[0], ppc_inst(PPC_RAW_LIS(_R12, PPC_HA(val)))))
return 0;
if (patch_instruction(&entry->jump[1], ppc_inst(PPC_RAW_ADDI(_R12, _R12, PPC_LO(val)))))
return 0;
if (patch_instruction(&entry->jump[2], ppc_inst(PPC_RAW_MTCTR(_R12))))
return 0;
if (patch_instruction(&entry->jump[3], ppc_inst(PPC_RAW_BCTR())))
return 0;
pr_debug("Initialized plt for 0x%x at %p\n", val, entry);
return (uint32_t)entry;
}
static int patch_location_16(uint32_t *loc, u16 value)
{
loc = PTR_ALIGN_DOWN(loc, sizeof(u32));
return patch_instruction(loc, ppc_inst((*loc & 0xffff0000) | value));
}
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *module)
{
unsigned int i;
Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
Elf32_Sym *sym;
uint32_t *location;
uint32_t value;
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
/* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rela[i].r_offset;
/* This is the symbol it is referring to. Note that all
undefined symbols have been resolved. */
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rela[i].r_info);
/* `Everything is relative'. */
value = sym->st_value + rela[i].r_addend;
switch (ELF32_R_TYPE(rela[i].r_info)) {
case R_PPC_ADDR32:
/* Simply set it */
*(uint32_t *)location = value;
break;
case R_PPC_ADDR16_LO:
/* Low half of the symbol */
if (patch_location_16(location, PPC_LO(value)))
return -EFAULT;
break;
case R_PPC_ADDR16_HI:
/* Higher half of the symbol */
if (patch_location_16(location, PPC_HI(value)))
return -EFAULT;
break;
case R_PPC_ADDR16_HA:
if (patch_location_16(location, PPC_HA(value)))
return -EFAULT;
break;
case R_PPC_REL24:
if ((int)(value - (uint32_t)location) < -0x02000000
|| (int)(value - (uint32_t)location) >= 0x02000000) {
value = do_plt_call(location, value,
sechdrs, module);
if (!value)
return -EFAULT;
}
/* Only replace bits 2 through 26 */
pr_debug("REL24 value = %08X. location = %08X\n",
value, (uint32_t)location);
pr_debug("Location before: %08X.\n",
*(uint32_t *)location);
value = (*(uint32_t *)location & ~PPC_LI_MASK) |
PPC_LI(value - (uint32_t)location);
if (patch_instruction(location, ppc_inst(value)))
return -EFAULT;
pr_debug("Location after: %08X.\n",
*(uint32_t *)location);
pr_debug("ie. jump to %08X+%08X = %08X\n",
*(uint32_t *)PPC_LI((uint32_t)location), (uint32_t)location,
(*(uint32_t *)PPC_LI((uint32_t)location)) + (uint32_t)location);
break;
case R_PPC_REL32:
/* 32-bit relative jump. */
*(uint32_t *)location = value - (uint32_t)location;
break;
default:
pr_err("%s: unknown ADD relocation: %u\n",
module->name,
ELF32_R_TYPE(rela[i].r_info));
return -ENOEXEC;
}
}
return 0;
}
#ifdef CONFIG_DYNAMIC_FTRACE
notrace int module_trampoline_target(struct module *mod, unsigned long addr,
unsigned long *target)
{
ppc_inst_t jmp[4];
/* Find where the trampoline jumps to */
if (copy_inst_from_kernel_nofault(jmp, (void *)addr))
return -EFAULT;
if (__copy_inst_from_kernel_nofault(jmp + 1, (void *)addr + 4))
return -EFAULT;
if (__copy_inst_from_kernel_nofault(jmp + 2, (void *)addr + 8))
return -EFAULT;
if (__copy_inst_from_kernel_nofault(jmp + 3, (void *)addr + 12))
return -EFAULT;
/* verify that this is what we expect it to be */
if ((ppc_inst_val(jmp[0]) & 0xffff0000) != PPC_RAW_LIS(_R12, 0))
return -EINVAL;
if ((ppc_inst_val(jmp[1]) & 0xffff0000) != PPC_RAW_ADDI(_R12, _R12, 0))
return -EINVAL;
if (ppc_inst_val(jmp[2]) != PPC_RAW_MTCTR(_R12))
return -EINVAL;
if (ppc_inst_val(jmp[3]) != PPC_RAW_BCTR())
return -EINVAL;
addr = (ppc_inst_val(jmp[1]) & 0xffff) | ((ppc_inst_val(jmp[0]) & 0xffff) << 16);
if (addr & 0x8000)
addr -= 0x10000;
*target = addr;
return 0;
}
int module_finalize_ftrace(struct module *module, const Elf_Shdr *sechdrs)
{
module: replace module_layout with module_memory module_layout manages different types of memory (text, data, rodata, etc.) in one allocation, which is problematic for some reasons: 1. It is hard to enable CONFIG_STRICT_MODULE_RWX. 2. It is hard to use huge pages in modules (and not break strict rwx). 3. Many archs uses module_layout for arch-specific data, but it is not obvious how these data are used (are they RO, RX, or RW?) Improve the scenario by replacing 2 (or 3) module_layout per module with up to 7 module_memory per module: MOD_TEXT, MOD_DATA, MOD_RODATA, MOD_RO_AFTER_INIT, MOD_INIT_TEXT, MOD_INIT_DATA, MOD_INIT_RODATA, and allocating them separately. This adds slightly more entries to mod_tree (from up to 3 entries per module, to up to 7 entries per module). However, this at most adds a small constant overhead to __module_address(), which is expected to be fast. Various archs use module_layout for different data. These data are put into different module_memory based on their location in module_layout. IOW, data that used to go with text is allocated with MOD_MEM_TYPE_TEXT; data that used to go with data is allocated with MOD_MEM_TYPE_DATA, etc. module_memory simplifies quite some of the module code. For example, ARCH_WANTS_MODULES_DATA_IN_VMALLOC is a lot cleaner, as it just uses a different allocator for the data. kernel/module/strict_rwx.c is also much cleaner with module_memory. Signed-off-by: Song Liu <song@kernel.org> Cc: Luis Chamberlain <mcgrof@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Guenter Roeck <linux@roeck-us.net> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
2023-02-06 16:28:02 -08:00
module->arch.tramp = do_plt_call(module->mem[MOD_TEXT].base,
(unsigned long)ftrace_caller,
sechdrs, module);
if (!module->arch.tramp)
return -ENOENT;
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
module: replace module_layout with module_memory module_layout manages different types of memory (text, data, rodata, etc.) in one allocation, which is problematic for some reasons: 1. It is hard to enable CONFIG_STRICT_MODULE_RWX. 2. It is hard to use huge pages in modules (and not break strict rwx). 3. Many archs uses module_layout for arch-specific data, but it is not obvious how these data are used (are they RO, RX, or RW?) Improve the scenario by replacing 2 (or 3) module_layout per module with up to 7 module_memory per module: MOD_TEXT, MOD_DATA, MOD_RODATA, MOD_RO_AFTER_INIT, MOD_INIT_TEXT, MOD_INIT_DATA, MOD_INIT_RODATA, and allocating them separately. This adds slightly more entries to mod_tree (from up to 3 entries per module, to up to 7 entries per module). However, this at most adds a small constant overhead to __module_address(), which is expected to be fast. Various archs use module_layout for different data. These data are put into different module_memory based on their location in module_layout. IOW, data that used to go with text is allocated with MOD_MEM_TYPE_TEXT; data that used to go with data is allocated with MOD_MEM_TYPE_DATA, etc. module_memory simplifies quite some of the module code. For example, ARCH_WANTS_MODULES_DATA_IN_VMALLOC is a lot cleaner, as it just uses a different allocator for the data. kernel/module/strict_rwx.c is also much cleaner with module_memory. Signed-off-by: Song Liu <song@kernel.org> Cc: Luis Chamberlain <mcgrof@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Guenter Roeck <linux@roeck-us.net> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
2023-02-06 16:28:02 -08:00
module->arch.tramp_regs = do_plt_call(module->mem[MOD_TEXT].base,
(unsigned long)ftrace_regs_caller,
sechdrs, module);
if (!module->arch.tramp_regs)
return -ENOENT;
#endif
return 0;
}
#endif