/* * arch/v850/kernel/module.c -- Architecture-specific module functions * * Copyright (C) 2002,03 NEC Electronics Corporation * Copyright (C) 2002,03 Miles Bader * Copyright (C) 2001,03 Rusty Russell * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. * * Written by Miles Bader * * Derived in part from arch/ppc/kernel/module.c */ #include #include #include #include #if 0 #define DEBUGP printk #else #define DEBUGP(fmt , ...) #endif void *module_alloc (unsigned long size) { return size == 0 ? 0 : vmalloc (size); } void module_free (struct module *mod, void *module_region) { vfree (module_region); /* FIXME: If module_region == mod->init_region, trim exception table entries. */ } int module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) { return 0; } /* Count how many different relocations (different symbol, different addend) */ static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) { unsigned int i, j, ret = 0; /* Sure, this is order(n^2), but it's usually short, and not time critical */ for (i = 0; i < num; i++) { for (j = 0; j < i; j++) { /* If this addend appeared before, it's already been counted */ if (ELF32_R_SYM(rela[i].r_info) == ELF32_R_SYM(rela[j].r_info) && rela[i].r_addend == rela[j].r_addend) break; } if (j == i) ret++; } return ret; } /* 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 */ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) != is_init) continue; if (sechdrs[i].sh_type == SHT_RELA) { DEBUGP("Found relocations in section %u\n", i); DEBUGP("Ptr: %p. Number: %u\n", (void *)hdr + sechdrs[i].sh_offset, sechdrs[i].sh_size / sizeof(Elf32_Rela)); ret += count_relocs((void *)hdr + sechdrs[i].sh_offset, sechdrs[i].sh_size / sizeof(Elf32_Rela)) * sizeof(struct v850_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 .pltinit 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) { printk("Module doesn't contain .plt or .plt.init 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; } int apply_relocate (Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *mod) { printk ("Barf\n"); return -ENOEXEC; } /* 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, Elf32_Shdr *sechdrs, struct module *mod) { struct v850_plt_entry *entry; /* Instructions used to do the indirect jump. */ uint32_t tramp[2]; /* We have to trash a register, so we assume that any control transfer more than 21-bits away must be a function call (so we can use a call-clobbered register). */ tramp[0] = 0x0621 + ((val & 0xffff) << 16); /* mov sym, r1 ... */ tramp[1] = ((val >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ /* Init, or core PLT? */ if (location >= mod->module_core && location < mod->module_core + mod->core_size) 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->tramp[0]) if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1]) return (uint32_t)entry; else entry++; entry->tramp[0] = tramp[0]; entry->tramp[1] = tramp[1]; return (uint32_t)entry; } int apply_relocate_add (Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *mod) { unsigned int i; Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; DEBUGP ("Applying 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 */ uint32_t *loc = ((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. */ Elf32_Sym *sym = ((Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM (rela[i].r_info)); uint32_t val = sym->st_value + rela[i].r_addend; switch (ELF32_R_TYPE (rela[i].r_info)) { case R_V850_32: /* We write two shorts instead of a long because even 32-bit insns only need half-word alignment, but 32-bit data writes need to be long-word aligned. */ val += ((uint16_t *)loc)[0]; val += ((uint16_t *)loc)[1] << 16; ((uint16_t *)loc)[0] = val & 0xffff; ((uint16_t *)loc)[1] = (val >> 16) & 0xffff; break; case R_V850_22_PCREL: /* Maybe jump indirectly via a PLT table entry. */ if ((int32_t)(val - (uint32_t)loc) > 0x1fffff || (int32_t)(val - (uint32_t)loc) < -0x200000) val = do_plt_call (loc, val, sechdrs, mod); val -= (uint32_t)loc; /* We write two shorts instead of a long because even 32-bit insns only need half-word alignment, but 32-bit data writes need to be long-word aligned. */ ((uint16_t *)loc)[0] = (*(uint16_t *)loc & 0xffc0) /* opcode + reg */ | ((val >> 16) & 0xffc03f); /* offs high */ ((uint16_t *)loc)[1] = (val & 0xffff); /* offs low */ break; default: printk (KERN_ERR "module %s: Unknown reloc: %u\n", mod->name, ELF32_R_TYPE (rela[i].r_info)); return -ENOEXEC; } } return 0; } void module_arch_cleanup(struct module *mod) { }