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
  19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  20
  21#include <linux/moduleloader.h>
  22#include <linux/elf.h>
  23#include <linux/vmalloc.h>
  24#include <linux/fs.h>
  25#include <linux/string.h>
  26#include <linux/kernel.h>
  27#include <linux/bug.h>
  28#include <linux/mm.h>
  29#include <linux/gfp.h>
  30#include <linux/jump_label.h>
  31#include <linux/random.h>
  32
  33#include <asm/page.h>
  34#include <asm/pgtable.h>
  35
  36#if 0
  37#define DEBUGP(fmt, ...)                                \
  38        printk(KERN_DEBUG fmt, ##__VA_ARGS__)
  39#else
  40#define DEBUGP(fmt, ...)                                \
  41do {                                                    \
  42        if (0)                                          \
  43                printk(KERN_DEBUG fmt, ##__VA_ARGS__);  \
  44} while (0)
  45#endif
  46
  47#ifdef CONFIG_RANDOMIZE_BASE
  48static unsigned long module_load_offset;
  49static int randomize_modules = 1;
  50
  51/* Mutex protects the module_load_offset. */
  52static DEFINE_MUTEX(module_kaslr_mutex);
  53
  54static int __init parse_nokaslr(char *p)
  55{
  56        randomize_modules = 0;
  57        return 0;
  58}
  59early_param("nokaslr", parse_nokaslr);
  60
  61static unsigned long int get_module_load_offset(void)
  62{
  63        if (randomize_modules) {
  64                mutex_lock(&module_kaslr_mutex);
  65                /*
  66                 * Calculate the module_load_offset the first time this
  67                 * code is called. Once calculated it stays the same until
  68                 * reboot.
  69                 */
  70                if (module_load_offset == 0)
  71                        module_load_offset =
  72                                (get_random_int() % 1024 + 1) * PAGE_SIZE;
  73                mutex_unlock(&module_kaslr_mutex);
  74        }
  75        return module_load_offset;
  76}
  77#else
  78static unsigned long int get_module_load_offset(void)
  79{
  80        return 0;
  81}
  82#endif
  83
  84void *module_alloc(unsigned long size)
  85{
  86        if (PAGE_ALIGN(size) > MODULES_LEN)
  87                return NULL;
  88        return __vmalloc_node_range(size, 1,
  89                                    MODULES_VADDR + get_module_load_offset(),
  90                                    MODULES_END, GFP_KERNEL | __GFP_HIGHMEM,
  91                                    PAGE_KERNEL_EXEC, NUMA_NO_NODE,
  92                                    __builtin_return_address(0));
  93}
  94
  95#ifdef CONFIG_X86_32
  96int apply_relocate(Elf32_Shdr *sechdrs,
  97                   const char *strtab,
  98                   unsigned int symindex,
  99                   unsigned int relsec,
 100                   struct module *me)
 101{
 102        unsigned int i;
 103        Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr;
 104        Elf32_Sym *sym;
 105        uint32_t *location;
 106
 107        DEBUGP("Applying relocate section %u to %u\n",
 108               relsec, sechdrs[relsec].sh_info);
 109        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 110                /* This is where to make the change */
 111                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
 112                        + rel[i].r_offset;
 113                /* This is the symbol it is referring to.  Note that all
 114                   undefined symbols have been resolved.  */
 115                sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
 116                        + ELF32_R_SYM(rel[i].r_info);
 117
 118                switch (ELF32_R_TYPE(rel[i].r_info)) {
 119                case R_386_32:
 120                        /* We add the value into the location given */
 121                        *location += sym->st_value;
 122                        break;
 123                case R_386_PC32:
 124                        /* Add the value, subtract its position */
 125                        *location += sym->st_value - (uint32_t)location;
 126                        break;
 127                default:
 128                        pr_err("%s: Unknown relocation: %u\n",
 129                               me->name, ELF32_R_TYPE(rel[i].r_info));
 130                        return -ENOEXEC;
 131                }
 132        }
 133        return 0;
 134}
 135#else /*X86_64*/
 136int apply_relocate_add(Elf64_Shdr *sechdrs,
 137                   const char *strtab,
 138                   unsigned int symindex,
 139                   unsigned int relsec,
 140                   struct module *me)
 141{
 142        unsigned int i;
 143        Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
 144        Elf64_Sym *sym;
 145        void *loc;
 146        u64 val;
 147
 148        DEBUGP("Applying relocate section %u to %u\n",
 149               relsec, sechdrs[relsec].sh_info);
 150        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 151                /* This is where to make the change */
 152                loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
 153                        + rel[i].r_offset;
 154
 155                /* This is the symbol it is referring to.  Note that all
 156                   undefined symbols have been resolved.  */
 157                sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
 158                        + ELF64_R_SYM(rel[i].r_info);
 159
 160                DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n",
 161                       (int)ELF64_R_TYPE(rel[i].r_info),
 162                       sym->st_value, rel[i].r_addend, (u64)loc);
 163
 164                val = sym->st_value + rel[i].r_addend;
 165
 166                switch (ELF64_R_TYPE(rel[i].r_info)) {
 167                case R_X86_64_NONE:
 168                        break;
 169                case R_X86_64_64:
 170                        *(u64 *)loc = val;
 171                        break;
 172                case R_X86_64_32:
 173                        *(u32 *)loc = val;
 174                        if (val != *(u32 *)loc)
 175                                goto overflow;
 176                        break;
 177                case R_X86_64_32S:
 178                        *(s32 *)loc = val;
 179                        if ((s64)val != *(s32 *)loc)
 180                                goto overflow;
 181                        break;
 182                case R_X86_64_PC32:
 183                        val -= (u64)loc;
 184                        *(u32 *)loc = val;
 185#if 0
 186                        if ((s64)val != *(s32 *)loc)
 187                                goto overflow;
 188#endif
 189                        break;
 190                default:
 191                        pr_err("%s: Unknown rela relocation: %llu\n",
 192                               me->name, ELF64_R_TYPE(rel[i].r_info));
 193                        return -ENOEXEC;
 194                }
 195        }
 196        return 0;
 197
 198overflow:
 199        pr_err("overflow in relocation type %d val %Lx\n",
 200               (int)ELF64_R_TYPE(rel[i].r_info), val);
 201        pr_err("`%s' likely not compiled with -mcmodel=kernel\n",
 202               me->name);
 203        return -ENOEXEC;
 204}
 205#endif
 206
 207int module_finalize(const Elf_Ehdr *hdr,
 208                    const Elf_Shdr *sechdrs,
 209                    struct module *me)
 210{
 211        const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
 212                *para = NULL;
 213        char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 214
 215        for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
 216                if (!strcmp(".text", secstrings + s->sh_name))
 217                        text = s;
 218                if (!strcmp(".altinstructions", secstrings + s->sh_name))
 219                        alt = s;
 220                if (!strcmp(".smp_locks", secstrings + s->sh_name))
 221                        locks = s;
 222                if (!strcmp(".parainstructions", secstrings + s->sh_name))
 223                        para = s;
 224        }
 225
 226        if (alt) {
 227                /* patch .altinstructions */
 228                void *aseg = (void *)alt->sh_addr;
 229                apply_alternatives(aseg, aseg + alt->sh_size);
 230        }
 231        if (locks && text) {
 232                void *lseg = (void *)locks->sh_addr;
 233                void *tseg = (void *)text->sh_addr;
 234                alternatives_smp_module_add(me, me->name,
 235                                            lseg, lseg + locks->sh_size,
 236                                            tseg, tseg + text->sh_size);
 237        }
 238
 239        if (para) {
 240                void *pseg = (void *)para->sh_addr;
 241                apply_paravirt(pseg, pseg + para->sh_size);
 242        }
 243
 244        /* make jump label nops */
 245        jump_label_apply_nops(me);
 246
 247        return 0;
 248}
 249
 250void module_arch_cleanup(struct module *mod)
 251{
 252        alternatives_smp_module_del(mod);
 253}
 254