libdwfl: Add dwfl_module_getsymtab_first_global.
[platform/upstream/elfutils.git] / libdwfl / dwfl_module_addrsym.c
1 /* Find debugging and symbol information for a module in libdwfl.
2    Copyright (C) 2005-2012 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 /* Returns the name of the symbol "closest" to ADDR.
32    Never returns symbols at addresses above ADDR.  */
33
34 const char *
35 dwfl_module_addrsym_elf (Dwfl_Module *mod, GElf_Addr addr,
36                          GElf_Sym *closest_sym, GElf_Word *shndxp,
37                          Elf **elfp, Dwarf_Addr *biasp)
38 {
39   int syments = INTUSE(dwfl_module_getsymtab) (mod);
40   if (syments < 0)
41     return NULL;
42
43   /* Return true iff we consider ADDR to lie in the same section as SYM.  */
44   GElf_Word addr_shndx = SHN_UNDEF;
45   Elf *addr_symelf = NULL;
46   inline bool same_section (const GElf_Sym *sym, Elf *symelf, GElf_Word shndx)
47     {
48       /* For absolute symbols and the like, only match exactly.  */
49       if (shndx >= SHN_LORESERVE)
50         return sym->st_value == addr;
51
52       /* Figure out what section ADDR lies in.  */
53       if (addr_shndx == SHN_UNDEF || addr_symelf != symelf)
54         {
55           GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symelf, addr);
56           Elf_Scn *scn = NULL;
57           addr_shndx = SHN_ABS;
58           addr_symelf = symelf;
59           while ((scn = elf_nextscn (symelf, scn)) != NULL)
60             {
61               GElf_Shdr shdr_mem;
62               GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
63               if (likely (shdr != NULL)
64                   && mod_addr >= shdr->sh_addr
65                   && mod_addr < shdr->sh_addr + shdr->sh_size)
66                 {
67                   addr_shndx = elf_ndxscn (scn);
68                   break;
69                 }
70             }
71         }
72
73       return shndx == addr_shndx && addr_symelf == symelf;
74     }
75
76   /* Keep track of the closest symbol we have seen so far.
77      Here we store only symbols with nonzero st_size.  */
78   const char *closest_name = NULL;
79   GElf_Word closest_shndx = SHN_UNDEF;
80   Elf *closest_elf = NULL;
81
82   /* Keep track of an eligible symbol with st_size == 0 as a fallback.  */
83   const char *sizeless_name = NULL;
84   GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
85   GElf_Word sizeless_shndx = SHN_UNDEF;
86   Elf *sizeless_elf = NULL;
87
88   /* Keep track of the lowest address a relevant sizeless symbol could have.  */
89   GElf_Addr min_label = 0;
90
91   /* Look through the symbol table for a matching symbol.  */
92   inline void search_table (int start, int end)
93     {
94       for (int i = start; i < end; ++i)
95         {
96           GElf_Sym sym;
97           GElf_Word shndx;
98           Elf *elf;
99           const char *name = INTUSE(dwfl_module_getsym_elf) (mod, i, &sym,
100                                                              &shndx, &elf,
101                                                              NULL);
102           if (name != NULL && name[0] != '\0'
103               && sym.st_shndx != SHN_UNDEF
104               && sym.st_value <= addr
105               && GELF_ST_TYPE (sym.st_info) != STT_SECTION
106               && GELF_ST_TYPE (sym.st_info) != STT_FILE
107               && GELF_ST_TYPE (sym.st_info) != STT_TLS)
108             {
109               /* Even if we don't choose this symbol, its existence excludes
110                  any sizeless symbol (assembly label) that is below its upper
111                  bound.  */
112               if (sym.st_value + sym.st_size > min_label)
113                 min_label = sym.st_value + sym.st_size;
114
115               if (sym.st_size == 0 || addr - sym.st_value < sym.st_size)
116                 {
117                   /* Return GELF_ST_BIND as higher-is-better integer.  */
118                   inline int binding_value (const GElf_Sym *symp)
119                     {
120                       switch (GELF_ST_BIND (symp->st_info))
121                       {
122                         case STB_GLOBAL:
123                           return 3;
124                         case STB_WEAK:
125                           return 2;
126                         case STB_LOCAL:
127                           return 1;
128                         default:
129                           return 0;
130                       }
131                     }
132                   /* This symbol is a better candidate than the current one
133                      if it's closer to ADDR or is global when it was local.  */
134                   if (closest_name == NULL
135                       || closest_sym->st_value < sym.st_value
136                       || binding_value (closest_sym) < binding_value (&sym))
137                     {
138                       if (sym.st_size != 0)
139                         {
140                           *closest_sym = sym;
141                           closest_shndx = shndx;
142                           closest_elf = elf;
143                           closest_name = name;
144                         }
145                       else if (closest_name == NULL
146                                && sym.st_value >= min_label
147                                && same_section (&sym, elf, shndx))
148                         {
149                           /* Handwritten assembly symbols sometimes have no
150                              st_size.  If no symbol with proper size includes
151                              the address, we'll use the closest one that is in
152                              the same section as ADDR.  */
153                           sizeless_sym = sym;
154                           sizeless_shndx = shndx;
155                           sizeless_elf = elf;
156                           sizeless_name = name;
157                         }
158                     }
159                   /* When the beginning of its range is no closer,
160                      the end of its range might be.  Otherwise follow
161                      GELF_ST_BIND preference.  If all are equal prefer
162                      the first symbol found.  */
163                   else if (sym.st_size != 0
164                            && closest_sym->st_value == sym.st_value
165                            && ((closest_sym->st_size > sym.st_size
166                                 && (binding_value (closest_sym)
167                                     <= binding_value (&sym)))
168                                || (closest_sym->st_size >= sym.st_size
169                                    && (binding_value (closest_sym)
170                                        < binding_value (&sym)))))
171                     {
172                       *closest_sym = sym;
173                       closest_shndx = shndx;
174                       closest_elf = elf;
175                       closest_name = name;
176                     }
177                 }
178             }
179         }
180     }
181
182   /* First go through global symbols.  mod->first_global and
183      mod->aux_first_global are setup by dwfl_module_getsymtab to the
184      index of the first global symbol in those symbol tables.  Both
185      are non-zero when the table exist, except when there is only a
186      dynsym table loaded through phdrs, then first_global is zero and
187      there will be no auxiliary table.  All symbols with local binding
188      come first in the symbol table, then all globals.  The zeroth,
189      null entry, in the auxiliary table is skipped if there is a main
190      table.  */
191   int first_global = INTUSE (dwfl_module_getsymtab_first_global) (mod);
192   if (first_global < 0)
193     return NULL;
194   search_table (first_global == 0 ? 1 : first_global, syments);
195
196   /* If we found nothing searching the global symbols, then try the locals.
197      Unless we have a global sizeless symbol that matches exactly.  */
198   if (closest_name == NULL && first_global > 1
199       && (sizeless_name == NULL || sizeless_sym.st_value != addr))
200     search_table (1, first_global);
201
202   /* If we found no proper sized symbol to use, fall back to the best
203      candidate sizeless symbol we found, if any.  */
204   if (closest_name == NULL
205       && sizeless_name != NULL && sizeless_sym.st_value >= min_label)
206     {
207       *closest_sym = sizeless_sym;
208       closest_shndx = sizeless_shndx;
209       closest_elf = sizeless_elf;
210       closest_name = sizeless_name;
211     }
212
213   if (shndxp != NULL)
214     *shndxp = closest_shndx;
215   if (elfp != NULL)
216     *elfp = closest_elf;
217   if (biasp != NULL)
218     *biasp = dwfl_adjusted_st_value (mod, closest_elf, 0);
219   return closest_name;
220 }
221 INTDEF (dwfl_module_addrsym_elf)
222
223
224 const char *
225 dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
226                      GElf_Sym *closest_sym, GElf_Word *shndxp)
227 {
228   return INTUSE(dwfl_module_addrsym_elf) (mod, addr, closest_sym, shndxp,
229                                           NULL, NULL);
230 }
231 INTDEF (dwfl_module_addrsym)