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
__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)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 *
dwfl_module_getsym_info(Dwfl_Module * mod,int ndx,GElf_Sym * sym,GElf_Addr * addr,GElf_Word * shndxp,Elf ** elfp,Dwarf_Addr * bias)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 }
INTDEF(dwfl_module_getsym_info)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)
217