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