Branch data Line data Source code
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 : 541451 : dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
36 : : GElf_Sym *closest_sym, GElf_Word *shndxp)
37 : : {
38 : 541451 : int syments = INTUSE(dwfl_module_getsymtab) (mod);
39 [ + - ]: 541451 : if (syments < 0)
40 : : return NULL;
41 : :
42 : : /* Return true iff we consider ADDR to lie in the same section as SYM. */
43 : 541451 : GElf_Word addr_shndx = SHN_UNDEF;
44 : 113476 : inline bool same_section (const GElf_Sym *sym, struct dwfl_file *symfile,
45 : : GElf_Word shndx)
46 : : {
47 : : /* For absolute symbols and the like, only match exactly. */
48 [ + + ]: 113476 : if (shndx >= SHN_LORESERVE)
49 : 28671 : return sym->st_value == addr;
50 : :
51 : : /* Figure out what section ADDR lies in. */
52 [ + + ]: 84805 : if (addr_shndx == SHN_UNDEF)
53 : : {
54 : 79139 : GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symfile, addr);
55 : 79139 : Elf_Scn *scn = NULL;
56 : 79139 : addr_shndx = SHN_ABS;
57 [ + + ]: 1050049 : while ((scn = elf_nextscn (symfile->elf, scn)) != NULL)
58 : : {
59 : : GElf_Shdr shdr_mem;
60 : 1050048 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
61 [ + - ]: 1050048 : if (likely (shdr != NULL)
62 [ + + ]: 1050048 : && mod_addr >= shdr->sh_addr
63 [ + + ]: 1050047 : && mod_addr < shdr->sh_addr + shdr->sh_size)
64 : : {
65 : 1050048 : addr_shndx = elf_ndxscn (scn);
66 : : break;
67 : : }
68 : : }
69 : : }
70 : :
71 : 113476 : return shndx == addr_shndx;
72 : : }
73 : :
74 : : /* Keep track of the closest symbol we have seen so far.
75 : : Here we store only symbols with nonzero st_size. */
76 : 541451 : const char *closest_name = NULL;
77 : 541451 : GElf_Word closest_shndx = SHN_UNDEF;
78 : :
79 : : /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
80 : 541451 : const char *sizeless_name = NULL;
81 : 541451 : GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
82 : 541451 : GElf_Word sizeless_shndx = SHN_UNDEF;
83 : :
84 : : /* Keep track of the lowest address a relevant sizeless symbol could have. */
85 : 541451 : GElf_Addr min_label = 0;
86 : :
87 : : /* Look through the symbol table for a matching symbol. */
88 : 871474 : inline void search_table (int start, int end)
89 : : {
90 [ + + ]: 464176758 : for (int i = start; i < end; ++i)
91 : : {
92 : : GElf_Sym sym;
93 : : GElf_Word shndx;
94 : 463305284 : const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx);
95 [ + - ][ + + ]: 463305284 : if (name != NULL && name[0] != '\0'
96 [ + + ]: 451730788 : && sym.st_shndx != SHN_UNDEF
97 [ + + ]: 403876031 : && sym.st_value <= addr
98 [ + - ]: 115013138 : && GELF_ST_TYPE (sym.st_info) != STT_SECTION
99 [ + + ]: 115013138 : && GELF_ST_TYPE (sym.st_info) != STT_FILE
100 [ + + ]: 82048453 : && GELF_ST_TYPE (sym.st_info) != STT_TLS)
101 : : {
102 : : /* Even if we don't choose this symbol, its existence excludes
103 : : any sizeless symbol (assembly label) that is below its upper
104 : : bound. */
105 [ + + ]: 81526777 : if (sym.st_value + sym.st_size > min_label)
106 : 7064829 : min_label = sym.st_value + sym.st_size;
107 : :
108 [ + + ][ + + ]: 81526777 : if (sym.st_size == 0 || addr - sym.st_value < sym.st_size)
109 : : {
110 : : /* Return GELF_ST_BIND as higher-is-better integer. */
111 : : inline int binding_value (const GElf_Sym *symp)
112 : : {
113 [ + - ][ + - ]: 1434405 : switch (GELF_ST_BIND (symp->st_info))
114 : : {
115 : : case STB_GLOBAL:
116 : : return 3;
117 : : case STB_WEAK:
118 : : return 2;
119 : : case STB_LOCAL:
120 : : return 1;
121 : : default:
122 : : return 0;
123 : : }
124 : : }
125 : : /* This symbol is a better candidate than the current one
126 : : if it's closer to ADDR or is global when it was local. */
127 [ + + ]: 3659962 : if (closest_name == NULL
128 [ + + ]: 1434411 : || closest_sym->st_value < sym.st_value
129 [ + + ]: 1434405 : || binding_value (closest_sym) < binding_value (&sym))
130 : : {
131 [ + + ]: 2225559 : if (sym.st_size != 0)
132 : : {
133 : 510592 : *closest_sym = sym;
134 : 510592 : closest_shndx = shndx;
135 : 510592 : closest_name = name;
136 : : }
137 [ + + ]: 1714967 : else if (closest_name == NULL
138 [ + + ]: 1714965 : && sym.st_value >= min_label
139 [ + + ][ + + ]: 113476 : && same_section (&sym,
140 : 113476 : ((size_t) i < mod->syments
141 : : ? mod->symfile
142 : : : &mod->aux_sym),
143 : : shndx))
144 : : {
145 : : /* Handwritten assembly symbols sometimes have no
146 : : st_size. If no symbol with proper size includes
147 : : the address, we'll use the closest one that is in
148 : : the same section as ADDR. */
149 : 79942 : sizeless_sym = sym;
150 : 79942 : sizeless_shndx = shndx;
151 : 79942 : sizeless_name = name;
152 : : }
153 : : }
154 : : /* When the beginning of its range is no closer,
155 : : the end of its range might be. Otherwise follow
156 : : GELF_ST_BIND preference. If all are equal prefer
157 : : the first symbol found. */
158 [ + + ]: 1434403 : else if (sym.st_size != 0
159 [ + - ]: 5094 : && closest_sym->st_value == sym.st_value
160 [ + + ]: 5094 : && ((closest_sym->st_size > sym.st_size
161 [ + - ][ - + ]: 8 : && (binding_value (closest_sym)
162 [ + - ]: 4 : <= binding_value (&sym)))
163 [ + - ]: 5090 : || (closest_sym->st_size >= sym.st_size
164 [ + - ][ - + ]: 10180 : && (binding_value (closest_sym)
165 [ + - ]: 5090 : < binding_value (&sym)))))
166 : : {
167 : 4 : *closest_sym = sym;
168 : 4 : closest_shndx = shndx;
169 : 4 : closest_name = name;
170 : : }
171 : : }
172 : : }
173 : : }
174 : 871474 : }
175 : :
176 : : /* First go through global symbols. mod->first_global and
177 : : mod->aux_first_global are setup by dwfl_module_getsymtab to the
178 : : index of the first global symbol in those symbol tables. Both
179 : : are non-zero when the table exist, except when there is only a
180 : : dynsym table loaded through phdrs, then first_global is zero and
181 : : there will be no auxiliary table. All symbols with local binding
182 : : come first in the symbol table, then all globals. The zeroth,
183 : : null entry, in the auxiliary table is skipped if there is a main
184 : : table. */
185 : 541451 : int first_global = mod->first_global + mod->aux_first_global;
186 [ + + ][ + + ]: 541451 : if (mod->syments > 0 && mod->aux_syments > 0)
187 : 12 : first_global--;
188 [ + + ]: 541451 : search_table (first_global == 0 ? 1 : first_global, syments);
189 : :
190 : : /* If we found nothing searching the global symbols, then try the locals.
191 : : Unless we have a global sizeless symbol that matches exactly. */
192 [ + + ][ + - ]: 541451 : if (closest_name == NULL && first_global > 1
193 [ + + ][ + + ]: 330156 : && (sizeless_name == NULL || sizeless_sym.st_value != addr))
194 : 330023 : search_table (1, first_global);
195 : :
196 : : /* If we found no proper sized symbol to use, fall back to the best
197 : : candidate sizeless symbol we found, if any. */
198 [ + + ]: 541451 : if (closest_name == NULL
199 [ + + ][ + + ]: 30865 : && sizeless_name != NULL && sizeless_sym.st_value >= min_label)
200 : : {
201 : 2278 : *closest_sym = sizeless_sym;
202 : 2278 : closest_shndx = sizeless_shndx;
203 : 2278 : closest_name = sizeless_name;
204 : : }
205 : :
206 [ + + ]: 541451 : if (shndxp != NULL)
207 : 140 : *shndxp = closest_shndx;
208 : 541451 : return closest_name;
209 : : }
210 : 1513544 : INTDEF (dwfl_module_addrsym)
|