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 : #ifdef HAVE_CONFIG_H
30 : # include <config.h>
31 : #endif
32 :
33 : #include "libdwflP.h"
34 :
35 : struct search_state
36 : {
37 : Dwfl_Module *mod;
38 : GElf_Addr addr;
39 :
40 : GElf_Sym *closest_sym;
41 : bool adjust_st_value;
42 : GElf_Word addr_shndx;
43 : Elf *addr_symelf;
44 :
45 : /* Keep track of the closest symbol we have seen so far.
46 : Here we store only symbols with nonzero st_size. */
47 : const char *closest_name;
48 : GElf_Addr closest_value;
49 : GElf_Word closest_shndx;
50 : Elf *closest_elf;
51 :
52 : /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
53 : const char *sizeless_name;
54 : GElf_Sym sizeless_sym;
55 : GElf_Addr sizeless_value;
56 : GElf_Word sizeless_shndx;
57 : Elf *sizeless_elf;
58 :
59 : /* Keep track of the lowest address a relevant sizeless symbol could have. */
60 : GElf_Addr min_label;
61 : };
62 :
63 : /* Return true iff we consider ADDR to lie in the same section as SYM. */
64 : static inline bool
65 35665 : same_section (struct search_state *state,
66 : GElf_Addr value, Elf *symelf, GElf_Word shndx)
67 : {
68 : /* For absolute symbols and the like, only match exactly. */
69 35665 : if (shndx >= SHN_LORESERVE)
70 24923 : return value == state->addr;
71 :
72 : /* If value might not be st_value, the shndx of the symbol might
73 : not match the section of the value. Explicitly look both up. */
74 10742 : if (! state->adjust_st_value)
75 : {
76 : Dwarf_Addr v;
77 10742 : if (state->addr_shndx == SHN_UNDEF)
78 : {
79 5598 : v = state->addr;
80 5598 : state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
81 : }
82 :
83 10742 : v = value;
84 10742 : return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
85 : }
86 :
87 : /* Figure out what section ADDR lies in. */
88 0 : if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
89 : {
90 0 : GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
91 : state->addr);
92 0 : Elf_Scn *scn = NULL;
93 0 : state->addr_shndx = SHN_ABS;
94 0 : state->addr_symelf = symelf;
95 0 : while ((scn = elf_nextscn (symelf, scn)) != NULL)
96 : {
97 : GElf_Shdr shdr_mem;
98 0 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
99 0 : if (likely (shdr != NULL)
100 0 : && mod_addr >= shdr->sh_addr
101 0 : && mod_addr < shdr->sh_addr + shdr->sh_size)
102 : {
103 0 : state->addr_shndx = elf_ndxscn (scn);
104 0 : break;
105 : }
106 : }
107 : }
108 :
109 0 : return shndx == state->addr_shndx && state->addr_symelf == symelf;
110 : }
111 :
112 : /* Return GELF_ST_BIND as higher-is-better integer. */
113 : static inline int
114 : binding_value (const GElf_Sym *symp)
115 : {
116 3256728 : switch (GELF_ST_BIND (symp->st_info))
117 : {
118 : case STB_GLOBAL:
119 : return 3;
120 : case STB_WEAK:
121 : return 2;
122 : case STB_LOCAL:
123 : return 1;
124 : default:
125 : return 0;
126 : }
127 : }
128 :
129 : /* Try one symbol and associated value from the search table. */
130 : static inline void
131 177419227 : try_sym_value (struct search_state *state,
132 : GElf_Addr value, GElf_Sym *sym,
133 : const char *name, GElf_Word shndx,
134 : Elf *elf, bool resolved)
135 : {
136 : /* Even if we don't choose this symbol, its existence excludes
137 : any sizeless symbol (assembly label) that is below its upper
138 : bound. */
139 177419227 : if (value + sym->st_size > state->min_label)
140 6633016 : state->min_label = value + sym->st_size;
141 :
142 177419227 : if (sym->st_size == 0 || state->addr - value < sym->st_size)
143 : {
144 : /* This symbol is a better candidate than the current one
145 : if it's closer to ADDR or is global when it was local. */
146 5239135 : if (state->closest_name == NULL
147 1628370 : || state->closest_value < value
148 4885092 : || binding_value (state->closest_sym) < binding_value (sym))
149 : {
150 3610810 : if (sym->st_size != 0)
151 : {
152 595663 : *state->closest_sym = *sym;
153 595663 : state->closest_value = value;
154 595663 : state->closest_shndx = shndx;
155 595663 : state->closest_elf = elf;
156 595663 : state->closest_name = name;
157 : }
158 3015147 : else if (state->closest_name == NULL
159 3015112 : && value >= state->min_label
160 35725 : && same_section (state, value,
161 60 : resolved ? state->mod->main.elf : elf,
162 : shndx))
163 : {
164 : /* Handwritten assembly symbols sometimes have no
165 : st_size. If no symbol with proper size includes
166 : the address, we'll use the closest one that is in
167 : the same section as ADDR. */
168 6205 : state->sizeless_sym = *sym;
169 6205 : state->sizeless_value = value;
170 6205 : state->sizeless_shndx = shndx;
171 6205 : state->sizeless_elf = elf;
172 6205 : state->sizeless_name = name;
173 : }
174 : }
175 : /* When the beginning of its range is no closer,
176 : the end of its range might be. Otherwise follow
177 : GELF_ST_BIND preference. If all are equal prefer
178 : the first symbol found. */
179 1628325 : else if (sym->st_size != 0
180 6492 : && state->closest_value == value
181 6491 : && ((state->closest_sym->st_size > sym->st_size
182 8 : && (binding_value (state->closest_sym)
183 4 : <= binding_value (sym)))
184 : || (state->closest_sym->st_size >= sym->st_size
185 : && (binding_value (state->closest_sym)
186 : < binding_value (sym)))))
187 : {
188 4 : *state->closest_sym = *sym;
189 4 : state->closest_value = value;
190 4 : state->closest_shndx = shndx;
191 4 : state->closest_elf = elf;
192 4 : state->closest_name = name;
193 : }
194 : }
195 177419227 : }
196 :
197 : /* Look through the symbol table for a matching symbol. */
198 : static inline void
199 980747 : search_table (struct search_state *state, int start, int end)
200 : {
201 797158118 : for (int i = start; i < end; ++i)
202 : {
203 : GElf_Sym sym;
204 : GElf_Addr value;
205 : GElf_Word shndx;
206 : Elf *elf;
207 : bool resolved;
208 796177371 : const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
209 : &shndx, &elf, NULL,
210 : &resolved,
211 796177371 : state->adjust_st_value);
212 796177371 : if (name != NULL && name[0] != '\0'
213 783450878 : && sym.st_shndx != SHN_UNDEF
214 713300296 : && value <= state->addr
215 235457907 : && GELF_ST_TYPE (sym.st_info) != STT_SECTION
216 235457907 : && GELF_ST_TYPE (sym.st_info) != STT_FILE
217 178127149 : && GELF_ST_TYPE (sym.st_info) != STT_TLS)
218 : {
219 177419223 : try_sym_value (state, value, &sym, name, shndx, elf, resolved);
220 :
221 : /* If this is an addrinfo variant and the value could be
222 : resolved then also try matching the (adjusted) st_value. */
223 177419223 : if (resolved && state->mod->e_type != ET_REL)
224 : {
225 : GElf_Addr adjusted_st_value;
226 662 : adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
227 : sym.st_value);
228 331 : if (value != adjusted_st_value
229 331 : && adjusted_st_value <= state->addr)
230 4 : try_sym_value (state, adjusted_st_value, &sym, name, shndx,
231 : elf, false);
232 : }
233 : }
234 : }
235 980747 : }
236 :
237 : /* Returns the name of the symbol "closest" to ADDR.
238 : Never returns symbols at addresses above ADDR.
239 :
240 : Wrapper for old dwfl_module_addrsym and new dwfl_module_addrinfo.
241 : adjust_st_value set to true returns adjusted SYM st_value, set to false
242 : it will not adjust SYM at all, but does match against resolved values. */
243 : static const char *
244 607688 : __libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
245 : GElf_Sym *_closest_sym, GElf_Word *shndxp,
246 : Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
247 : {
248 607688 : int syments = INTUSE(dwfl_module_getsymtab) (_mod);
249 607688 : if (syments < 0)
250 : return NULL;
251 :
252 607679 : struct search_state state =
253 : {
254 : .addr = _addr,
255 : .mod = _mod,
256 : .closest_sym = _closest_sym,
257 : .adjust_st_value = _adjust_st_value,
258 : .addr_shndx = SHN_UNDEF,
259 : .addr_symelf = NULL,
260 : .closest_name = NULL,
261 : .closest_value = 0,
262 : .closest_shndx = SHN_UNDEF,
263 : .closest_elf = NULL,
264 : .sizeless_name = NULL,
265 : .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
266 : .sizeless_value = 0,
267 : .sizeless_shndx = SHN_UNDEF,
268 : .sizeless_elf = NULL,
269 : .min_label = 0
270 : };
271 :
272 : /* First go through global symbols. mod->first_global and
273 : mod->aux_first_global are setup by dwfl_module_getsymtab to the
274 : index of the first global symbol in those symbol tables. Both
275 : are non-zero when the table exist, except when there is only a
276 : dynsym table loaded through phdrs, then first_global is zero and
277 : there will be no auxiliary table. All symbols with local binding
278 : come first in the symbol table, then all globals. The zeroth,
279 : null entry, in the auxiliary table is skipped if there is a main
280 : table. */
281 607679 : int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
282 607679 : if (first_global < 0)
283 : return NULL;
284 607679 : search_table (&state, first_global == 0 ? 1 : first_global, syments);
285 :
286 : /* If we found nothing searching the global symbols, then try the locals.
287 : Unless we have a global sizeless symbol that matches exactly. */
288 607679 : if (state.closest_name == NULL && first_global > 1
289 373130 : && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
290 373068 : search_table (&state, 1, first_global);
291 :
292 : /* If we found no proper sized symbol to use, fall back to the best
293 : candidate sizeless symbol we found, if any. */
294 607679 : if (state.closest_name == NULL
295 12026 : && state.sizeless_name != NULL
296 1051 : && state.sizeless_value >= state.min_label)
297 : {
298 1014 : *state.closest_sym = state.sizeless_sym;
299 1014 : state.closest_value = state.sizeless_value;
300 1014 : state.closest_shndx = state.sizeless_shndx;
301 1014 : state.closest_elf = state.sizeless_elf;
302 1014 : state.closest_name = state.sizeless_name;
303 : }
304 :
305 607679 : *off = state.addr - state.closest_value;
306 :
307 607679 : if (shndxp != NULL)
308 181 : *shndxp = state.closest_shndx;
309 607679 : if (elfp != NULL)
310 181 : *elfp = state.closest_elf;
311 607679 : if (biasp != NULL)
312 362 : *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
313 607679 : return state.closest_name;
314 : }
315 :
316 :
317 : const char *
318 0 : dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
319 : GElf_Sym *closest_sym, GElf_Word *shndxp)
320 : {
321 : GElf_Off off;
322 0 : return __libdwfl_addrsym (mod, addr, &off, closest_sym, shndxp,
323 : NULL, NULL, true);
324 : }
325 : INTDEF (dwfl_module_addrsym)
326 :
327 : const char
328 607688 : *dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address,
329 : GElf_Off *offset, GElf_Sym *sym,
330 : GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *bias)
331 : {
332 607688 : return __libdwfl_addrsym (mod, address, offset, sym, shndxp, elfp, bias,
333 : false);
334 : }
335 : INTDEF (dwfl_module_addrinfo)
|