I have not done extensive tests yet, but this patch appears to be working. I'd appreciate people giving it a try and let me know how it goes. There is one worrisome "FIXME" in that file, which is not clear to me. Ralf? Jun
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 Fri Jul 25 15:49:23 2003 +++ linux/arch/mips/kernel/module-elf32.c Fri Jan 23 17:58:42 2004 @@ -23,14 +23,11 @@ #include <linux/string.h> #include <linux/kernel.h> -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 @@ -78,6 +75,35 @@ 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; +} + +#undef KERN_ERR +#define KERN_ERR + int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, @@ -85,19 +111,20 @@ 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); 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", @@ -116,99 +143,52 @@ 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: /* - * 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 mistmatch : %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", @@ -225,6 +205,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;