linux/arch/m32r/kernel/module.c
<<
>>
Prefs
   1/*  Kernel module help for M32R.
   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    You should have received a copy of the GNU General Public License
  14    along with this program; if not, write to the Free Software
  15    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  16*/
  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
  25#if 0
  26#define DEBUGP printk
  27#else
  28#define DEBUGP(fmt...)
  29#endif
  30
  31#define COPY_UNALIGNED_WORD(sw, tw, align) \
  32{ \
  33        void *__s = &(sw), *__t = &(tw); \
  34        unsigned short *__s2 = __s, *__t2 =__t; \
  35        unsigned char *__s1 = __s, *__t1 =__t; \
  36        switch ((align)) \
  37        { \
  38        case 0: \
  39                *(unsigned long *) __t = *(unsigned long *) __s; \
  40                break; \
  41        case 2: \
  42                *__t2++ = *__s2++; \
  43                *__t2 = *__s2; \
  44                break; \
  45        default: \
  46                *__t1++ = *__s1++; \
  47                *__t1++ = *__s1++; \
  48                *__t1++ = *__s1++; \
  49                *__t1 = *__s1; \
  50                break; \
  51        } \
  52}
  53
  54#define COPY_UNALIGNED_HWORD(sw, tw, align) \
  55  { \
  56    void *__s = &(sw), *__t = &(tw); \
  57    unsigned short *__s2 = __s, *__t2 =__t; \
  58    unsigned char *__s1 = __s, *__t1 =__t; \
  59    switch ((align)) \
  60    { \
  61    case 0: \
  62      *__t2 = *__s2; \
  63      break; \
  64    default: \
  65      *__t1++ = *__s1++; \
  66      *__t1 = *__s1; \
  67      break; \
  68    } \
  69  }
  70
  71int apply_relocate_add(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_Rela *rel = (void *)sechdrs[relsec].sh_addr;
  79        Elf32_Sym *sym;
  80        Elf32_Addr relocation;
  81        uint32_t *location;
  82        uint32_t value;
  83        unsigned short *hlocation;
  84        unsigned short hvalue;
  85        int svalue;
  86        int align;
  87
  88        DEBUGP("Applying relocate section %u to %u\n", relsec,
  89               sechdrs[relsec].sh_info);
  90        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
  91                /* This is where to make the change */
  92                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
  93                        + rel[i].r_offset;
  94                /* This is the symbol it is referring to.  Note that all
  95                   undefined symbols have been resolved.  */
  96                sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
  97                        + ELF32_R_SYM(rel[i].r_info);
  98                relocation = sym->st_value + rel[i].r_addend;
  99                align = (int)location & 3;
 100
 101                switch (ELF32_R_TYPE(rel[i].r_info)) {
 102                case R_M32R_32_RELA:
 103                        COPY_UNALIGNED_WORD (*location, value, align);
 104                        value += relocation;
 105                        COPY_UNALIGNED_WORD (value, *location, align);
 106                        break;
 107                case R_M32R_HI16_ULO_RELA:
 108                        COPY_UNALIGNED_WORD (*location, value, align);
 109                        relocation = (relocation >>16) & 0xffff;
 110                        /* RELA must has 0 at relocation field. */
 111                        value += relocation;
 112                        COPY_UNALIGNED_WORD (value, *location, align);
 113                        break;
 114                case R_M32R_HI16_SLO_RELA:
 115                        COPY_UNALIGNED_WORD (*location, value, align);
 116                        if (relocation & 0x8000) relocation += 0x10000;
 117                        relocation = (relocation >>16) & 0xffff;
 118                        /* RELA must has 0 at relocation field. */
 119                        value += relocation;
 120                        COPY_UNALIGNED_WORD (value, *location, align);
 121                        break;
 122                case R_M32R_16_RELA:
 123                        hlocation = (unsigned short *)location;
 124                        relocation = relocation & 0xffff;
 125                        /* RELA must has 0 at relocation field. */
 126                        hvalue = relocation;
 127                        COPY_UNALIGNED_WORD (hvalue, *hlocation, align);
 128                        break;
 129                case R_M32R_SDA16_RELA:
 130                case R_M32R_LO16_RELA:
 131                        COPY_UNALIGNED_WORD (*location, value, align);
 132                        relocation = relocation & 0xffff;
 133                        /* RELA must has 0 at relocation field. */
 134                        value += relocation;
 135                        COPY_UNALIGNED_WORD (value, *location, align);
 136                        break;
 137                case R_M32R_24_RELA:
 138                        COPY_UNALIGNED_WORD (*location, value, align);
 139                        relocation = relocation & 0xffffff;
 140                        /* RELA must has 0 at relocation field. */
 141                        value += relocation;
 142                        COPY_UNALIGNED_WORD (value, *location, align);
 143                        break;
 144                case R_M32R_18_PCREL_RELA:
 145                        relocation = (relocation - (Elf32_Addr) location);
 146                        if (relocation < -0x20000 || 0x1fffc < relocation)
 147                                {
 148                                        printk(KERN_ERR "module %s: relocation overflow: %u\n",
 149                                        me->name, relocation);
 150                                        return -ENOEXEC;
 151                                }
 152                        COPY_UNALIGNED_WORD (*location, value, align);
 153                        if (value & 0xffff)
 154                                {
 155                                        /* RELA must has 0 at relocation field. */
 156                                        printk(KERN_ERR "module %s: illegal relocation field: %u\n",
 157                                        me->name, value);
 158                                        return -ENOEXEC;
 159                                }
 160                        relocation = (relocation >> 2) & 0xffff;
 161                        value += relocation;
 162                        COPY_UNALIGNED_WORD (value, *location, align);
 163                        break;
 164                case R_M32R_10_PCREL_RELA:
 165                        hlocation = (unsigned short *)location;
 166                        relocation = (relocation - (Elf32_Addr) location);
 167                        COPY_UNALIGNED_HWORD (*hlocation, hvalue, align);
 168                        svalue = (int)hvalue;
 169                        svalue = (signed char)svalue << 2;
 170                        relocation += svalue;
 171                        relocation = (relocation >> 2) & 0xff;
 172                        hvalue = hvalue & 0xff00;
 173                        hvalue += relocation;
 174                        COPY_UNALIGNED_HWORD (hvalue, *hlocation, align);
 175                        break;
 176                case R_M32R_26_PCREL_RELA:
 177                        relocation = (relocation - (Elf32_Addr) location);
 178                        if (relocation < -0x2000000 || 0x1fffffc < relocation)
 179                                {
 180                                        printk(KERN_ERR "module %s: relocation overflow: %u\n",
 181                                        me->name, relocation);
 182                                        return -ENOEXEC;
 183                                }
 184                        COPY_UNALIGNED_WORD (*location, value, align);
 185                        if (value & 0xffffff)
 186                                {
 187                                        /* RELA must has 0 at relocation field. */
 188                                        printk(KERN_ERR "module %s: illegal relocation field: %u\n",
 189                                        me->name, value);
 190                                        return -ENOEXEC;
 191                                }
 192                        relocation = (relocation >> 2) & 0xffffff;
 193                        value += relocation;
 194                        COPY_UNALIGNED_WORD (value, *location, align);
 195                        break;
 196                default:
 197                        printk(KERN_ERR "module %s: Unknown relocation: %u\n",
 198                               me->name, ELF32_R_TYPE(rel[i].r_info));
 199                        return -ENOEXEC;
 200                }
 201        }
 202        return 0;
 203}
 204