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 | |
parent | 6e76c11f89e9bd0b8bb7185dc754bf6c7dac572b (diff) | |
download | glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.zip glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.tar.gz glibc-48025aa9ed3b9a5f5f3b1310eec79b66fb645c17.tar.bz2 |
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.
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | sysdeps/hppa/dl-fptr.c | 59 | ||||
-rw-r--r-- | sysdeps/hppa/dl-lookupcfg.h | 4 |
3 files changed, 55 insertions, 17 deletions
@@ -1,3 +1,12 @@ +2016-01-08 John David Anglin <danglin@gcc.gnu.org> + + [BZ #19415] + * sysdeps/hppa/dl-fptr.c (_dl_fixup): Declare. + (elf_machine_resolve): New. Return address of _dl_runtime_resolve. + (_dl_lookup_address): Rewrite using function resolver trampoline. + * sysdeps/hppa/dl-lookupcfg.h (DL_LOOKUP_ADDRESS): Don't clear bottom + two bits in address. + 2016-01-07 Mike Frysinger <vapier@gentoo.org> * longlong.h: Change !__SHMEDIA__ to diff --git a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c index 6b2e331..083242b 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]; +} diff --git a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h index 998180c..3f1d14a 100644 --- a/sysdeps/hppa/dl-lookupcfg.h +++ b/sysdeps/hppa/dl-lookupcfg.h @@ -31,9 +31,7 @@ rtld_hidden_proto (_dl_symbol_address) Elf32_Addr _dl_lookup_address (const void *address); -/* Clear the bottom two bits so generic code can find the fdesc entry */ -#define DL_LOOKUP_ADDRESS(addr) \ - (_dl_lookup_address ((void *)((unsigned long)addr & ~3))) +#define DL_LOOKUP_ADDRESS(addr) _dl_lookup_address ((const void *) addr) void attribute_hidden _dl_unmap (struct link_map *map); |