diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/parisc/kernel/module.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/parisc/kernel/module.c')
-rw-r--r-- | arch/parisc/kernel/module.c | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c new file mode 100644 index 000000000000..f27cfe4771b8 --- /dev/null +++ b/arch/parisc/kernel/module.c | |||
@@ -0,0 +1,822 @@ | |||
1 | /* Kernel dynamically loadable module help for PARISC. | ||
2 | * | ||
3 | * The best reference for this stuff is probably the Processor- | ||
4 | * Specific ELF Supplement for PA-RISC: | ||
5 | * http://ftp.parisc-linux.org/docs/arch/elf-pa-hp.pdf | ||
6 | * | ||
7 | * Linux/PA-RISC Project (http://www.parisc-linux.org/) | ||
8 | * Copyright (C) 2003 Randolph Chung <tausq at debian . org> | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | * | ||
25 | * | ||
26 | * Notes: | ||
27 | * - SEGREL32 handling | ||
28 | * We are not doing SEGREL32 handling correctly. According to the ABI, we | ||
29 | * should do a value offset, like this: | ||
30 | * if (is_init(me, (void *)val)) | ||
31 | * val -= (uint32_t)me->module_init; | ||
32 | * else | ||
33 | * val -= (uint32_t)me->module_core; | ||
34 | * However, SEGREL32 is used only for PARISC unwind entries, and we want | ||
35 | * those entries to have an absolute address, and not just an offset. | ||
36 | * | ||
37 | * The unwind table mechanism has the ability to specify an offset for | ||
38 | * the unwind table; however, because we split off the init functions into | ||
39 | * a different piece of memory, it is not possible to do this using a | ||
40 | * single offset. Instead, we use the above hack for now. | ||
41 | */ | ||
42 | |||
43 | #include <linux/moduleloader.h> | ||
44 | #include <linux/elf.h> | ||
45 | #include <linux/vmalloc.h> | ||
46 | #include <linux/fs.h> | ||
47 | #include <linux/string.h> | ||
48 | #include <linux/kernel.h> | ||
49 | |||
50 | #include <asm/unwind.h> | ||
51 | |||
52 | #if 0 | ||
53 | #define DEBUGP printk | ||
54 | #else | ||
55 | #define DEBUGP(fmt...) | ||
56 | #endif | ||
57 | |||
58 | #define CHECK_RELOC(val, bits) \ | ||
59 | if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ | ||
60 | ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \ | ||
61 | printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ | ||
62 | me->name, strtab + sym->st_name, (unsigned long)val, bits); \ | ||
63 | return -ENOEXEC; \ | ||
64 | } | ||
65 | |||
66 | /* Maximum number of GOT entries. We use a long displacement ldd from | ||
67 | * the bottom of the table, which has a maximum signed displacement of | ||
68 | * 0x3fff; however, since we're only going forward, this becomes | ||
69 | * 0x1fff, and thus, since each GOT entry is 8 bytes long we can have | ||
70 | * at most 1023 entries */ | ||
71 | #define MAX_GOTS 1023 | ||
72 | |||
73 | /* three functions to determine where in the module core | ||
74 | * or init pieces the location is */ | ||
75 | static inline int is_init(struct module *me, void *loc) | ||
76 | { | ||
77 | return (loc >= me->module_init && | ||
78 | loc <= (me->module_init + me->init_size)); | ||
79 | } | ||
80 | |||
81 | static inline int is_core(struct module *me, void *loc) | ||
82 | { | ||
83 | return (loc >= me->module_core && | ||
84 | loc <= (me->module_core + me->core_size)); | ||
85 | } | ||
86 | |||
87 | static inline int is_local(struct module *me, void *loc) | ||
88 | { | ||
89 | return is_init(me, loc) || is_core(me, loc); | ||
90 | } | ||
91 | |||
92 | |||
93 | #ifndef __LP64__ | ||
94 | struct got_entry { | ||
95 | Elf32_Addr addr; | ||
96 | }; | ||
97 | |||
98 | #define Elf_Fdesc Elf32_Fdesc | ||
99 | |||
100 | struct stub_entry { | ||
101 | Elf32_Word insns[2]; /* each stub entry has two insns */ | ||
102 | }; | ||
103 | #else | ||
104 | struct got_entry { | ||
105 | Elf64_Addr addr; | ||
106 | }; | ||
107 | |||
108 | #define Elf_Fdesc Elf64_Fdesc | ||
109 | |||
110 | struct stub_entry { | ||
111 | Elf64_Word insns[4]; /* each stub entry has four insns */ | ||
112 | }; | ||
113 | #endif | ||
114 | |||
115 | /* Field selection types defined by hppa */ | ||
116 | #define rnd(x) (((x)+0x1000)&~0x1fff) | ||
117 | /* fsel: full 32 bits */ | ||
118 | #define fsel(v,a) ((v)+(a)) | ||
119 | /* lsel: select left 21 bits */ | ||
120 | #define lsel(v,a) (((v)+(a))>>11) | ||
121 | /* rsel: select right 11 bits */ | ||
122 | #define rsel(v,a) (((v)+(a))&0x7ff) | ||
123 | /* lrsel with rounding of addend to nearest 8k */ | ||
124 | #define lrsel(v,a) (((v)+rnd(a))>>11) | ||
125 | /* rrsel with rounding of addend to nearest 8k */ | ||
126 | #define rrsel(v,a) ((((v)+rnd(a))&0x7ff)+((a)-rnd(a))) | ||
127 | |||
128 | #define mask(x,sz) ((x) & ~((1<<(sz))-1)) | ||
129 | |||
130 | |||
131 | /* The reassemble_* functions prepare an immediate value for | ||
132 | insertion into an opcode. pa-risc uses all sorts of weird bitfields | ||
133 | in the instruction to hold the value. */ | ||
134 | static inline int reassemble_14(int as14) | ||
135 | { | ||
136 | return (((as14 & 0x1fff) << 1) | | ||
137 | ((as14 & 0x2000) >> 13)); | ||
138 | } | ||
139 | |||
140 | static inline int reassemble_17(int as17) | ||
141 | { | ||
142 | return (((as17 & 0x10000) >> 16) | | ||
143 | ((as17 & 0x0f800) << 5) | | ||
144 | ((as17 & 0x00400) >> 8) | | ||
145 | ((as17 & 0x003ff) << 3)); | ||
146 | } | ||
147 | |||
148 | static inline int reassemble_21(int as21) | ||
149 | { | ||
150 | return (((as21 & 0x100000) >> 20) | | ||
151 | ((as21 & 0x0ffe00) >> 8) | | ||
152 | ((as21 & 0x000180) << 7) | | ||
153 | ((as21 & 0x00007c) << 14) | | ||
154 | ((as21 & 0x000003) << 12)); | ||
155 | } | ||
156 | |||
157 | static inline int reassemble_22(int as22) | ||
158 | { | ||
159 | return (((as22 & 0x200000) >> 21) | | ||
160 | ((as22 & 0x1f0000) << 5) | | ||
161 | ((as22 & 0x00f800) << 5) | | ||
162 | ((as22 & 0x000400) >> 8) | | ||
163 | ((as22 & 0x0003ff) << 3)); | ||
164 | } | ||
165 | |||
166 | void *module_alloc(unsigned long size) | ||
167 | { | ||
168 | if (size == 0) | ||
169 | return NULL; | ||
170 | return vmalloc(size); | ||
171 | } | ||
172 | |||
173 | #ifndef __LP64__ | ||
174 | static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n) | ||
175 | { | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n) | ||
180 | { | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) | ||
185 | { | ||
186 | unsigned long cnt = 0; | ||
187 | |||
188 | for (; n > 0; n--, rela++) | ||
189 | { | ||
190 | switch (ELF32_R_TYPE(rela->r_info)) { | ||
191 | case R_PARISC_PCREL17F: | ||
192 | case R_PARISC_PCREL22F: | ||
193 | cnt++; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | return cnt; | ||
198 | } | ||
199 | #else | ||
200 | static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n) | ||
201 | { | ||
202 | unsigned long cnt = 0; | ||
203 | |||
204 | for (; n > 0; n--, rela++) | ||
205 | { | ||
206 | switch (ELF64_R_TYPE(rela->r_info)) { | ||
207 | case R_PARISC_LTOFF21L: | ||
208 | case R_PARISC_LTOFF14R: | ||
209 | case R_PARISC_PCREL22F: | ||
210 | cnt++; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | return cnt; | ||
215 | } | ||
216 | |||
217 | static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n) | ||
218 | { | ||
219 | unsigned long cnt = 0; | ||
220 | |||
221 | for (; n > 0; n--, rela++) | ||
222 | { | ||
223 | switch (ELF64_R_TYPE(rela->r_info)) { | ||
224 | case R_PARISC_FPTR64: | ||
225 | cnt++; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return cnt; | ||
230 | } | ||
231 | |||
232 | static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) | ||
233 | { | ||
234 | unsigned long cnt = 0; | ||
235 | |||
236 | for (; n > 0; n--, rela++) | ||
237 | { | ||
238 | switch (ELF64_R_TYPE(rela->r_info)) { | ||
239 | case R_PARISC_PCREL22F: | ||
240 | cnt++; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | return cnt; | ||
245 | } | ||
246 | #endif | ||
247 | |||
248 | |||
249 | /* Free memory returned from module_alloc */ | ||
250 | void module_free(struct module *mod, void *module_region) | ||
251 | { | ||
252 | vfree(module_region); | ||
253 | /* FIXME: If module_region == mod->init_region, trim exception | ||
254 | table entries. */ | ||
255 | } | ||
256 | |||
257 | #define CONST | ||
258 | int module_frob_arch_sections(CONST Elf_Ehdr *hdr, | ||
259 | CONST Elf_Shdr *sechdrs, | ||
260 | CONST char *secstrings, | ||
261 | struct module *me) | ||
262 | { | ||
263 | unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0; | ||
264 | unsigned int i; | ||
265 | |||
266 | for (i = 1; i < hdr->e_shnum; i++) { | ||
267 | const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; | ||
268 | unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); | ||
269 | |||
270 | if (strncmp(secstrings + sechdrs[i].sh_name, | ||
271 | ".PARISC.unwind", 14) == 0) | ||
272 | me->arch.unwind_section = i; | ||
273 | |||
274 | if (sechdrs[i].sh_type != SHT_RELA) | ||
275 | continue; | ||
276 | |||
277 | /* some of these are not relevant for 32-bit/64-bit | ||
278 | * we leave them here to make the code common. the | ||
279 | * compiler will do its thing and optimize out the | ||
280 | * stuff we don't need | ||
281 | */ | ||
282 | gots += count_gots(rels, nrels); | ||
283 | fdescs += count_fdescs(rels, nrels); | ||
284 | if(strncmp(secstrings + sechdrs[i].sh_name, | ||
285 | ".rela.init", 10) == 0) | ||
286 | init_stubs += count_stubs(rels, nrels); | ||
287 | else | ||
288 | stubs += count_stubs(rels, nrels); | ||
289 | } | ||
290 | |||
291 | /* align things a bit */ | ||
292 | me->core_size = ALIGN(me->core_size, 16); | ||
293 | me->arch.got_offset = me->core_size; | ||
294 | me->core_size += gots * sizeof(struct got_entry); | ||
295 | |||
296 | me->core_size = ALIGN(me->core_size, 16); | ||
297 | me->arch.fdesc_offset = me->core_size; | ||
298 | me->core_size += fdescs * sizeof(Elf_Fdesc); | ||
299 | |||
300 | me->core_size = ALIGN(me->core_size, 16); | ||
301 | me->arch.stub_offset = me->core_size; | ||
302 | me->core_size += stubs * sizeof(struct stub_entry); | ||
303 | |||
304 | me->init_size = ALIGN(me->init_size, 16); | ||
305 | me->arch.init_stub_offset = me->init_size; | ||
306 | me->init_size += init_stubs * sizeof(struct stub_entry); | ||
307 | |||
308 | me->arch.got_max = gots; | ||
309 | me->arch.fdesc_max = fdescs; | ||
310 | me->arch.stub_max = stubs; | ||
311 | me->arch.init_stub_max = init_stubs; | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | #ifdef __LP64__ | ||
317 | static Elf64_Word get_got(struct module *me, unsigned long value, long addend) | ||
318 | { | ||
319 | unsigned int i; | ||
320 | struct got_entry *got; | ||
321 | |||
322 | value += addend; | ||
323 | |||
324 | BUG_ON(value == 0); | ||
325 | |||
326 | got = me->module_core + me->arch.got_offset; | ||
327 | for (i = 0; got[i].addr; i++) | ||
328 | if (got[i].addr == value) | ||
329 | goto out; | ||
330 | |||
331 | BUG_ON(++me->arch.got_count > me->arch.got_max); | ||
332 | |||
333 | got[i].addr = value; | ||
334 | out: | ||
335 | DEBUGP("GOT ENTRY %d[%x] val %lx\n", i, i*sizeof(struct got_entry), | ||
336 | value); | ||
337 | return i * sizeof(struct got_entry); | ||
338 | } | ||
339 | #endif /* __LP64__ */ | ||
340 | |||
341 | #ifdef __LP64__ | ||
342 | static Elf_Addr get_fdesc(struct module *me, unsigned long value) | ||
343 | { | ||
344 | Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset; | ||
345 | |||
346 | if (!value) { | ||
347 | printk(KERN_ERR "%s: zero OPD requested!\n", me->name); | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | /* Look for existing fdesc entry. */ | ||
352 | while (fdesc->addr) { | ||
353 | if (fdesc->addr == value) | ||
354 | return (Elf_Addr)fdesc; | ||
355 | fdesc++; | ||
356 | } | ||
357 | |||
358 | BUG_ON(++me->arch.fdesc_count > me->arch.fdesc_max); | ||
359 | |||
360 | /* Create new one */ | ||
361 | fdesc->addr = value; | ||
362 | fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset; | ||
363 | return (Elf_Addr)fdesc; | ||
364 | } | ||
365 | #endif /* __LP64__ */ | ||
366 | |||
367 | static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | ||
368 | int millicode, int init_section) | ||
369 | { | ||
370 | unsigned long i; | ||
371 | struct stub_entry *stub; | ||
372 | |||
373 | if(init_section) { | ||
374 | i = me->arch.init_stub_count++; | ||
375 | BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max); | ||
376 | stub = me->module_init + me->arch.init_stub_offset + | ||
377 | i * sizeof(struct stub_entry); | ||
378 | } else { | ||
379 | i = me->arch.stub_count++; | ||
380 | BUG_ON(me->arch.stub_count > me->arch.stub_max); | ||
381 | stub = me->module_core + me->arch.stub_offset + | ||
382 | i * sizeof(struct stub_entry); | ||
383 | } | ||
384 | |||
385 | #ifndef __LP64__ | ||
386 | /* for 32-bit the stub looks like this: | ||
387 | * ldil L'XXX,%r1 | ||
388 | * be,n R'XXX(%sr4,%r1) | ||
389 | */ | ||
390 | //value = *(unsigned long *)((value + addend) & ~3); /* why? */ | ||
391 | |||
392 | stub->insns[0] = 0x20200000; /* ldil L'XXX,%r1 */ | ||
393 | stub->insns[1] = 0xe0202002; /* be,n R'XXX(%sr4,%r1) */ | ||
394 | |||
395 | stub->insns[0] |= reassemble_21(lrsel(value, addend)); | ||
396 | stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); | ||
397 | |||
398 | #else | ||
399 | /* for 64-bit we have two kinds of stubs: | ||
400 | * for normal function calls: | ||
401 | * ldd 0(%dp),%dp | ||
402 | * ldd 10(%dp), %r1 | ||
403 | * bve (%r1) | ||
404 | * ldd 18(%dp), %dp | ||
405 | * | ||
406 | * for millicode: | ||
407 | * ldil 0, %r1 | ||
408 | * ldo 0(%r1), %r1 | ||
409 | * ldd 10(%r1), %r1 | ||
410 | * bve,n (%r1) | ||
411 | */ | ||
412 | if (!millicode) | ||
413 | { | ||
414 | stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */ | ||
415 | stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */ | ||
416 | stub->insns[2] = 0xe820d000; /* bve (%r1) */ | ||
417 | stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */ | ||
418 | |||
419 | stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff); | ||
420 | } | ||
421 | else | ||
422 | { | ||
423 | stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ | ||
424 | stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ | ||
425 | stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */ | ||
426 | stub->insns[3] = 0xe820d002; /* bve,n (%r1) */ | ||
427 | |||
428 | stub->insns[0] |= reassemble_21(lrsel(value, addend)); | ||
429 | stub->insns[1] |= reassemble_14(rrsel(value, addend)); | ||
430 | } | ||
431 | #endif | ||
432 | |||
433 | return (Elf_Addr)stub; | ||
434 | } | ||
435 | |||
436 | int apply_relocate(Elf_Shdr *sechdrs, | ||
437 | const char *strtab, | ||
438 | unsigned int symindex, | ||
439 | unsigned int relsec, | ||
440 | struct module *me) | ||
441 | { | ||
442 | /* parisc should not need this ... */ | ||
443 | printk(KERN_ERR "module %s: RELOCATION unsupported\n", | ||
444 | me->name); | ||
445 | return -ENOEXEC; | ||
446 | } | ||
447 | |||
448 | #ifndef __LP64__ | ||
449 | int apply_relocate_add(Elf_Shdr *sechdrs, | ||
450 | const char *strtab, | ||
451 | unsigned int symindex, | ||
452 | unsigned int relsec, | ||
453 | struct module *me) | ||
454 | { | ||
455 | int i; | ||
456 | Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
457 | Elf32_Sym *sym; | ||
458 | Elf32_Word *loc; | ||
459 | Elf32_Addr val; | ||
460 | Elf32_Sword addend; | ||
461 | Elf32_Addr dot; | ||
462 | //unsigned long dp = (unsigned long)$global$; | ||
463 | register unsigned long dp asm ("r27"); | ||
464 | |||
465 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
466 | sechdrs[relsec].sh_info); | ||
467 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
468 | /* This is where to make the change */ | ||
469 | loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
470 | + rel[i].r_offset; | ||
471 | /* This is the symbol it is referring to */ | ||
472 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
473 | + ELF32_R_SYM(rel[i].r_info); | ||
474 | if (!sym->st_value) { | ||
475 | printk(KERN_WARNING "%s: Unknown symbol %s\n", | ||
476 | me->name, strtab + sym->st_name); | ||
477 | return -ENOENT; | ||
478 | } | ||
479 | //dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03; | ||
480 | dot = (Elf32_Addr)loc & ~0x03; | ||
481 | |||
482 | val = sym->st_value; | ||
483 | addend = rel[i].r_addend; | ||
484 | |||
485 | #if 0 | ||
486 | #define r(t) ELF32_R_TYPE(rel[i].r_info)==t ? #t : | ||
487 | DEBUGP("Symbol %s loc 0x%x val 0x%x addend 0x%x: %s\n", | ||
488 | strtab + sym->st_name, | ||
489 | (uint32_t)loc, val, addend, | ||
490 | r(R_PARISC_PLABEL32) | ||
491 | r(R_PARISC_DIR32) | ||
492 | r(R_PARISC_DIR21L) | ||
493 | r(R_PARISC_DIR14R) | ||
494 | r(R_PARISC_SEGREL32) | ||
495 | r(R_PARISC_DPREL21L) | ||
496 | r(R_PARISC_DPREL14R) | ||
497 | r(R_PARISC_PCREL17F) | ||
498 | r(R_PARISC_PCREL22F) | ||
499 | "UNKNOWN"); | ||
500 | #undef r | ||
501 | #endif | ||
502 | |||
503 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
504 | case R_PARISC_PLABEL32: | ||
505 | /* 32-bit function address */ | ||
506 | /* no function descriptors... */ | ||
507 | *loc = fsel(val, addend); | ||
508 | break; | ||
509 | case R_PARISC_DIR32: | ||
510 | /* direct 32-bit ref */ | ||
511 | *loc = fsel(val, addend); | ||
512 | break; | ||
513 | case R_PARISC_DIR21L: | ||
514 | /* left 21 bits of effective address */ | ||
515 | val = lrsel(val, addend); | ||
516 | *loc = mask(*loc, 21) | reassemble_21(val); | ||
517 | break; | ||
518 | case R_PARISC_DIR14R: | ||
519 | /* right 14 bits of effective address */ | ||
520 | val = rrsel(val, addend); | ||
521 | *loc = mask(*loc, 14) | reassemble_14(val); | ||
522 | break; | ||
523 | case R_PARISC_SEGREL32: | ||
524 | /* 32-bit segment relative address */ | ||
525 | /* See note about special handling of SEGREL32 at | ||
526 | * the beginning of this file. | ||
527 | */ | ||
528 | *loc = fsel(val, addend); | ||
529 | break; | ||
530 | case R_PARISC_DPREL21L: | ||
531 | /* left 21 bit of relative address */ | ||
532 | val = lrsel(val - dp, addend); | ||
533 | *loc = mask(*loc, 21) | reassemble_21(val); | ||
534 | break; | ||
535 | case R_PARISC_DPREL14R: | ||
536 | /* right 14 bit of relative address */ | ||
537 | val = rrsel(val - dp, addend); | ||
538 | *loc = mask(*loc, 14) | reassemble_14(val); | ||
539 | break; | ||
540 | case R_PARISC_PCREL17F: | ||
541 | /* 17-bit PC relative address */ | ||
542 | val = get_stub(me, val, addend, 0, is_init(me, loc)); | ||
543 | val = (val - dot - 8)/4; | ||
544 | CHECK_RELOC(val, 17) | ||
545 | *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); | ||
546 | break; | ||
547 | case R_PARISC_PCREL22F: | ||
548 | /* 22-bit PC relative address; only defined for pa20 */ | ||
549 | val = get_stub(me, val, addend, 0, is_init(me, loc)); | ||
550 | DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", | ||
551 | strtab + sym->st_name, (unsigned long)loc, addend, | ||
552 | val) | ||
553 | val = (val - dot - 8)/4; | ||
554 | CHECK_RELOC(val, 22); | ||
555 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); | ||
556 | break; | ||
557 | |||
558 | default: | ||
559 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
560 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
561 | return -ENOEXEC; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | #else | ||
569 | int apply_relocate_add(Elf_Shdr *sechdrs, | ||
570 | const char *strtab, | ||
571 | unsigned int symindex, | ||
572 | unsigned int relsec, | ||
573 | struct module *me) | ||
574 | { | ||
575 | int i; | ||
576 | Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
577 | Elf64_Sym *sym; | ||
578 | Elf64_Word *loc; | ||
579 | Elf64_Xword *loc64; | ||
580 | Elf64_Addr val; | ||
581 | Elf64_Sxword addend; | ||
582 | Elf64_Addr dot; | ||
583 | |||
584 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
585 | sechdrs[relsec].sh_info); | ||
586 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
587 | /* This is where to make the change */ | ||
588 | loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
589 | + rel[i].r_offset; | ||
590 | /* This is the symbol it is referring to */ | ||
591 | sym = (Elf64_Sym *)sechdrs[symindex].sh_addr | ||
592 | + ELF64_R_SYM(rel[i].r_info); | ||
593 | if (!sym->st_value) { | ||
594 | printk(KERN_WARNING "%s: Unknown symbol %s\n", | ||
595 | me->name, strtab + sym->st_name); | ||
596 | return -ENOENT; | ||
597 | } | ||
598 | //dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03; | ||
599 | dot = (Elf64_Addr)loc & ~0x03; | ||
600 | loc64 = (Elf64_Xword *)loc; | ||
601 | |||
602 | val = sym->st_value; | ||
603 | addend = rel[i].r_addend; | ||
604 | |||
605 | #if 0 | ||
606 | #define r(t) ELF64_R_TYPE(rel[i].r_info)==t ? #t : | ||
607 | printk("Symbol %s loc %p val 0x%Lx addend 0x%Lx: %s\n", | ||
608 | strtab + sym->st_name, | ||
609 | loc, val, addend, | ||
610 | r(R_PARISC_LTOFF14R) | ||
611 | r(R_PARISC_LTOFF21L) | ||
612 | r(R_PARISC_PCREL22F) | ||
613 | r(R_PARISC_DIR64) | ||
614 | r(R_PARISC_SEGREL32) | ||
615 | r(R_PARISC_FPTR64) | ||
616 | "UNKNOWN"); | ||
617 | #undef r | ||
618 | #endif | ||
619 | |||
620 | switch (ELF64_R_TYPE(rel[i].r_info)) { | ||
621 | case R_PARISC_LTOFF21L: | ||
622 | /* LT-relative; left 21 bits */ | ||
623 | val = get_got(me, val, addend); | ||
624 | DEBUGP("LTOFF21L Symbol %s loc %p val %lx\n", | ||
625 | strtab + sym->st_name, | ||
626 | loc, val); | ||
627 | val = lrsel(val, 0); | ||
628 | *loc = mask(*loc, 21) | reassemble_21(val); | ||
629 | break; | ||
630 | case R_PARISC_LTOFF14R: | ||
631 | /* L(ltoff(val+addend)) */ | ||
632 | /* LT-relative; right 14 bits */ | ||
633 | val = get_got(me, val, addend); | ||
634 | val = rrsel(val, 0); | ||
635 | DEBUGP("LTOFF14R Symbol %s loc %p val %lx\n", | ||
636 | strtab + sym->st_name, | ||
637 | loc, val); | ||
638 | *loc = mask(*loc, 14) | reassemble_14(val); | ||
639 | break; | ||
640 | case R_PARISC_PCREL22F: | ||
641 | /* PC-relative; 22 bits */ | ||
642 | DEBUGP("PCREL22F Symbol %s loc %p val %lx\n", | ||
643 | strtab + sym->st_name, | ||
644 | loc, val); | ||
645 | /* can we reach it locally? */ | ||
646 | if(!is_local(me, (void *)val)) { | ||
647 | if (strncmp(strtab + sym->st_name, "$$", 2) | ||
648 | == 0) | ||
649 | val = get_stub(me, val, addend, 1, | ||
650 | is_init(me, loc)); | ||
651 | else | ||
652 | val = get_stub(me, val, addend, 0, | ||
653 | is_init(me, loc)); | ||
654 | } | ||
655 | DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", | ||
656 | strtab + sym->st_name, loc, sym->st_value, | ||
657 | addend, val); | ||
658 | /* FIXME: local symbols work as long as the | ||
659 | * core and init pieces aren't separated too | ||
660 | * far. If this is ever broken, you will trip | ||
661 | * the check below. The way to fix it would | ||
662 | * be to generate local stubs to go between init | ||
663 | * and core */ | ||
664 | if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 || | ||
665 | (Elf64_Sxword)(val - dot - 8) < -0x800000) { | ||
666 | printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n", | ||
667 | me->name, strtab + sym->st_name); | ||
668 | return -ENOEXEC; | ||
669 | } | ||
670 | val = (val - dot - 8)/4; | ||
671 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); | ||
672 | break; | ||
673 | case R_PARISC_DIR64: | ||
674 | /* 64-bit effective address */ | ||
675 | *loc64 = val + addend; | ||
676 | break; | ||
677 | case R_PARISC_SEGREL32: | ||
678 | /* 32-bit segment relative address */ | ||
679 | /* See note about special handling of SEGREL32 at | ||
680 | * the beginning of this file. | ||
681 | */ | ||
682 | *loc = fsel(val, addend); | ||
683 | break; | ||
684 | case R_PARISC_FPTR64: | ||
685 | /* 64-bit function address */ | ||
686 | if(is_local(me, (void *)(val + addend))) { | ||
687 | *loc64 = get_fdesc(me, val+addend); | ||
688 | DEBUGP("FDESC for %s at %p points to %lx\n", | ||
689 | strtab + sym->st_name, *loc64, | ||
690 | ((Elf_Fdesc *)*loc64)->addr); | ||
691 | } else { | ||
692 | /* if the symbol is not local to this | ||
693 | * module then val+addend is a pointer | ||
694 | * to the function descriptor */ | ||
695 | DEBUGP("Non local FPTR64 Symbol %s loc %p val %lx\n", | ||
696 | strtab + sym->st_name, | ||
697 | loc, val); | ||
698 | *loc64 = val + addend; | ||
699 | } | ||
700 | break; | ||
701 | |||
702 | default: | ||
703 | printk(KERN_ERR "module %s: Unknown relocation: %Lu\n", | ||
704 | me->name, ELF64_R_TYPE(rel[i].r_info)); | ||
705 | return -ENOEXEC; | ||
706 | } | ||
707 | } | ||
708 | return 0; | ||
709 | } | ||
710 | #endif | ||
711 | |||
712 | static void | ||
713 | register_unwind_table(struct module *me, | ||
714 | const Elf_Shdr *sechdrs) | ||
715 | { | ||
716 | unsigned char *table, *end; | ||
717 | unsigned long gp; | ||
718 | |||
719 | if (!me->arch.unwind_section) | ||
720 | return; | ||
721 | |||
722 | table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr; | ||
723 | end = table + sechdrs[me->arch.unwind_section].sh_size; | ||
724 | gp = (Elf_Addr)me->module_core + me->arch.got_offset; | ||
725 | |||
726 | DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n", | ||
727 | me->arch.unwind_section, table, end, gp); | ||
728 | me->arch.unwind = unwind_table_add(me->name, 0, gp, table, end); | ||
729 | } | ||
730 | |||
731 | static void | ||
732 | deregister_unwind_table(struct module *me) | ||
733 | { | ||
734 | if (me->arch.unwind) | ||
735 | unwind_table_remove(me->arch.unwind); | ||
736 | } | ||
737 | |||
738 | int module_finalize(const Elf_Ehdr *hdr, | ||
739 | const Elf_Shdr *sechdrs, | ||
740 | struct module *me) | ||
741 | { | ||
742 | int i; | ||
743 | unsigned long nsyms; | ||
744 | const char *strtab = NULL; | ||
745 | Elf_Sym *newptr, *oldptr; | ||
746 | Elf_Shdr *symhdr = NULL; | ||
747 | #ifdef DEBUG | ||
748 | Elf_Fdesc *entry; | ||
749 | u32 *addr; | ||
750 | |||
751 | entry = (Elf_Fdesc *)me->init; | ||
752 | printk("FINALIZE, ->init FPTR is %p, GP %lx ADDR %lx\n", entry, | ||
753 | entry->gp, entry->addr); | ||
754 | addr = (u32 *)entry->addr; | ||
755 | printk("INSNS: %x %x %x %x\n", | ||
756 | addr[0], addr[1], addr[2], addr[3]); | ||
757 | printk("stubs used %ld, stubs max %ld\n" | ||
758 | "init_stubs used %ld, init stubs max %ld\n" | ||
759 | "got entries used %ld, gots max %ld\n" | ||
760 | "fdescs used %ld, fdescs max %ld\n", | ||
761 | me->arch.stub_count, me->arch.stub_max, | ||
762 | me->arch.init_stub_count, me->arch.init_stub_max, | ||
763 | me->arch.got_count, me->arch.got_max, | ||
764 | me->arch.fdesc_count, me->arch.fdesc_max); | ||
765 | #endif | ||
766 | |||
767 | register_unwind_table(me, sechdrs); | ||
768 | |||
769 | /* haven't filled in me->symtab yet, so have to find it | ||
770 | * ourselves */ | ||
771 | for (i = 1; i < hdr->e_shnum; i++) { | ||
772 | if(sechdrs[i].sh_type == SHT_SYMTAB | ||
773 | && (sechdrs[i].sh_type & SHF_ALLOC)) { | ||
774 | int strindex = sechdrs[i].sh_link; | ||
775 | /* FIXME: AWFUL HACK | ||
776 | * The cast is to drop the const from | ||
777 | * the sechdrs pointer */ | ||
778 | symhdr = (Elf_Shdr *)&sechdrs[i]; | ||
779 | strtab = (char *)sechdrs[strindex].sh_addr; | ||
780 | break; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | DEBUGP("module %s: strtab %p, symhdr %p\n", | ||
785 | me->name, strtab, symhdr); | ||
786 | |||
787 | if(me->arch.got_count > MAX_GOTS) { | ||
788 | printk(KERN_ERR "%s: Global Offset Table overflow (used %ld, allowed %d\n", me->name, me->arch.got_count, MAX_GOTS); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | |||
792 | /* no symbol table */ | ||
793 | if(symhdr == NULL) | ||
794 | return 0; | ||
795 | |||
796 | oldptr = (void *)symhdr->sh_addr; | ||
797 | newptr = oldptr + 1; /* we start counting at 1 */ | ||
798 | nsyms = symhdr->sh_size / sizeof(Elf_Sym); | ||
799 | DEBUGP("OLD num_symtab %lu\n", nsyms); | ||
800 | |||
801 | for (i = 1; i < nsyms; i++) { | ||
802 | oldptr++; /* note, count starts at 1 so preincrement */ | ||
803 | if(strncmp(strtab + oldptr->st_name, | ||
804 | ".L", 2) == 0) | ||
805 | continue; | ||
806 | |||
807 | if(newptr != oldptr) | ||
808 | *newptr++ = *oldptr; | ||
809 | else | ||
810 | newptr++; | ||
811 | |||
812 | } | ||
813 | nsyms = newptr - (Elf_Sym *)symhdr->sh_addr; | ||
814 | DEBUGP("NEW num_symtab %lu\n", nsyms); | ||
815 | symhdr->sh_size = nsyms * sizeof(Elf_Sym); | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | void module_arch_cleanup(struct module *mod) | ||
820 | { | ||
821 | deregister_unwind_table(mod); | ||
822 | } | ||