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 #if 0 33 #define DEBUGP printk 34 #else 35 #define DEBUGP(fmt...) 36 #endif 37 38 void *module_alloc(unsigned long size) 39 { 40 struct vm_struct *area; 41 42 if (!size) 43 return NULL; 44 size = PAGE_ALIGN(size); 45 if (size > MODULES_LEN) 46 return NULL; 47 48 area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END); 49 if (!area) 50 return NULL; 51 52 return __vmalloc_area(area, GFP_KERNEL | __GFP_HIGHMEM, 53 PAGE_KERNEL_EXEC); 54 } 55 56 /* Free memory returned from module_alloc */ 57 void module_free(struct module *mod, void *module_region) 58 { 59 vfree(module_region); 60 } 61 62 /* We don't need anything special. */ 63 int module_frob_arch_sections(Elf_Ehdr *hdr, 64 Elf_Shdr *sechdrs, 65 char *secstrings, 66 struct module *mod) 67 { 68 return 0; 69 } 70 71 #ifdef CONFIG_X86_32 72 int apply_relocate(Elf32_Shdr *sechdrs, 73 const char *strtab, 74 unsigned int symindex, 75 unsigned int relsec, 76 struct module *me) 77 { 78 unsigned int i; 79 Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; 80 Elf32_Sym *sym; 81 uint32_t *location; 82 83 DEBUGP("Applying relocate section %u to %u\n", relsec, 84 sechdrs[relsec].sh_info); 85 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 86 /* This is where to make the change */ 87 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 88 + rel[i].r_offset; 89 /* This is the symbol it is referring to. Note that all 90 undefined symbols have been resolved. */ 91 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 92 + ELF32_R_SYM(rel[i].r_info); 93 94 switch (ELF32_R_TYPE(rel[i].r_info)) { 95 case R_386_32: 96 /* We add the value into the location given */ 97 *location += sym->st_value; 98 break; 99 case R_386_PC32: 100 /* Add the value, subtract its postition */ 101 *location += sym->st_value - (uint32_t)location; 102 break; 103 default: 104 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 105 me->name, ELF32_R_TYPE(rel[i].r_info)); 106 return -ENOEXEC; 107 } 108 } 109 return 0; 110 } 111 112 int apply_relocate_add(Elf32_Shdr *sechdrs, 113 const char *strtab, 114 unsigned int symindex, 115 unsigned int relsec, 116 struct module *me) 117 { 118 printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", 119 me->name); 120 return -ENOEXEC; 121 } 122 #else /*X86_64*/ 123 int apply_relocate_add(Elf64_Shdr *sechdrs, 124 const char *strtab, 125 unsigned int symindex, 126 unsigned int relsec, 127 struct module *me) 128 { 129 unsigned int i; 130 Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; 131 Elf64_Sym *sym; 132 void *loc; 133 u64 val; 134 135 DEBUGP("Applying relocate section %u to %u\n", relsec, 136 sechdrs[relsec].sh_info); 137 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 138 /* This is where to make the change */ 139 loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 140 + rel[i].r_offset; 141 142 /* This is the symbol it is referring to. Note that all 143 undefined symbols have been resolved. */ 144 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr 145 + ELF64_R_SYM(rel[i].r_info); 146 147 DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n", 148 (int)ELF64_R_TYPE(rel[i].r_info), 149 sym->st_value, rel[i].r_addend, (u64)loc); 150 151 val = sym->st_value + rel[i].r_addend; 152 153 switch (ELF64_R_TYPE(rel[i].r_info)) { 154 case R_X86_64_NONE: 155 break; 156 case R_X86_64_64: 157 *(u64 *)loc = val; 158 break; 159 case R_X86_64_32: 160 *(u32 *)loc = val; 161 if (val != *(u32 *)loc) 162 goto overflow; 163 break; 164 case R_X86_64_32S: 165 *(s32 *)loc = val; 166 if ((s64)val != *(s32 *)loc) 167 goto overflow; 168 break; 169 case R_X86_64_PC32: 170 val -= (u64)loc; 171 *(u32 *)loc = val; 172 #if 0 173 if ((s64)val != *(s32 *)loc) 174 goto overflow; 175 #endif 176 break; 177 default: 178 printk(KERN_ERR "module %s: Unknown rela relocation: %llu\n", 179 me->name, ELF64_R_TYPE(rel[i].r_info)); 180 return -ENOEXEC; 181 } 182 } 183 return 0; 184 185 overflow: 186 printk(KERN_ERR "overflow in relocation type %d val %Lx\n", 187 (int)ELF64_R_TYPE(rel[i].r_info), val); 188 printk(KERN_ERR "`%s' likely not compiled with -mcmodel=kernel\n", 189 me->name); 190 return -ENOEXEC; 191 } 192 193 int apply_relocate(Elf_Shdr *sechdrs, 194 const char *strtab, 195 unsigned int symindex, 196 unsigned int relsec, 197 struct module *me) 198 { 199 printk(KERN_ERR "non add relocation not supported\n"); 200 return -ENOSYS; 201 } 202 203 #endif 204 205 int module_finalize(const Elf_Ehdr *hdr, 206 const Elf_Shdr *sechdrs, 207 struct module *me) 208 { 209 const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL, 210 *para = NULL; 211 char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 212 213 for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 214 if (!strcmp(".text", secstrings + s->sh_name)) 215 text = s; 216 if (!strcmp(".altinstructions", secstrings + s->sh_name)) 217 alt = s; 218 if (!strcmp(".smp_locks", secstrings + s->sh_name)) 219 locks = s; 220 if (!strcmp(".parainstructions", secstrings + s->sh_name)) 221 para = s; 222 } 223 224 if (alt) { 225 /* patch .altinstructions */ 226 void *aseg = (void *)alt->sh_addr; 227 apply_alternatives(aseg, aseg + alt->sh_size); 228 } 229 if (locks && text) { 230 void *lseg = (void *)locks->sh_addr; 231 void *tseg = (void *)text->sh_addr; 232 alternatives_smp_module_add(me, me->name, 233 lseg, lseg + locks->sh_size, 234 tseg, tseg + text->sh_size); 235 } 236 237 if (para) { 238 void *pseg = (void *)para->sh_addr; 239 apply_paravirt(pseg, pseg + para->sh_size); 240 } 241 242 return module_bug_finalize(hdr, sechdrs, me); 243 } 244 245 void module_arch_cleanup(struct module *mod) 246 { 247 alternatives_smp_module_del(mod); 248 module_bug_cleanup(mod); 249 } 250