Blame libdwfl/dwfl_module_addrsym.c

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