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