X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fparisc%2Fkernel%2Fmodule.c;h=f50b982b0834ce8b2598d1b66d1c06b5306ca29f;hb=refs%2Fremotes%2Fvserver;hp=1ac525b53b8c8a8411f11c81db31756f5e0bae4e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index 1ac525b53..f50b982b0 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c @@ -2,7 +2,7 @@ * * The best reference for this stuff is probably the Processor- * Specific ELF Supplement for PA-RISC: - * http://ftp.parisc-linux.org/docs/elf-pa-hp.pdf + * http://ftp.parisc-linux.org/docs/arch/elf-pa-hp.pdf * * Linux/PA-RISC Project (http://www.parisc-linux.org/) * Copyright (C) 2003 Randolph Chung @@ -21,6 +21,23 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Notes: + * - SEGREL32 handling + * We are not doing SEGREL32 handling correctly. According to the ABI, we + * should do a value offset, like this: + * if (in_init(me, (void *)val)) + * val -= (uint32_t)me->module_init; + * else + * val -= (uint32_t)me->module_core; + * However, SEGREL32 is used only for PARISC unwind entries, and we want + * those entries to have an absolute address, and not just an offset. + * + * The unwind table mechanism has the ability to specify an offset for + * the unwind table; however, because we split off the init functions into + * a different piece of memory, it is not possible to do this using a + * single offset. Instead, we use the above hack for now. */ #include @@ -30,6 +47,8 @@ #include #include +#include + #if 0 #define DEBUGP printk #else @@ -53,21 +72,27 @@ /* three functions to determine where in the module core * or init pieces the location is */ -static inline int is_init(struct module *me, void *loc) +static inline int in_init(struct module *me, void *loc) { return (loc >= me->module_init && loc <= (me->module_init + me->init_size)); } -static inline int is_core(struct module *me, void *loc) +static inline int in_core(struct module *me, void *loc) { return (loc >= me->module_core && loc <= (me->module_core + me->core_size)); } -static inline int is_local(struct module *me, void *loc) +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 is_init(me, loc) || is_core(me, loc); + return (in_init(me, loc) && in_init(me, dot)) || + (in_core(me, loc) && in_core(me, dot)); } @@ -248,6 +273,10 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); + if (strncmp(secstrings + sechdrs[i].sh_name, + ".PARISC.unwind", 14) == 0) + me->arch.unwind_section = i; + if (sechdrs[i].sh_type != SHT_RELA) continue; @@ -341,8 +370,14 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value) } #endif /* __LP64__ */ +enum elf_stub_type { + ELF_STUB_GOT, + ELF_STUB_MILLI, + ELF_STUB_DIRECT, +}; + static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, - int millicode, int init_section) + enum elf_stub_type stub_type, int init_section) { unsigned long i; struct stub_entry *stub; @@ -373,7 +408,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); #else -/* for 64-bit we have two kinds of stubs: +/* for 64-bit we have three kinds of stubs: * for normal function calls: * ldd 0(%dp),%dp * ldd 10(%dp), %r1 @@ -385,18 +420,23 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, * ldo 0(%r1), %r1 * ldd 10(%r1), %r1 * bve,n (%r1) + * + * for direct branches (jumps between different section of the + * same module): + * ldil 0, %r1 + * ldo 0(%r1), %r1 + * bve,n (%r1) */ - if (!millicode) - { + switch (stub_type) { + case ELF_STUB_GOT: stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */ stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */ stub->insns[2] = 0xe820d000; /* bve (%r1) */ stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */ stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff); - } - else - { + break; + case ELF_STUB_MILLI: stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */ @@ -404,7 +444,17 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, stub->insns[0] |= reassemble_21(lrsel(value, addend)); stub->insns[1] |= reassemble_14(rrsel(value, addend)); + break; + case ELF_STUB_DIRECT: + stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ + stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ + stub->insns[2] = 0xe820d002; /* bve,n (%r1) */ + + stub->insns[0] |= reassemble_21(lrsel(value, addend)); + stub->insns[1] |= reassemble_14(rrsel(value, addend)); + break; } + #endif return (Elf_Addr)stub; @@ -499,7 +549,9 @@ int apply_relocate_add(Elf_Shdr *sechdrs, break; case R_PARISC_SEGREL32: /* 32-bit segment relative address */ - val -= (uint32_t)me->module_core; + /* See note about special handling of SEGREL32 at + * the beginning of this file. + */ *loc = fsel(val, addend); break; case R_PARISC_DPREL21L: @@ -514,14 +566,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs, break; case R_PARISC_PCREL17F: /* 17-bit PC relative address */ - val = get_stub(me, val, addend, 0, is_init(me, loc)); + val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); 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, 0, is_init(me, loc)); + 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) @@ -618,14 +670,24 @@ int apply_relocate_add(Elf_Shdr *sechdrs, strtab + sym->st_name, loc, val); /* can we reach it locally? */ - if(!is_local(me, (void *)val)) { - if (strncmp(strtab + sym->st_name, "$$", 2) + 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) == 0) - val = get_stub(me, val, addend, 1, - is_init(me, loc)); + val = get_stub(me, val, addend, ELF_STUB_MILLI, + in_init(me, loc)); else - val = get_stub(me, val, addend, 0, - is_init(me, loc)); + val = get_stub(me, val, addend, ELF_STUB_GOT, + in_init(me, loc)); } DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", strtab + sym->st_name, loc, sym->st_value, @@ -651,12 +713,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs, break; case R_PARISC_SEGREL32: /* 32-bit segment relative address */ - val -= (uint64_t)me->module_core; + /* See note about special handling of SEGREL32 at + * the beginning of this file. + */ *loc = fsel(val, addend); break; case R_PARISC_FPTR64: /* 64-bit function address */ - if(is_local(me, (void *)(val + addend))) { + if(in_local(me, (void *)(val + addend))) { *loc64 = get_fdesc(me, val+addend); DEBUGP("FDESC for %s at %p points to %lx\n", strtab + sym->st_name, *loc64, @@ -682,6 +746,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs, } #endif +static void +register_unwind_table(struct module *me, + const Elf_Shdr *sechdrs) +{ + unsigned char *table, *end; + unsigned long gp; + + if (!me->arch.unwind_section) + return; + + table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr; + end = table + sechdrs[me->arch.unwind_section].sh_size; + gp = (Elf_Addr)me->module_core + me->arch.got_offset; + + DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n", + me->arch.unwind_section, table, end, gp); + me->arch.unwind = unwind_table_add(me->name, 0, gp, table, end); +} + +static void +deregister_unwind_table(struct module *me) +{ + if (me->arch.unwind) + unwind_table_remove(me->arch.unwind); +} + int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *me) @@ -711,6 +801,8 @@ int module_finalize(const Elf_Ehdr *hdr, me->arch.fdesc_count, me->arch.fdesc_max); #endif + register_unwind_table(me, sechdrs); + /* haven't filled in me->symtab yet, so have to find it * ourselves */ for (i = 1; i < hdr->e_shnum; i++) { @@ -763,4 +855,5 @@ int module_finalize(const Elf_Ehdr *hdr, void module_arch_cleanup(struct module *mod) { + deregister_unwind_table(mod); }