From 0572433b5beb636de1a49ec6b4fdab830c38cdc5 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Wed, 14 Jun 2017 10:47:25 +0930 Subject: PowerPC64 ELFv2 PPC64_OPT_LOCALENTRY ELFv2 functions with localentry:0 are those with a single entry point, ie. global entry == local entry, that have no requirement on r2 or r12 and guarantee r2 is unchanged on return. Such an external function can be called via the PLT without saving r2 or restoring it on return, avoiding a common load-hit-store for small functions. This patch implements the ld.so changes necessary for this optimization. ld.so needs to check that an optimized plt call sequence is in fact calling a function implemented with localentry:0, end emit a fatal error otherwise. The elf/testobj6.c change is to stop "error while loading shared libraries: expected localentry:0 `preload'" when running elf/preloadtest, which we'd get otherwise. * elf/elf.h (PPC64_OPT_LOCALENTRY): Define. * sysdeps/alpha/dl-machine.h (elf_machine_fixup_plt): Add refsym and sym parameters. Adjust callers. * sysdeps/aarch64/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/arm/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/generic/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/hppa/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/i386/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/ia64/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/m68k/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/microblaze/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/mips/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/nios2/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/powerpc/powerpc32/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/s390/s390-32/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/s390/s390-64/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/sh/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/sparc/sparc32/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/sparc/sparc64/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/tile/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/x86_64/dl-machine.h (elf_machine_fixup_plt): Likewise. * sysdeps/powerpc/powerpc64/dl-machine.c (_dl_error_localentry): New. (_dl_reloc_overflow): Increase buffser size. Formatting. * sysdeps/powerpc/powerpc64/dl-machine.h (ppc64_local_entry_offset): Delete reloc param, add refsym and sym. Check optimized plt call stubs for localentry:0 functions. Adjust callers. (elf_machine_fixup_plt, elf_machine_plt_conflict): Add refsym and sym parameters. Adjust callers. (_dl_reloc_overflow): Move attribute. (_dl_error_localentry): Declare. * elf/dl-runtime.c (_dl_fixup): Save original sym. Pass refsym and sym to elf_machine_fixup_plt. * elf/testobj6.c (preload): Call printf. --- sysdeps/powerpc/powerpc64/dl-machine.c | 24 ++++++++++++--- sysdeps/powerpc/powerpc64/dl-machine.h | 54 ++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 26 deletions(-) (limited to 'sysdeps/powerpc/powerpc64') diff --git a/sysdeps/powerpc/powerpc64/dl-machine.c b/sysdeps/powerpc/powerpc64/dl-machine.c index 0eccc6621a..2cfd43b21b 100644 --- a/sysdeps/powerpc/powerpc64/dl-machine.c +++ b/sysdeps/powerpc/powerpc64/dl-machine.c @@ -24,11 +24,11 @@ void _dl_reloc_overflow (struct link_map *map, - const char *name, - Elf64_Addr *const reloc_addr, - const Elf64_Sym *refsym) + const char *name, + Elf64_Addr *const reloc_addr, + const Elf64_Sym *refsym) { - char buffer[128]; + char buffer[1024]; char *t; t = stpcpy (buffer, name); t = stpcpy (t, " reloc at 0x"); @@ -45,3 +45,19 @@ _dl_reloc_overflow (struct link_map *map, t = stpcpy (t, " out of range"); _dl_signal_error (0, map->l_name, NULL, buffer); } + +#if _CALL_ELF == 2 +void +_dl_error_localentry (struct link_map *map, const Elf64_Sym *refsym) +{ + char buffer[1024]; + char *t; + const char *strtab; + + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + t = stpcpy (buffer, "expected localentry:0 `"); + t = stpcpy (t, strtab + refsym->st_name); + t = stpcpy (t, "'"); + _dl_signal_error (0, map->l_name, NULL, buffer); +} +#endif diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h index 6391b3a558..aeb91b8f69 100644 --- a/sysdeps/powerpc/powerpc64/dl-machine.h +++ b/sysdeps/powerpc/powerpc64/dl-machine.h @@ -440,20 +440,30 @@ elf_machine_runtime_setup (struct link_map *map, int lazy, int profile) } #if _CALL_ELF == 2 -/* If the PLT entry whose reloc is 'reloc' resolves to a function in - the same object, return the target function's local entry point - offset if usable. */ +extern void attribute_hidden _dl_error_localentry (struct link_map *map, + const Elf64_Sym *refsym); + +/* If the PLT entry resolves to a function in the same object, return + the target function's local entry point offset if usable. */ static inline Elf64_Addr __attribute__ ((always_inline)) ppc64_local_entry_offset (struct link_map *map, lookup_t sym_map, - const Elf64_Rela *reloc) + const ElfW(Sym) *refsym, const ElfW(Sym) *sym) { - const Elf64_Sym *symtab; - const Elf64_Sym *sym; - /* If the target function is in a different object, we cannot use the local entry point. */ if (sym_map != map) - return 0; + { + /* Check that optimized plt call stubs for localentry:0 functions + are not being satisfied by a non-zero localentry symbol. */ + if (map->l_info[DT_PPC64(OPT)] + && (map->l_info[DT_PPC64(OPT)]->d_un.d_val & PPC64_OPT_LOCALENTRY) != 0 + && refsym->st_info == ELFW(ST_INFO) (STB_GLOBAL, STT_FUNC) + && (STO_PPC64_LOCAL_MASK & refsym->st_other) == 0 + && (STO_PPC64_LOCAL_MASK & sym->st_other) != 0) + _dl_error_localentry (map, refsym); + + return 0; + } /* If the linker inserted multiple TOCs, we cannot use the local entry point. */ @@ -461,16 +471,13 @@ ppc64_local_entry_offset (struct link_map *map, lookup_t sym_map, && (map->l_info[DT_PPC64(OPT)]->d_un.d_val & PPC64_OPT_MULTI_TOC)) return 0; - /* Otherwise, we can use the local entry point. Retrieve its offset - from the symbol's ELF st_other field. */ - symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); - sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; - /* If the target function is an ifunc then the local entry offset is for the resolver, not the final destination. */ if (__builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)) return 0; + /* Otherwise, we can use the local entry point. Retrieve its offset + from the symbol's ELF st_other field. */ return PPC64_LOCAL_ENTRY_OFFSET (sym->st_other); } #endif @@ -479,6 +486,7 @@ ppc64_local_entry_offset (struct link_map *map, lookup_t sym_map, routine. */ static inline Elf64_Addr __attribute__ ((always_inline)) elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map, + const ElfW(Sym) *refsym, const ElfW(Sym) *sym, const Elf64_Rela *reloc, Elf64_Addr *reloc_addr, Elf64_Addr finaladdr) { @@ -534,7 +542,7 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map, PPC_DCBST (&plt->fd_func); PPC_ISYNC; #else - finaladdr += ppc64_local_entry_offset (map, sym_map, reloc); + finaladdr += ppc64_local_entry_offset (map, sym_map, refsym, sym); *reloc_addr = finaladdr; #endif @@ -543,6 +551,7 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map, static inline void __attribute__ ((always_inline)) elf_machine_plt_conflict (struct link_map *map, lookup_t sym_map, + const ElfW(Sym) *refsym, const ElfW(Sym) *sym, const Elf64_Rela *reloc, Elf64_Addr *reloc_addr, Elf64_Addr finaladdr) { @@ -565,7 +574,7 @@ elf_machine_plt_conflict (struct link_map *map, lookup_t sym_map, PPC_DCBST (&plt->fd_toc); PPC_SYNC; #else - finaladdr += ppc64_local_entry_offset (map, sym_map, reloc); + finaladdr += ppc64_local_entry_offset (map, sym_map, refsym, sym); *reloc_addr = finaladdr; #endif } @@ -604,11 +613,10 @@ elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc, #define dont_expect(X) __builtin_expect ((X), 0) -extern void _dl_reloc_overflow (struct link_map *map, - const char *name, - Elf64_Addr *const reloc_addr, - const Elf64_Sym *refsym) - attribute_hidden; +extern void attribute_hidden _dl_reloc_overflow (struct link_map *map, + const char *name, + Elf64_Addr *const reloc_addr, + const Elf64_Sym *refsym); auto inline void __attribute__ ((always_inline)) elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc, @@ -728,9 +736,11 @@ elf_machine_rela (struct link_map *map, /* Fall thru */ case R_PPC64_JMP_SLOT: #ifdef RESOLVE_CONFLICT_FIND_MAP - elf_machine_plt_conflict (map, sym_map, reloc, reloc_addr, value); + elf_machine_plt_conflict (map, sym_map, refsym, sym, + reloc, reloc_addr, value); #else - elf_machine_fixup_plt (map, sym_map, reloc, reloc_addr, value); + elf_machine_fixup_plt (map, sym_map, refsym, sym, + reloc, reloc_addr, value); #endif return; -- cgit 1.4.1