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
35const char *
36internal_function
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
200const char *
201dwfl_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}
210INTDEF (dwfl_module_getsym_info)
211
212const char *
213dwfl_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}
220INTDEF (dwfl_module_getsym)
221