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