Root/
1 | /* |
2 | * AVR32-specific kernel module loader |
3 | * |
4 | * Copyright (C) 2005-2006 Atmel Corporation |
5 | * |
6 | * GOT initialization parts are based on the s390 version |
7 | * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH, |
8 | * IBM Corporation |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. |
13 | */ |
14 | |
15 | #include <linux/bug.h> |
16 | #include <linux/elf.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | #include <linux/moduleloader.h> |
20 | #include <linux/vmalloc.h> |
21 | |
22 | void module_free(struct module *mod, void *module_region) |
23 | { |
24 | vfree(mod->arch.syminfo); |
25 | mod->arch.syminfo = NULL; |
26 | |
27 | vfree(module_region); |
28 | } |
29 | |
30 | static inline int check_rela(Elf32_Rela *rela, struct module *module, |
31 | char *strings, Elf32_Sym *symbols) |
32 | { |
33 | struct mod_arch_syminfo *info; |
34 | |
35 | info = module->arch.syminfo + ELF32_R_SYM(rela->r_info); |
36 | switch (ELF32_R_TYPE(rela->r_info)) { |
37 | case R_AVR32_GOT32: |
38 | case R_AVR32_GOT16: |
39 | case R_AVR32_GOT8: |
40 | case R_AVR32_GOT21S: |
41 | case R_AVR32_GOT18SW: /* mcall */ |
42 | case R_AVR32_GOT16S: /* ld.w */ |
43 | if (rela->r_addend != 0) { |
44 | printk(KERN_ERR |
45 | "GOT relocation against %s at offset %u with addend\n", |
46 | strings + symbols[ELF32_R_SYM(rela->r_info)].st_name, |
47 | rela->r_offset); |
48 | return -ENOEXEC; |
49 | } |
50 | if (info->got_offset == -1UL) { |
51 | info->got_offset = module->arch.got_size; |
52 | module->arch.got_size += sizeof(void *); |
53 | } |
54 | pr_debug("GOT[%3lu] %s\n", info->got_offset, |
55 | strings + symbols[ELF32_R_SYM(rela->r_info)].st_name); |
56 | break; |
57 | } |
58 | |
59 | return 0; |
60 | } |
61 | |
62 | int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, |
63 | char *secstrings, struct module *module) |
64 | { |
65 | Elf32_Shdr *symtab; |
66 | Elf32_Sym *symbols; |
67 | Elf32_Rela *rela; |
68 | char *strings; |
69 | int nrela, i, j; |
70 | int ret; |
71 | |
72 | /* Find the symbol table */ |
73 | symtab = NULL; |
74 | for (i = 0; i < hdr->e_shnum; i++) |
75 | switch (sechdrs[i].sh_type) { |
76 | case SHT_SYMTAB: |
77 | symtab = &sechdrs[i]; |
78 | break; |
79 | } |
80 | if (!symtab) { |
81 | printk(KERN_ERR "module %s: no symbol table\n", module->name); |
82 | return -ENOEXEC; |
83 | } |
84 | |
85 | /* Allocate room for one syminfo structure per symbol. */ |
86 | module->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym); |
87 | module->arch.syminfo = vmalloc(module->arch.nsyms |
88 | * sizeof(struct mod_arch_syminfo)); |
89 | if (!module->arch.syminfo) |
90 | return -ENOMEM; |
91 | |
92 | symbols = (void *)hdr + symtab->sh_offset; |
93 | strings = (void *)hdr + sechdrs[symtab->sh_link].sh_offset; |
94 | for (i = 0; i < module->arch.nsyms; i++) { |
95 | if (symbols[i].st_shndx == SHN_UNDEF && |
96 | strcmp(strings + symbols[i].st_name, |
97 | "_GLOBAL_OFFSET_TABLE_") == 0) |
98 | /* "Define" it as absolute. */ |
99 | symbols[i].st_shndx = SHN_ABS; |
100 | module->arch.syminfo[i].got_offset = -1UL; |
101 | module->arch.syminfo[i].got_initialized = 0; |
102 | } |
103 | |
104 | /* Allocate GOT entries for symbols that need it. */ |
105 | module->arch.got_size = 0; |
106 | for (i = 0; i < hdr->e_shnum; i++) { |
107 | if (sechdrs[i].sh_type != SHT_RELA) |
108 | continue; |
109 | nrela = sechdrs[i].sh_size / sizeof(Elf32_Rela); |
110 | rela = (void *)hdr + sechdrs[i].sh_offset; |
111 | for (j = 0; j < nrela; j++) { |
112 | ret = check_rela(rela + j, module, |
113 | strings, symbols); |
114 | if (ret) |
115 | goto out_free_syminfo; |
116 | } |
117 | } |
118 | |
119 | /* |
120 | * Increase core size to make room for GOT and set start |
121 | * offset for GOT. |
122 | */ |
123 | module->core_size = ALIGN(module->core_size, 4); |
124 | module->arch.got_offset = module->core_size; |
125 | module->core_size += module->arch.got_size; |
126 | |
127 | return 0; |
128 | |
129 | out_free_syminfo: |
130 | vfree(module->arch.syminfo); |
131 | module->arch.syminfo = NULL; |
132 | |
133 | return ret; |
134 | } |
135 | |
136 | static inline int reloc_overflow(struct module *module, const char *reloc_name, |
137 | Elf32_Addr relocation) |
138 | { |
139 | printk(KERN_ERR "module %s: Value %lx does not fit relocation %s\n", |
140 | module->name, (unsigned long)relocation, reloc_name); |
141 | return -ENOEXEC; |
142 | } |
143 | |
144 | #define get_u16(loc) (*((uint16_t *)loc)) |
145 | #define put_u16(loc, val) (*((uint16_t *)loc) = (val)) |
146 | |
147 | int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, |
148 | unsigned int symindex, unsigned int relindex, |
149 | struct module *module) |
150 | { |
151 | Elf32_Shdr *symsec = sechdrs + symindex; |
152 | Elf32_Shdr *relsec = sechdrs + relindex; |
153 | Elf32_Shdr *dstsec = sechdrs + relsec->sh_info; |
154 | Elf32_Rela *rel = (void *)relsec->sh_addr; |
155 | unsigned int i; |
156 | int ret = 0; |
157 | |
158 | for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rela); i++, rel++) { |
159 | struct mod_arch_syminfo *info; |
160 | Elf32_Sym *sym; |
161 | Elf32_Addr relocation; |
162 | uint32_t *location; |
163 | uint32_t value; |
164 | |
165 | location = (void *)dstsec->sh_addr + rel->r_offset; |
166 | sym = (Elf32_Sym *)symsec->sh_addr + ELF32_R_SYM(rel->r_info); |
167 | relocation = sym->st_value + rel->r_addend; |
168 | |
169 | info = module->arch.syminfo + ELF32_R_SYM(rel->r_info); |
170 | |
171 | /* Initialize GOT entry if necessary */ |
172 | switch (ELF32_R_TYPE(rel->r_info)) { |
173 | case R_AVR32_GOT32: |
174 | case R_AVR32_GOT16: |
175 | case R_AVR32_GOT8: |
176 | case R_AVR32_GOT21S: |
177 | case R_AVR32_GOT18SW: |
178 | case R_AVR32_GOT16S: |
179 | if (!info->got_initialized) { |
180 | Elf32_Addr *gotent; |
181 | |
182 | gotent = (module->module_core |
183 | + module->arch.got_offset |
184 | + info->got_offset); |
185 | *gotent = relocation; |
186 | info->got_initialized = 1; |
187 | } |
188 | |
189 | relocation = info->got_offset; |
190 | break; |
191 | } |
192 | |
193 | switch (ELF32_R_TYPE(rel->r_info)) { |
194 | case R_AVR32_32: |
195 | case R_AVR32_32_CPENT: |
196 | *location = relocation; |
197 | break; |
198 | case R_AVR32_22H_PCREL: |
199 | relocation -= (Elf32_Addr)location; |
200 | if ((relocation & 0xffe00001) != 0 |
201 | && (relocation & 0xffc00001) != 0xffc00000) |
202 | return reloc_overflow(module, |
203 | "R_AVR32_22H_PCREL", |
204 | relocation); |
205 | relocation >>= 1; |
206 | |
207 | value = *location; |
208 | value = ((value & 0xe1ef0000) |
209 | | (relocation & 0xffff) |
210 | | ((relocation & 0x10000) << 4) |
211 | | ((relocation & 0x1e0000) << 8)); |
212 | *location = value; |
213 | break; |
214 | case R_AVR32_11H_PCREL: |
215 | relocation -= (Elf32_Addr)location; |
216 | if ((relocation & 0xfffffc01) != 0 |
217 | && (relocation & 0xfffff801) != 0xfffff800) |
218 | return reloc_overflow(module, |
219 | "R_AVR32_11H_PCREL", |
220 | relocation); |
221 | value = get_u16(location); |
222 | value = ((value & 0xf00c) |
223 | | ((relocation & 0x1fe) << 3) |
224 | | ((relocation & 0x600) >> 9)); |
225 | put_u16(location, value); |
226 | break; |
227 | case R_AVR32_9H_PCREL: |
228 | relocation -= (Elf32_Addr)location; |
229 | if ((relocation & 0xffffff01) != 0 |
230 | && (relocation & 0xfffffe01) != 0xfffffe00) |
231 | return reloc_overflow(module, |
232 | "R_AVR32_9H_PCREL", |
233 | relocation); |
234 | value = get_u16(location); |
235 | value = ((value & 0xf00f) |
236 | | ((relocation & 0x1fe) << 3)); |
237 | put_u16(location, value); |
238 | break; |
239 | case R_AVR32_9UW_PCREL: |
240 | relocation -= ((Elf32_Addr)location) & 0xfffffffc; |
241 | if ((relocation & 0xfffffc03) != 0) |
242 | return reloc_overflow(module, |
243 | "R_AVR32_9UW_PCREL", |
244 | relocation); |
245 | value = get_u16(location); |
246 | value = ((value & 0xf80f) |
247 | | ((relocation & 0x1fc) << 2)); |
248 | put_u16(location, value); |
249 | break; |
250 | case R_AVR32_GOTPC: |
251 | /* |
252 | * R6 = PC - (PC - GOT) |
253 | * |
254 | * At this point, relocation contains the |
255 | * value of PC. Just subtract the value of |
256 | * GOT, and we're done. |
257 | */ |
258 | pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n", |
259 | relocation, module->arch.got_offset, |
260 | module->module_core); |
261 | relocation -= ((unsigned long)module->module_core |
262 | + module->arch.got_offset); |
263 | *location = relocation; |
264 | break; |
265 | case R_AVR32_GOT18SW: |
266 | if ((relocation & 0xfffe0003) != 0 |
267 | && (relocation & 0xfffc0000) != 0xfffc0000) |
268 | return reloc_overflow(module, "R_AVR32_GOT18SW", |
269 | relocation); |
270 | relocation >>= 2; |
271 | /* fall through */ |
272 | case R_AVR32_GOT16S: |
273 | if ((relocation & 0xffff8000) != 0 |
274 | && (relocation & 0xffff0000) != 0xffff0000) |
275 | return reloc_overflow(module, "R_AVR32_GOT16S", |
276 | relocation); |
277 | pr_debug("GOT reloc @ 0x%x -> %u\n", |
278 | rel->r_offset, relocation); |
279 | value = *location; |
280 | value = ((value & 0xffff0000) |
281 | | (relocation & 0xffff)); |
282 | *location = value; |
283 | break; |
284 | |
285 | default: |
286 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", |
287 | module->name, ELF32_R_TYPE(rel->r_info)); |
288 | return -ENOEXEC; |
289 | } |
290 | } |
291 | |
292 | return ret; |
293 | } |
294 | |
295 | int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, |
296 | struct module *module) |
297 | { |
298 | vfree(module->arch.syminfo); |
299 | module->arch.syminfo = NULL; |
300 | |
301 | return 0; |
302 | } |
303 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9