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