diff options
author | John David Anglin <dave.anglin@bell.net> | 2016-01-02 09:48:18 -0500 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2016-01-08 02:19:26 -0500 |
commit | 48025aa9ed3b9a5f5f3b1310eec79b66fb645c17 (patch) | |
tree | ca262547243148a546d8989f77b5180782374083 /sysdeps/hppa/dl-fptr.c | |
parent | 6e76c11f89e9bd0b8bb7185dc754bf6c7dac572b (diff) | |
download | glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.tar.gz glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.tar.xz glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.zip |
hppa: fix dladdr [BZ #19415]
The attached patch fixes dladdr on hppa. Instead of using the generic version of _dl_lookup_address, we use an implementation more or less modeled after __canonicalize_funcptr_for_compare() in gcc. The function pointer is analyzed and if it points to the trampoline used to call _dl_runtime_resolve just before the global offset table, then we call _dl_fixup to resolve the function pointer. Then, we return the instruction pointer from the first word of the descriptor. The change fixes the testcase provided in [BZ #19415] and the Debian nss package now builds successfully.
Diffstat (limited to 'sysdeps/hppa/dl-fptr.c')
-rw-r--r-- | sysdeps/hppa/dl-fptr.c | 59 |
1 files changed, 45 insertions, 14 deletions
diff --git a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c index 6b2e331f28..083242b7e3 100644 --- a/sysdeps/hppa/dl-fptr.c +++ b/sysdeps/hppa/dl-fptr.c @@ -315,23 +315,54 @@ _dl_unmap (struct link_map *map) map->l_mach.fptr_table = NULL; } +extern ElfW(Addr) _dl_fixup (struct link_map *, ElfW(Word)) attribute_hidden; -ElfW(Addr) -_dl_lookup_address (const void *address) +static inline Elf32_Addr +elf_machine_resolve (void) { - ElfW(Addr) addr = (ElfW(Addr)) address; - struct fdesc_table *t; - unsigned long int i; + Elf32_Addr addr; - for (t = local.root; t != NULL; t = t->next) - { - i = (struct fdesc *) addr - &t->fdesc[0]; - if (i < t->first_unused && addr == (ElfW(Addr)) &t->fdesc[i]) - { - addr = t->fdesc[i].ip; - break; - } - } + asm ("b,l 1f,%0\n" +" depi 0,31,2,%0\n" +"1: addil L'_dl_runtime_resolve - ($PIC_pcrel$0 - 8),%0\n" +" ldo R'_dl_runtime_resolve - ($PIC_pcrel$0 - 12)(%%r1),%0\n" + : "=r" (addr) : : "r1"); return addr; } + +ElfW(Addr) +_dl_lookup_address (const void *address) +{ + ElfW(Addr) addr = (ElfW(Addr)) address; + unsigned int *desc, *gptr; + + /* Check for special cases. */ + if ((int) addr == -1 + || (unsigned int) addr < 4096 + || !((unsigned int) addr & 2)) + return addr; + + /* Clear least-significant two bits from descriptor address. */ + desc = (unsigned int *) ((unsigned int) addr & ~3); + + /* Check if descriptor requires resolution. The following trampoline is + used in each global offset table for function resolution: + + ldw 0(r20),r22 + bv r0(r22) + ldw 4(r20),r21 + tramp: b,l .-12,r20 + depwi 0,31,2,r20 + .word _dl_runtime_resolve + .word "_dl_runtime_resolve ltp" + got: .word _DYNAMIC + .word "struct link map address" */ + gptr = (unsigned int *) desc[0]; + if (gptr[0] == 0xea9f1fdd /* b,l .-12,r20 */ + && gptr[1] == 0xd6801c1e /* depwi 0,31,2,r20 */ + && (ElfW(Addr)) gptr[2] == elf_machine_resolve ()) + _dl_fixup ((struct link_map *) gptr[5], (ElfW(Word)) desc[1]); + + return (ElfW(Addr)) desc[0]; +} |