diff options
Diffstat (limited to 'arch/parisc')
-rw-r--r-- | arch/parisc/kernel/module.c | 63 |
1 files changed, 50 insertions, 13 deletions
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index f27cfe4771b8..aee311884f3f 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c | |||
@@ -89,6 +89,12 @@ static inline int is_local(struct module *me, void *loc) | |||
89 | return is_init(me, loc) || is_core(me, loc); | 89 | return is_init(me, loc) || is_core(me, loc); |
90 | } | 90 | } |
91 | 91 | ||
92 | static inline int is_local_section(struct module *me, void *loc, void *dot) | ||
93 | { | ||
94 | return (is_init(me, loc) && is_init(me, dot)) || | ||
95 | (is_core(me, loc) && is_core(me, dot)); | ||
96 | } | ||
97 | |||
92 | 98 | ||
93 | #ifndef __LP64__ | 99 | #ifndef __LP64__ |
94 | struct got_entry { | 100 | struct got_entry { |
@@ -364,8 +370,14 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value) | |||
364 | } | 370 | } |
365 | #endif /* __LP64__ */ | 371 | #endif /* __LP64__ */ |
366 | 372 | ||
373 | enum elf_stub_type { | ||
374 | ELF_STUB_GOT, | ||
375 | ELF_STUB_MILLI, | ||
376 | ELF_STUB_DIRECT, | ||
377 | }; | ||
378 | |||
367 | static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | 379 | static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, |
368 | int millicode, int init_section) | 380 | enum elf_stub_type stub_type, int init_section) |
369 | { | 381 | { |
370 | unsigned long i; | 382 | unsigned long i; |
371 | struct stub_entry *stub; | 383 | struct stub_entry *stub; |
@@ -396,7 +408,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | |||
396 | stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); | 408 | stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); |
397 | 409 | ||
398 | #else | 410 | #else |
399 | /* for 64-bit we have two kinds of stubs: | 411 | /* for 64-bit we have three kinds of stubs: |
400 | * for normal function calls: | 412 | * for normal function calls: |
401 | * ldd 0(%dp),%dp | 413 | * ldd 0(%dp),%dp |
402 | * ldd 10(%dp), %r1 | 414 | * ldd 10(%dp), %r1 |
@@ -408,18 +420,23 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | |||
408 | * ldo 0(%r1), %r1 | 420 | * ldo 0(%r1), %r1 |
409 | * ldd 10(%r1), %r1 | 421 | * ldd 10(%r1), %r1 |
410 | * bve,n (%r1) | 422 | * bve,n (%r1) |
423 | * | ||
424 | * for direct branches (jumps between different section of the | ||
425 | * same module): | ||
426 | * ldil 0, %r1 | ||
427 | * ldo 0(%r1), %r1 | ||
428 | * bve,n (%r1) | ||
411 | */ | 429 | */ |
412 | if (!millicode) | 430 | switch (stub_type) { |
413 | { | 431 | case ELF_STUB_GOT: |
414 | stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */ | 432 | stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */ |
415 | stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */ | 433 | stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */ |
416 | stub->insns[2] = 0xe820d000; /* bve (%r1) */ | 434 | stub->insns[2] = 0xe820d000; /* bve (%r1) */ |
417 | stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */ | 435 | stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */ |
418 | 436 | ||
419 | stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff); | 437 | stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff); |
420 | } | 438 | break; |
421 | else | 439 | case ELF_STUB_MILLI: |
422 | { | ||
423 | stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ | 440 | stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ |
424 | stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ | 441 | stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ |
425 | stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */ | 442 | stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */ |
@@ -427,7 +444,17 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | |||
427 | 444 | ||
428 | stub->insns[0] |= reassemble_21(lrsel(value, addend)); | 445 | stub->insns[0] |= reassemble_21(lrsel(value, addend)); |
429 | stub->insns[1] |= reassemble_14(rrsel(value, addend)); | 446 | stub->insns[1] |= reassemble_14(rrsel(value, addend)); |
447 | break; | ||
448 | case ELF_STUB_DIRECT: | ||
449 | stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ | ||
450 | stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ | ||
451 | stub->insns[2] = 0xe820d002; /* bve,n (%r1) */ | ||
452 | |||
453 | stub->insns[0] |= reassemble_21(lrsel(value, addend)); | ||
454 | stub->insns[1] |= reassemble_14(rrsel(value, addend)); | ||
455 | break; | ||
430 | } | 456 | } |
457 | |||
431 | #endif | 458 | #endif |
432 | 459 | ||
433 | return (Elf_Addr)stub; | 460 | return (Elf_Addr)stub; |
@@ -539,14 +566,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
539 | break; | 566 | break; |
540 | case R_PARISC_PCREL17F: | 567 | case R_PARISC_PCREL17F: |
541 | /* 17-bit PC relative address */ | 568 | /* 17-bit PC relative address */ |
542 | val = get_stub(me, val, addend, 0, is_init(me, loc)); | 569 | val = get_stub(me, val, addend, ELF_STUB_GOT, is_init(me, loc)); |
543 | val = (val - dot - 8)/4; | 570 | val = (val - dot - 8)/4; |
544 | CHECK_RELOC(val, 17) | 571 | CHECK_RELOC(val, 17) |
545 | *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); | 572 | *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); |
546 | break; | 573 | break; |
547 | case R_PARISC_PCREL22F: | 574 | case R_PARISC_PCREL22F: |
548 | /* 22-bit PC relative address; only defined for pa20 */ | 575 | /* 22-bit PC relative address; only defined for pa20 */ |
549 | val = get_stub(me, val, addend, 0, is_init(me, loc)); | 576 | val = get_stub(me, val, addend, ELF_STUB_GOT, is_init(me, loc)); |
550 | DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", | 577 | DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", |
551 | strtab + sym->st_name, (unsigned long)loc, addend, | 578 | strtab + sym->st_name, (unsigned long)loc, addend, |
552 | val) | 579 | val) |
@@ -643,13 +670,23 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
643 | strtab + sym->st_name, | 670 | strtab + sym->st_name, |
644 | loc, val); | 671 | loc, val); |
645 | /* can we reach it locally? */ | 672 | /* can we reach it locally? */ |
646 | if(!is_local(me, (void *)val)) { | 673 | if(!is_local_section(me, (void *)val, (void *)dot)) { |
647 | if (strncmp(strtab + sym->st_name, "$$", 2) | 674 | |
675 | if (is_local(me, (void *)val)) | ||
676 | /* this is the case where the | ||
677 | * symbol is local to the | ||
678 | * module, but in a different | ||
679 | * section, so stub the jump | ||
680 | * in case it's more than 22 | ||
681 | * bits away */ | ||
682 | val = get_stub(me, val, addend, ELF_STUB_DIRECT, | ||
683 | is_init(me, loc)); | ||
684 | else if (strncmp(strtab + sym->st_name, "$$", 2) | ||
648 | == 0) | 685 | == 0) |
649 | val = get_stub(me, val, addend, 1, | 686 | val = get_stub(me, val, addend, ELF_STUB_MILLI, |
650 | is_init(me, loc)); | 687 | is_init(me, loc)); |
651 | else | 688 | else |
652 | val = get_stub(me, val, addend, 0, | 689 | val = get_stub(me, val, addend, ELF_STUB_GOT, |
653 | is_init(me, loc)); | 690 | is_init(me, loc)); |
654 | } | 691 | } |
655 | DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", | 692 | DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", |