1/* Kernel module help for x86-64 2 Copyright (C) 2001 Rusty Russell. 3 Copyright (C) 2002,2003 Andi Kleen, SuSE Labs. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18*/ 19#include <linux/moduleloader.h> 20#include <linux/elf.h> 21#include <linux/vmalloc.h> 22#include <linux/fs.h> 23#include <linux/string.h> 24#include <linux/kernel.h> 25#include <linux/slab.h> 26#include <linux/bug.h> 27 28#include <asm/system.h> 29#include <asm/page.h> 30#include <asm/pgtable.h> 31 32#define DEBUGP(fmt...) 33 34#ifndef CONFIG_UML 35void module_free(struct module *mod, void *module_region) 36{ 37 vfree(module_region); 38} 39 40void *module_alloc(unsigned long size) 41{ 42 struct vm_struct *area; 43 44 if (!size) 45 return NULL; 46 size = PAGE_ALIGN(size); 47 if (size > MODULES_LEN) 48 return NULL; 49 50 area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END); 51 if (!area) 52 return NULL; 53 54 return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC); 55} 56#endif 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 67int apply_relocate_add(Elf64_Shdr *sechdrs, 68 const char *strtab, 69 unsigned int symindex, 70 unsigned int relsec, 71 struct module *me) 72{ 73 unsigned int i; 74 Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; 75 Elf64_Sym *sym; 76 void *loc; 77 u64 val; 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 loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 84 + rel[i].r_offset; 85 86 /* This is the symbol it is referring to. Note that all 87 undefined symbols have been resolved. */ 88 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr 89 + ELF64_R_SYM(rel[i].r_info); 90 91 DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n", 92 (int)ELF64_R_TYPE(rel[i].r_info), 93 sym->st_value, rel[i].r_addend, (u64)loc); 94 95 val = sym->st_value + rel[i].r_addend; 96 97 switch (ELF64_R_TYPE(rel[i].r_info)) { 98 case R_X86_64_NONE: 99 break; 100 case R_X86_64_64: 101 *(u64 *)loc = val; 102 break; 103 case R_X86_64_32: 104 *(u32 *)loc = val; 105 if (val != *(u32 *)loc) 106 goto overflow; 107 break; 108 case R_X86_64_32S: 109 *(s32 *)loc = val; 110 if ((s64)val != *(s32 *)loc) 111 goto overflow; 112 break; 113 case R_X86_64_PC32: 114 val -= (u64)loc; 115 *(u32 *)loc = val; 116 break; 117 default: 118 printk(KERN_ERR "module %s: Unknown rela relocation: %Lu\n", 119 me->name, ELF64_R_TYPE(rel[i].r_info)); 120 return -ENOEXEC; 121 } 122 } 123 return 0; 124 125overflow: 126 printk(KERN_ERR "overflow in relocation type %d val %Lx\n", 127 (int)ELF64_R_TYPE(rel[i].r_info), val); 128 printk(KERN_ERR "`%s' likely not compiled with -mcmodel=kernel\n", 129 me->name); 130 return -ENOEXEC; 131} 132 133int apply_relocate(Elf_Shdr *sechdrs, 134 const char *strtab, 135 unsigned int symindex, 136 unsigned int relsec, 137 struct module *me) 138{ 139 printk("non add relocation not supported\n"); 140 return -ENOSYS; 141} 142 143int module_finalize(const Elf_Ehdr *hdr, 144 const Elf_Shdr *sechdrs, 145 struct module *me) 146{ 147 const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL; 148 char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 149 150 for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 151 if (!strcmp(".text", secstrings + s->sh_name)) 152 text = s; 153 if (!strcmp(".altinstructions", secstrings + s->sh_name)) 154 alt = s; 155 if (!strcmp(".smp_locks", secstrings + s->sh_name)) 156 locks= s; 157 } 158 159 if (alt) { 160 /* patch .altinstructions */ 161 void *aseg = (void *)alt->sh_addr; 162 apply_alternatives(aseg, aseg + alt->sh_size); 163 } 164 if (locks && text) { 165 void *lseg = (void *)locks->sh_addr; 166 void *tseg = (void *)text->sh_addr; 167 alternatives_smp_module_add(me, me->name, 168 lseg, lseg + locks->sh_size, 169 tseg, tseg + text->sh_size); 170 } 171 172 return module_bug_finalize(hdr, sechdrs, me); 173} 174 175void module_arch_cleanup(struct module *mod) 176{ 177 alternatives_smp_module_del(mod); 178 module_bug_cleanup(mod); 179} 180