b6084a958f
In a reference to PR ld/19908 make ld.so respect symbol export classes aka visibility and treat STV_HIDDEN and STV_INTERNAL symbols as local, preventing such symbols from preempting exported symbols. According to the ELF gABI[1] neither STV_HIDDEN nor STV_INTERNAL symbols are supposed to be present in linked binaries: "A hidden symbol contained in a relocatable object must be either removed or converted to STB_LOCAL binding by the link-editor when the relocatable object is included in an executable file or shared object." "An internal symbol contained in a relocatable object must be either removed or converted to STB_LOCAL binding by the link-editor when the relocatable object is included in an executable file or shared object." however some GNU binutils versions produce such symbols in some cases. PR ld/19908 is one and we also have this note in scripts/abilist.awk: so clearly there is linked code out there which contains such symbols which is prone to symbol table misinterpretation, and it'll be more productive if we handle this gracefully, under the Robustness Principle: "be liberal in what you accept, and conservative in what you produce", especially as this is a simple (STV_HIDDEN|STV_INTERNAL) => STB_LOCAL mapping. References: [1] "System V Application Binary Interface - DRAFT - 24 April 2001", The Santa Cruz Operation, Inc., "Symbol Table", <http://www.sco.com/developers/gabi/2001-04-24/ch4.symtab.html> * sysdeps/generic/ldsodefs.h (dl_symbol_visibility_binds_local_p): New inline function. * elf/dl-addr.c (determine_info): Treat hidden and internal symbols as local. * elf/dl-lookup.c (do_lookup_x): Likewise. * elf/dl-reloc.c (RESOLVE_MAP): Likewise.
163 lines
5.0 KiB
C
163 lines
5.0 KiB
C
/* Locate the shared object symbol nearest a given address.
|
|
Copyright (C) 1996-2016 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <dlfcn.h>
|
|
#include <stddef.h>
|
|
#include <ldsodefs.h>
|
|
|
|
|
|
static inline void
|
|
__attribute ((always_inline))
|
|
determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
|
|
struct link_map **mapp, const ElfW(Sym) **symbolp)
|
|
{
|
|
/* Now we know what object the address lies in. */
|
|
info->dli_fname = match->l_name;
|
|
info->dli_fbase = (void *) match->l_map_start;
|
|
|
|
/* If this is the main program the information is incomplete. */
|
|
if (__builtin_expect (match->l_name[0], 'a') == '\0'
|
|
&& match->l_type == lt_executable)
|
|
info->dli_fname = _dl_argv[0];
|
|
|
|
const ElfW(Sym) *symtab
|
|
= (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]);
|
|
const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]);
|
|
|
|
ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
|
|
|
|
const ElfW(Sym) *matchsym = NULL;
|
|
if (match->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
|
|
+ DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] != NULL)
|
|
{
|
|
/* We look at all symbol table entries referenced by the hash
|
|
table. */
|
|
for (Elf_Symndx bucket = 0; bucket < match->l_nbuckets; ++bucket)
|
|
{
|
|
Elf32_Word symndx = match->l_gnu_buckets[bucket];
|
|
if (symndx != 0)
|
|
{
|
|
const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx];
|
|
|
|
do
|
|
{
|
|
/* The hash table never references local symbols so
|
|
we can omit that test here. */
|
|
if ((symtab[symndx].st_shndx != SHN_UNDEF
|
|
|| symtab[symndx].st_value != 0)
|
|
&& ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS
|
|
&& DL_ADDR_SYM_MATCH (match, &symtab[symndx],
|
|
matchsym, addr)
|
|
&& symtab[symndx].st_name < strtabsize)
|
|
matchsym = (ElfW(Sym) *) &symtab[symndx];
|
|
|
|
++symndx;
|
|
}
|
|
while ((*hasharr++ & 1u) == 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const ElfW(Sym) *symtabend;
|
|
if (match->l_info[DT_HASH] != NULL)
|
|
symtabend = (symtab
|
|
+ ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]);
|
|
else
|
|
/* There is no direct way to determine the number of symbols in the
|
|
dynamic symbol table and no hash table is present. The ELF
|
|
binary is ill-formed but what shall we do? Use the beginning of
|
|
the string table which generally follows the symbol table. */
|
|
symtabend = (const ElfW(Sym) *) strtab;
|
|
|
|
for (; (void *) symtab < (void *) symtabend; ++symtab)
|
|
if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
|
|
|| ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
|
|
&& __glibc_likely (!dl_symbol_visibility_binds_local_p (symtab))
|
|
&& ELFW(ST_TYPE) (symtab->st_info) != STT_TLS
|
|
&& (symtab->st_shndx != SHN_UNDEF
|
|
|| symtab->st_value != 0)
|
|
&& DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr)
|
|
&& symtab->st_name < strtabsize)
|
|
matchsym = (ElfW(Sym) *) symtab;
|
|
}
|
|
|
|
if (mapp)
|
|
*mapp = match;
|
|
if (symbolp)
|
|
*symbolp = matchsym;
|
|
|
|
if (matchsym)
|
|
{
|
|
/* We found a symbol close by. Fill in its name and exact
|
|
address. */
|
|
lookup_t matchl = LOOKUP_VALUE (match);
|
|
|
|
info->dli_sname = strtab + matchsym->st_name;
|
|
info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym);
|
|
}
|
|
else
|
|
{
|
|
/* No symbol matches. We return only the containing object. */
|
|
info->dli_sname = NULL;
|
|
info->dli_saddr = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
internal_function
|
|
_dl_addr (const void *address, Dl_info *info,
|
|
struct link_map **mapp, const ElfW(Sym) **symbolp)
|
|
{
|
|
const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address);
|
|
int result = 0;
|
|
|
|
/* Protect against concurrent loads and unloads. */
|
|
__rtld_lock_lock_recursive (GL(dl_load_lock));
|
|
|
|
struct link_map *l = _dl_find_dso_for_object (addr);
|
|
|
|
if (l)
|
|
{
|
|
determine_info (addr, l, info, mapp, symbolp);
|
|
result = 1;
|
|
}
|
|
|
|
__rtld_lock_unlock_recursive (GL(dl_load_lock));
|
|
|
|
return result;
|
|
}
|
|
libc_hidden_def (_dl_addr)
|
|
|
|
/* Return non-zero if ADDR lies within one of L's segments. */
|
|
int
|
|
internal_function
|
|
_dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr)
|
|
{
|
|
int n = l->l_phnum;
|
|
const ElfW(Addr) reladdr = addr - l->l_addr;
|
|
|
|
while (--n >= 0)
|
|
if (l->l_phdr[n].p_type == PT_LOAD
|
|
&& reladdr - l->l_phdr[n].p_vaddr >= 0
|
|
&& reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz)
|
|
return 1;
|
|
return 0;
|
|
}
|