1/* 2 * AVR32-specific kernel module loader 3 * 4 * Copyright (C) 2005-2006 Atmel Corporation 5 * 6 * GOT initialization parts are based on the s390 version 7 * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH, 8 * IBM Corporation 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15#include <linux/bug.h> 16#include <linux/elf.h> 17#include <linux/kernel.h> 18#include <linux/module.h> 19#include <linux/moduleloader.h> 20#include <linux/vmalloc.h> 21 22void module_free(struct module *mod, void *module_region) 23{ 24 vfree(mod->arch.syminfo); 25 mod->arch.syminfo = NULL; 26 27 vfree(module_region); 28} 29 30static inline int check_rela(Elf32_Rela *rela, struct module *module, 31 char *strings, Elf32_Sym *symbols) 32{ 33 struct mod_arch_syminfo *info; 34 35 info = module->arch.syminfo + ELF32_R_SYM(rela->r_info); 36 switch (ELF32_R_TYPE(rela->r_info)) { 37 case R_AVR32_GOT32: 38 case R_AVR32_GOT16: 39 case R_AVR32_GOT8: 40 case R_AVR32_GOT21S: 41 case R_AVR32_GOT18SW: /* mcall */ 42 case R_AVR32_GOT16S: /* ld.w */ 43 if (rela->r_addend != 0) { 44 printk(KERN_ERR 45 "GOT relocation against %s at offset %u with addend\n", 46 strings + symbols[ELF32_R_SYM(rela->r_info)].st_name, 47 rela->r_offset); 48 return -ENOEXEC; 49 } 50 if (info->got_offset == -1UL) { 51 info->got_offset = module->arch.got_size; 52 module->arch.got_size += sizeof(void *); 53 } 54 pr_debug("GOT[%3lu] %s\n", info->got_offset, 55 strings + symbols[ELF32_R_SYM(rela->r_info)].st_name); 56 break; 57 } 58 59 return 0; 60} 61 62int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, 63 char *secstrings, struct module *module) 64{ 65 Elf32_Shdr *symtab; 66 Elf32_Sym *symbols; 67 Elf32_Rela *rela; 68 char *strings; 69 int nrela, i, j; 70 int ret; 71 72 /* Find the symbol table */ 73 symtab = NULL; 74 for (i = 0; i < hdr->e_shnum; i++) 75 switch (sechdrs[i].sh_type) { 76 case SHT_SYMTAB: 77 symtab = &sechdrs[i]; 78 break; 79 } 80 if (!symtab) { 81 printk(KERN_ERR "module %s: no symbol table\n", module->name); 82 return -ENOEXEC; 83 } 84 85 /* Allocate room for one syminfo structure per symbol. */ 86 module->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym); 87 module->arch.syminfo = vmalloc(module->arch.nsyms 88 * sizeof(struct mod_arch_syminfo)); 89 if (!module->arch.syminfo) 90 return -ENOMEM; 91 92 symbols = (void *)hdr + symtab->sh_offset; 93 strings = (void *)hdr + sechdrs[symtab->sh_link].sh_offset; 94 for (i = 0; i < module->arch.nsyms; i++) { 95 if (symbols[i].st_shndx == SHN_UNDEF && 96 strcmp(strings + symbols[i].st_name, 97 "_GLOBAL_OFFSET_TABLE_") == 0) 98 /* "Define" it as absolute. */ 99 symbols[i].st_shndx = SHN_ABS; 100 module->arch.syminfo[i].got_offset = -1UL; 101 module->arch.syminfo[i].got_initialized = 0; 102 } 103 104 /* Allocate GOT entries for symbols that need it. */ 105 module->arch.got_size = 0; 106 for (i = 0; i < hdr->e_shnum; i++) { 107 if (sechdrs[i].sh_type != SHT_RELA) 108 continue; 109 nrela = sechdrs[i].sh_size / sizeof(Elf32_Rela); 110 rela = (void *)hdr + sechdrs[i].sh_offset; 111 for (j = 0; j < nrela; j++) { 112 ret = check_rela(rela + j, module, 113 strings, symbols); 114 if (ret) 115 goto out_free_syminfo; 116 } 117 } 118 119 /* 120 * Increase core size to make room for GOT and set start 121 * offset for GOT. 122 */ 123 module->core_size = ALIGN(module->core_size, 4); 124 module->arch.got_offset = module->core_size; 125 module->core_size += module->arch.got_size; 126 127 return 0; 128 129out_free_syminfo: 130 vfree(module->arch.syminfo); 131 module->arch.syminfo = NULL; 132 133 return ret; 134} 135 136static inline int reloc_overflow(struct module *module, const char *reloc_name, 137 Elf32_Addr relocation) 138{ 139 printk(KERN_ERR "module %s: Value %lx does not fit relocation %s\n", 140 module->name, (unsigned long)relocation, reloc_name); 141 return -ENOEXEC; 142} 143 144#define get_u16(loc) (*((uint16_t *)loc)) 145#define put_u16(loc, val) (*((uint16_t *)loc) = (val)) 146 147int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, 148 unsigned int symindex, unsigned int relindex, 149 struct module *module) 150{ 151 Elf32_Shdr *symsec = sechdrs + symindex; 152 Elf32_Shdr *relsec = sechdrs + relindex; 153 Elf32_Shdr *dstsec = sechdrs + relsec->sh_info; 154 Elf32_Rela *rel = (void *)relsec->sh_addr; 155 unsigned int i; 156 int ret = 0; 157 158 for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rela); i++, rel++) { 159 struct mod_arch_syminfo *info; 160 Elf32_Sym *sym; 161 Elf32_Addr relocation; 162 uint32_t *location; 163 uint32_t value; 164 165 location = (void *)dstsec->sh_addr + rel->r_offset; 166 sym = (Elf32_Sym *)symsec->sh_addr + ELF32_R_SYM(rel->r_info); 167 relocation = sym->st_value + rel->r_addend; 168 169 info = module->arch.syminfo + ELF32_R_SYM(rel->r_info); 170 171 /* Initialize GOT entry if necessary */ 172 switch (ELF32_R_TYPE(rel->r_info)) { 173 case R_AVR32_GOT32: 174 case R_AVR32_GOT16: 175 case R_AVR32_GOT8: 176 case R_AVR32_GOT21S: 177 case R_AVR32_GOT18SW: 178 case R_AVR32_GOT16S: 179 if (!info->got_initialized) { 180 Elf32_Addr *gotent; 181 182 gotent = (module->module_core 183 + module->arch.got_offset 184 + info->got_offset); 185 *gotent = relocation; 186 info->got_initialized = 1; 187 } 188 189 relocation = info->got_offset; 190 break; 191 } 192 193 switch (ELF32_R_TYPE(rel->r_info)) { 194 case R_AVR32_32: 195 case R_AVR32_32_CPENT: 196 *location = relocation; 197 break; 198 case R_AVR32_22H_PCREL: 199 relocation -= (Elf32_Addr)location; 200 if ((relocation & 0xffe00001) != 0 201 && (relocation & 0xffc00001) != 0xffc00000) 202 return reloc_overflow(module, 203 "R_AVR32_22H_PCREL", 204 relocation); 205 relocation >>= 1; 206 207 value = *location; 208 value = ((value & 0xe1ef0000) 209 | (relocation & 0xffff) 210 | ((relocation & 0x10000) << 4) 211 | ((relocation & 0x1e0000) << 8)); 212 *location = value; 213 break; 214 case R_AVR32_11H_PCREL: 215 relocation -= (Elf32_Addr)location; 216 if ((relocation & 0xfffffc01) != 0 217 && (relocation & 0xfffff801) != 0xfffff800) 218 return reloc_overflow(module, 219 "R_AVR32_11H_PCREL", 220 relocation); 221 value = get_u16(location); 222 value = ((value & 0xf00c) 223 | ((relocation & 0x1fe) << 3) 224 | ((relocation & 0x600) >> 9)); 225 put_u16(location, value); 226 break; 227 case R_AVR32_9H_PCREL: 228 relocation -= (Elf32_Addr)location; 229 if ((relocation & 0xffffff01) != 0 230 && (relocation & 0xfffffe01) != 0xfffffe00) 231 return reloc_overflow(module, 232 "R_AVR32_9H_PCREL", 233 relocation); 234 value = get_u16(location); 235 value = ((value & 0xf00f) 236 | ((relocation & 0x1fe) << 3)); 237 put_u16(location, value); 238 break; 239 case R_AVR32_9UW_PCREL: 240 relocation -= ((Elf32_Addr)location) & 0xfffffffc; 241 if ((relocation & 0xfffffc03) != 0) 242 return reloc_overflow(module, 243 "R_AVR32_9UW_PCREL", 244 relocation); 245 value = get_u16(location); 246 value = ((value & 0xf80f) 247 | ((relocation & 0x1fc) << 2)); 248 put_u16(location, value); 249 break; 250 case R_AVR32_GOTPC: 251 /* 252 * R6 = PC - (PC - GOT) 253 * 254 * At this point, relocation contains the 255 * value of PC. Just subtract the value of 256 * GOT, and we're done. 257 */ 258 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n", 259 relocation, module->arch.got_offset, 260 module->module_core); 261 relocation -= ((unsigned long)module->module_core 262 + module->arch.got_offset); 263 *location = relocation; 264 break; 265 case R_AVR32_GOT18SW: 266 if ((relocation & 0xfffe0003) != 0 267 && (relocation & 0xfffc0000) != 0xfffc0000) 268 return reloc_overflow(module, "R_AVR32_GOT18SW", 269 relocation); 270 relocation >>= 2; 271 /* fall through */ 272 case R_AVR32_GOT16S: 273 if ((relocation & 0xffff8000) != 0 274 && (relocation & 0xffff0000) != 0xffff0000) 275 return reloc_overflow(module, "R_AVR32_GOT16S", 276 relocation); 277 pr_debug("GOT reloc @ 0x%x -> %u\n", 278 rel->r_offset, relocation); 279 value = *location; 280 value = ((value & 0xffff0000) 281 | (relocation & 0xffff)); 282 *location = value; 283 break; 284 285 default: 286 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 287 module->name, ELF32_R_TYPE(rel->r_info)); 288 return -ENOEXEC; 289 } 290 } 291 292 return ret; 293} 294 295int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 296 struct module *module) 297{ 298 vfree(module->arch.syminfo); 299 module->arch.syminfo = NULL; 300 301 return 0; 302} 303