1da0c48c4Sopenharmony_ci/* Find debugging and symbol information for a module in libdwfl.
2da0c48c4Sopenharmony_ci   Copyright (C) 2006-2014 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include "libdwflP.h"
34da0c48c4Sopenharmony_ci
35da0c48c4Sopenharmony_ciconst char *
36da0c48c4Sopenharmony_ciinternal_function
37da0c48c4Sopenharmony_ci__libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr,
38da0c48c4Sopenharmony_ci		  GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp,
39da0c48c4Sopenharmony_ci		  bool *resolved, bool adjust_st_value)
40da0c48c4Sopenharmony_ci{
41da0c48c4Sopenharmony_ci  if (unlikely (mod == NULL))
42da0c48c4Sopenharmony_ci    return NULL;
43da0c48c4Sopenharmony_ci
44da0c48c4Sopenharmony_ci  if (unlikely (mod->symdata == NULL))
45da0c48c4Sopenharmony_ci    {
46da0c48c4Sopenharmony_ci      int result = INTUSE(dwfl_module_getsymtab) (mod);
47da0c48c4Sopenharmony_ci      if (result < 0)
48da0c48c4Sopenharmony_ci	return NULL;
49da0c48c4Sopenharmony_ci    }
50da0c48c4Sopenharmony_ci
51da0c48c4Sopenharmony_ci  /* All local symbols should come before all global symbols.  If we
52da0c48c4Sopenharmony_ci     have an auxiliary table make sure all the main locals come first,
53da0c48c4Sopenharmony_ci     then all aux locals, then all main globals and finally all aux globals.
54da0c48c4Sopenharmony_ci     And skip the auxiliary table zero undefined entry.  */
55da0c48c4Sopenharmony_ci  GElf_Word shndx;
56da0c48c4Sopenharmony_ci  int tndx = ndx;
57da0c48c4Sopenharmony_ci  int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
58da0c48c4Sopenharmony_ci  Elf *elf;
59da0c48c4Sopenharmony_ci  Elf_Data *symdata;
60da0c48c4Sopenharmony_ci  Elf_Data *symxndxdata;
61da0c48c4Sopenharmony_ci  Elf_Data *symstrdata;
62da0c48c4Sopenharmony_ci  if (mod->aux_symdata == NULL
63da0c48c4Sopenharmony_ci      || ndx < mod->first_global)
64da0c48c4Sopenharmony_ci    {
65da0c48c4Sopenharmony_ci      /* main symbol table (locals).  */
66da0c48c4Sopenharmony_ci      tndx = ndx;
67da0c48c4Sopenharmony_ci      elf = mod->symfile->elf;
68da0c48c4Sopenharmony_ci      symdata = mod->symdata;
69da0c48c4Sopenharmony_ci      symxndxdata = mod->symxndxdata;
70da0c48c4Sopenharmony_ci      symstrdata = mod->symstrdata;
71da0c48c4Sopenharmony_ci    }
72da0c48c4Sopenharmony_ci  else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero)
73da0c48c4Sopenharmony_ci    {
74da0c48c4Sopenharmony_ci      /* aux symbol table (locals).  */
75da0c48c4Sopenharmony_ci      tndx = ndx - mod->first_global + skip_aux_zero;
76da0c48c4Sopenharmony_ci      elf = mod->aux_sym.elf;
77da0c48c4Sopenharmony_ci      symdata = mod->aux_symdata;
78da0c48c4Sopenharmony_ci      symxndxdata = mod->aux_symxndxdata;
79da0c48c4Sopenharmony_ci      symstrdata = mod->aux_symstrdata;
80da0c48c4Sopenharmony_ci    }
81da0c48c4Sopenharmony_ci  else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
82da0c48c4Sopenharmony_ci    {
83da0c48c4Sopenharmony_ci      /* main symbol table (globals).  */
84da0c48c4Sopenharmony_ci      tndx = ndx - mod->aux_first_global + skip_aux_zero;
85da0c48c4Sopenharmony_ci      elf = mod->symfile->elf;
86da0c48c4Sopenharmony_ci      symdata = mod->symdata;
87da0c48c4Sopenharmony_ci      symxndxdata = mod->symxndxdata;
88da0c48c4Sopenharmony_ci      symstrdata = mod->symstrdata;
89da0c48c4Sopenharmony_ci    }
90da0c48c4Sopenharmony_ci  else
91da0c48c4Sopenharmony_ci    {
92da0c48c4Sopenharmony_ci      /* aux symbol table (globals).  */
93da0c48c4Sopenharmony_ci      tndx = ndx - mod->syments + skip_aux_zero;
94da0c48c4Sopenharmony_ci      elf = mod->aux_sym.elf;
95da0c48c4Sopenharmony_ci      symdata = mod->aux_symdata;
96da0c48c4Sopenharmony_ci      symxndxdata = mod->aux_symxndxdata;
97da0c48c4Sopenharmony_ci      symstrdata = mod->aux_symstrdata;
98da0c48c4Sopenharmony_ci    }
99da0c48c4Sopenharmony_ci  sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
100da0c48c4Sopenharmony_ci
101da0c48c4Sopenharmony_ci  if (unlikely (sym == NULL))
102da0c48c4Sopenharmony_ci    {
103da0c48c4Sopenharmony_ci      __libdwfl_seterrno (DWFL_E_LIBELF);
104da0c48c4Sopenharmony_ci      return NULL;
105da0c48c4Sopenharmony_ci    }
106da0c48c4Sopenharmony_ci
107da0c48c4Sopenharmony_ci  if (sym->st_shndx != SHN_XINDEX)
108da0c48c4Sopenharmony_ci    shndx = sym->st_shndx;
109da0c48c4Sopenharmony_ci
110da0c48c4Sopenharmony_ci  /* Figure out whether this symbol points into an SHF_ALLOC section.  */
111da0c48c4Sopenharmony_ci  bool alloc = true;
112da0c48c4Sopenharmony_ci  if ((shndxp != NULL || mod->e_type != ET_REL)
113da0c48c4Sopenharmony_ci      && (sym->st_shndx == SHN_XINDEX
114da0c48c4Sopenharmony_ci	  || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
115da0c48c4Sopenharmony_ci    {
116da0c48c4Sopenharmony_ci      GElf_Shdr shdr_mem;
117da0c48c4Sopenharmony_ci      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
118da0c48c4Sopenharmony_ci      alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
119da0c48c4Sopenharmony_ci    }
120da0c48c4Sopenharmony_ci
121da0c48c4Sopenharmony_ci  /* In case of an value in an allocated section the main Elf Ebl
122da0c48c4Sopenharmony_ci     might know where the real value is (e.g. for function
123da0c48c4Sopenharmony_ci     descriptors).  */
124da0c48c4Sopenharmony_ci
125da0c48c4Sopenharmony_ci  char *ident;
126da0c48c4Sopenharmony_ci  GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl);
127da0c48c4Sopenharmony_ci  *resolved = false;
128da0c48c4Sopenharmony_ci  if (! adjust_st_value && mod->e_type != ET_REL && alloc
129da0c48c4Sopenharmony_ci      && (GELF_ST_TYPE (sym->st_info) == STT_FUNC
130da0c48c4Sopenharmony_ci	  || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
131da0c48c4Sopenharmony_ci	      && (ident = elf_getident (elf, NULL)) != NULL
132da0c48c4Sopenharmony_ci	      && ident[EI_OSABI] == ELFOSABI_LINUX)))
133da0c48c4Sopenharmony_ci    {
134da0c48c4Sopenharmony_ci      if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
135da0c48c4Sopenharmony_ci	{
136da0c48c4Sopenharmony_ci	  if (elf != mod->main.elf)
137da0c48c4Sopenharmony_ci	    {
138da0c48c4Sopenharmony_ci	      st_value = dwfl_adjusted_st_value (mod, elf, st_value);
139da0c48c4Sopenharmony_ci	      st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value);
140da0c48c4Sopenharmony_ci	    }
141da0c48c4Sopenharmony_ci
142da0c48c4Sopenharmony_ci	  *resolved = ebl_resolve_sym_value (mod->ebl, &st_value);
143da0c48c4Sopenharmony_ci	  if (! *resolved)
144da0c48c4Sopenharmony_ci	    st_value = sym->st_value;
145da0c48c4Sopenharmony_ci	}
146da0c48c4Sopenharmony_ci    }
147da0c48c4Sopenharmony_ci
148da0c48c4Sopenharmony_ci  if (shndxp != NULL)
149da0c48c4Sopenharmony_ci    /* Yield -1 in case of a non-SHF_ALLOC section.  */
150da0c48c4Sopenharmony_ci    *shndxp = alloc ? shndx : (GElf_Word) -1;
151da0c48c4Sopenharmony_ci
152da0c48c4Sopenharmony_ci  switch (sym->st_shndx)
153da0c48c4Sopenharmony_ci    {
154da0c48c4Sopenharmony_ci    case SHN_ABS:		/* XXX sometimes should use bias?? */
155da0c48c4Sopenharmony_ci    case SHN_UNDEF:
156da0c48c4Sopenharmony_ci    case SHN_COMMON:
157da0c48c4Sopenharmony_ci      break;
158da0c48c4Sopenharmony_ci
159da0c48c4Sopenharmony_ci    default:
160da0c48c4Sopenharmony_ci      if (mod->e_type == ET_REL)
161da0c48c4Sopenharmony_ci	{
162da0c48c4Sopenharmony_ci	  /* In an ET_REL file, the symbol table values are relative
163da0c48c4Sopenharmony_ci	     to the section, not to the module's load base.  */
164da0c48c4Sopenharmony_ci	  size_t symshstrndx = SHN_UNDEF;
165da0c48c4Sopenharmony_ci	  Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
166da0c48c4Sopenharmony_ci							&symshstrndx,
167da0c48c4Sopenharmony_ci							shndx, &st_value);
168da0c48c4Sopenharmony_ci	  if (unlikely (result != DWFL_E_NOERROR))
169da0c48c4Sopenharmony_ci	    {
170da0c48c4Sopenharmony_ci	      __libdwfl_seterrno (result);
171da0c48c4Sopenharmony_ci	      return NULL;
172da0c48c4Sopenharmony_ci	    }
173da0c48c4Sopenharmony_ci	}
174da0c48c4Sopenharmony_ci      else if (alloc)
175da0c48c4Sopenharmony_ci	/* Apply the bias to the symbol value.  */
176da0c48c4Sopenharmony_ci	st_value = dwfl_adjusted_st_value (mod,
177da0c48c4Sopenharmony_ci					   *resolved ? mod->main.elf : elf,
178da0c48c4Sopenharmony_ci					   st_value);
179da0c48c4Sopenharmony_ci      break;
180da0c48c4Sopenharmony_ci    }
181da0c48c4Sopenharmony_ci
182da0c48c4Sopenharmony_ci  if (adjust_st_value)
183da0c48c4Sopenharmony_ci    sym->st_value = st_value;
184da0c48c4Sopenharmony_ci
185da0c48c4Sopenharmony_ci  if (addr != NULL)
186da0c48c4Sopenharmony_ci    *addr = st_value;
187da0c48c4Sopenharmony_ci
188da0c48c4Sopenharmony_ci  if (unlikely (sym->st_name >= symstrdata->d_size))
189da0c48c4Sopenharmony_ci    {
190da0c48c4Sopenharmony_ci      __libdwfl_seterrno (DWFL_E_BADSTROFF);
191da0c48c4Sopenharmony_ci      return NULL;
192da0c48c4Sopenharmony_ci    }
193da0c48c4Sopenharmony_ci  if (elfp)
194da0c48c4Sopenharmony_ci    *elfp = elf;
195da0c48c4Sopenharmony_ci  if (biasp)
196da0c48c4Sopenharmony_ci    *biasp = dwfl_adjusted_st_value (mod, elf, 0);
197da0c48c4Sopenharmony_ci  return (const char *) symstrdata->d_buf + sym->st_name;
198da0c48c4Sopenharmony_ci}
199da0c48c4Sopenharmony_ci
200da0c48c4Sopenharmony_ciconst char *
201da0c48c4Sopenharmony_cidwfl_module_getsym_info (Dwfl_Module *mod, int ndx,
202da0c48c4Sopenharmony_ci			 GElf_Sym *sym, GElf_Addr *addr,
203da0c48c4Sopenharmony_ci			 GElf_Word *shndxp,
204da0c48c4Sopenharmony_ci			 Elf **elfp, Dwarf_Addr *bias)
205da0c48c4Sopenharmony_ci{
206da0c48c4Sopenharmony_ci  bool resolved;
207da0c48c4Sopenharmony_ci  return __libdwfl_getsym (mod, ndx, sym, addr, shndxp, elfp, bias,
208da0c48c4Sopenharmony_ci			   &resolved, false);
209da0c48c4Sopenharmony_ci}
210da0c48c4Sopenharmony_ciINTDEF (dwfl_module_getsym_info)
211da0c48c4Sopenharmony_ci
212da0c48c4Sopenharmony_ciconst char *
213da0c48c4Sopenharmony_cidwfl_module_getsym (Dwfl_Module *mod, int ndx,
214da0c48c4Sopenharmony_ci		    GElf_Sym *sym, GElf_Word *shndxp)
215da0c48c4Sopenharmony_ci{
216da0c48c4Sopenharmony_ci  bool resolved;
217da0c48c4Sopenharmony_ci  return __libdwfl_getsym (mod, ndx, sym, NULL, shndxp, NULL, NULL,
218da0c48c4Sopenharmony_ci			   &resolved, true);
219da0c48c4Sopenharmony_ci}
220da0c48c4Sopenharmony_ciINTDEF (dwfl_module_getsym)
221