1da0c48c4Sopenharmony_ci/* Test program for libdwfl symbol resolving
2da0c48c4Sopenharmony_ci   Copyright (C) 2013 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 the GNU General Public License as published by
7da0c48c4Sopenharmony_ci   the Free Software Foundation; either version 3 of the License, or
8da0c48c4Sopenharmony_ci   (at your option) any later version.
9da0c48c4Sopenharmony_ci
10da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
11da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
12da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13da0c48c4Sopenharmony_ci   GNU General Public License for more details.
14da0c48c4Sopenharmony_ci
15da0c48c4Sopenharmony_ci   You should have received a copy of the GNU General Public License
16da0c48c4Sopenharmony_ci   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci#include <config.h>
19da0c48c4Sopenharmony_ci#include <assert.h>
20da0c48c4Sopenharmony_ci#include <inttypes.h>
21da0c48c4Sopenharmony_ci#include ELFUTILS_HEADER(dwfl)
22da0c48c4Sopenharmony_ci#include <elf.h>
23da0c48c4Sopenharmony_ci#include <dwarf.h>
24da0c48c4Sopenharmony_ci#include <argp.h>
25da0c48c4Sopenharmony_ci#include <stdio.h>
26da0c48c4Sopenharmony_ci#include <stdio_ext.h>
27da0c48c4Sopenharmony_ci#include <stdlib.h>
28da0c48c4Sopenharmony_ci#include <string.h>
29da0c48c4Sopenharmony_ci
30da0c48c4Sopenharmony_cistatic const char *
31da0c48c4Sopenharmony_cigelf_type (GElf_Sym *sym)
32da0c48c4Sopenharmony_ci{
33da0c48c4Sopenharmony_ci  switch (GELF_ST_TYPE (sym->st_info))
34da0c48c4Sopenharmony_ci    {
35da0c48c4Sopenharmony_ci    case STT_NOTYPE:
36da0c48c4Sopenharmony_ci      return "NOTYPE";
37da0c48c4Sopenharmony_ci    case STT_OBJECT:
38da0c48c4Sopenharmony_ci      return "OBJECT";
39da0c48c4Sopenharmony_ci    case STT_FUNC:
40da0c48c4Sopenharmony_ci      return "FUNC";
41da0c48c4Sopenharmony_ci    case STT_SECTION:
42da0c48c4Sopenharmony_ci      return "SECTION";
43da0c48c4Sopenharmony_ci    case STT_FILE:
44da0c48c4Sopenharmony_ci      return "FILE";
45da0c48c4Sopenharmony_ci    case STT_COMMON:
46da0c48c4Sopenharmony_ci      return "COMMON";
47da0c48c4Sopenharmony_ci    case STT_TLS:
48da0c48c4Sopenharmony_ci      return "TLS";
49da0c48c4Sopenharmony_ci    default:
50da0c48c4Sopenharmony_ci      return "UNKNOWN";
51da0c48c4Sopenharmony_ci    }
52da0c48c4Sopenharmony_ci}
53da0c48c4Sopenharmony_ci
54da0c48c4Sopenharmony_cistatic const char *
55da0c48c4Sopenharmony_cigelf_bind (GElf_Sym *sym)
56da0c48c4Sopenharmony_ci{
57da0c48c4Sopenharmony_ci  switch (GELF_ST_BIND (sym->st_info))
58da0c48c4Sopenharmony_ci    {
59da0c48c4Sopenharmony_ci    case STB_LOCAL:
60da0c48c4Sopenharmony_ci      return "LOCAL";
61da0c48c4Sopenharmony_ci    case STB_GLOBAL:
62da0c48c4Sopenharmony_ci      return "GLOBAL";
63da0c48c4Sopenharmony_ci    case STB_WEAK:
64da0c48c4Sopenharmony_ci      return "WEAK";
65da0c48c4Sopenharmony_ci    default:
66da0c48c4Sopenharmony_ci      return "UNKNOWN";
67da0c48c4Sopenharmony_ci    }
68da0c48c4Sopenharmony_ci}
69da0c48c4Sopenharmony_ci
70da0c48c4Sopenharmony_cistatic int
71da0c48c4Sopenharmony_cigelf_bind_order (GElf_Sym *sym)
72da0c48c4Sopenharmony_ci{
73da0c48c4Sopenharmony_ci  switch (GELF_ST_BIND (sym->st_info))
74da0c48c4Sopenharmony_ci    {
75da0c48c4Sopenharmony_ci    case STB_LOCAL:
76da0c48c4Sopenharmony_ci      return 1;
77da0c48c4Sopenharmony_ci    case STB_WEAK:
78da0c48c4Sopenharmony_ci      return 2;
79da0c48c4Sopenharmony_ci    case STB_GLOBAL:
80da0c48c4Sopenharmony_ci      return 3;
81da0c48c4Sopenharmony_ci    default:
82da0c48c4Sopenharmony_ci      return 0;
83da0c48c4Sopenharmony_ci    }
84da0c48c4Sopenharmony_ci}
85da0c48c4Sopenharmony_ci
86da0c48c4Sopenharmony_cistatic const char *
87da0c48c4Sopenharmony_cielf_section_name (Elf *elf, GElf_Word shndx)
88da0c48c4Sopenharmony_ci{
89da0c48c4Sopenharmony_ci  GElf_Ehdr ehdr;
90da0c48c4Sopenharmony_ci  GElf_Shdr shdr;
91da0c48c4Sopenharmony_ci  Elf_Scn *scn = elf_getscn (elf, shndx);
92da0c48c4Sopenharmony_ci  gelf_getshdr (scn, &shdr);
93da0c48c4Sopenharmony_ci  gelf_getehdr (elf, &ehdr);
94da0c48c4Sopenharmony_ci  return elf_strptr (elf, ehdr.e_shstrndx, shdr.sh_name);
95da0c48c4Sopenharmony_ci}
96da0c48c4Sopenharmony_ci
97da0c48c4Sopenharmony_cibool
98da0c48c4Sopenharmony_ciaddr_in_section (Elf *elf, GElf_Word shndx, GElf_Addr addr)
99da0c48c4Sopenharmony_ci{
100da0c48c4Sopenharmony_ci  GElf_Shdr shdr;
101da0c48c4Sopenharmony_ci  Elf_Scn *scn = elf_getscn (elf, shndx);
102da0c48c4Sopenharmony_ci  gelf_getshdr (scn, &shdr);
103da0c48c4Sopenharmony_ci  return addr >= shdr.sh_addr && addr < shdr.sh_addr + shdr.sh_size;
104da0c48c4Sopenharmony_ci}
105da0c48c4Sopenharmony_ci
106da0c48c4Sopenharmony_cistatic int
107da0c48c4Sopenharmony_cilist_syms (struct Dwfl_Module *mod,
108da0c48c4Sopenharmony_ci	   void **user __attribute__ ((unused)), const char *mod_name,
109da0c48c4Sopenharmony_ci	   Dwarf_Addr low_addr __attribute__ ((unused)),
110da0c48c4Sopenharmony_ci	   void *arg __attribute__ ((unused)))
111da0c48c4Sopenharmony_ci{
112da0c48c4Sopenharmony_ci  int syms = dwfl_module_getsymtab (mod);
113da0c48c4Sopenharmony_ci  if (syms < 0)
114da0c48c4Sopenharmony_ci    {
115da0c48c4Sopenharmony_ci      printf ("%s: %s\n", mod_name, dwfl_errmsg (-1));
116da0c48c4Sopenharmony_ci      return DWARF_CB_OK;
117da0c48c4Sopenharmony_ci    }
118da0c48c4Sopenharmony_ci
119da0c48c4Sopenharmony_ci  for (int ndx = 0; ndx < syms; ndx++)
120da0c48c4Sopenharmony_ci    {
121da0c48c4Sopenharmony_ci      GElf_Sym sym;
122da0c48c4Sopenharmony_ci      GElf_Word shndxp;
123da0c48c4Sopenharmony_ci      Elf *elf;
124da0c48c4Sopenharmony_ci      Dwarf_Addr bias;
125da0c48c4Sopenharmony_ci      const char *name = dwfl_module_getsym (mod, ndx, &sym, &shndxp);
126da0c48c4Sopenharmony_ci
127da0c48c4Sopenharmony_ci      printf("%4d: %s\t%s\t%s (%" PRIu64 ") %#" PRIx64,
128da0c48c4Sopenharmony_ci	     ndx, gelf_type (&sym), gelf_bind (&sym), name,
129da0c48c4Sopenharmony_ci	     sym.st_size, sym.st_value);
130da0c48c4Sopenharmony_ci
131da0c48c4Sopenharmony_ci      /* The info variant doesn't adjust st_value but returns the (possible)
132da0c48c4Sopenharmony_ci	 adjusted value separately. */
133da0c48c4Sopenharmony_ci      GElf_Addr value;
134da0c48c4Sopenharmony_ci      GElf_Sym isym;
135da0c48c4Sopenharmony_ci      name = dwfl_module_getsym_info (mod, ndx, &isym, &value, &shndxp,
136da0c48c4Sopenharmony_ci				      &elf, &bias);
137da0c48c4Sopenharmony_ci
138da0c48c4Sopenharmony_ci      GElf_Ehdr ehdr;
139da0c48c4Sopenharmony_ci      gelf_getehdr (elf, &ehdr);
140da0c48c4Sopenharmony_ci
141da0c48c4Sopenharmony_ci      // getsym st_values might or might not be adjusted depending on section.
142da0c48c4Sopenharmony_ci      // For ET_REL the adjustment is section relative.
143da0c48c4Sopenharmony_ci      assert (sym.st_value == isym.st_value
144da0c48c4Sopenharmony_ci	      || sym.st_value == isym.st_value + bias
145da0c48c4Sopenharmony_ci	      || ehdr.e_type == ET_REL);
146da0c48c4Sopenharmony_ci
147da0c48c4Sopenharmony_ci      /* And the reverse, which works for function symbols at least.
148da0c48c4Sopenharmony_ci	 Note this only works because the st.value is adjusted by
149da0c48c4Sopenharmony_ci	 dwfl_module_getsym ().  */
150da0c48c4Sopenharmony_ci      if (GELF_ST_TYPE (sym.st_info) == STT_FUNC && shndxp != SHN_UNDEF)
151da0c48c4Sopenharmony_ci	{
152da0c48c4Sopenharmony_ci	  /* Make sure the adjusted value really falls in the elf section. */
153da0c48c4Sopenharmony_ci          assert (addr_in_section (elf, shndxp, sym.st_value - bias));
154da0c48c4Sopenharmony_ci
155da0c48c4Sopenharmony_ci	  GElf_Addr addr = value;
156da0c48c4Sopenharmony_ci	  GElf_Sym asym;
157da0c48c4Sopenharmony_ci	  GElf_Word ashndxp;
158da0c48c4Sopenharmony_ci	  Elf *aelf;
159da0c48c4Sopenharmony_ci	  Dwarf_Addr abias;
160da0c48c4Sopenharmony_ci	  GElf_Off off;
161da0c48c4Sopenharmony_ci	  const char *aname = dwfl_module_addrinfo (mod, addr, &off, &asym,
162da0c48c4Sopenharmony_ci						    &ashndxp, &aelf, &abias);
163da0c48c4Sopenharmony_ci
164da0c48c4Sopenharmony_ci	  /* Make sure the adjusted value really falls in the elf section. */
165da0c48c4Sopenharmony_ci          assert (addr_in_section (aelf, ashndxp, asym.st_value)
166da0c48c4Sopenharmony_ci		  || ehdr.e_type == ET_REL);
167da0c48c4Sopenharmony_ci
168da0c48c4Sopenharmony_ci	  /* Either they are the same symbol (name), the binding of
169da0c48c4Sopenharmony_ci	     asym is "stronger" (or equal) to sym or asym is more specific
170da0c48c4Sopenharmony_ci	     (has a lower address) than sym.  */
171da0c48c4Sopenharmony_ci	  assert ((strcmp (name, aname) == 0
172da0c48c4Sopenharmony_ci		   || gelf_bind_order (&asym) >= gelf_bind_order (&sym))
173da0c48c4Sopenharmony_ci		  && value <= sym.st_value);
174da0c48c4Sopenharmony_ci
175da0c48c4Sopenharmony_ci	  addr = sym.st_value;
176da0c48c4Sopenharmony_ci	  int res = dwfl_module_relocate_address (mod, &addr);
177da0c48c4Sopenharmony_ci	  assert (res != -1);
178da0c48c4Sopenharmony_ci	  if (shndxp < SHN_LORESERVE)
179da0c48c4Sopenharmony_ci	    printf(", rel: %#" PRIx64 " (%s)", addr,
180da0c48c4Sopenharmony_ci		   elf_section_name (elf, shndxp));
181da0c48c4Sopenharmony_ci	  else
182da0c48c4Sopenharmony_ci	    printf(", rel: %#" PRIx64 "", addr);
183da0c48c4Sopenharmony_ci
184da0c48c4Sopenharmony_ci	  /* Print the section of the actual value if different from sym.  */
185da0c48c4Sopenharmony_ci	  if (value != isym.st_value + bias && ehdr.e_type != ET_REL)
186da0c48c4Sopenharmony_ci	    {
187da0c48c4Sopenharmony_ci	      GElf_Addr ebias;
188da0c48c4Sopenharmony_ci	      addr = value;
189da0c48c4Sopenharmony_ci	      Elf_Scn *scn = dwfl_module_address_section (mod, &addr, &ebias);
190da0c48c4Sopenharmony_ci	      GElf_Shdr shdr_mem;
191da0c48c4Sopenharmony_ci	      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
192da0c48c4Sopenharmony_ci	      Elf *melf = dwfl_module_getelf (mod, &ebias);
193da0c48c4Sopenharmony_ci	      gelf_getehdr (melf, &ehdr);
194da0c48c4Sopenharmony_ci	      const char *sname = elf_strptr (melf, ehdr.e_shstrndx,
195da0c48c4Sopenharmony_ci					      shdr->sh_name);
196da0c48c4Sopenharmony_ci	      printf (" [%#" PRIx64 ", rel: %#" PRIx64 " (%s)]",
197da0c48c4Sopenharmony_ci		      value, addr, sname);
198da0c48c4Sopenharmony_ci	    }
199da0c48c4Sopenharmony_ci
200da0c48c4Sopenharmony_ci	}
201da0c48c4Sopenharmony_ci      printf ("\n");
202da0c48c4Sopenharmony_ci    }
203da0c48c4Sopenharmony_ci
204da0c48c4Sopenharmony_ci  return DWARF_CB_OK;
205da0c48c4Sopenharmony_ci}
206da0c48c4Sopenharmony_ci
207da0c48c4Sopenharmony_ciint
208da0c48c4Sopenharmony_cimain (int argc, char *argv[])
209da0c48c4Sopenharmony_ci{
210da0c48c4Sopenharmony_ci  int remaining;
211da0c48c4Sopenharmony_ci  Dwfl *dwfl;
212da0c48c4Sopenharmony_ci  error_t res;
213da0c48c4Sopenharmony_ci
214da0c48c4Sopenharmony_ci  res = argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl);
215da0c48c4Sopenharmony_ci  assert (res == 0 && dwfl != NULL);
216da0c48c4Sopenharmony_ci
217da0c48c4Sopenharmony_ci  ptrdiff_t off = 0;
218da0c48c4Sopenharmony_ci  do
219da0c48c4Sopenharmony_ci    off = dwfl_getmodules (dwfl, list_syms, NULL, off);
220da0c48c4Sopenharmony_ci  while (off > 0);
221da0c48c4Sopenharmony_ci
222da0c48c4Sopenharmony_ci  dwfl_end (dwfl);
223da0c48c4Sopenharmony_ci
224da0c48c4Sopenharmony_ci  return off;
225da0c48c4Sopenharmony_ci}
226