linux/arch/x86/kernel/module_64.c
<<
>>
Prefs
   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/mm.h>
  26#include <linux/slab.h>
  27#include <linux/bug.h>
  28
  29#include <asm/system.h>
  30#include <asm/page.h>
  31#include <asm/pgtable.h>
  32
  33#define DEBUGP(fmt...) 
  34
  35#ifndef CONFIG_UML
  36void module_free(struct module *mod, void *module_region)
  37{
  38        vfree(module_region);
  39        /* FIXME: If module_region == mod->init_region, trim exception
  40           table entries. */
  41}
  42
  43void *module_alloc(unsigned long size)
  44{
  45        struct vm_struct *area;
  46
  47        if (!size)
  48                return NULL;
  49        size = PAGE_ALIGN(size);
  50        if (size > MODULES_LEN)
  51                return NULL;
  52
  53        area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
  54        if (!area)
  55                return NULL;
  56
  57        return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
  58}
  59#endif
  60
  61/* We don't need anything special. */
  62int module_frob_arch_sections(Elf_Ehdr *hdr,
  63                              Elf_Shdr *sechdrs,
  64                              char *secstrings,
  65                              struct module *mod)
  66{
  67        return 0;
  68}
  69
  70int apply_relocate_add(Elf64_Shdr *sechdrs,
  71                   const char *strtab,
  72                   unsigned int symindex,
  73                   unsigned int relsec,
  74                   struct module *me)
  75{
  76        unsigned int i;
  77        Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
  78        Elf64_Sym *sym;
  79        void *loc;
  80        u64 val; 
  81
  82        DEBUGP("Applying relocate section %u to %u\n", relsec,
  83               sechdrs[relsec].sh_info);
  84        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
  85                /* This is where to make the change */
  86                loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
  87                        + rel[i].r_offset;
  88
  89                /* This is the symbol it is referring to.  Note that all
  90                   undefined symbols have been resolved.  */
  91                sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
  92                        + ELF64_R_SYM(rel[i].r_info);
  93
  94                DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n",
  95                       (int)ELF64_R_TYPE(rel[i].r_info), 
  96                       sym->st_value, rel[i].r_addend, (u64)loc);
  97
  98                val = sym->st_value + rel[i].r_addend; 
  99
 100                switch (ELF64_R_TYPE(rel[i].r_info)) {
 101                case R_X86_64_NONE:
 102                        break;
 103                case R_X86_64_64:
 104                        *(u64 *)loc = val;
 105                        break;
 106                case R_X86_64_32:
 107                        *(u32 *)loc = val;
 108                        if (val != *(u32 *)loc)
 109                                goto overflow;
 110                        break;
 111                case R_X86_64_32S:
 112                        *(s32 *)loc = val;
 113                        if ((s64)val != *(s32 *)loc)
 114                                goto overflow;
 115                        break;
 116                case R_X86_64_PC32: 
 117                        val -= (u64)loc;
 118                        *(u32 *)loc = val;
 119#if 0
 120                        if ((s64)val != *(s32 *)loc)
 121                                goto overflow; 
 122#endif
 123                        break;
 124                default:
 125                        printk(KERN_ERR "module %s: Unknown rela relocation: %Lu\n",
 126                               me->name, ELF64_R_TYPE(rel[i].r_info));
 127                        return -ENOEXEC;
 128                }
 129        }
 130        return 0;
 131
 132overflow:
 133        printk(KERN_ERR "overflow in relocation type %d val %Lx\n", 
 134               (int)ELF64_R_TYPE(rel[i].r_info), val);
 135        printk(KERN_ERR "`%s' likely not compiled with -mcmodel=kernel\n",
 136               me->name);
 137        return -ENOEXEC;
 138}
 139
 140int apply_relocate(Elf_Shdr *sechdrs,
 141                   const char *strtab,
 142                   unsigned int symindex,
 143                   unsigned int relsec,
 144                   struct module *me)
 145{
 146        printk("non add relocation not supported\n");
 147        return -ENOSYS;
 148} 
 149
 150int module_finalize(const Elf_Ehdr *hdr,
 151                    const Elf_Shdr *sechdrs,
 152                    struct module *me)
 153{
 154        const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
 155                *para = NULL;
 156        char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 157
 158        for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
 159                if (!strcmp(".text", secstrings + s->sh_name))
 160                        text = s;
 161                if (!strcmp(".altinstructions", secstrings + s->sh_name))
 162                        alt = s;
 163                if (!strcmp(".smp_locks", secstrings + s->sh_name))
 164                        locks= s;
 165                if (!strcmp(".parainstructions", secstrings + s->sh_name))
 166                        para = s;
 167        }
 168
 169        if (alt) {
 170                /* patch .altinstructions */
 171                void *aseg = (void *)alt->sh_addr;
 172                apply_alternatives(aseg, aseg + alt->sh_size);
 173        }
 174        if (locks && text) {
 175                void *lseg = (void *)locks->sh_addr;
 176                void *tseg = (void *)text->sh_addr;
 177                alternatives_smp_module_add(me, me->name,
 178                                            lseg, lseg + locks->sh_size,
 179                                            tseg, tseg + text->sh_size);
 180        }
 181
 182        if (para) {
 183                void *pseg = (void *)para->sh_addr;
 184                apply_paravirt(pseg, pseg + para->sh_size);
 185        }
 186
 187        return module_bug_finalize(hdr, sechdrs, me);
 188}
 189
 190void module_arch_cleanup(struct module *mod)
 191{
 192        alternatives_smp_module_del(mod);
 193        module_bug_cleanup(mod);
 194}
 195
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.