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