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