diff options
| -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", |
