Blame libdwfl/dwfl_module_getsym.c

Packit Service 97d2fb
/* Find debugging and symbol information for a module in libdwfl.
Packit Service 97d2fb
   Copyright (C) 2006-2014 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr,
Packit Service 97d2fb
		  GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp,
Packit Service 97d2fb
		  bool *resolved, bool adjust_st_value)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (unlikely (mod == NULL))
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (mod->symdata == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      int result = INTUSE(dwfl_module_getsymtab) (mod);
Packit Service 97d2fb
      if (result < 0)
Packit Service 97d2fb
	return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* All local symbols should come before all global symbols.  If we
Packit Service 97d2fb
     have an auxiliary table make sure all the main locals come first,
Packit Service 97d2fb
     then all aux locals, then all main globals and finally all aux globals.
Packit Service 97d2fb
     And skip the auxiliary table zero undefined entry.  */
Packit Service 97d2fb
  GElf_Word shndx;
Packit Service 97d2fb
  int tndx = ndx;
Packit Service 97d2fb
  int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
  Elf_Data *symdata;
Packit Service 97d2fb
  Elf_Data *symxndxdata;
Packit Service 97d2fb
  Elf_Data *symstrdata;
Packit Service 97d2fb
  if (mod->aux_symdata == NULL
Packit Service 97d2fb
      || ndx < mod->first_global)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* main symbol table (locals).  */
Packit Service 97d2fb
      tndx = ndx;
Packit Service 97d2fb
      elf = mod->symfile->elf;
Packit Service 97d2fb
      symdata = mod->symdata;
Packit Service 97d2fb
      symxndxdata = mod->symxndxdata;
Packit Service 97d2fb
      symstrdata = mod->symstrdata;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* aux symbol table (locals).  */
Packit Service 97d2fb
      tndx = ndx - mod->first_global + skip_aux_zero;
Packit Service 97d2fb
      elf = mod->aux_sym.elf;
Packit Service 97d2fb
      symdata = mod->aux_symdata;
Packit Service 97d2fb
      symxndxdata = mod->aux_symxndxdata;
Packit Service 97d2fb
      symstrdata = mod->aux_symstrdata;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* main symbol table (globals).  */
Packit Service 97d2fb
      tndx = ndx - mod->aux_first_global + skip_aux_zero;
Packit Service 97d2fb
      elf = mod->symfile->elf;
Packit Service 97d2fb
      symdata = mod->symdata;
Packit Service 97d2fb
      symxndxdata = mod->symxndxdata;
Packit Service 97d2fb
      symstrdata = mod->symstrdata;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* aux symbol table (globals).  */
Packit Service 97d2fb
      tndx = ndx - mod->syments + skip_aux_zero;
Packit Service 97d2fb
      elf = mod->aux_sym.elf;
Packit Service 97d2fb
      symdata = mod->aux_symdata;
Packit Service 97d2fb
      symxndxdata = mod->aux_symxndxdata;
Packit Service 97d2fb
      symstrdata = mod->aux_symstrdata;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (sym == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (sym->st_shndx != SHN_XINDEX)
Packit Service 97d2fb
    shndx = sym->st_shndx;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Figure out whether this symbol points into an SHF_ALLOC section.  */
Packit Service 97d2fb
  bool alloc = true;
Packit Service 97d2fb
  if ((shndxp != NULL || mod->e_type != ET_REL)
Packit Service 97d2fb
      && (sym->st_shndx == SHN_XINDEX
Packit Service 97d2fb
	  || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      GElf_Shdr shdr_mem;
Packit Service 97d2fb
      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
Packit Service 97d2fb
      alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* In case of an value in an allocated section the main Elf Ebl
Packit Service 97d2fb
     might know where the real value is (e.g. for function
Packit Service 97d2fb
     descriptors).  */
Packit Service 97d2fb
Packit Service 97d2fb
  char *ident;
Packit Service 97d2fb
  GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl);
Packit Service 97d2fb
  *resolved = false;
Packit Service 97d2fb
  if (! adjust_st_value && mod->e_type != ET_REL && alloc
Packit Service 97d2fb
      && (GELF_ST_TYPE (sym->st_info) == STT_FUNC
Packit Service 97d2fb
	  || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
Packit Service 97d2fb
	      && (ident = elf_getident (elf, NULL)) != NULL
Packit Service 97d2fb
	      && ident[EI_OSABI] == ELFOSABI_LINUX)))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (elf != mod->main.elf)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      st_value = dwfl_adjusted_st_value (mod, elf, st_value);
Packit Service 97d2fb
	      st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  *resolved = ebl_resolve_sym_value (mod->ebl, &st_value);
Packit Service 97d2fb
	  if (! *resolved)
Packit Service 97d2fb
	    st_value = sym->st_value;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (shndxp != NULL)
Packit Service 97d2fb
    /* Yield -1 in case of a non-SHF_ALLOC section.  */
Packit Service 97d2fb
    *shndxp = alloc ? shndx : (GElf_Word) -1;
Packit Service 97d2fb
Packit Service 97d2fb
  switch (sym->st_shndx)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case SHN_ABS:		/* XXX sometimes should use bias?? */
Packit Service 97d2fb
    case SHN_UNDEF:
Packit Service 97d2fb
    case SHN_COMMON:
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      if (mod->e_type == ET_REL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* In an ET_REL file, the symbol table values are relative
Packit Service 97d2fb
	     to the section, not to the module's load base.  */
Packit Service 97d2fb
	  size_t symshstrndx = SHN_UNDEF;
Packit Service 97d2fb
	  Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
Packit Service 97d2fb
							&symshstrndx,
Packit Service 97d2fb
							shndx, &st_value);
Packit Service 97d2fb
	  if (unlikely (result != DWFL_E_NOERROR))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (result);
Packit Service 97d2fb
	      return NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else if (alloc)
Packit Service 97d2fb
	/* Apply the bias to the symbol value.  */
Packit Service 97d2fb
	st_value = dwfl_adjusted_st_value (mod,
Packit Service 97d2fb
					   *resolved ? mod->main.elf : elf,
Packit Service 97d2fb
					   st_value);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (adjust_st_value)
Packit Service 97d2fb
    sym->st_value = st_value;
Packit Service 97d2fb
Packit Service 97d2fb
  if (addr != NULL)
Packit Service 97d2fb
    *addr = st_value;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (sym->st_name >= symstrdata->d_size))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_BADSTROFF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (elfp)
Packit Service 97d2fb
    *elfp = elf;
Packit Service 97d2fb
  if (biasp)
Packit Service 97d2fb
    *biasp = dwfl_adjusted_st_value (mod, elf, 0);
Packit Service 97d2fb
  return (const char *) symstrdata->d_buf + sym->st_name;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
dwfl_module_getsym_info (Dwfl_Module *mod, int ndx,
Packit Service 97d2fb
			 GElf_Sym *sym, GElf_Addr *addr,
Packit Service 97d2fb
			 GElf_Word *shndxp,
Packit Service 97d2fb
			 Elf **elfp, Dwarf_Addr *bias)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool resolved;
Packit Service 97d2fb
  return __libdwfl_getsym (mod, ndx, sym, addr, shndxp, elfp, bias,
Packit Service 97d2fb
			   &resolved, false);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_getsym_info)
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
dwfl_module_getsym (Dwfl_Module *mod, int ndx,
Packit Service 97d2fb
		    GElf_Sym *sym, GElf_Word *shndxp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool resolved;
Packit Service 97d2fb
  return __libdwfl_getsym (mod, ndx, sym, NULL, shndxp, NULL, NULL,
Packit Service 97d2fb
			   &resolved, true);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_getsym)