From 48025aa9ed3b9a5f5f3b1310eec79b66fb645c17 Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Sat, 2 Jan 2016 09:48:18 -0500 Subject: [PATCH] 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. --- ChangeLog | 9 ++++++ sysdeps/hppa/dl-fptr.c | 59 ++++++++++++++++++++++++++++--------- sysdeps/hppa/dl-lookupcfg.h | 4 +-- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4db27e679f..56298db37c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2016-01-08 John David Anglin + + [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 * longlong.h: Change !__SHMEDIA__ to 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]; +} diff --git a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h index 998180c81d..3f1d14aa2f 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);