xref: /third_party/elfutils/tests/addrcfi.c (revision da0c48c4)
1/* Test program for CFI handling.
2   Copyright (C) 2009-2010, 2013, 2015 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 <dwarf.h>
23#include <argp.h>
24#include <stdio.h>
25#include <stdio_ext.h>
26#include <locale.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "../libdw/known-dwarf.h"
31
32static const char *
33op_name (unsigned int code)
34{
35  static const char *const known[] =
36    {
37#define DWARF_ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME,
38      DWARF_ALL_KNOWN_DW_OP
39#undef DWARF_ONE_KNOWN_DW_OP
40    };
41
42  if (likely (code < sizeof (known) / sizeof (known[0])))
43    return known[code];
44
45  return NULL;
46}
47
48static void
49print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias)
50{
51  if (result < 0)
52    printf ("indeterminate (%s)\n", dwarf_errmsg (-1));
53  else if (nops == 0)
54    printf ("%s\n", ops == NULL ? "same_value" : "undefined");
55  else
56    {
57      printf ("%s expression:", result == 0 ? "location" : "value");
58      for (size_t i = 0; i < nops; ++i)
59	{
60	  printf (" %s", op_name(ops[i].atom));
61	  if (ops[i].number2 == 0)
62	    {
63	      if (ops[i].atom == DW_OP_addr)
64		printf ("(%#" PRIx64 ")", ops[i].number + bias);
65	      else if (ops[i].number != 0)
66		printf ("(%" PRId64 ")", ops[i].number);
67	    }
68	  else
69	    printf ("(%" PRId64 ",%" PRId64 ")",
70		    ops[i].number, ops[i].number2);
71	}
72      puts ("");
73    }
74}
75
76struct stuff
77{
78  Dwarf_Frame *frame;
79  Dwarf_Addr bias;
80};
81
82static int
83print_register (void *arg,
84		int regno,
85		const char *setname,
86		const char *prefix,
87		const char *regname,
88		int bits __attribute__ ((unused)),
89		int type __attribute__ ((unused)))
90{
91  struct stuff *stuff = arg;
92
93  printf ("\t%s reg%u (%s%s): ", setname, regno, prefix, regname);
94
95  Dwarf_Op ops_mem[3];
96  Dwarf_Op *ops;
97  size_t nops;
98  int result = dwarf_frame_register (stuff->frame, regno, ops_mem, &ops, &nops);
99  print_detail (result, ops, nops, stuff->bias);
100
101  return DWARF_CB_OK;
102}
103
104static int
105handle_cfi (Dwfl *dwfl, const char *which, Dwarf_CFI *cfi,
106	    GElf_Addr pc, struct stuff *stuff)
107{
108  if (cfi == NULL)
109    {
110      printf ("handle_cfi no CFI (%s): %s\n", which, dwarf_errmsg (-1));
111      return -1;
112    }
113
114  int result = dwarf_cfi_addrframe (cfi, pc - stuff->bias, &stuff->frame);
115  if (result != 0)
116    {
117      printf ("dwarf_cfi_addrframe (%s): %s\n", which, dwarf_errmsg (-1));
118      return 1;
119    }
120
121  Dwarf_Addr start = pc;
122  Dwarf_Addr end = pc;
123  bool signalp;
124  int ra_regno = dwarf_frame_info (stuff->frame, &start, &end, &signalp);
125  if (ra_regno >= 0)
126    {
127      start += stuff->bias;
128      end += stuff->bias;
129    }
130
131  printf ("%s has %#" PRIx64 " => [%#" PRIx64 ", %#" PRIx64 "):\n",
132	  which, pc, start, end);
133
134  if (ra_regno < 0)
135    printf ("\treturn address register unavailable (%s)\n",
136	    dwarf_errmsg (0));
137  else
138    printf ("\treturn address in reg%u%s\n",
139	    ra_regno, signalp ? " (signal frame)" : "");
140
141  // Point cfa_ops to dummy to match print_detail expectations.
142  // (nops == 0 && cfa_ops != NULL => "undefined")
143  Dwarf_Op dummy;
144  Dwarf_Op *cfa_ops = &dummy;
145  size_t cfa_nops;
146  result = dwarf_frame_cfa (stuff->frame, &cfa_ops, &cfa_nops);
147
148  printf ("\tCFA ");
149  print_detail (result, cfa_ops, cfa_nops, stuff->bias);
150
151  (void) dwfl_module_register_names (dwfl_addrmodule (dwfl, pc),
152				     &print_register, stuff);
153
154  return 0;
155}
156
157static int
158handle_address (GElf_Addr pc, Dwfl *dwfl)
159{
160  Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc);
161
162  struct stuff stuff;
163  stuff.frame = NULL;
164  stuff.bias = 0;
165  int res = handle_cfi (dwfl, ".eh_frame",
166			dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff);
167  free (stuff.frame);
168
169  stuff.frame = NULL;
170  stuff.bias = 0;
171  res &= handle_cfi (dwfl, ".debug_frame",
172		     dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff);
173  free (stuff.frame);
174
175  return res;
176}
177
178int
179main (int argc, char *argv[])
180{
181  int remaining;
182
183  /* Set locale.  */
184  (void) setlocale (LC_ALL, "");
185
186  Dwfl *dwfl = NULL;
187  (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl);
188  assert (dwfl != NULL);
189
190  int result = 0;
191
192  /* Now handle the addresses.  In case none are given on the command
193     line, read from stdin.  */
194  if (remaining == argc)
195    {
196      /* We use no threads here which can interfere with handling a stream.  */
197      (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
198
199      char *buf = NULL;
200      size_t len = 0;
201      while (!feof_unlocked (stdin))
202	{
203	  if (getline (&buf, &len, stdin) < 0)
204	    break;
205
206	  char *endp;
207	  uintmax_t addr = strtoumax (buf, &endp, 0);
208	  if (endp != buf)
209	    result |= handle_address (addr, dwfl);
210	  else
211	    result = 1;
212	}
213
214      free (buf);
215    }
216  else
217    {
218      do
219	{
220	  char *endp;
221	  uintmax_t addr = strtoumax (argv[remaining], &endp, 0);
222	  if (endp != argv[remaining])
223	    result |= handle_address (addr, dwfl);
224	  else
225	    result = 1;
226	}
227      while (++remaining < argc);
228    }
229
230  dwfl_end (dwfl);
231
232  return result;
233}
234