Blame libdwfl/dwfl_module_addrsym.c

Packit Service 97d2fb
/* Find debugging and symbol information for a module in libdwfl.
Packit Service 97d2fb
   Copyright (C) 2005-2013 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
struct search_state
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Module *mod;
Packit Service 97d2fb
  GElf_Addr addr;
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Sym *closest_sym;
Packit Service 97d2fb
  bool adjust_st_value;
Packit Service 97d2fb
  GElf_Word addr_shndx;
Packit Service 97d2fb
  Elf *addr_symelf;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Keep track of the closest symbol we have seen so far.
Packit Service 97d2fb
     Here we store only symbols with nonzero st_size.  */
Packit Service 97d2fb
  const char *closest_name;
Packit Service 97d2fb
  GElf_Addr closest_value;
Packit Service 97d2fb
  GElf_Word closest_shndx;
Packit Service 97d2fb
  Elf *closest_elf;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Keep track of an eligible symbol with st_size == 0 as a fallback.  */
Packit Service 97d2fb
  const char *sizeless_name;
Packit Service 97d2fb
  GElf_Sym sizeless_sym;
Packit Service 97d2fb
  GElf_Addr sizeless_value;
Packit Service 97d2fb
  GElf_Word sizeless_shndx;
Packit Service 97d2fb
  Elf *sizeless_elf;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Keep track of the lowest address a relevant sizeless symbol could have.  */
Packit Service 97d2fb
  GElf_Addr min_label;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
/* Return true iff we consider ADDR to lie in the same section as SYM.  */
Packit Service 97d2fb
static inline bool
Packit Service 97d2fb
same_section (struct search_state *state,
Packit Service 97d2fb
	      GElf_Addr value, Elf *symelf, GElf_Word shndx)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* For absolute symbols and the like, only match exactly.  */
Packit Service 97d2fb
  if (shndx >= SHN_LORESERVE)
Packit Service 97d2fb
    return value == state->addr;
Packit Service 97d2fb
Packit Service 97d2fb
  /* If value might not be st_value, the shndx of the symbol might
Packit Service 97d2fb
      not match the section of the value. Explicitly look both up.  */
Packit Service 97d2fb
  if (! state->adjust_st_value)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Addr v;
Packit Service 97d2fb
      if (state->addr_shndx == SHN_UNDEF)
Packit Service 97d2fb
        {
Packit Service 97d2fb
          v = state->addr;
Packit Service 97d2fb
          state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
Packit Service 97d2fb
        }
Packit Service 97d2fb
Packit Service 97d2fb
      v = value;
Packit Service 97d2fb
      return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Figure out what section ADDR lies in.  */
Packit Service 97d2fb
  if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
Packit Service 97d2fb
						   state->addr);
Packit Service 97d2fb
      Elf_Scn *scn = NULL;
Packit Service 97d2fb
      state->addr_shndx = SHN_ABS;
Packit Service 97d2fb
      state->addr_symelf = symelf;
Packit Service 97d2fb
      while ((scn = elf_nextscn (symelf, scn)) != NULL)
Packit Service 97d2fb
        {
Packit Service 97d2fb
          GElf_Shdr shdr_mem;
Packit Service 97d2fb
          GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit Service 97d2fb
          if (likely (shdr != NULL)
Packit Service 97d2fb
              && mod_addr >= shdr->sh_addr
Packit Service 97d2fb
              && mod_addr < shdr->sh_addr + shdr->sh_size)
Packit Service 97d2fb
            {
Packit Service 97d2fb
              state->addr_shndx = elf_ndxscn (scn);
Packit Service 97d2fb
              break;
Packit Service 97d2fb
            }
Packit Service 97d2fb
        }
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return shndx == state->addr_shndx && state->addr_symelf == symelf;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Return GELF_ST_BIND as higher-is-better integer.  */
Packit Service 97d2fb
static inline int
Packit Service 97d2fb
binding_value (const GElf_Sym *symp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  switch (GELF_ST_BIND (symp->st_info))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case STB_GLOBAL:
Packit Service 97d2fb
      return 3;
Packit Service 97d2fb
    case STB_WEAK:
Packit Service 97d2fb
      return 2;
Packit Service 97d2fb
    case STB_LOCAL:
Packit Service 97d2fb
      return 1;
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      return 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Try one symbol and associated value from the search table.  */
Packit Service 97d2fb
static inline void
Packit Service 97d2fb
try_sym_value (struct search_state *state,
Packit Service 97d2fb
               GElf_Addr value, GElf_Sym *sym,
Packit Service 97d2fb
               const char *name, GElf_Word shndx,
Packit Service 97d2fb
               Elf *elf, bool resolved)
Packit Service 97d2fb
{
Packit Service 97d2fb
    /* Even if we don't choose this symbol, its existence excludes
Packit Service 97d2fb
       any sizeless symbol (assembly label) that is below its upper
Packit Service 97d2fb
       bound.  */
Packit Service 97d2fb
    if (value + sym->st_size > state->min_label)
Packit Service 97d2fb
      state->min_label = value + sym->st_size;
Packit Service 97d2fb
Packit Service 97d2fb
    if (sym->st_size == 0 || state->addr - value < sym->st_size)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	/* This symbol is a better candidate than the current one
Packit Service 97d2fb
	   if it's closer to ADDR or is global when it was local.  */
Packit Service 97d2fb
	if (state->closest_name == NULL
Packit Service 97d2fb
	    || state->closest_value < value
Packit Service 97d2fb
	    || binding_value (state->closest_sym) < binding_value (sym))
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    if (sym->st_size != 0)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		*state->closest_sym = *sym;
Packit Service 97d2fb
		state->closest_value = value;
Packit Service 97d2fb
		state->closest_shndx = shndx;
Packit Service 97d2fb
		state->closest_elf = elf;
Packit Service 97d2fb
		state->closest_name = name;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	    else if (state->closest_name == NULL
Packit Service 97d2fb
		     && value >= state->min_label
Packit Service 97d2fb
		     && same_section (state, value,
Packit Service 97d2fb
				      resolved ? state->mod->main.elf : elf,
Packit Service 97d2fb
				      shndx))
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		/* Handwritten assembly symbols sometimes have no
Packit Service 97d2fb
		   st_size.  If no symbol with proper size includes
Packit Service 97d2fb
		   the address, we'll use the closest one that is in
Packit Service 97d2fb
		   the same section as ADDR.  */
Packit Service 97d2fb
		state->sizeless_sym = *sym;
Packit Service 97d2fb
		state->sizeless_value = value;
Packit Service 97d2fb
		state->sizeless_shndx = shndx;
Packit Service 97d2fb
		state->sizeless_elf = elf;
Packit Service 97d2fb
		state->sizeless_name = name;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	/* When the beginning of its range is no closer,
Packit Service 97d2fb
	   the end of its range might be.  Otherwise follow
Packit Service 97d2fb
	   GELF_ST_BIND preference.  If all are equal prefer
Packit Service 97d2fb
	   the first symbol found.  */
Packit Service 97d2fb
	else if (sym->st_size != 0
Packit Service 97d2fb
		 && state->closest_value == value
Packit Service 97d2fb
		 && ((state->closest_sym->st_size > sym->st_size
Packit Service 97d2fb
		      && (binding_value (state->closest_sym)
Packit Service 97d2fb
			  <= binding_value (sym)))
Packit Service 97d2fb
		     || (state->closest_sym->st_size >= sym->st_size
Packit Service 97d2fb
			 && (binding_value (state->closest_sym)
Packit Service 97d2fb
			     < binding_value (sym)))))
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    *state->closest_sym = *sym;
Packit Service 97d2fb
	    state->closest_value = value;
Packit Service 97d2fb
	    state->closest_shndx = shndx;
Packit Service 97d2fb
	    state->closest_elf = elf;
Packit Service 97d2fb
	    state->closest_name = name;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
      }
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Look through the symbol table for a matching symbol.  */
Packit Service 97d2fb
static inline void
Packit Service 97d2fb
search_table (struct search_state *state, int start, int end)
Packit Service 97d2fb
{
Packit Service 97d2fb
      for (int i = start; i < end; ++i)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  GElf_Sym sym;
Packit Service 97d2fb
	  GElf_Addr value;
Packit Service 97d2fb
	  GElf_Word shndx;
Packit Service 97d2fb
	  Elf *elf;
Packit Service 97d2fb
	  bool resolved;
Packit Service 97d2fb
	  const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
Packit Service 97d2fb
					       &shndx, &elf, NULL,
Packit Service 97d2fb
					       &resolved,
Packit Service 97d2fb
					       state->adjust_st_value);
Packit Service 97d2fb
	  if (name != NULL && name[0] != '\0'
Packit Service 97d2fb
	      && sym.st_shndx != SHN_UNDEF
Packit Service 97d2fb
	      && value <= state->addr
Packit Service 97d2fb
	      && GELF_ST_TYPE (sym.st_info) != STT_SECTION
Packit Service 97d2fb
	      && GELF_ST_TYPE (sym.st_info) != STT_FILE
Packit Service 97d2fb
	      && GELF_ST_TYPE (sym.st_info) != STT_TLS)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      try_sym_value (state, value, &sym, name, shndx, elf, resolved);
Packit Service 97d2fb
Packit Service 97d2fb
	      /* If this is an addrinfo variant and the value could be
Packit Service 97d2fb
		 resolved then also try matching the (adjusted) st_value.  */
Packit Service 97d2fb
	      if (resolved && state->mod->e_type != ET_REL)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  GElf_Addr adjusted_st_value;
Packit Service 97d2fb
		  adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
Packit Service 97d2fb
							      sym.st_value);
Packit Service 97d2fb
		  if (value != adjusted_st_value
Packit Service 97d2fb
		      && adjusted_st_value <= state->addr)
Packit Service 97d2fb
		    try_sym_value (state, adjusted_st_value, &sym, name, shndx,
Packit Service 97d2fb
				   elf, false);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Returns the name of the symbol "closest" to ADDR.
Packit Service 97d2fb
   Never returns symbols at addresses above ADDR.
Packit Service 97d2fb
Packit Service 97d2fb
   Wrapper for old dwfl_module_addrsym and new dwfl_module_addrinfo.
Packit Service 97d2fb
   adjust_st_value set to true returns adjusted SYM st_value, set to false
Packit Service 97d2fb
   it will not adjust SYM at all, but does match against resolved values.   */
Packit Service 97d2fb
static const char *
Packit Service 97d2fb
__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
Packit Service 97d2fb
		   GElf_Sym *_closest_sym, GElf_Word *shndxp,
Packit Service 97d2fb
		   Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int syments = INTUSE(dwfl_module_getsymtab) (_mod);
Packit Service 97d2fb
  if (syments < 0)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  struct search_state state =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      .addr = _addr,
Packit Service 97d2fb
      .mod = _mod,
Packit Service 97d2fb
      .closest_sym = _closest_sym,
Packit Service 97d2fb
      .adjust_st_value = _adjust_st_value,
Packit Service 97d2fb
      .addr_shndx = SHN_UNDEF,
Packit Service 97d2fb
      .addr_symelf = NULL,
Packit Service 97d2fb
      .closest_name = NULL,
Packit Service 97d2fb
      .closest_value = 0,
Packit Service 97d2fb
      .closest_shndx = SHN_UNDEF,
Packit Service 97d2fb
      .closest_elf = NULL,
Packit Service 97d2fb
      .sizeless_name = NULL,
Packit Service 97d2fb
      .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
Packit Service 97d2fb
      .sizeless_value = 0,
Packit Service 97d2fb
      .sizeless_shndx = SHN_UNDEF,
Packit Service 97d2fb
      .sizeless_elf = NULL,
Packit Service 97d2fb
      .min_label = 0
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
  /* First go through global symbols.  mod->first_global and
Packit Service 97d2fb
     mod->aux_first_global are setup by dwfl_module_getsymtab to the
Packit Service 97d2fb
     index of the first global symbol in those symbol tables.  Both
Packit Service 97d2fb
     are non-zero when the table exist, except when there is only a
Packit Service 97d2fb
     dynsym table loaded through phdrs, then first_global is zero and
Packit Service 97d2fb
     there will be no auxiliary table.  All symbols with local binding
Packit Service 97d2fb
     come first in the symbol table, then all globals.  The zeroth,
Packit Service 97d2fb
     null entry, in the auxiliary table is skipped if there is a main
Packit Service 97d2fb
     table.  */
Packit Service 97d2fb
  int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
Packit Service 97d2fb
  if (first_global < 0)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
  search_table (&state, first_global == 0 ? 1 : first_global, syments);
Packit Service 97d2fb
Packit Service 97d2fb
  /* If we found nothing searching the global symbols, then try the locals.
Packit Service 97d2fb
     Unless we have a global sizeless symbol that matches exactly.  */
Packit Service 97d2fb
  if (state.closest_name == NULL && first_global > 1
Packit Service 97d2fb
      && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
Packit Service 97d2fb
    search_table (&state, 1, first_global);
Packit Service 97d2fb
Packit Service 97d2fb
  /* If we found no proper sized symbol to use, fall back to the best
Packit Service 97d2fb
     candidate sizeless symbol we found, if any.  */
Packit Service 97d2fb
  if (state.closest_name == NULL
Packit Service 97d2fb
      && state.sizeless_name != NULL
Packit Service 97d2fb
      && state.sizeless_value >= state.min_label)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      *state.closest_sym = state.sizeless_sym;
Packit Service 97d2fb
      state.closest_value = state.sizeless_value;
Packit Service 97d2fb
      state.closest_shndx = state.sizeless_shndx;
Packit Service 97d2fb
      state.closest_elf = state.sizeless_elf;
Packit Service 97d2fb
      state.closest_name = state.sizeless_name;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  *off = state.addr - state.closest_value;
Packit Service 97d2fb
Packit Service 97d2fb
  if (shndxp != NULL)
Packit Service 97d2fb
    *shndxp = state.closest_shndx;
Packit Service 97d2fb
  if (elfp != NULL)
Packit Service 97d2fb
    *elfp = state.closest_elf;
Packit Service 97d2fb
  if (biasp != NULL)
Packit Service 97d2fb
    *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
Packit Service 97d2fb
  return state.closest_name;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
Packit Service 97d2fb
		     GElf_Sym *closest_sym, GElf_Word *shndxp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  GElf_Off off;
Packit Service 97d2fb
  return __libdwfl_addrsym (mod, addr, &off, closest_sym, shndxp,
Packit Service 97d2fb
			    NULL, NULL, true);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_addrsym)
Packit Service 97d2fb
Packit Service 97d2fb
const char
Packit Service 97d2fb
*dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address,
Packit Service 97d2fb
		       GElf_Off *offset, GElf_Sym *sym,
Packit Service 97d2fb
		       GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *bias)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return __libdwfl_addrsym (mod, address, offset, sym, shndxp, elfp, bias,
Packit Service 97d2fb
			    false);
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_addrinfo)