Home | History | Annotate | Download | only in tests
      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 
     32 static const char *
     33 op_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 
     48 static void
     49 print_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 
     76 struct stuff
     77 {
     78   Dwarf_Frame *frame;
     79   Dwarf_Addr bias;
     80 };
     81 
     82 static int
     83 print_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[2];
     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 
    104 static int
    105 handle_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 
    157 static int
    158 handle_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 
    178 int
    179 main (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