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