1/* Kernel module help for x86. 2 Copyright (C) 2001 Rusty Russell. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17*/ 18#include <linux/moduleloader.h> 19#include <linux/elf.h> 20#include <linux/vmalloc.h> 21#include <linux/fs.h> 22#include <linux/string.h> 23#include <linux/kernel.h> 24#include <linux/bug.h> 25#include <linux/mm.h> 26#include <linux/gfp.h> 27 28#include <asm/system.h> 29#include <asm/page.h> 30#include <asm/pgtable.h> 31 32#define DEBUGP(fmt...) 33 34void *module_alloc(unsigned long size) 35{ 36 struct vm_struct *area; 37 38 if (!size) 39 return NULL; 40 size = PAGE_ALIGN(size); 41 if (size > MODULES_LEN) 42 return NULL; 43 44 area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END); 45 if (!area) 46 return NULL; 47 48 return __vmalloc_area(area, GFP_KERNEL | __GFP_HIGHMEM, 49 PAGE_KERNEL_EXEC); 50} 51 52/* Free memory returned from module_alloc */ 53void module_free(struct module *mod, void *module_region) 54{ 55 vfree(module_region); 56} 57 58/* We don't need anything special. */ 59int module_frob_arch_sections(Elf_Ehdr *hdr, 60 Elf_Shdr *sechdrs, 61 char *secstrings, 62 struct module *mod) 63{ 64 return 0; 65} 66 67#ifdef CONFIG_X86_32 68int apply_relocate(Elf32_Shdr *sechdrs, 69 const char *strtab, 70 unsigned int symindex, 71 unsigned int relsec, 72 struct module *me) 73{ 74 unsigned int i; 75 Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; 76 Elf32_Sym *sym; 77 uint32_t *location; 78 79 DEBUGP("Applying relocate section %u to %u\n", relsec, 80 sechdrs[relsec].sh_info); 81 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 82 /* This is where to make the change */ 83 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 84 + rel[i].r_offset; 85 /* This is the symbol it is referring to. Note that all 86 undefined symbols have been resolved. */ 87 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 88 + ELF32_R_SYM(rel[i].r_info); 89 90 switch (ELF32_R_TYPE(rel[i].r_info)) { 91 case R_386_32: 92 /* We add the value into the location given */ 93 *location += sym->st_value; 94 break; 95 case R_386_PC32: 96 /* Add the value, subtract its postition */ 97 *location += sym->st_value - (uint32_t)location; 98 break; 99 default: 100 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 101 me->name, ELF32_R_TYPE(rel[i].r_info)); 102 return -ENOEXEC; 103 } 104 } 105 return 0; 106} 107 108int apply_relocate_add(Elf32_Shdr *sechdrs, 109 const char *strtab, 110 unsigned int symindex, 111 unsigned int relsec, 112 struct module *me) 113{ 114 printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", 115 me->name); 116 return -ENOEXEC; 117} 118#else /*X86_64*/ 119int apply_relocate_add(Elf64_Shdr *sechdrs, 120 const char *strtab, 121 unsigned int symindex, 122 unsigned int relsec, 123 struct module *me) 124{ 125 unsigned int i; 126 Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; 127 Elf64_Sym *sym; 128 void *loc; 129 u64 val; 130 131 DEBUGP("Applying relocate section %u to %u\n", relsec, 132 sechdrs[relsec].sh_info); 133 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 134 /* This is where to make the change */ 135 loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 136 + rel[i].r_offset; 137 138 /* This is the symbol it is referring to. Note that all 139 undefined symbols have been resolved. */ 140 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr 141 + ELF64_R_SYM(rel[i].r_info); 142 143 DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n", 144 (int)ELF64_R_TYPE(rel[i].r_info), 145 sym->st_value, rel[i].r_addend, (u64)loc); 146 147 val = sym->st_value + rel[i].r_addend; 148 149 switch (ELF64_R_TYPE(rel[i].r_info)) { 150 case R_X86_64_NONE: 151 break; 152 case R_X86_64_64: 153 *(u64 *)loc = val; 154 break; 155 case R_X86_64_32: 156 *(u32 *)loc = val; 157 if (val != *(u32 *)loc) 158 goto overflow; 159 break; 160 case R_X86_64_32S: 161 *(s32 *)loc = val; 162 if ((s64)val != *(s32 *)loc) 163 goto overflow; 164 break; 165 case R_X86_64_PC32: 166 val -= (u64)loc; 167 *(u32 *)loc = val; 168 break; 169 default: 170 printk(KERN_ERR "module %s: Unknown rela relocation: %llu\n", 171 me->name, ELF64_R_TYPE(rel[i].r_info)); 172 return -ENOEXEC; 173 } 174 } 175 return 0; 176 177overflow: 178 printk(KERN_ERR "overflow in relocation type %d val %Lx\n", 179 (int)ELF64_R_TYPE(rel[i].r_info), val); 180 printk(KERN_ERR "`%s' likely not compiled with -mcmodel=kernel\n", 181 me->name); 182 return -ENOEXEC; 183} 184 185int apply_relocate(Elf_Shdr *sechdrs, 186 const char *strtab, 187 unsigned int symindex, 188 unsigned int relsec, 189 struct module *me) 190{ 191 printk(KERN_ERR "non add relocation not supported\n"); 192 return -ENOSYS; 193} 194 195#endif 196 197int module_finalize(const Elf_Ehdr *hdr, 198 const Elf_Shdr *sechdrs, 199 struct module *me) 200{ 201 const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL, 202 *para = NULL; 203 char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 204 205 for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 206 if (!strcmp(".text", secstrings + s->sh_name)) 207 text = s; 208 if (!strcmp(".altinstructions", secstrings + s->sh_name)) 209 alt = s; 210 if (!strcmp(".smp_locks", secstrings + s->sh_name)) 211 locks = s; 212 if (!strcmp(".parainstructions", secstrings + s->sh_name)) 213 para = s; 214 } 215 216 if (alt) { 217 /* patch .altinstructions */ 218 void *aseg = (void *)alt->sh_addr; 219 apply_alternatives(aseg, aseg + alt->sh_size); 220 } 221 if (locks && text) { 222 void *lseg = (void *)locks->sh_addr; 223 void *tseg = (void *)text->sh_addr; 224 alternatives_smp_module_add(me, me->name, 225 lseg, lseg + locks->sh_size, 226 tseg, tseg + text->sh_size); 227 } 228 229 if (para) { 230 void *pseg = (void *)para->sh_addr; 231 apply_paravirt(pseg, pseg + para->sh_size); 232 } 233 234 return 0; 235} 236 237void module_arch_cleanup(struct module *mod) 238{ 239 alternatives_smp_module_del(mod); 240} 241