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