LCOV - code coverage report
Current view: top level - libdwfl - dwfl_module_addrsym.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 88 103 85.4 %
Date: 2017-01-05 09:15:16 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Find debugging and symbol information for a module in libdwfl.
       2             :    Copyright (C) 2005-2013 Red Hat, Inc.
       3             :    This file is part of elfutils.
       4             : 
       5             :    This file is free software; you can redistribute it and/or modify
       6             :    it under the terms of either
       7             : 
       8             :      * the GNU Lesser General Public License as published by the Free
       9             :        Software Foundation; either version 3 of the License, or (at
      10             :        your option) any later version
      11             : 
      12             :    or
      13             : 
      14             :      * the GNU General Public License as published by the Free
      15             :        Software Foundation; either version 2 of the License, or (at
      16             :        your option) any later version
      17             : 
      18             :    or both in parallel, as here.
      19             : 
      20             :    elfutils is distributed in the hope that it will be useful, but
      21             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      22             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23             :    General Public License for more details.
      24             : 
      25             :    You should have received copies of the GNU General Public License and
      26             :    the GNU Lesser General Public License along with this program.  If
      27             :    not, see <http://www.gnu.org/licenses/>.  */
      28             : 
      29             : #include "libdwflP.h"
      30             : 
      31             : struct search_state
      32             : {
      33             :   Dwfl_Module *mod;
      34             :   GElf_Addr addr;
      35             : 
      36             :   GElf_Sym *closest_sym;
      37             :   bool adjust_st_value;
      38             :   GElf_Word addr_shndx;
      39             :   Elf *addr_symelf;
      40             : 
      41             :   /* Keep track of the closest symbol we have seen so far.
      42             :      Here we store only symbols with nonzero st_size.  */
      43             :   const char *closest_name;
      44             :   GElf_Addr closest_value;
      45             :   GElf_Word closest_shndx;
      46             :   Elf *closest_elf;
      47             : 
      48             :   /* Keep track of an eligible symbol with st_size == 0 as a fallback.  */
      49             :   const char *sizeless_name;
      50             :   GElf_Sym sizeless_sym;
      51             :   GElf_Addr sizeless_value;
      52             :   GElf_Word sizeless_shndx;
      53             :   Elf *sizeless_elf;
      54             : 
      55             :   /* Keep track of the lowest address a relevant sizeless symbol could have.  */
      56             :   GElf_Addr min_label;
      57             : };
      58             : 
      59             : /* Return true iff we consider ADDR to lie in the same section as SYM.  */
      60             : static inline bool
      61       56441 : same_section (struct search_state *state,
      62             :               GElf_Addr value, Elf *symelf, GElf_Word shndx)
      63             : {
      64             :   /* For absolute symbols and the like, only match exactly.  */
      65       56441 :   if (shndx >= SHN_LORESERVE)
      66       46858 :     return value == state->addr;
      67             : 
      68             :   /* If value might not be st_value, the shndx of the symbol might
      69             :       not match the section of the value. Explicitly look both up.  */
      70        9583 :   if (! state->adjust_st_value)
      71             :     {
      72             :       Dwarf_Addr v;
      73        9583 :       if (state->addr_shndx == SHN_UNDEF)
      74             :         {
      75        6388 :           v = state->addr;
      76        6388 :           state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
      77             :         }
      78             : 
      79        9583 :       v = value;
      80        9583 :       return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
      81             :     }
      82             : 
      83             :   /* Figure out what section ADDR lies in.  */
      84           0 :   if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
      85             :     {
      86           0 :       GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
      87             :                                                    state->addr);
      88           0 :       Elf_Scn *scn = NULL;
      89           0 :       state->addr_shndx = SHN_ABS;
      90           0 :       state->addr_symelf = symelf;
      91           0 :       while ((scn = elf_nextscn (symelf, scn)) != NULL)
      92             :         {
      93             :           GElf_Shdr shdr_mem;
      94           0 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      95           0 :           if (likely (shdr != NULL)
      96           0 :               && mod_addr >= shdr->sh_addr
      97           0 :               && mod_addr < shdr->sh_addr + shdr->sh_size)
      98             :             {
      99           0 :               state->addr_shndx = elf_ndxscn (scn);
     100           0 :               break;
     101             :             }
     102             :         }
     103             :     }
     104             : 
     105           0 :   return shndx == state->addr_shndx && state->addr_symelf == symelf;
     106             : }
     107             : 
     108             : /* Return GELF_ST_BIND as higher-is-better integer.  */
     109             : static inline int
     110             : binding_value (const GElf_Sym *symp)
     111             : {
     112     2884398 :   switch (GELF_ST_BIND (symp->st_info))
     113             :     {
     114             :     case STB_GLOBAL:
     115             :       return 3;
     116             :     case STB_WEAK:
     117             :       return 2;
     118             :     case STB_LOCAL:
     119             :       return 1;
     120             :     default:
     121             :       return 0;
     122             :     }
     123             : }
     124             : 
     125             : /* Try one symbol and associated value from the search table.  */
     126             : static inline void
     127   124595055 : try_sym_value (struct search_state *state,
     128             :                GElf_Addr value, GElf_Sym *sym,
     129             :                const char *name, GElf_Word shndx,
     130             :                Elf *elf, bool resolved)
     131             : {
     132             :     /* Even if we don't choose this symbol, its existence excludes
     133             :        any sizeless symbol (assembly label) that is below its upper
     134             :        bound.  */
     135   124595055 :     if (value + sym->st_size > state->min_label)
     136     6533904 :       state->min_label = value + sym->st_size;
     137             : 
     138   124595055 :     if (sym->st_size == 0 || state->addr - value < sym->st_size)
     139             :       {
     140             :         /* This symbol is a better candidate than the current one
     141             :            if it's closer to ADDR or is global when it was local.  */
     142     4318811 :         if (state->closest_name == NULL
     143     1442205 :             || state->closest_value < value
     144     4326597 :             || binding_value (state->closest_sym) < binding_value (sym))
     145             :           {
     146     2876635 :             if (sym->st_size != 0)
     147             :               {
     148      568040 :                 *state->closest_sym = *sym;
     149      568040 :                 state->closest_value = value;
     150      568040 :                 state->closest_shndx = shndx;
     151      568040 :                 state->closest_elf = elf;
     152      568040 :                 state->closest_name = name;
     153             :               }
     154     2308595 :             else if (state->closest_name == NULL
     155     2308574 :                      && value >= state->min_label
     156       56501 :                      && same_section (state, value,
     157          60 :                                       resolved ? state->mod->main.elf : elf,
     158             :                                       shndx))
     159             :               {
     160             :                 /* Handwritten assembly symbols sometimes have no
     161             :                    st_size.  If no symbol with proper size includes
     162             :                    the address, we'll use the closest one that is in
     163             :                    the same section as ADDR.  */
     164        4612 :                 state->sizeless_sym = *sym;
     165        4612 :                 state->sizeless_value = value;
     166        4612 :                 state->sizeless_shndx = shndx;
     167        4612 :                 state->sizeless_elf = elf;
     168        4612 :                 state->sizeless_name = name;
     169             :               }
     170             :           }
     171             :         /* When the beginning of its range is no closer,
     172             :            the end of its range might be.  Otherwise follow
     173             :            GELF_ST_BIND preference.  If all are equal prefer
     174             :            the first symbol found.  */
     175     1442176 :         else if (sym->st_size != 0
     176        5960 :                  && state->closest_value == value
     177        5959 :                  && ((state->closest_sym->st_size > sym->st_size
     178           8 :                       && (binding_value (state->closest_sym)
     179           4 :                           <= binding_value (sym)))
     180             :                      || (state->closest_sym->st_size >= sym->st_size
     181             :                          && (binding_value (state->closest_sym)
     182             :                              < binding_value (sym)))))
     183             :           {
     184           4 :             *state->closest_sym = *sym;
     185           4 :             state->closest_value = value;
     186           4 :             state->closest_shndx = shndx;
     187           4 :             state->closest_elf = elf;
     188           4 :             state->closest_name = name;
     189             :           }
     190             :       }
     191   124595055 : }
     192             : 
     193             : /* Look through the symbol table for a matching symbol.  */
     194             : static inline void
     195      946702 : search_table (struct search_state *state, int start, int end)
     196             : {
     197   676007548 :       for (int i = start; i < end; ++i)
     198             :         {
     199             :           GElf_Sym sym;
     200             :           GElf_Addr value;
     201             :           GElf_Word shndx;
     202             :           Elf *elf;
     203             :           bool resolved;
     204   675060846 :           const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
     205             :                                                &shndx, &elf, NULL,
     206             :                                                &resolved,
     207   675060846 :                                                state->adjust_st_value);
     208   675060846 :           if (name != NULL && name[0] != '\0'
     209   662684290 :               && sym.st_shndx != SHN_UNDEF
     210   599290906 :               && value <= state->addr
     211   176205105 :               && GELF_ST_TYPE (sym.st_info) != STT_SECTION
     212   176205105 :               && GELF_ST_TYPE (sym.st_info) != STT_FILE
     213   125296640 :               && GELF_ST_TYPE (sym.st_info) != STT_TLS)
     214             :             {
     215   124595051 :               try_sym_value (state, value, &sym, name, shndx, elf, resolved);
     216             : 
     217             :               /* If this is an addrinfo variant and the value could be
     218             :                  resolved then also try matching the (adjusted) st_value.  */
     219   124595051 :               if (resolved && state->mod->e_type != ET_REL)
     220             :                 {
     221             :                   GElf_Addr adjusted_st_value;
     222         564 :                   adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
     223             :                                                               sym.st_value);
     224         282 :                   if (value != adjusted_st_value
     225         282 :                       && adjusted_st_value <= state->addr)
     226           4 :                     try_sym_value (state, adjusted_st_value, &sym, name, shndx,
     227             :                                    elf, false);
     228             :                 }
     229             :             }
     230             :         }
     231      946702 : }
     232             : 
     233             : /* Returns the name of the symbol "closest" to ADDR.
     234             :    Never returns symbols at addresses above ADDR.  */
     235             : const char *
     236             : internal_function
     237      592050 : __libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
     238             :                    GElf_Sym *_closest_sym, GElf_Word *shndxp,
     239             :                    Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
     240             : {
     241      592050 :   int syments = INTUSE(dwfl_module_getsymtab) (_mod);
     242      592050 :   if (syments < 0)
     243             :     return NULL;
     244             : 
     245      592049 :   struct search_state state =
     246             :     {
     247             :       .addr = _addr,
     248             :       .mod = _mod,
     249             :       .closest_sym = _closest_sym,
     250             :       .adjust_st_value = _adjust_st_value,
     251             :       .addr_shndx = SHN_UNDEF,
     252             :       .addr_symelf = NULL,
     253             :       .closest_name = NULL,
     254             :       .closest_value = 0,
     255             :       .closest_shndx = SHN_UNDEF,
     256             :       .closest_elf = NULL,
     257             :       .sizeless_name = NULL,
     258             :       .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
     259             :       .sizeless_value = 0,
     260             :       .sizeless_shndx = SHN_UNDEF,
     261             :       .sizeless_elf = NULL,
     262             :       .min_label = 0
     263             :     };
     264             : 
     265             :   /* First go through global symbols.  mod->first_global and
     266             :      mod->aux_first_global are setup by dwfl_module_getsymtab to the
     267             :      index of the first global symbol in those symbol tables.  Both
     268             :      are non-zero when the table exist, except when there is only a
     269             :      dynsym table loaded through phdrs, then first_global is zero and
     270             :      there will be no auxiliary table.  All symbols with local binding
     271             :      come first in the symbol table, then all globals.  The zeroth,
     272             :      null entry, in the auxiliary table is skipped if there is a main
     273             :      table.  */
     274      592049 :   int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
     275      592049 :   if (first_global < 0)
     276             :     return NULL;
     277      592049 :   search_table (&state, first_global == 0 ? 1 : first_global, syments);
     278             : 
     279             :   /* If we found nothing searching the global symbols, then try the locals.
     280             :      Unless we have a global sizeless symbol that matches exactly.  */
     281      592049 :   if (state.closest_name == NULL && first_global > 1
     282      354700 :       && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
     283      354653 :     search_table (&state, 1, first_global);
     284             : 
     285             :   /* If we found no proper sized symbol to use, fall back to the best
     286             :      candidate sizeless symbol we found, if any.  */
     287      592049 :   if (state.closest_name == NULL
     288       24017 :       && state.sizeless_name != NULL
     289        2716 :       && state.sizeless_value >= state.min_label)
     290             :     {
     291        1867 :       *state.closest_sym = state.sizeless_sym;
     292        1867 :       state.closest_value = state.sizeless_value;
     293        1867 :       state.closest_shndx = state.sizeless_shndx;
     294        1867 :       state.closest_elf = state.sizeless_elf;
     295        1867 :       state.closest_name = state.sizeless_name;
     296             :     }
     297             : 
     298      592049 :   *off = state.addr - state.closest_value;
     299             : 
     300      592049 :   if (shndxp != NULL)
     301         181 :     *shndxp = state.closest_shndx;
     302      592049 :   if (elfp != NULL)
     303         181 :     *elfp = state.closest_elf;
     304      592049 :   if (biasp != NULL)
     305         362 :     *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
     306      592049 :   return state.closest_name;
     307             : }
     308             : 
     309             : 
     310             : const char *
     311           0 : dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
     312             :                      GElf_Sym *closest_sym, GElf_Word *shndxp)
     313             : {
     314             :   GElf_Off off;
     315           0 :   return __libdwfl_addrsym (mod, addr, &off, closest_sym, shndxp,
     316             :                             NULL, NULL, true);
     317             : }
     318             : INTDEF (dwfl_module_addrsym)
     319             : 
     320             : const char
     321      592050 : *dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address,
     322             :                        GElf_Off *offset, GElf_Sym *sym,
     323             :                        GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *bias)
     324             : {
     325      592050 :   return __libdwfl_addrsym (mod, address, offset, sym, shndxp, elfp, bias,
     326             :                             false);
     327             : }
     328             : INTDEF (dwfl_module_addrinfo)

Generated by: LCOV version 1.12