1 /* Find debugging and symbol information for a module in libdwfl.
2 Copyright (C) 2006-2014 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 const char *
36 internal_function
__libdwfl_getsym(Dwfl_Module * mod,int ndx,GElf_Sym * sym,GElf_Addr * addr,GElf_Word * shndxp,Elf ** elfp,Dwarf_Addr * biasp,bool * resolved,bool adjust_st_value)37 __libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr,
38 GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp,
39 bool *resolved, bool adjust_st_value)
40 {
41 if (unlikely (mod == NULL))
42 return NULL;
43
44 if (unlikely (mod->symdata == NULL))
45 {
46 int result = INTUSE(dwfl_module_getsymtab) (mod);
47 if (result < 0)
48 return NULL;
49 }
50
51 /* All local symbols should come before all global symbols. If we
52 have an auxiliary table make sure all the main locals come first,
53 then all aux locals, then all main globals and finally all aux globals.
54 And skip the auxiliary table zero undefined entry. */
55 GElf_Word shndx;
56 int tndx = ndx;
57 int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
58 Elf *elf;
59 Elf_Data *symdata;
60 Elf_Data *symxndxdata;
61 Elf_Data *symstrdata;
62 if (mod->aux_symdata == NULL
63 || ndx < mod->first_global)
64 {
65 /* main symbol table (locals). */
66 tndx = ndx;
67 elf = mod->symfile->elf;
68 symdata = mod->symdata;
69 symxndxdata = mod->symxndxdata;
70 symstrdata = mod->symstrdata;
71 }
72 else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero)
73 {
74 /* aux symbol table (locals). */
75 tndx = ndx - mod->first_global + skip_aux_zero;
76 elf = mod->aux_sym.elf;
77 symdata = mod->aux_symdata;
78 symxndxdata = mod->aux_symxndxdata;
79 symstrdata = mod->aux_symstrdata;
80 }
81 else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
82 {
83 /* main symbol table (globals). */
84 tndx = ndx - mod->aux_first_global + skip_aux_zero;
85 elf = mod->symfile->elf;
86 symdata = mod->symdata;
87 symxndxdata = mod->symxndxdata;
88 symstrdata = mod->symstrdata;
89 }
90 else
91 {
92 /* aux symbol table (globals). */
93 tndx = ndx - mod->syments + skip_aux_zero;
94 elf = mod->aux_sym.elf;
95 symdata = mod->aux_symdata;
96 symxndxdata = mod->aux_symxndxdata;
97 symstrdata = mod->aux_symstrdata;
98 }
99 sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
100
101 if (unlikely (sym == NULL))
102 {
103 __libdwfl_seterrno (DWFL_E_LIBELF);
104 return NULL;
105 }
106
107 if (sym->st_shndx != SHN_XINDEX)
108 shndx = sym->st_shndx;
109
110 /* Figure out whether this symbol points into an SHF_ALLOC section. */
111 bool alloc = true;
112 if ((shndxp != NULL || mod->e_type != ET_REL)
113 && (sym->st_shndx == SHN_XINDEX
114 || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
115 {
116 GElf_Shdr shdr_mem;
117 GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
118 alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
119 }
120
121 /* In case of an value in an allocated section the main Elf Ebl
122 might know where the real value is (e.g. for function
123 descriptors). */
124
125 char *ident;
126 GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl);
127 *resolved = false;
128 if (! adjust_st_value && mod->e_type != ET_REL && alloc
129 && (GELF_ST_TYPE (sym->st_info) == STT_FUNC
130 || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
131 && (ident = elf_getident (elf, NULL)) != NULL
132 && ident[EI_OSABI] == ELFOSABI_LINUX)))
133 {
134 if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
135 {
136 if (elf != mod->main.elf)
137 {
138 st_value = dwfl_adjusted_st_value (mod, elf, st_value);
139 st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value);
140 }
141
142 *resolved = ebl_resolve_sym_value (mod->ebl, &st_value);
143 if (! *resolved)
144 st_value = sym->st_value;
145 }
146 }
147
148 if (shndxp != NULL)
149 /* Yield -1 in case of a non-SHF_ALLOC section. */
150 *shndxp = alloc ? shndx : (GElf_Word) -1;
151
152 switch (sym->st_shndx)
153 {
154 case SHN_ABS: /* XXX sometimes should use bias?? */
155 case SHN_UNDEF:
156 case SHN_COMMON:
157 break;
158
159 default:
160 if (mod->e_type == ET_REL)
161 {
162 /* In an ET_REL file, the symbol table values are relative
163 to the section, not to the module's load base. */
164 size_t symshstrndx = SHN_UNDEF;
165 Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
166 &symshstrndx,
167 shndx, &st_value);
168 if (unlikely (result != DWFL_E_NOERROR))
169 {
170 __libdwfl_seterrno (result);
171 return NULL;
172 }
173 }
174 else if (alloc)
175 /* Apply the bias to the symbol value. */
176 st_value = dwfl_adjusted_st_value (mod,
177 *resolved ? mod->main.elf : elf,
178 st_value);
179 break;
180 }
181
182 if (adjust_st_value)
183 sym->st_value = st_value;
184
185 if (addr != NULL)
186 *addr = st_value;
187
188 if (unlikely (sym->st_name >= symstrdata->d_size))
189 {
190 __libdwfl_seterrno (DWFL_E_BADSTROFF);
191 return NULL;
192 }
193 if (elfp)
194 *elfp = elf;
195 if (biasp)
196 *biasp = dwfl_adjusted_st_value (mod, elf, 0);
197 return (const char *) symstrdata->d_buf + sym->st_name;
198 }
199
200 const char *
dwfl_module_getsym_info(Dwfl_Module * mod,int ndx,GElf_Sym * sym,GElf_Addr * addr,GElf_Word * shndxp,Elf ** elfp,Dwarf_Addr * bias)201 dwfl_module_getsym_info (Dwfl_Module *mod, int ndx,
202 GElf_Sym *sym, GElf_Addr *addr,
203 GElf_Word *shndxp,
204 Elf **elfp, Dwarf_Addr *bias)
205 {
206 bool resolved;
207 return __libdwfl_getsym (mod, ndx, sym, addr, shndxp, elfp, bias,
208 &resolved, false);
209 }
INTDEF(dwfl_module_getsym_info)210 INTDEF (dwfl_module_getsym_info)
211
212 const char *
213 dwfl_module_getsym (Dwfl_Module *mod, int ndx,
214 GElf_Sym *sym, GElf_Word *shndxp)
215 {
216 bool resolved;
217 return __libdwfl_getsym (mod, ndx, sym, NULL, shndxp, NULL, NULL,
218 &resolved, true);
219 }
220 INTDEF (dwfl_module_getsym)
221