linux/arch/metag/kernel/module.c
<<
>>
Prefs
   1/*  Kernel module help for Meta.
   2
   3    This program is free software; you can redistribute it and/or modify
   4    it under the terms of the GNU General Public License as published by
   5    the Free Software Foundation; either version 2 of the License, or
   6    (at your option) any later version.
   7
   8    This program is distributed in the hope that it will be useful,
   9    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11    GNU General Public License for more details.
  12*/
  13#include <linux/moduleloader.h>
  14#include <linux/elf.h>
  15#include <linux/vmalloc.h>
  16#include <linux/fs.h>
  17#include <linux/string.h>
  18#include <linux/kernel.h>
  19#include <linux/sort.h>
  20
  21#include <asm/unaligned.h>
  22
  23/* Count how many different relocations (different symbol, different
  24   addend) */
  25static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
  26{
  27        unsigned int i, r_info, r_addend, _count_relocs;
  28
  29        _count_relocs = 0;
  30        r_info = 0;
  31        r_addend = 0;
  32        for (i = 0; i < num; i++)
  33                /* Only count relbranch relocs, others don't need stubs */
  34                if (ELF32_R_TYPE(rela[i].r_info) == R_METAG_RELBRANCH &&
  35                    (r_info != ELF32_R_SYM(rela[i].r_info) ||
  36                     r_addend != rela[i].r_addend)) {
  37                        _count_relocs++;
  38                        r_info = ELF32_R_SYM(rela[i].r_info);
  39                        r_addend = rela[i].r_addend;
  40                }
  41
  42        return _count_relocs;
  43}
  44
  45static int relacmp(const void *_x, const void *_y)
  46{
  47        const Elf32_Rela *x, *y;
  48
  49        y = (Elf32_Rela *)_x;
  50        x = (Elf32_Rela *)_y;
  51
  52        /* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to
  53         * make the comparison cheaper/faster. It won't affect the sorting or
  54         * the counting algorithms' performance
  55         */
  56        if (x->r_info < y->r_info)
  57                return -1;
  58        else if (x->r_info > y->r_info)
  59                return 1;
  60        else if (x->r_addend < y->r_addend)
  61                return -1;
  62        else if (x->r_addend > y->r_addend)
  63                return 1;
  64        else
  65                return 0;
  66}
  67
  68static void relaswap(void *_x, void *_y, int size)
  69{
  70        uint32_t *x, *y, tmp;
  71        int i;
  72
  73        y = (uint32_t *)_x;
  74        x = (uint32_t *)_y;
  75
  76        for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) {
  77                tmp = x[i];
  78                x[i] = y[i];
  79                y[i] = tmp;
  80        }
  81}
  82
  83/* Get the potential trampolines size required of the init and
  84   non-init sections */
  85static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
  86                                  const Elf32_Shdr *sechdrs,
  87                                  const char *secstrings,
  88                                  int is_init)
  89{
  90        unsigned long ret = 0;
  91        unsigned i;
  92
  93        /* Everything marked ALLOC (this includes the exported
  94           symbols) */
  95        for (i = 1; i < hdr->e_shnum; i++) {
  96                /* If it's called *.init*, and we're not init, we're
  97                   not interested */
  98                if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != NULL)
  99                    != is_init)
 100                        continue;
 101
 102                /* We don't want to look at debug sections. */
 103                if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != NULL)
 104                        continue;
 105
 106                if (sechdrs[i].sh_type == SHT_RELA) {
 107                        pr_debug("Found relocations in section %u\n", i);
 108                        pr_debug("Ptr: %p.  Number: %u\n",
 109                                 (void *)hdr + sechdrs[i].sh_offset,
 110                                 sechdrs[i].sh_size / sizeof(Elf32_Rela));
 111
 112                        /* Sort the relocation information based on a symbol and
 113                         * addend key. This is a stable O(n*log n) complexity
 114                         * alogrithm but it will reduce the complexity of
 115                         * count_relocs() to linear complexity O(n)
 116                         */
 117                        sort((void *)hdr + sechdrs[i].sh_offset,
 118                             sechdrs[i].sh_size / sizeof(Elf32_Rela),
 119                             sizeof(Elf32_Rela), relacmp, relaswap);
 120
 121                        ret += count_relocs((void *)hdr
 122                                             + sechdrs[i].sh_offset,
 123                                             sechdrs[i].sh_size
 124                                             / sizeof(Elf32_Rela))
 125                                * sizeof(struct metag_plt_entry);
 126                }
 127        }
 128
 129        return ret;
 130}
 131
 132int module_frob_arch_sections(Elf32_Ehdr *hdr,
 133                              Elf32_Shdr *sechdrs,
 134                              char *secstrings,
 135                              struct module *me)
 136{
 137        unsigned int i;
 138
 139        /* Find .plt and .init.plt sections */
 140        for (i = 0; i < hdr->e_shnum; i++) {
 141                if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0)
 142                        me->arch.init_plt_section = i;
 143                else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
 144                        me->arch.core_plt_section = i;
 145        }
 146        if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
 147                pr_err("Module doesn't contain .plt or .init.plt sections.\n");
 148                return -ENOEXEC;
 149        }
 150
 151        /* Override their sizes */
 152        sechdrs[me->arch.core_plt_section].sh_size
 153                = get_plt_size(hdr, sechdrs, secstrings, 0);
 154        sechdrs[me->arch.core_plt_section].sh_type = SHT_NOBITS;
 155        sechdrs[me->arch.init_plt_section].sh_size
 156                = get_plt_size(hdr, sechdrs, secstrings, 1);
 157        sechdrs[me->arch.init_plt_section].sh_type = SHT_NOBITS;
 158        return 0;
 159}
 160
 161/* Set up a trampoline in the PLT to bounce us to the distant function */
 162static uint32_t do_plt_call(void *location, Elf32_Addr val,
 163                            Elf32_Shdr *sechdrs, struct module *mod)
 164{
 165        struct metag_plt_entry *entry;
 166        /* Instructions used to do the indirect jump.  */
 167        uint32_t tramp[2];
 168
 169        /* We have to trash a register, so we assume that any control
 170           transfer more than 21-bits away must be a function call
 171           (so we can use a call-clobbered register).  */
 172
 173        /* MOVT D0Re0,#HI(v) */
 174        tramp[0] = 0x02000005 | (((val & 0xffff0000) >> 16) << 3);
 175        /* JUMP D0Re0,#LO(v) */
 176        tramp[1] = 0xac000001 | ((val & 0x0000ffff) << 3);
 177
 178        /* Init, or core PLT? */
 179        if (location >= mod->module_core
 180            && location < mod->module_core + mod->core_size)
 181                entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
 182        else
 183                entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
 184
 185        /* Find this entry, or if that fails, the next avail. entry */
 186        while (entry->tramp[0])
 187                if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1])
 188                        return (uint32_t)entry;
 189                else
 190                        entry++;
 191
 192        entry->tramp[0] = tramp[0];
 193        entry->tramp[1] = tramp[1];
 194
 195        return (uint32_t)entry;
 196}
 197
 198int apply_relocate_add(Elf32_Shdr *sechdrs,
 199                   const char *strtab,
 200                   unsigned int symindex,
 201                   unsigned int relsec,
 202                   struct module *me)
 203{
 204        unsigned int i;
 205        Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
 206        Elf32_Sym *sym;
 207        Elf32_Addr relocation;
 208        uint32_t *location;
 209        int32_t value;
 210
 211        pr_debug("Applying relocate section %u to %u\n", relsec,
 212                 sechdrs[relsec].sh_info);
 213        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 214                /* This is where to make the change */
 215                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
 216                        + rel[i].r_offset;
 217                /* This is the symbol it is referring to.  Note that all
 218                   undefined symbols have been resolved.  */
 219                sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
 220                        + ELF32_R_SYM(rel[i].r_info);
 221                relocation = sym->st_value + rel[i].r_addend;
 222
 223                switch (ELF32_R_TYPE(rel[i].r_info)) {
 224                case R_METAG_NONE:
 225                        break;
 226                case R_METAG_HIADDR16:
 227                        relocation >>= 16;
 228                case R_METAG_LOADDR16:
 229                        *location = (*location & 0xfff80007) |
 230                                ((relocation & 0xffff) << 3);
 231                        break;
 232                case R_METAG_ADDR32:
 233                        /*
 234                         * Packed data structures may cause a misaligned
 235                         * R_METAG_ADDR32 to be emitted.
 236                         */
 237                        put_unaligned(relocation, location);
 238                        break;
 239                case R_METAG_GETSETOFF:
 240                        *location += ((relocation & 0xfff) << 7);
 241                        break;
 242                case R_METAG_RELBRANCH:
 243                        if (*location & (0x7ffff << 5)) {
 244                                pr_err("bad relbranch relocation\n");
 245                                break;
 246                        }
 247
 248                        /* This jump is too big for the offset slot. Build
 249                         * a PLT to jump through to get to where we want to go.
 250                         * NB: 21bit check - not scaled to 19bit yet
 251                         */
 252                        if (((int32_t)(relocation -
 253                                       (uint32_t)location) > 0xfffff) ||
 254                            ((int32_t)(relocation -
 255                                       (uint32_t)location) < -0xfffff)) {
 256                                relocation = do_plt_call(location, relocation,
 257                                                         sechdrs, me);
 258                        }
 259
 260                        value = relocation - (uint32_t)location;
 261
 262                        /* branch instruction aligned */
 263                        value /= 4;
 264
 265                        if ((value > 0x7ffff) || (value < -0x7ffff)) {
 266                                /*
 267                                 * this should have been caught by the code
 268                                 * above!
 269                                 */
 270                                pr_err("overflow of relbranch reloc\n");
 271                        }
 272
 273                        *location = (*location & (~(0x7ffff << 5))) |
 274                                ((value & 0x7ffff) << 5);
 275                        break;
 276
 277                default:
 278                        pr_err("module %s: Unknown relocation: %u\n",
 279                               me->name, ELF32_R_TYPE(rel[i].r_info));
 280                        return -ENOEXEC;
 281                }
 282        }
 283        return 0;
 284}
 285