Branch data Line data Source code
1 : : /* Find debugging and symbol information for a module in libdwfl.
2 : : Copyright (C) 2005-2011 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 : 525486 : dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
36 : : GElf_Sym *closest_sym, GElf_Word *shndxp)
37 : : {
38 : 525486 : int syments = INTUSE(dwfl_module_getsymtab) (mod);
39 [ + - ]: 525486 : if (syments < 0)
40 : : return NULL;
41 : :
42 : : /* Return true iff we consider ADDR to lie in the same section as SYM. */
43 : 525486 : GElf_Word addr_shndx = SHN_UNDEF;
44 : 685062 : inline bool same_section (const GElf_Sym *sym, GElf_Word shndx)
45 : : {
46 : : /* For absolute symbols and the like, only match exactly. */
47 [ + + ]: 685062 : if (shndx >= SHN_LORESERVE)
48 : 536299 : return sym->st_value == addr;
49 : :
50 : : /* Figure out what section ADDR lies in. */
51 [ + + ]: 148763 : if (addr_shndx == SHN_UNDEF)
52 : : {
53 : 154206 : GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, addr);
54 : 77103 : Elf_Scn *scn = NULL;
55 : 77103 : addr_shndx = SHN_ABS;
56 [ + + ]: 1047130 : while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL)
57 : : {
58 : : GElf_Shdr shdr_mem;
59 : 1046213 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
60 [ + - ]: 1046213 : if (likely (shdr != NULL)
61 [ + + ]: 1046213 : && mod_addr >= shdr->sh_addr
62 [ + + ]: 1046212 : && mod_addr < shdr->sh_addr + shdr->sh_size)
63 : : {
64 : 1046213 : addr_shndx = elf_ndxscn (scn);
65 : : break;
66 : : }
67 : : }
68 : : }
69 : :
70 : 685062 : return shndx == addr_shndx;
71 : : }
72 : :
73 : : /* Keep track of the closest symbol we have seen so far.
74 : : Here we store only symbols with nonzero st_size. */
75 : 525486 : const char *closest_name = NULL;
76 : 525486 : GElf_Word closest_shndx = SHN_UNDEF;
77 : :
78 : : /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
79 : 525486 : const char *sizeless_name = NULL;
80 : 525486 : GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
81 : 525486 : GElf_Word sizeless_shndx = SHN_UNDEF;
82 : :
83 : : /* Keep track of the lowest address a relevant sizeless symbol could have. */
84 : 525486 : GElf_Addr min_label = 0;
85 : :
86 : : /* Look through the symbol table for a matching symbol. */
87 : 858840 : inline void search_table (int start, int end)
88 : : {
89 [ + + ]: 458911629 : for (int i = start; i < end; ++i)
90 : : {
91 : : GElf_Sym sym;
92 : : GElf_Word shndx;
93 : 458052789 : const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx);
94 [ + - ][ + + ]: 458052789 : if (name != NULL && name[0] != '\0'
95 [ + + ]: 446690452 : && sym.st_shndx != SHN_UNDEF
96 [ + + ]: 400580840 : && sym.st_value <= addr
97 [ + - ]: 108493417 : && GELF_ST_TYPE (sym.st_info) != STT_SECTION
98 [ + + ]: 108493417 : && GELF_ST_TYPE (sym.st_info) != STT_FILE
99 [ + + ]: 74357300 : && GELF_ST_TYPE (sym.st_info) != STT_TLS)
100 : : {
101 : : /* Even if we don't choose this symbol, its existence excludes
102 : : any sizeless symbol (assembly label) that is below its upper
103 : : bound. */
104 [ + + ]: 73833078 : if (sym.st_value + sym.st_size > min_label)
105 : 6225718 : min_label = sym.st_value + sym.st_size;
106 : :
107 [ + + ][ + + ]: 73833078 : if (sym.st_size == 0 || addr - sym.st_value < sym.st_size)
108 : : {
109 : : /* Return GELF_ST_BIND as higher-is-better integer. */
110 : : inline int binding_value (const GElf_Sym *symp)
111 : : {
112 [ + - ][ + - ]: 1220525 : 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 : : /* This symbol is a better candidate than the current one
125 : : if it's closer to ADDR or is global when it was local. */
126 [ + + ]: 3636894 : if (closest_name == NULL
127 [ + + ]: 1220531 : || closest_sym->st_value < sym.st_value
128 [ + + ]: 1220525 : || binding_value (closest_sym) < binding_value (&sym))
129 : : {
130 [ + + ]: 2416371 : if (sym.st_size != 0)
131 : : {
132 : 442064 : *closest_sym = sym;
133 : 442064 : closest_shndx = shndx;
134 : 442064 : closest_name = name;
135 : : }
136 [ + + ]: 1974307 : else if (closest_name == NULL
137 [ + + ]: 1974305 : && sym.st_value >= min_label
138 [ + + ]: 685062 : && same_section (&sym, shndx))
139 : : {
140 : : /* Handwritten assembly symbols sometimes have no
141 : : st_size. If no symbol with proper size includes
142 : : the address, we'll use the closest one that is in
143 : : the same section as ADDR. */
144 : 149340 : sizeless_sym = sym;
145 : 149340 : sizeless_shndx = shndx;
146 : 149340 : sizeless_name = name;
147 : : }
148 : : }
149 : : /* When the beginning of its range is no closer,
150 : : the end of its range might be. Otherwise follow
151 : : GELF_ST_BIND preference. If all are equal prefer
152 : : the first symbol found. */
153 [ + + ]: 1220523 : else if (sym.st_size != 0
154 [ + - ]: 3435 : && closest_sym->st_value == sym.st_value
155 [ + + ]: 3435 : && ((closest_sym->st_size > sym.st_size
156 [ + - ][ - + ]: 8 : && (binding_value (closest_sym)
157 [ + - ]: 4 : <= binding_value (&sym)))
158 [ + - ]: 3431 : || (closest_sym->st_size >= sym.st_size
159 [ + - ][ - + ]: 6862 : && (binding_value (closest_sym)
160 [ + - ]: 3431 : < binding_value (&sym)))))
161 : : {
162 : 4 : *closest_sym = sym;
163 : 4 : closest_shndx = shndx;
164 : 4 : closest_name = name;
165 : : }
166 : : }
167 : : }
168 : : }
169 : 858840 : }
170 : :
171 : : /* First go through global symbols. mod->first_global is setup by
172 : : dwfl_module_getsymtab to the index of the first global symbol in
173 : : the module's symbol table, or -1 when unknown. All symbols with
174 : : local binding come first in the symbol table, then all globals. */
175 [ + + ]: 525486 : search_table (mod->first_global < 0 ? 1 : mod->first_global, syments);
176 : :
177 : : /* If we found nothing searching the global symbols, then try the locals.
178 : : Unless we have a global sizeless symbol that matches exactly. */
179 [ + + ][ + - ]: 525486 : if (closest_name == NULL && mod->first_global > 1
180 [ + + ][ + + ]: 334346 : && (sizeless_name == NULL || sizeless_sym.st_value != addr))
181 : 333354 : search_table (1, mod->first_global);
182 : :
183 : : /* If we found no proper sized symbol to use, fall back to the best
184 : : candidate sizeless symbol we found, if any. */
185 [ + + ]: 525486 : if (closest_name == NULL
186 [ + + ][ + + ]: 83428 : && sizeless_name != NULL && sizeless_sym.st_value >= min_label)
187 : : {
188 : 3103 : *closest_sym = sizeless_sym;
189 : 3103 : closest_shndx = sizeless_shndx;
190 : 3103 : closest_name = sizeless_name;
191 : : }
192 : :
193 [ + + ]: 525486 : if (shndxp != NULL)
194 : 83 : *shndxp = closest_shndx;
195 : 525486 : return closest_name;
196 : : }
197 : 1297628 : INTDEF (dwfl_module_addrsym)
|