diff -Nru linux/arch/mips/kernel/module-elf32.c.orig linux/arch/mips/kernel/module-elf32.c --- linux/arch/mips/kernel/module-elf32.c.orig Mon Jan 26 15:44:18 2004 +++ linux/arch/mips/kernel/module-elf32.c Tue Jan 27 15:39:05 2004 @@ -23,14 +23,11 @@ #include #include -struct mips_hi16 { - struct mips_hi16 *next; +struct mips_hilo16 { Elf32_Addr *addr; Elf32_Addr value; }; -static struct mips_hi16 *mips_hi16_list; - #if 0 #define DEBUGP printk #else @@ -53,6 +50,23 @@ table entries. */ } +/* We don't need anything special. */ +long module_core_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->core_size; +} + +long module_init_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->init_size; +} + int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, char *secstrings, @@ -61,6 +75,39 @@ return 0; } +/* + * For the hi16, we should set ((AHL+S) - (short)(AHL+S)) >> 16 + * For the lo16, we should set (AHL+S) & 0xffff + * where + * AHL = (AHI << 16 ) + (short)ALO + * whereis + * AHI = *(hi16 instr loc) & 0xffff // before reloc + * ALO = *(lo16 instr loc) & 0xffff // before reloc + */ +#define U32_TO_SHORT(x) (((x & 0xffff) ^ 0x8000) - 0x8000) +static void relocate_hilo16(struct mips_hilo16 *hi, struct mips_hilo16 *lo) +{ + u32 AHI, ALO, AHL_S, res_hi, res_lo; + + AHI = *(hi->addr) & 0xffff; + ALO = *(lo->addr) & 0xffff; + AHL_S = (AHI << 16) + U32_TO_SHORT(ALO) + hi->value; + + res_lo = AHL_S & 0xffff; + res_hi = (AHL_S >> 16) + ((res_lo & 0x8000)==0 ? 0 : 1); + res_hi &= 0xffff; + + *hi->addr = (*hi->addr & ~0xffff) | res_hi; + *lo->addr = (*lo->addr & ~0xffff) | res_lo; + + /* clear hi entry to mark it is used */ + hi->addr = 0; + hi->value = 0; +} + +#undef KERN_ERR +#define KERN_ERR + int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, @@ -68,19 +115,22 @@ struct module *me) { unsigned int i; - Elf32_Rel *rel = (void *)sechdrs[relsec].sh_offset; + Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; Elf32_Sym *sym; uint32_t *location; Elf32_Addr v; + struct mips_hilo16 hi16, lo16; DEBUGP("Applying relocate section %u to %u\n", relsec, sechdrs[relsec].sh_info); + hi16.addr = 0; + hi16.value = 0; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ - location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* This is the symbol it is referring to */ - sym = (Elf32_Sym *)sechdrs[symindex].sh_offset + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM(rel[i].r_info); if (!sym->st_value) { printk(KERN_WARNING "%s: Unknown symbol %s\n", @@ -99,99 +149,60 @@ break; case R_MIPS_26: - if (v % 4) + if (v % 4) { printk(KERN_ERR "module %s: dangerous relocation\n", me->name); return -ENOEXEC; + } if ((v & 0xf0000000) != - (((unsigned long)location + 4) & 0xf0000000)) + (((unsigned long)location + 4) & 0xf0000000)) { printk(KERN_ERR "module %s: relocation overflow\n", me->name); return -ENOEXEC; + } *location = (*location & ~0x03ffffff) | ((*location + (v >> 2)) & 0x03ffffff); break; - case R_MIPS_HI16: { - struct mips_hi16 *n; - + case R_MIPS_HI16: /* * We cannot relocate this one now because we don't * know the value of the carry we need to add. Save * the information, and let LO16 do the actual * relocation. */ - n = (struct mips_hi16 *) kmalloc(sizeof *n, GFP_KERNEL); - n->addr = location; - n->value = v; - n->next = mips_hi16_list; - mips_hi16_list = n; + hi16.addr = location; + hi16.value = v; break; - } - - case R_MIPS_LO16: { - unsigned long insnlo = *location; - Elf32_Addr val, vallo; - - /* Sign extend the addend we extract from the lo insn. */ - vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; - - if (mips_hi16_list != NULL) { - struct mips_hi16 *l; - - l = mips_hi16_list; - while (l != NULL) { - struct mips_hi16 *next; - unsigned long insn; - - /* - * The value for the HI16 had best be - * the same. - */ - printk(KERN_ERR "module %s: dangerous " - "relocation\n", me->name); - return -ENOEXEC; - - /* - * Do the HI16 relocation. Note that - * we actually don't need to know - * anything about the LO16 itself, - * except where to find the low 16 bits - * of the addend needed by the LO16. - */ - insn = *l->addr; - val = ((insn & 0xffff) << 16) + vallo; - val += v; - - /* - * Account for the sign extension that - * will happen in the low bits. - */ - val = ((val >> 16) + ((val & 0x8000) != - 0)) & 0xffff; - - insn = (insn & ~0xffff) | val; - *l->addr = insn; - - next = l->next; - kfree(l); - l = next; - } - mips_hi16_list = NULL; + case R_MIPS_LO16: + /* + * If there is no valid hi16 entry, we can't handle it + */ + if (hi16.addr == 0 && hi16.value == 0) { + printk(KERN_ERR "module %s: Can't handle orphan LO16 entry for location 0x%p\n", + me->name, + location); } - /* - * Ok, we're done with the HI16 relocs. Now deal with - * the LO16. + * This lo16 entry must have the same value as + * the preceeding or last hi16 entry. */ - val = v + vallo; - insnlo = (insnlo & ~0xffff) | (val & 0xffff); - *location = insnlo; + if (v != hi16.value) { + printk(KERN_ERR "module %s: HI16/LO16 value mismatch : %08x vs %08x\n", + me->name, + hi16.value, + v); + return -ENOEXEC; + } + + lo16.addr = location; + lo16.value = v; + + relocate_hilo16(&hi16, &lo16); break; - } default: printk(KERN_ERR "module %s: Unknown relocation: %u\n", @@ -208,6 +219,9 @@ unsigned int relsec, struct module *me) { + if (sechdrs[relsec].sh_size == 0) + return 0; + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", me->name); return -ENOEXEC;