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 #include <linux/jump_label.h> 28 29 #include <asm/system.h> 30 #include <asm/page.h> 31 #include <asm/pgtable.h> 32 33 #if 0 34 #define DEBUGP printk 35 #else 36 #define DEBUGP(fmt...) 37 #endif 38 39 void *module_alloc(unsigned long size) 40 { 41 if (PAGE_ALIGN(size) > MODULES_LEN) 42 return NULL; 43 return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, 44 GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC, 45 -1, __builtin_return_address(0)); 46 } 47 48 /* Free memory returned from module_alloc */ 49 void module_free(struct module *mod, void *module_region) 50 { 51 vfree(module_region); 52 } 53 54 /* We don't need anything special. */ 55 int module_frob_arch_sections(Elf_Ehdr *hdr, 56 Elf_Shdr *sechdrs, 57 char *secstrings, 58 struct module *mod) 59 { 60 return 0; 61 } 62 63 #ifdef CONFIG_X86_32 64 int apply_relocate(Elf32_Shdr *sechdrs, 65 const char *strtab, 66 unsigned int symindex, 67 unsigned int relsec, 68 struct module *me) 69 { 70 unsigned int i; 71 Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; 72 Elf32_Sym *sym; 73 uint32_t *location; 74 75 DEBUGP("Applying relocate section %u to %u\n", relsec, 76 sechdrs[relsec].sh_info); 77 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 78 /* This is where to make the change */ 79 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 80 + rel[i].r_offset; 81 /* This is the symbol it is referring to. Note that all 82 undefined symbols have been resolved. */ 83 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 84 + ELF32_R_SYM(rel[i].r_info); 85 86 switch (ELF32_R_TYPE(rel[i].r_info)) { 87 case R_386_32: 88 /* We add the value into the location given */ 89 *location += sym->st_value; 90 break; 91 case R_386_PC32: 92 /* Add the value, subtract its postition */ 93 *location += sym->st_value - (uint32_t)location; 94 break; 95 default: 96 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 97 me->name, ELF32_R_TYPE(rel[i].r_info)); 98 return -ENOEXEC; 99 } 100 } 101 return 0; 102 } 103 104 int apply_relocate_add(Elf32_Shdr *sechdrs, 105 const char *strtab, 106 unsigned int symindex, 107 unsigned int relsec, 108 struct module *me) 109 { 110 printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", 111 me->name); 112 return -ENOEXEC; 113 } 114 #else /*X86_64*/ 115 int apply_relocate_add(Elf64_Shdr *sechdrs, 116 const char *strtab, 117 unsigned int symindex, 118 unsigned int relsec, 119 struct module *me) 120 { 121 unsigned int i; 122 Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; 123 Elf64_Sym *sym; 124 void *loc; 125 u64 val; 126 127 DEBUGP("Applying relocate section %u to %u\n", relsec, 128 sechdrs[relsec].sh_info); 129 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 130 /* This is where to make the change */ 131 loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 132 + rel[i].r_offset; 133 134 /* This is the symbol it is referring to. Note that all 135 undefined symbols have been resolved. */ 136 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr 137 + ELF64_R_SYM(rel[i].r_info); 138 139 DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n", 140 (int)ELF64_R_TYPE(rel[i].r_info), 141 sym->st_value, rel[i].r_addend, (u64)loc); 142 143 val = sym->st_value + rel[i].r_addend; 144 145 switch (ELF64_R_TYPE(rel[i].r_info)) { 146 case R_X86_64_NONE: 147 break; 148 case R_X86_64_64: 149 *(u64 *)loc = val; 150 break; 151 case R_X86_64_32: 152 *(u32 *)loc = val; 153 if (val != *(u32 *)loc) 154 goto overflow; 155 break; 156 case R_X86_64_32S: 157 *(s32 *)loc = val; 158 if ((s64)val != *(s32 *)loc) 159 goto overflow; 160 break; 161 case R_X86_64_PC32: 162 val -= (u64)loc; 163 *(u32 *)loc = val; 164 #if 0 165 if ((s64)val != *(s32 *)loc) 166 goto overflow; 167 #endif 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 177 overflow: 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 185 int 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 197 int 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 /* make jump label nops */ 235 jump_label_apply_nops(me); 236 237 return 0; 238 } 239 240 void module_arch_cleanup(struct module *mod) 241 { 242 alternatives_smp_module_del(mod); 243 } 244