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
31const char *
32internal_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
196const char *
197dwfl_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}
206INTDEF (dwfl_module_getsym_info)
207
208const char *
209dwfl_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}
216INTDEF (dwfl_module_getsym)
217