* [PATCH] parisc: fix module loading failure of large kernel modules @ 2008-12-29 14:07 Helge Deller 2008-12-29 14:09 ` [PATCH 1/2] module.c: fix module loading failure of large modules Helge Deller 2008-12-29 14:10 ` [PATCH 2/2] parisc: " Helge Deller 0 siblings, 2 replies; 8+ messages in thread From: Helge Deller @ 2008-12-29 14:07 UTC (permalink / raw) To: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Moritz Muehlenhoff, Linus, Andrew Morton Cc: Helge Deller The parisc port (esp. the 32bit kernel) currently lacks the ability to load large kernel modules like xfs or ipv6. This is a long outstanding bug and has already been reported a few times, e.g.: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=350482, http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=401439, http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508489 The symptom is like this: # modprobe xfs FATAL: Error inserting xfs (/lib/modules/2.6.26-1-parisc/kernel/fs/xfs/xfs.ko): Invalid module format In dmesg: module xfs relocation of symbol xfs_btree_read_bufs is out of range (0x3ffefffe in 17 bits) The reason for the failure is, that the architecture only provides the R_PARISC_PCREL17F (for 32bit kernels) and R_PARISC_PCREL22F (for PA2.0 and 64bit kernels) relocations, which sometimes can't reach the target address of the stub entry if the kernel module is too large. Currently parisc (like other architectures) creates one big PLT section for all stubs at the beginning of the init and core sections. The following two patches changes the parisc module loader to put stubs in between the code sections instead, so that the distance to the stubs more easily fits into the available 17/22 bits. The first patch touches the generic module loader and adds a call to the new module_additional_section_size() function to get_offset() if CONFIG_ARCH_WANTS_STUBS_BEHIND_SECTIONS is defined. On parisc this function returns the additional bytes for the stub area of a given section. The second patch implements the parisc-specific changes. Tested with 32- and 64bit parisc kernels. Helge ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] module.c: fix module loading failure of large modules 2008-12-29 14:07 [PATCH] parisc: fix module loading failure of large kernel modules Helge Deller @ 2008-12-29 14:09 ` Helge Deller 2008-12-29 14:10 ` [PATCH 2/2] parisc: " Helge Deller 1 sibling, 0 replies; 8+ messages in thread From: Helge Deller @ 2008-12-29 14:09 UTC (permalink / raw) To: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Moritz Muehlenhoff, Linus, Andrew Morton Cc: Helge Deller [-- Attachment #1: Type: text/plain, Size: 426 bytes --] [PATCH 1/2] module.c: fix module loading failure of large modules When creating the final layout of a kernel module in memory, allow the module loader to reserve some additional memory behind a given section. This is currently only needed for the parisc port which needs to put the stub entries there to fulfill the 17/22bit PCREL relocations with large kernel modules like xfs. Signed-off-by: Helge Deller <deller@gmx.de> [-- Attachment #2: modules.patch1 --] [-- Type: text/plain, Size: 1894 bytes --] diff --git a/include/linux/moduleloader.h b/include/linux/moduleloader.h index eb10339..65fb34c 100644 --- a/include/linux/moduleloader.h +++ b/include/linux/moduleloader.h @@ -13,6 +13,9 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, char *secstrings, struct module *mod); +unsigned long module_additional_section_size(struct module *mod, + unsigned int section); + /* Allocator used for allocating struct module, core sections and init sections. Returns NULL on failure. */ void *module_alloc(unsigned long size); diff --git a/kernel/module.c b/kernel/module.c index 1f4cc00..c7662da 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1579,12 +1579,16 @@ static int simplify_symbols(Elf_Shdr *sechdrs, } /* Update size with this section: return offset. */ -static long get_offset(unsigned int *size, Elf_Shdr *sechdr) +static long get_offset(struct module *mod, unsigned int *size, + Elf_Shdr *sechdr, unsigned int section) { long ret; ret = ALIGN(*size, sechdr->sh_addralign ?: 1); *size = ret + sechdr->sh_size; +#ifdef CONFIG_ARCH_WANTS_STUBS_BEHIND_SECTIONS + *size += module_additional_section_size(mod, section); +#endif return ret; } @@ -1622,7 +1626,7 @@ static void layout_sections(struct module *mod, || strncmp(secstrings + s->sh_name, ".init", 5) == 0) continue; - s->sh_entsize = get_offset(&mod->core_size, s); + s->sh_entsize = get_offset(mod, &mod->core_size, s, i); DEBUGP("\t%s\n", secstrings + s->sh_name); } if (m == 0) @@ -1640,7 +1644,7 @@ static void layout_sections(struct module *mod, || strncmp(secstrings + s->sh_name, ".init", 5) != 0) continue; - s->sh_entsize = (get_offset(&mod->init_size, s) + s->sh_entsize = (get_offset(mod, &mod->init_size, s, i) | INIT_OFFSET_MASK); DEBUGP("\t%s\n", secstrings + s->sh_name); } ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-29 14:07 [PATCH] parisc: fix module loading failure of large kernel modules Helge Deller 2008-12-29 14:09 ` [PATCH 1/2] module.c: fix module loading failure of large modules Helge Deller @ 2008-12-29 14:10 ` Helge Deller 2008-12-29 15:46 ` Sam Ravnborg 2008-12-29 16:14 ` John David Anglin 1 sibling, 2 replies; 8+ messages in thread From: Helge Deller @ 2008-12-29 14:10 UTC (permalink / raw) To: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Moritz Muehlenhoff, Linus, Andrew Morton Cc: Helge Deller [-- Attachment #1: Type: text/plain, Size: 982 bytes --] [PATCH 2/2] parisc: fix module loading failure of large modules On 32bit (and sometimes 64bit) and with big kernel modules like xfs or ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may fail to reach their PLT stub if we only create one big stub array for all sections at the beginning of the core or init section. With this patch we now instead append individual PLT stub entries directly at the end of the code sections where the stubs are actually called. This reduces the distance between the PCREL location and the stub entry so that the relocations can be fulfilled. The kernel module loader will call module_additional_section_size() and request us to return the amount of additional memory we need for the stubs of each section. The final section size of the code segment will then be increased by that value when the kernel layouts the final addresses of all sections. Tested with 32- and 64bit kernels. Signed-off-by: Helge Deller <deller@gmx.de> [-- Attachment #2: modules.patch2 --] [-- Type: text/plain, Size: 14083 bytes --] diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 644a70b..cbb622f 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -34,6 +34,9 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config ARCH_WANTS_STUBS_BEHIND_SECTIONS + def_bool y + config ARCH_HAS_ILOG2_U32 bool default n diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h index c2cb49e..1f41234 100644 --- a/arch/parisc/include/asm/module.h +++ b/arch/parisc/include/asm/module.h @@ -23,8 +23,10 @@ struct mod_arch_specific { unsigned long got_offset, got_count, got_max; unsigned long fdesc_offset, fdesc_count, fdesc_max; - unsigned long stub_offset, stub_count, stub_max; - unsigned long init_stub_offset, init_stub_count, init_stub_max; + struct { + unsigned long stub_offset; + unsigned int stub_entries; + } *section; int unwind_section; struct unwind_table *unwind; }; diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index 44138c3..f57caf3 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c @@ -6,6 +6,7 @@ * * Linux/PA-RISC Project (http://www.parisc-linux.org/) * Copyright (C) 2003 Randolph Chung <tausq at debian . org> + * Copyright (C) 2008 Helge Deller <deller@gmx.de> * * * This program is free software; you can redistribute it and/or modify @@ -24,6 +25,21 @@ * * * Notes: + * - PLT stub handling + * On 32bit (and sometimes 64bit) and with big kernel modules like xfs or + * ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may + * fail to reach its PLT stub if we only create one big stub array for + * all sections at the beginning of the core or init section. + * Instead we now append individual PLT stub entries directly at the end + * of the code sections where the stubs are actually called. + * This reduces the distance between the PCREL location and the stub entry + * so that the relocations can be fulfilled. + * The kernel module loader will call module_additional_section_size() + * and request us to return the amount of additional memory we need for + * the stubs of each section. The final section size of the code segment + * will then be increased by that value when the kernel layouts the final + * addresses of all sections. + * * - SEGREL32 handling * We are not doing SEGREL32 handling correctly. According to the ABI, we * should do a value offset, like this: @@ -58,9 +74,13 @@ #define DEBUGP(fmt...) #endif +#define RELOC_REACHABLE(val, bits) \ + (( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ + ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) ? \ + 0 : 1) + #define CHECK_RELOC(val, bits) \ - if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ - ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \ + if (!RELOC_REACHABLE(val, bits)) { \ printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ me->name, strtab + sym->st_name, (unsigned long)val, bits); \ return -ENOEXEC; \ @@ -92,13 +112,6 @@ static inline int in_local(struct module *me, void *loc) return in_init(me, loc) || in_core(me, loc); } -static inline int in_local_section(struct module *me, void *loc, void *dot) -{ - return (in_init(me, loc) && in_init(me, dot)) || - (in_core(me, loc) && in_core(me, dot)); -} - - #ifndef CONFIG_64BIT struct got_entry { Elf32_Addr addr; @@ -258,23 +271,42 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) /* Free memory returned from module_alloc */ void module_free(struct module *mod, void *module_region) { + kfree(mod->arch.section); + mod->arch.section = NULL; + vfree(module_region); /* FIXME: If module_region == mod->init_region, trim exception table entries. */ } +/* return number of additional bytes to reserve for a section */ +unsigned long module_additional_section_size(struct module *mod, + unsigned int section) +{ + /* size needed for all stubs of this section (including + * one additional for correct alignment of the stubs) */ + return (mod->arch.section[section].stub_entries + 1) + * sizeof(struct stub_entry); +} + #define CONST int module_frob_arch_sections(CONST Elf_Ehdr *hdr, CONST Elf_Shdr *sechdrs, CONST char *secstrings, struct module *me) { - unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0; + unsigned long gots = 0, fdescs = 0, len; unsigned int i; + len = hdr->e_shnum * sizeof(me->arch.section[0]); + me->arch.section = kzalloc(len, GFP_KERNEL); + if (!me->arch.section) + return -ENOMEM; + for (i = 1; i < hdr->e_shnum; i++) { - const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; + const Elf_Rela *rels = (void *)sechdrs[i].sh_addr; unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); + unsigned int count, s; if (strncmp(secstrings + sechdrs[i].sh_name, ".PARISC.unwind", 14) == 0) @@ -290,11 +322,24 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, */ gots += count_gots(rels, nrels); fdescs += count_fdescs(rels, nrels); - if(strncmp(secstrings + sechdrs[i].sh_name, - ".rela.init", 10) == 0) - init_stubs += count_stubs(rels, nrels); - else - stubs += count_stubs(rels, nrels); + + /* XXX: By sorting the relocs and finding duplicate entries + * we could reduce the number of necessary stubs and save + * some memory. */ + count = count_stubs(rels, nrels); + if (!count) + continue; + + /* so we need relocation stubs. reserve necessary memory. */ + /* sh_info gives the section for which we need to add stubs. */ + s = sechdrs[i].sh_info; + + /* amunt of stubs we want to add to this section */ + WARN_ON(me->arch.section[s].stub_entries); + me->arch.section[s].stub_entries += count; + + /* stubs start at end of the section */ + me->arch.section[s].stub_offset = sechdrs[s].sh_size; } /* align things a bit */ @@ -306,18 +351,8 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, me->arch.fdesc_offset = me->core_size; me->core_size += fdescs * sizeof(Elf_Fdesc); - me->core_size = ALIGN(me->core_size, 16); - me->arch.stub_offset = me->core_size; - me->core_size += stubs * sizeof(struct stub_entry); - - me->init_size = ALIGN(me->init_size, 16); - me->arch.init_stub_offset = me->init_size; - me->init_size += init_stubs * sizeof(struct stub_entry); - me->arch.got_max = gots; me->arch.fdesc_max = fdescs; - me->arch.stub_max = stubs; - me->arch.init_stub_max = init_stubs; return 0; } @@ -380,22 +415,18 @@ enum elf_stub_type { }; static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, - enum elf_stub_type stub_type, int init_section) + enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec) { - unsigned long i; struct stub_entry *stub; - if(init_section) { - i = me->arch.init_stub_count++; - BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max); - stub = me->module_init + me->arch.init_stub_offset + - i * sizeof(struct stub_entry); - } else { - i = me->arch.stub_count++; - BUG_ON(me->arch.stub_count > me->arch.stub_max); - stub = me->module_core + me->arch.stub_offset + - i * sizeof(struct stub_entry); - } + /* do not write outside available stub area */ + BUG_ON(0 == me->arch.section[targetsec].stub_entries--); + + /* calculate address of next stub entry, take care of alignment */ + loc0 += me->arch.section[targetsec].stub_offset; + loc0 = ALIGN(loc0, sizeof(struct stub_entry)); + stub = (void *) loc0; + me->arch.section[targetsec].stub_offset += sizeof(struct stub_entry); #ifndef CONFIG_64BIT /* for 32-bit the stub looks like this: @@ -489,15 +520,19 @@ int apply_relocate_add(Elf_Shdr *sechdrs, Elf32_Addr val; Elf32_Sword addend; Elf32_Addr dot; + Elf_Addr loc0; + unsigned int targetsec = sechdrs[relsec].sh_info; //unsigned long dp = (unsigned long)$global$; register unsigned long dp asm ("r27"); DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); + targetsec); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + loc = (void *)sechdrs[targetsec].sh_addr + rel[i].r_offset; + /* This is the start of the target section */ + loc0 = sechdrs[targetsec].sh_addr; /* This is the symbol it is referring to */ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM(rel[i].r_info); @@ -569,19 +604,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs, break; case R_PARISC_PCREL17F: /* 17-bit PC relative address */ - val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); + /* calculate direct call offset */ + val += addend; val = (val - dot - 8)/4; - CHECK_RELOC(val, 17) + if (!RELOC_REACHABLE(val, 17)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, addend, + ELF_STUB_DIRECT, loc0, targetsec); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 17); + } *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); break; case R_PARISC_PCREL22F: /* 22-bit PC relative address; only defined for pa20 */ - val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); - DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", - strtab + sym->st_name, (unsigned long)loc, addend, - val) + /* calculate direct call offset */ + val += addend; val = (val - dot - 8)/4; - CHECK_RELOC(val, 22); + if (!RELOC_REACHABLE(val, 22)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, addend, + ELF_STUB_DIRECT, loc0, targetsec); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); + } *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); break; @@ -610,13 +658,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, Elf64_Addr val; Elf64_Sxword addend; Elf64_Addr dot; + Elf_Addr loc0; + unsigned int targetsec = sechdrs[relsec].sh_info; DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); + targetsec); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + loc = (void *)sechdrs[targetsec].sh_addr + rel[i].r_offset; + /* This is the start of the target section */ + loc0 = sechdrs[targetsec].sh_addr; /* This is the symbol it is referring to */ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + ELF64_R_SYM(rel[i].r_info); @@ -672,42 +724,40 @@ int apply_relocate_add(Elf_Shdr *sechdrs, DEBUGP("PCREL22F Symbol %s loc %p val %lx\n", strtab + sym->st_name, loc, val); + val += addend; /* can we reach it locally? */ - if(!in_local_section(me, (void *)val, (void *)dot)) { - - if (in_local(me, (void *)val)) - /* this is the case where the - * symbol is local to the - * module, but in a different - * section, so stub the jump - * in case it's more than 22 - * bits away */ - val = get_stub(me, val, addend, ELF_STUB_DIRECT, - in_init(me, loc)); - else if (strncmp(strtab + sym->st_name, "$$", 2) + if (in_local(me, (void *)val)) { + /* this is the case where the symbol is local + * to the module, but in a different section, + * so stub the jump in case it's more than 22 + * bits away */ + val = (val - dot - 8)/4; + if (!RELOC_REACHABLE(val, 22)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, + addend, ELF_STUB_DIRECT, + loc0, targetsec); + } else { + /* Ok, we can reach it directly. */ + val = sym->st_value; + val += addend; + } + } else { + val = sym->st_value; + if (strncmp(strtab + sym->st_name, "$$", 2) == 0) val = get_stub(me, val, addend, ELF_STUB_MILLI, - in_init(me, loc)); + loc0, targetsec); else val = get_stub(me, val, addend, ELF_STUB_GOT, - in_init(me, loc)); + loc0, targetsec); } DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", strtab + sym->st_name, loc, sym->st_value, addend, val); - /* FIXME: local symbols work as long as the - * core and init pieces aren't separated too - * far. If this is ever broken, you will trip - * the check below. The way to fix it would - * be to generate local stubs to go between init - * and core */ - if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 || - (Elf64_Sxword)(val - dot - 8) < -0x800000) { - printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n", - me->name, strtab + sym->st_name); - return -ENOEXEC; - } val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); break; case R_PARISC_DIR64: @@ -794,12 +844,8 @@ int module_finalize(const Elf_Ehdr *hdr, addr = (u32 *)entry->addr; printk("INSNS: %x %x %x %x\n", addr[0], addr[1], addr[2], addr[3]); - printk("stubs used %ld, stubs max %ld\n" - "init_stubs used %ld, init stubs max %ld\n" - "got entries used %ld, gots max %ld\n" + printk("got entries used %ld, gots max %ld\n" "fdescs used %ld, fdescs max %ld\n", - me->arch.stub_count, me->arch.stub_max, - me->arch.init_stub_count, me->arch.init_stub_max, me->arch.got_count, me->arch.got_max, me->arch.fdesc_count, me->arch.fdesc_max); #endif @@ -829,7 +875,10 @@ int module_finalize(const Elf_Ehdr *hdr, me->name, me->arch.got_count, MAX_GOTS); return -EINVAL; } - + + kfree(me->arch.section); + me->arch.section = NULL; + /* no symbol table */ if(symhdr == NULL) return 0; ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-29 14:10 ` [PATCH 2/2] parisc: " Helge Deller @ 2008-12-29 15:46 ` Sam Ravnborg 2008-12-29 16:09 ` Helge Deller 2008-12-29 16:14 ` John David Anglin 1 sibling, 1 reply; 8+ messages in thread From: Sam Ravnborg @ 2008-12-29 15:46 UTC (permalink / raw) To: Helge Deller Cc: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Moritz Muehlenhoff, Linus, Andrew Morton On Mon, Dec 29, 2008 at 03:10:28PM +0100, Helge Deller wrote: > [PATCH 2/2] parisc: fix module loading failure of large modules > > On 32bit (and sometimes 64bit) and with big kernel modules like xfs or > ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may > fail to reach their PLT stub if we only create one big stub array for > all sections at the beginning of the core or init section. > > With this patch we now instead append individual PLT stub entries > directly at the end of the code sections where the stubs are actually > called. This reduces the distance between the PCREL location and the > stub entry so that the relocations can be fulfilled. > > The kernel module loader will call module_additional_section_size() and > request us to return the amount of additional memory we need for the > stubs of each section. The final section size of the code segment will > then be increased by that value when the kernel layouts the final > addresses of all sections. > > Tested with 32- and 64bit kernels. > > Signed-off-by: Helge Deller <deller@gmx.de> > > diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig > index 644a70b..cbb622f 100644 > --- a/arch/parisc/Kconfig > +++ b/arch/parisc/Kconfig > @@ -34,6 +34,9 @@ config RWSEM_GENERIC_SPINLOCK > config RWSEM_XCHGADD_ALGORITHM > bool > > +config ARCH_WANTS_STUBS_BEHIND_SECTIONS > + def_bool y > + The recommended practive today is: In some relevant Kconfig file add: config HAVE_MODULE_SECTION_STUBS bool And then in arch/$ARCH/Kconfig do: config PARISC ... select HAVE_MODULE_SECTION_STUBS ... The select are supposed to be sorted alphabetically but people seems to use a different alphabet for each arch. Sam ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-29 15:46 ` Sam Ravnborg @ 2008-12-29 16:09 ` Helge Deller 0 siblings, 0 replies; 8+ messages in thread From: Helge Deller @ 2008-12-29 16:09 UTC (permalink / raw) To: Sam Ravnborg Cc: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Moritz Muehlenhoff, Linus, Andrew Morton, Helge Deller Sam Ravnborg wrote: > On Mon, Dec 29, 2008 at 03:10:28PM +0100, Helge Deller wrote: >> [PATCH 2/2] parisc: fix module loading failure of large modules >> >> On 32bit (and sometimes 64bit) and with big kernel modules like xfs or >> ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may >> fail to reach their PLT stub if we only create one big stub array for >> all sections at the beginning of the core or init section. >> >> With this patch we now instead append individual PLT stub entries >> directly at the end of the code sections where the stubs are actually >> called. This reduces the distance between the PCREL location and the >> stub entry so that the relocations can be fulfilled. >> >> The kernel module loader will call module_additional_section_size() and >> request us to return the amount of additional memory we need for the >> stubs of each section. The final section size of the code segment will >> then be increased by that value when the kernel layouts the final >> addresses of all sections. >> >> Tested with 32- and 64bit kernels. >> >> Signed-off-by: Helge Deller <deller@gmx.de> >> > >> diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig >> index 644a70b..cbb622f 100644 >> --- a/arch/parisc/Kconfig >> +++ b/arch/parisc/Kconfig >> @@ -34,6 +34,9 @@ config RWSEM_GENERIC_SPINLOCK >> config RWSEM_XCHGADD_ALGORITHM >> bool >> >> +config ARCH_WANTS_STUBS_BEHIND_SECTIONS >> + def_bool y >> + > > The recommended practive today is: > > In some relevant Kconfig file add: > > config HAVE_MODULE_SECTION_STUBS > bool > > And then in arch/$ARCH/Kconfig do: > > config PARISC > ... > select HAVE_MODULE_SECTION_STUBS > ... > > The select are supposed to be sorted alphabetically > but people seems to use a different alphabet for > each arch. Thanks Sam, I'll change that and respin. Helge ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-29 14:10 ` [PATCH 2/2] parisc: " Helge Deller 2008-12-29 15:46 ` Sam Ravnborg @ 2008-12-29 16:14 ` John David Anglin 2008-12-29 17:56 ` Helge Deller 1 sibling, 1 reply; 8+ messages in thread From: John David Anglin @ 2008-12-29 16:14 UTC (permalink / raw) To: Helge Deller Cc: linux-parisc, linux-kernel, kyle, randolph, jmm, torvalds, akpm, deller Hi Helge, > With this patch we now instead append individual PLT stub entries > directly at the end of the code sections where the stubs are actually > called. This reduces the distance between the PCREL location and the > stub entry so that the relocations can be fulfilled. GCC for PA-RISC assumes that the stubs will be placed before the code section. This is what HP-UX does. It's also not possible to compute the distance to the end of a function in GCC due to the way branch shortening is done. If the distance to the start of the code section is too large, GCC outputs a long call. If you can insert them before the code section where the stubs are called, this will minimize the chance that a pc-relative call will not reach its stub. It is possible to put the stubs after the code section if you can ensure the distance for each call isn't too large. I believe GNU ld does some consolidation of stub blocks. Great patch! Dave -- J. David Anglin dave.anglin@nrc-cnrc.gc.ca National Research Council of Canada (613) 990-0752 (FAX: 952-6602) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-29 16:14 ` John David Anglin @ 2008-12-29 17:56 ` Helge Deller 0 siblings, 0 replies; 8+ messages in thread From: Helge Deller @ 2008-12-29 17:56 UTC (permalink / raw) To: John David Anglin Cc: linux-parisc, linux-kernel, kyle, randolph, jmm, torvalds, akpm, Helge Deller John David Anglin wrote: > Hi Helge, > >> With this patch we now instead append individual PLT stub entries >> directly at the end of the code sections where the stubs are actually >> called. This reduces the distance between the PCREL location and the >> stub entry so that the relocations can be fulfilled. > > GCC for PA-RISC assumes that the stubs will be placed before the code > section. This is what HP-UX does. It's also not possible to compute > the distance to the end of a function in GCC due to the way branch > shortening is done. If the distance to the start of the code section > is too large, GCC outputs a long call. Interesting. I didn't knew that. > If you can insert them before the code section where the stubs are > called, this will minimize the chance that a pc-relative call will > not reach its stub. Yes, good idea. I'll change that and send an updated patch. > Great patch! Thanks! Helge ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] parisc: fix module loading failure of large kernel modules (take 2) @ 2008-12-29 20:34 Helge Deller 2008-12-30 22:45 ` Rusty Russell 0 siblings, 1 reply; 8+ messages in thread From: Helge Deller @ 2008-12-29 20:34 UTC (permalink / raw) To: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Linus, Andrew Morton, Sam Ravnborg, John David Anglin This is the second take of the patch series. Changes to previous version: - new CONFIG_HAVE_MODULE_SECTION_STUBS config option - put stub entries of a code section in front of the section ____________ The parisc port (esp. the 32bit kernel) currently lacks the ability to load large kernel modules like xfs or ipv6. This is a long outstanding bug and has already been reported a few times, e.g.: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=350482, http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=401439, http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508489 The symptom is like this: # modprobe xfs FATAL: Error inserting xfs (/lib/modules/2.6.26-1-parisc/kernel/fs/xfs/xfs.ko): Invalid module format In dmesg: module xfs relocation of symbol xfs_btree_read_bufs is out of range (0x3ffefffe in 17 bits) The reason for the failure is, that the architecture only provides the R_PARISC_PCREL17F (for 32bit kernels) and R_PARISC_PCREL22F (for PA2.0 and 64bit kernels) relocations, which sometimes can't reach the target address of the stub entry if the kernel module is too large. Currently parisc (like other architectures) creates one big PLT section for all stubs at the beginning of the init and core sections. The following two patches changes the parisc module loader to put stubs for the code sections in front of each section, so that the distance to the stubs more easily fits into the available 17/22 bits. The first patch touches the generic module loader and adds a call to the new module_additional_section_size() function to get_offset() if CONFIG_HAVE_MODULE_SECTION_STUBS is defined. On parisc this function returns the additional bytes for the stub area of a given section. The second patch implements the parisc-specific changes. Tested with 32- and 64bit parisc kernels. Helge ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] parisc: fix module loading failure of large kernel modules (take 2) 2008-12-29 20:34 [PATCH] parisc: fix module loading failure of large kernel modules (take 2) Helge Deller @ 2008-12-30 22:45 ` Rusty Russell 2008-12-31 11:31 ` [PATCH] parisc: fix module loading failure of large kernel modules (take 4) Helge Deller 0 siblings, 1 reply; 8+ messages in thread From: Rusty Russell @ 2008-12-30 22:45 UTC (permalink / raw) To: Helge Deller Cc: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Linus, Andrew Morton, Sam Ravnborg, John David Anglin On Tuesday 30 December 2008 07:04:54 Helge Deller wrote: > This is the second take of the patch series. > Changes to previous version: > - new CONFIG_HAVE_MODULE_SECTION_STUBS config option > - put stub entries of a code section in front of the section > > ____________ > The parisc port (esp. the 32bit kernel) currently lacks the ability to > load large kernel modules like xfs or ipv6. This is a long outstanding > bug and has already been reported a few times, e.g.: > http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=350482, > http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=401439, > http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508489 > > The symptom is like this: > # modprobe xfs > FATAL: Error inserting xfs > (/lib/modules/2.6.26-1-parisc/kernel/fs/xfs/xfs.ko): Invalid module > format > > In dmesg: > module xfs relocation of symbol xfs_btree_read_bufs is out of range > (0x3ffefffe in 17 bits) > > The reason for the failure is, that the architecture only provides the > R_PARISC_PCREL17F (for 32bit kernels) and R_PARISC_PCREL22F (for PA2.0 > and 64bit kernels) relocations, which sometimes can't reach the target > address of the stub entry if the kernel module is too large. Currently > parisc (like other architectures) creates one big PLT section for all > stubs at the beginning of the init and core sections. > > The following two patches changes the parisc module loader to put stubs > for the code sections in front of each section, so that the distance to > the stubs more easily fits into the available 17/22 bits. So now any one section has to pass 17 bits to break? How close are you with the xfs module? But it's kind of nasty, overloading sh_entsize further. Could we instead do something like add a arch_module_section_size() weak fn which you can overload? We'd use that in get_offset() so our layout and size calculations were correct, and use sh_size everywhere else. Cheers, Rusty. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] parisc: fix module loading failure of large kernel modules (take 4) 2008-12-30 22:45 ` Rusty Russell @ 2008-12-31 11:31 ` Helge Deller 2008-12-31 11:36 ` [PATCH 2/2] parisc: fix module loading failure of large modules Helge Deller 0 siblings, 1 reply; 8+ messages in thread From: Helge Deller @ 2008-12-31 11:31 UTC (permalink / raw) To: Rusty Russell Cc: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Linus, Andrew Morton, Sam Ravnborg, John David Anglin [PATCH 1/2] module.c: fix module loading failure of large kernel modules When creating the final layout of a kernel module in memory, allow the module loader to reserve some additional memory in front of a given section. This is currently only needed for the parisc port which needs to put the stub entries there to fulfill the 17/22bit PCREL relocations with large kernel modules like xfs. Differences of this patch to previous versions: - added weak funtion arch_module_section_size() - no kernel config options needed - no overloading of sh_entsize Signed-off-by: Helge Deller <deller@gmx.de> diffstat: include/linux/moduleloader.h | 4 ++++ kernel/module.c | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/linux/moduleloader.h b/include/linux/moduleloader.h index eb10339..f2b1b62 100644 --- a/include/linux/moduleloader.h +++ b/include/linux/moduleloader.h @@ -13,6 +13,10 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, char *secstrings, struct module *mod); +/* Additional bytes needed by arch in front of individual sections */ +unsigned int arch_module_section_size(struct module *mod, + unsigned int section); + /* Allocator used for allocating struct module, core sections and init sections. Returns NULL on failure. */ void *module_alloc(unsigned long size); diff --git a/kernel/module.c b/kernel/module.c index 1f4cc00..5b91b17 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1578,11 +1578,21 @@ static int simplify_symbols(Elf_Shdr *sechdrs, return ret; } +/* Additional bytes needed by arch in front of individual sections */ +unsigned int __attribute__ ((weak)) arch_module_section_size( + struct module *mod, unsigned int section) +{ + /* default implementation just returns zero */ + return 0; +} + /* Update size with this section: return offset. */ -static long get_offset(unsigned int *size, Elf_Shdr *sechdr) +static long get_offset(struct module *mod, unsigned int *size, + Elf_Shdr *sechdr, unsigned int section) { long ret; + *size += arch_module_section_size(mod, section); ret = ALIGN(*size, sechdr->sh_addralign ?: 1); *size = ret + sechdr->sh_size; return ret; @@ -1622,7 +1632,7 @@ static void layout_sections(struct module *mod, || strncmp(secstrings + s->sh_name, ".init", 5) == 0) continue; - s->sh_entsize = get_offset(&mod->core_size, s); + s->sh_entsize = get_offset(mod, &mod->core_size, s, i); DEBUGP("\t%s\n", secstrings + s->sh_name); } if (m == 0) @@ -1640,7 +1650,7 @@ static void layout_sections(struct module *mod, || strncmp(secstrings + s->sh_name, ".init", 5) != 0) continue; - s->sh_entsize = (get_offset(&mod->init_size, s) + s->sh_entsize = (get_offset(mod, &mod->init_size, s, i) | INIT_OFFSET_MASK); DEBUGP("\t%s\n", secstrings + s->sh_name); } ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] parisc: fix module loading failure of large modules 2008-12-31 11:31 ` [PATCH] parisc: fix module loading failure of large kernel modules (take 4) Helge Deller @ 2008-12-31 11:36 ` Helge Deller 0 siblings, 0 replies; 8+ messages in thread From: Helge Deller @ 2008-12-31 11:36 UTC (permalink / raw) To: Rusty Russell Cc: linux-parisc, Linux Kernel Development, Kyle McMartin, Randolph Chung, Linus, Andrew Morton, Sam Ravnborg, John David Anglin [PATCH 2/2] parisc: fix module loading failure of large modules (take 3) On 32bit (and sometimes 64bit) and with big kernel modules like xfs or ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may fail to reach their PLT stub if we only create one big stub array for all sections at the beginning of the core or init section. With this patch we now instead add individual PLT stub entries directly in front of the code sections where the stubs are actually called. This reduces the distance between the PCREL location and the stub entry so that the relocations can be fulfilled. While calculating the final layout of the kernel module in memory, the kernel module loader calls arch_module_section_size() to request the to be reserved amount of memory in front of each individual section. Tested with 32- and 64bit kernels. Signed-off-by: Helge Deller <deller@gmx.de> diffstat: include/asm/module.h | 6 - kernel/module.c | 217 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 140 insertions(+), 83 deletions(-) diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h index c2cb49e..1f41234 100644 --- a/arch/parisc/include/asm/module.h +++ b/arch/parisc/include/asm/module.h @@ -23,8 +23,10 @@ struct mod_arch_specific { unsigned long got_offset, got_count, got_max; unsigned long fdesc_offset, fdesc_count, fdesc_max; - unsigned long stub_offset, stub_count, stub_max; - unsigned long init_stub_offset, init_stub_count, init_stub_max; + struct { + unsigned long stub_offset; + unsigned int stub_entries; + } *section; int unwind_section; struct unwind_table *unwind; }; diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index 44138c3..3228007 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c @@ -6,6 +6,7 @@ * * Linux/PA-RISC Project (http://www.parisc-linux.org/) * Copyright (C) 2003 Randolph Chung <tausq at debian . org> + * Copyright (C) 2008 Helge Deller <deller@gmx.de> * * * This program is free software; you can redistribute it and/or modify @@ -24,6 +25,19 @@ * * * Notes: + * - PLT stub handling + * On 32bit (and sometimes 64bit) and with big kernel modules like xfs or + * ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may + * fail to reach their PLT stub if we only create one big stub array for + * all sections at the beginning of the core or init section. + * Instead we now insert individual PLT stub entries directly in front of + * of the code sections where the stubs are actually called. + * This reduces the distance between the PCREL location and the stub entry + * so that the relocations can be fulfilled. + * While calculating the final layout of the kernel module in memory, the + * kernel module loader calls arch_module_section_size() to request the + * to be reserved amount of memory in front of each individual section. + * * - SEGREL32 handling * We are not doing SEGREL32 handling correctly. According to the ABI, we * should do a value offset, like this: @@ -58,9 +72,13 @@ #define DEBUGP(fmt...) #endif +#define RELOC_REACHABLE(val, bits) \ + (( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ + ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) ? \ + 0 : 1) + #define CHECK_RELOC(val, bits) \ - if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ - ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \ + if (!RELOC_REACHABLE(val, bits)) { \ printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ me->name, strtab + sym->st_name, (unsigned long)val, bits); \ return -ENOEXEC; \ @@ -92,13 +110,6 @@ static inline int in_local(struct module *me, void *loc) return in_init(me, loc) || in_core(me, loc); } -static inline int in_local_section(struct module *me, void *loc, void *dot) -{ - return (in_init(me, loc) && in_init(me, dot)) || - (in_core(me, loc) && in_core(me, dot)); -} - - #ifndef CONFIG_64BIT struct got_entry { Elf32_Addr addr; @@ -258,23 +269,43 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) /* Free memory returned from module_alloc */ void module_free(struct module *mod, void *module_region) { + kfree(mod->arch.section); + mod->arch.section = NULL; + vfree(module_region); /* FIXME: If module_region == mod->init_region, trim exception table entries. */ } + +/* Additional bytes needed in front of individual sections. */ +unsigned int arch_module_section_size(struct module *mod, + unsigned int section) +{ + /* size needed for all stubs of this section (including + * one additional for correct alignment of the stubs) */ + return (mod->arch.section[section].stub_entries + 1) + * sizeof(struct stub_entry); +} + #define CONST int module_frob_arch_sections(CONST Elf_Ehdr *hdr, CONST Elf_Shdr *sechdrs, CONST char *secstrings, struct module *me) { - unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0; + unsigned long gots = 0, fdescs = 0, len; unsigned int i; + len = hdr->e_shnum * sizeof(me->arch.section[0]); + me->arch.section = kzalloc(len, GFP_KERNEL); + if (!me->arch.section) + return -ENOMEM; + for (i = 1; i < hdr->e_shnum; i++) { - const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; + const Elf_Rela *rels = (void *)sechdrs[i].sh_addr; unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); + unsigned int count, s; if (strncmp(secstrings + sechdrs[i].sh_name, ".PARISC.unwind", 14) == 0) @@ -290,11 +321,23 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, */ gots += count_gots(rels, nrels); fdescs += count_fdescs(rels, nrels); - if(strncmp(secstrings + sechdrs[i].sh_name, - ".rela.init", 10) == 0) - init_stubs += count_stubs(rels, nrels); - else - stubs += count_stubs(rels, nrels); + + /* XXX: By sorting the relocs and finding duplicate entries + * we could reduce the number of necessary stubs and save + * some memory. */ + count = count_stubs(rels, nrels); + if (!count) + continue; + + /* so we need relocation stubs. reserve necessary memory. */ + /* sh_info gives the section for which we need to add stubs. */ + s = sechdrs[i].sh_info; + + /* each code section should only have one relocation section */ + WARN_ON(me->arch.section[s].stub_entries); + + /* store number of stubs we need for this section */ + me->arch.section[s].stub_entries += count; } /* align things a bit */ @@ -306,18 +349,8 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, me->arch.fdesc_offset = me->core_size; me->core_size += fdescs * sizeof(Elf_Fdesc); - me->core_size = ALIGN(me->core_size, 16); - me->arch.stub_offset = me->core_size; - me->core_size += stubs * sizeof(struct stub_entry); - - me->init_size = ALIGN(me->init_size, 16); - me->arch.init_stub_offset = me->init_size; - me->init_size += init_stubs * sizeof(struct stub_entry); - me->arch.got_max = gots; me->arch.fdesc_max = fdescs; - me->arch.stub_max = stubs; - me->arch.init_stub_max = init_stubs; return 0; } @@ -380,23 +413,27 @@ enum elf_stub_type { }; static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, - enum elf_stub_type stub_type, int init_section) + enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec) { - unsigned long i; struct stub_entry *stub; - if(init_section) { - i = me->arch.init_stub_count++; - BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max); - stub = me->module_init + me->arch.init_stub_offset + - i * sizeof(struct stub_entry); - } else { - i = me->arch.stub_count++; - BUG_ON(me->arch.stub_count > me->arch.stub_max); - stub = me->module_core + me->arch.stub_offset + - i * sizeof(struct stub_entry); + /* initialize stub_offset to point in front of the section */ + if (!me->arch.section[targetsec].stub_offset) { + loc0 -= (me->arch.section[targetsec].stub_entries + 1) * + sizeof(struct stub_entry); + /* get correct alignment for the stubs */ + loc0 = ALIGN(loc0, sizeof(struct stub_entry)); + me->arch.section[targetsec].stub_offset = loc0; } + /* get address of stub entry */ + stub = (void *) me->arch.section[targetsec].stub_offset; + me->arch.section[targetsec].stub_offset += sizeof(struct stub_entry); + + /* do not write outside available stub area */ + BUG_ON(0 == me->arch.section[targetsec].stub_entries--); + + #ifndef CONFIG_64BIT /* for 32-bit the stub looks like this: * ldil L'XXX,%r1 @@ -489,15 +526,19 @@ int apply_relocate_add(Elf_Shdr *sechdrs, Elf32_Addr val; Elf32_Sword addend; Elf32_Addr dot; + Elf_Addr loc0; + unsigned int targetsec = sechdrs[relsec].sh_info; //unsigned long dp = (unsigned long)$global$; register unsigned long dp asm ("r27"); DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); + targetsec); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + loc = (void *)sechdrs[targetsec].sh_addr + rel[i].r_offset; + /* This is the start of the target section */ + loc0 = sechdrs[targetsec].sh_addr; /* This is the symbol it is referring to */ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM(rel[i].r_info); @@ -569,19 +610,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs, break; case R_PARISC_PCREL17F: /* 17-bit PC relative address */ - val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); + /* calculate direct call offset */ + val += addend; val = (val - dot - 8)/4; - CHECK_RELOC(val, 17) + if (!RELOC_REACHABLE(val, 17)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, addend, + ELF_STUB_DIRECT, loc0, targetsec); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 17); + } *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); break; case R_PARISC_PCREL22F: /* 22-bit PC relative address; only defined for pa20 */ - val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); - DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", - strtab + sym->st_name, (unsigned long)loc, addend, - val) + /* calculate direct call offset */ + val += addend; val = (val - dot - 8)/4; - CHECK_RELOC(val, 22); + if (!RELOC_REACHABLE(val, 22)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, addend, + ELF_STUB_DIRECT, loc0, targetsec); + val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); + } *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); break; @@ -610,13 +664,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, Elf64_Addr val; Elf64_Sxword addend; Elf64_Addr dot; + Elf_Addr loc0; + unsigned int targetsec = sechdrs[relsec].sh_info; DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); + targetsec); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + loc = (void *)sechdrs[targetsec].sh_addr + rel[i].r_offset; + /* This is the start of the target section */ + loc0 = sechdrs[targetsec].sh_addr; /* This is the symbol it is referring to */ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + ELF64_R_SYM(rel[i].r_info); @@ -672,42 +730,40 @@ int apply_relocate_add(Elf_Shdr *sechdrs, DEBUGP("PCREL22F Symbol %s loc %p val %lx\n", strtab + sym->st_name, loc, val); + val += addend; /* can we reach it locally? */ - if(!in_local_section(me, (void *)val, (void *)dot)) { - - if (in_local(me, (void *)val)) - /* this is the case where the - * symbol is local to the - * module, but in a different - * section, so stub the jump - * in case it's more than 22 - * bits away */ - val = get_stub(me, val, addend, ELF_STUB_DIRECT, - in_init(me, loc)); - else if (strncmp(strtab + sym->st_name, "$$", 2) + if (in_local(me, (void *)val)) { + /* this is the case where the symbol is local + * to the module, but in a different section, + * so stub the jump in case it's more than 22 + * bits away */ + val = (val - dot - 8)/4; + if (!RELOC_REACHABLE(val, 22)) { + /* direct distance too far, create + * stub entry instead */ + val = get_stub(me, sym->st_value, + addend, ELF_STUB_DIRECT, + loc0, targetsec); + } else { + /* Ok, we can reach it directly. */ + val = sym->st_value; + val += addend; + } + } else { + val = sym->st_value; + if (strncmp(strtab + sym->st_name, "$$", 2) == 0) val = get_stub(me, val, addend, ELF_STUB_MILLI, - in_init(me, loc)); + loc0, targetsec); else val = get_stub(me, val, addend, ELF_STUB_GOT, - in_init(me, loc)); + loc0, targetsec); } DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", strtab + sym->st_name, loc, sym->st_value, addend, val); - /* FIXME: local symbols work as long as the - * core and init pieces aren't separated too - * far. If this is ever broken, you will trip - * the check below. The way to fix it would - * be to generate local stubs to go between init - * and core */ - if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 || - (Elf64_Sxword)(val - dot - 8) < -0x800000) { - printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n", - me->name, strtab + sym->st_name); - return -ENOEXEC; - } val = (val - dot - 8)/4; + CHECK_RELOC(val, 22); *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); break; case R_PARISC_DIR64: @@ -794,12 +850,8 @@ int module_finalize(const Elf_Ehdr *hdr, addr = (u32 *)entry->addr; printk("INSNS: %x %x %x %x\n", addr[0], addr[1], addr[2], addr[3]); - printk("stubs used %ld, stubs max %ld\n" - "init_stubs used %ld, init stubs max %ld\n" - "got entries used %ld, gots max %ld\n" + printk("got entries used %ld, gots max %ld\n" "fdescs used %ld, fdescs max %ld\n", - me->arch.stub_count, me->arch.stub_max, - me->arch.init_stub_count, me->arch.init_stub_max, me->arch.got_count, me->arch.got_max, me->arch.fdesc_count, me->arch.fdesc_max); #endif @@ -829,7 +881,10 @@ int module_finalize(const Elf_Ehdr *hdr, me->name, me->arch.got_count, MAX_GOTS); return -EINVAL; } - + + kfree(me->arch.section); + me->arch.section = NULL; + /* no symbol table */ if(symhdr == NULL) return 0; ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2008-12-31 11:37 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-12-29 14:07 [PATCH] parisc: fix module loading failure of large kernel modules Helge Deller 2008-12-29 14:09 ` [PATCH 1/2] module.c: fix module loading failure of large modules Helge Deller 2008-12-29 14:10 ` [PATCH 2/2] parisc: " Helge Deller 2008-12-29 15:46 ` Sam Ravnborg 2008-12-29 16:09 ` Helge Deller 2008-12-29 16:14 ` John David Anglin 2008-12-29 17:56 ` Helge Deller -- strict thread matches above, loose matches on Subject: below -- 2008-12-29 20:34 [PATCH] parisc: fix module loading failure of large kernel modules (take 2) Helge Deller 2008-12-30 22:45 ` Rusty Russell 2008-12-31 11:31 ` [PATCH] parisc: fix module loading failure of large kernel modules (take 4) Helge Deller 2008-12-31 11:36 ` [PATCH 2/2] parisc: fix module loading failure of large modules Helge Deller
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox