Blame tests/varlocs.c

Packit 032894
/* Test program for dwarf location functions.
Packit 032894
   Copyright (C) 2013, 2015, 2017, 2018 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#include <config.h>
Packit 032894
#include <assert.h>
Packit 032894
#include <argp.h>
Packit 032894
#include <inttypes.h>
Packit 032894
#include <errno.h>
Packit 032894
#include ELFUTILS_HEADER(dw)
Packit 032894
#include ELFUTILS_HEADER(dwfl)
Packit 032894
#include <dwarf.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <sys/types.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <unistd.h>
Packit 032894
Packit 032894
#include "system.h"
Packit 032894
#include "../libdw/known-dwarf.h"
Packit 032894
Packit 032894
// The Dwarf, Dwarf_CFIs and address bias of
Packit 032894
// cfi table to adjust DWARF addresses against.
Packit 032894
// Needed for DW_OP_call_frame_cfa.
Packit 032894
static Dwarf *dw;
Packit 032894
Dwarf_CFI *cfi_debug;
Packit 032894
Dwarf_Addr cfi_debug_bias;
Packit 032894
Dwarf_CFI *cfi_eh;
Packit 032894
Dwarf_Addr cfi_eh_bias;
Packit 032894
Packit 032894
bool is_ET_REL;
Packit 032894
bool is_debug;
Packit 032894
Packit 032894
// Whether the current function has a DW_AT_frame_base defined.
Packit 032894
// Needed for DW_OP_fbreg.
Packit 032894
bool has_frame_base;
Packit 032894
Packit 032894
static void
Packit 032894
print_die (Dwarf_Die *die, const char *what, int indent)
Packit 032894
{
Packit 032894
  Dwarf_Addr entrypc;
Packit 032894
  const char *name = dwarf_diename (die) ?: "<unknown>";
Packit 032894
  if (dwarf_entrypc (die, &entrypc) == 0)
Packit 032894
    printf ("%*s[%" PRIx64 "] %s '%s'@%" PRIx64 "\n", indent * 2, "",
Packit 032894
	    dwarf_dieoffset (die), what, name, entrypc);
Packit 032894
  else
Packit 032894
    printf ("%*s[%" PRIx64 "] %s '%s'\n", indent * 2, "",
Packit 032894
	    dwarf_dieoffset (die), what, name);
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
dwarf_encoding_string (unsigned int code)
Packit 032894
{
Packit 032894
  static const char *const known[] =
Packit 032894
    {
Packit 032894
#define DWARF_ONE_KNOWN_DW_ATE(NAME, CODE) [CODE] = #NAME,
Packit 032894
      DWARF_ALL_KNOWN_DW_ATE
Packit 032894
#undef DWARF_ONE_KNOWN_DW_ATE
Packit 032894
    };
Packit 032894
Packit 032894
  if (likely (code < sizeof (known) / sizeof (known[0])))
Packit 032894
    return known[code];
Packit 032894
Packit 032894
  return NULL;
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
dwarf_tag_string (unsigned int tag)
Packit 032894
{
Packit 032894
  switch (tag)
Packit 032894
    {
Packit 032894
#define DWARF_ONE_KNOWN_DW_TAG(NAME, CODE) case CODE: return #NAME;
Packit 032894
      DWARF_ALL_KNOWN_DW_TAG
Packit 032894
#undef DWARF_ONE_KNOWN_DW_TAG
Packit 032894
    default:
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
dwarf_attr_string (unsigned int attrnum)
Packit 032894
{
Packit 032894
  switch (attrnum)
Packit 032894
    {
Packit 032894
#define DWARF_ONE_KNOWN_DW_AT(NAME, CODE) case CODE: return #NAME;
Packit 032894
      DWARF_ALL_KNOWN_DW_AT
Packit 032894
#undef DWARF_ONE_KNOWN_DW_AT
Packit 032894
    default:
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
dwarf_form_string (unsigned int form)
Packit 032894
{
Packit 032894
  switch (form)
Packit 032894
    {
Packit 032894
#define DWARF_ONE_KNOWN_DW_FORM(NAME, CODE) case CODE: return #NAME;
Packit 032894
      DWARF_ALL_KNOWN_DW_FORM
Packit 032894
#undef DWARF_ONE_KNOWN_DW_FORM
Packit 032894
    default:
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
/* BASE must be a base type DIE referenced by a typed DWARF expression op.  */
Packit 032894
static void
Packit 032894
print_base_type (Dwarf_Die *base)
Packit 032894
{
Packit 032894
  if (dwarf_tag (base) != DW_TAG_base_type)
Packit 032894
    error (EXIT_FAILURE, 0, "not a base type");
Packit 032894
Packit 032894
  Dwarf_Attribute encoding;
Packit 032894
  Dwarf_Word enctype = 0;
Packit 032894
  if (dwarf_attr (base, DW_AT_encoding, &encoding) == NULL
Packit 032894
      || dwarf_formudata (&encoding, &enctype) != 0)
Packit 032894
    error (EXIT_FAILURE, 0, "base type without encoding");
Packit 032894
Packit 032894
  Dwarf_Attribute bsize;
Packit 032894
  Dwarf_Word bits;
Packit 032894
  if (dwarf_attr (base, DW_AT_byte_size, &bsize) != NULL
Packit 032894
      && dwarf_formudata (&bsize, &bits) == 0)
Packit 032894
    bits *= 8;
Packit 032894
  else if (dwarf_attr (base, DW_AT_bit_size, &bsize) == NULL
Packit 032894
	   || dwarf_formudata (&bsize, &bits) != 0)
Packit 032894
    error (EXIT_FAILURE, 0, "base type without byte or bit size");
Packit 032894
Packit 032894
  printf ("{%s,%s,%" PRIu64 "@[%" PRIx64 "]}",
Packit 032894
	  dwarf_diename (base),
Packit 032894
	  dwarf_encoding_string (enctype),
Packit 032894
	  bits,
Packit 032894
	  dwarf_dieoffset (base));
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
dwarf_opcode_string (unsigned int code)
Packit 032894
{
Packit 032894
  static const char *const known[] =
Packit 032894
    {
Packit 032894
#define DWARF_ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME,
Packit 032894
      DWARF_ALL_KNOWN_DW_OP
Packit 032894
#undef DWARF_ONE_KNOWN_DW_OP
Packit 032894
    };
Packit 032894
Packit 032894
  if (likely (code < sizeof (known) / sizeof (known[0])))
Packit 032894
    return known[code];
Packit 032894
Packit 032894
  return NULL;
Packit 032894
}
Packit 032894
Packit 032894
// Forward reference for print_expr_block.
Packit 032894
static void print_expr (Dwarf_Attribute *, Dwarf_Op *, Dwarf_Addr, int);
Packit 032894
Packit 032894
static void
Packit 032894
print_expr_block (Dwarf_Attribute *attr, Dwarf_Op *exprs, int len,
Packit 032894
		  Dwarf_Addr addr, int depth)
Packit 032894
{
Packit 032894
  printf ("{");
Packit 032894
  for (int i = 0; i < len; i++)
Packit 032894
    {
Packit 032894
      print_expr (attr, &exprs[i], addr, depth);
Packit 032894
      printf ("%s", (i + 1 < len ? ", " : ""));
Packit 032894
    }
Packit 032894
  printf ("}");
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
print_expr_block_addrs (Dwarf_Attribute *attr,
Packit 032894
			Dwarf_Addr begin, Dwarf_Addr end,
Packit 032894
			Dwarf_Op *exprs, int len)
Packit 032894
{
Packit 032894
  printf ("      [%" PRIx64 ",%" PRIx64 ") ", begin, end);
Packit 032894
  print_expr_block (attr, exprs, len, begin, 0);
Packit 032894
  printf ("\n");
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
print_expr (Dwarf_Attribute *attr, Dwarf_Op *expr, Dwarf_Addr addr, int depth)
Packit 032894
{
Packit 032894
#define MAX_DEPTH 64
Packit 032894
  if (depth++ > MAX_DEPTH)
Packit 032894
    error (EXIT_FAILURE, 0, "print_expr recursion depth exceeded");
Packit 032894
Packit 032894
  uint8_t atom = expr->atom;
Packit 032894
  const char *opname = dwarf_opcode_string (atom);
Packit 032894
  assert (opname != NULL);
Packit 032894
Packit 032894
  switch (atom)
Packit 032894
    {
Packit 032894
    case DW_OP_deref:
Packit 032894
    case DW_OP_dup:
Packit 032894
    case DW_OP_drop:
Packit 032894
    case DW_OP_over:
Packit 032894
    case DW_OP_swap:
Packit 032894
    case DW_OP_rot:
Packit 032894
    case DW_OP_xderef:
Packit 032894
    case DW_OP_abs:
Packit 032894
    case DW_OP_and:
Packit 032894
    case DW_OP_div:
Packit 032894
    case DW_OP_minus:
Packit 032894
    case DW_OP_mod:
Packit 032894
    case DW_OP_mul:
Packit 032894
    case DW_OP_neg:
Packit 032894
    case DW_OP_not:
Packit 032894
    case DW_OP_or:
Packit 032894
    case DW_OP_plus:
Packit 032894
    case DW_OP_shl:
Packit 032894
    case DW_OP_shr:
Packit 032894
    case DW_OP_shra:
Packit 032894
    case DW_OP_xor:
Packit 032894
    case DW_OP_eq:
Packit 032894
    case DW_OP_ge:
Packit 032894
    case DW_OP_gt:
Packit 032894
    case DW_OP_le:
Packit 032894
    case DW_OP_lt:
Packit 032894
    case DW_OP_ne:
Packit 032894
    case DW_OP_lit0 ... DW_OP_lit31:
Packit 032894
    case DW_OP_reg0 ... DW_OP_reg31:
Packit 032894
    case DW_OP_nop:
Packit 032894
    case DW_OP_stack_value:
Packit 032894
      /* No arguments. */
Packit 032894
      printf ("%s", opname);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_form_tls_address:
Packit 032894
      /* No arguments. Special. Pops an address and pushes the
Packit 032894
	 corresponding address in the current thread local
Packit 032894
	 storage. Uses the thread local storage block of the defining
Packit 032894
	 module (executable, shared library). */
Packit 032894
      printf ("%s", opname);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_GNU_push_tls_address:
Packit 032894
      /* No arguments. Special. Not the same as DW_OP_form_tls_address.
Packit 032894
	 Pops an offset into the current thread local strorage and
Packit 032894
	 pushes back the actual address. */
Packit 032894
      printf ("%s", opname);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_call_frame_cfa:
Packit 032894
      /* No arguments. Special. Pushes Call Frame Address as computed
Packit 032894
	 by CFI data (dwarf_cfi_addrframe will fetch that info (either from
Packit 032894
	 the .eh_frame or .debug_frame CFI) and dwarf_frame_cfa translatesr
Packit 032894
         the CFI instructions into a plain DWARF expression.
Packit 032894
	 Never used in CFI itself. */
Packit 032894
Packit 032894
      if (attr == NULL)
Packit 032894
	error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
Packit 032894
      printf ("%s ", opname);
Packit 032894
      if (cfi_eh == NULL && cfi_debug == NULL && !is_debug)
Packit 032894
	error (EXIT_FAILURE, 0, "DW_OP_call_frame_cfa used but no cfi found.");
Packit 032894
Packit 032894
      Dwarf_Frame *frame;
Packit 032894
      if (dwarf_cfi_addrframe (cfi_eh, addr + cfi_eh_bias, &frame) == 0
Packit 032894
	  || dwarf_cfi_addrframe (cfi_debug, addr + cfi_debug_bias,
Packit 032894
				  &frame) == 0)
Packit 032894
	{
Packit 032894
	  Dwarf_Op *cfa_ops;
Packit 032894
	  size_t cfa_nops;
Packit 032894
	  if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwarf_frame_cfa 0x%" PRIx64 ": %s",
Packit 032894
		   addr, dwarf_errmsg (-1));
Packit 032894
	  if (cfa_nops < 1)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwarf_frame_cfa no ops");
Packit 032894
	  print_expr_block (NULL, cfa_ops, cfa_nops, 0, depth);
Packit 032894
	  free (frame);
Packit 032894
	}
Packit 032894
      else if (is_ET_REL || is_debug)
Packit 032894
	{
Packit 032894
	  /* XXX In ET_REL files there might be an .eh_frame with relocations
Packit 032894
	     we don't handle (e.g. X86_64_PC32). Maybe we should?  */
Packit 032894
	  printf ("{...}");
Packit 032894
	}
Packit 032894
      else
Packit 032894
	error (EXIT_FAILURE, 0, "dwarf_cfi_addrframe 0x%" PRIx64 ": %s",
Packit 032894
	       addr, dwarf_errmsg (-1));
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_push_object_address:
Packit 032894
      /* No arguments. Special. Pushes object address explicitly.
Packit 032894
       Normally only done implicitly by DW_AT_data_member_location.
Packit 032894
       Never used in CFI. */
Packit 032894
      if (attr == NULL)
Packit 032894
	error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
      printf ("%s", opname);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_addr:
Packit 032894
      /* 1 address argument. */
Packit 032894
      printf ("%s(0x%" PRIx64 ")", opname, (Dwarf_Addr) expr->number);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_const1u:
Packit 032894
    case DW_OP_const2u:
Packit 032894
    case DW_OP_const4u:
Packit 032894
    case DW_OP_const8u:
Packit 032894
    case DW_OP_constu:
Packit 032894
    case DW_OP_pick:
Packit 032894
    case DW_OP_plus_uconst:
Packit 032894
    case DW_OP_regx:
Packit 032894
    case DW_OP_piece:
Packit 032894
    case DW_OP_deref_size:
Packit 032894
    case DW_OP_xderef_size:
Packit 032894
      /* 1 numeric unsigned argument. */
Packit 032894
      printf ("%s(%" PRIu64 ")", opname, expr->number);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_call2:
Packit 032894
    case DW_OP_call4:
Packit 032894
    case DW_OP_call_ref:
Packit 032894
      /* 1 DIE offset argument for more ops in location attribute of DIE.
Packit 032894
         Never used in CFI.  */
Packit 032894
      {
Packit 032894
	if (attr == NULL)
Packit 032894
	  error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
Packit 032894
	Dwarf_Attribute call_attr;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &call_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for %s error %s",
Packit 032894
		 opname, dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Die call_die;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &call_die) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die for %s error %s",
Packit 032894
		 opname, dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Op *call_ops;
Packit 032894
	size_t call_len;
Packit 032894
	if (dwarf_getlocation (&call_attr, &call_ops, &call_len) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation for entry: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("%s([%" PRIx64 "]) ", opname, dwarf_dieoffset (&call_die));
Packit 032894
	print_expr_block (&call_attr, call_ops, call_len, addr, depth);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_const1s:
Packit 032894
    case DW_OP_const2s:
Packit 032894
    case DW_OP_const4s:
Packit 032894
    case DW_OP_const8s:
Packit 032894
    case DW_OP_consts:
Packit 032894
    case DW_OP_skip:
Packit 032894
    case DW_OP_bra:
Packit 032894
    case DW_OP_breg0 ... DW_OP_breg31:
Packit 032894
      /* 1 numeric signed argument. */
Packit 032894
      printf ("%s(%" PRId64 ")", opname, (Dwarf_Sword) expr->number);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_fbreg:
Packit 032894
      /* 1 numeric signed argument. Offset from frame base. */
Packit 032894
      if (attr == NULL)
Packit 032894
	  error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
Packit 032894
      if (! has_frame_base)
Packit 032894
	error (EXIT_FAILURE, 0, "DW_OP_fbreg used without a frame base");
Packit 032894
Packit 032894
      printf ("%s(%" PRId64 ")", opname, (Dwarf_Sword) expr->number);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_bregx:
Packit 032894
      /* 2 arguments, unsigned register number, signed offset. */
Packit 032894
      printf ("%s(%" PRIu64 ",%" PRId64 ")", opname,
Packit 032894
	      expr->number, (Dwarf_Sword) expr->number2);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_bit_piece:
Packit 032894
      /* 2 arguments, unsigned size, unsigned offset. */
Packit 032894
      printf ("%s(%" PRIu64 ",%" PRIu64 ")", opname,
Packit 032894
	      expr->number, expr->number2);
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_implicit_value:
Packit 032894
      /* Special, unsigned size plus block. */
Packit 032894
      {
Packit 032894
	Dwarf_Attribute const_attr;
Packit 032894
	Dwarf_Block block;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &const_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	if (dwarf_formblock (&const_attr, &block) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_formblock: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	/* This is the "old" way. Check they result in the same.  */
Packit 032894
	Dwarf_Block block_impl;
Packit 032894
	if (dwarf_getlocation_implicit_value (attr, expr, &block_impl) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_implicit_value: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	assert (expr->number == block.length);
Packit 032894
	assert (block.length == block_impl.length);
Packit 032894
	printf ("%s(%" PRIu64 "){", opname, block.length);
Packit 032894
	for (size_t i = 0; i < block.length; i++)
Packit 032894
	  {
Packit 032894
	    printf ("%02x", block.data[i]);
Packit 032894
	    assert (block.data[i] == block_impl.data[i]);
Packit 032894
	  }
Packit 032894
	printf("}");
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_implicit_pointer:
Packit 032894
    case DW_OP_GNU_implicit_pointer:
Packit 032894
      /* Special, DIE offset, signed offset. Referenced DIE has a
Packit 032894
	 location or const_value attribute. */
Packit 032894
      {
Packit 032894
	if (attr == NULL)
Packit 032894
	  error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
Packit 032894
	Dwarf_Attribute attrval;
Packit 032894
	if (dwarf_getlocation_implicit_pointer (attr, expr, &attrval) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_implicit_pointer: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	// Sanity check, results should be the same.
Packit 032894
	Dwarf_Attribute attrval2;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &attrval2) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	assert (dwarf_whatattr (&attrval) == dwarf_whatattr (&attrval2));
Packit 032894
	assert (dwarf_whatform (&attrval) == dwarf_whatform (&attrval2));
Packit 032894
	// In theory two different valp pointers could point to the same
Packit 032894
	// value. But here we really expect them to be the equal.
Packit 032894
	assert (attrval.valp == attrval2.valp);
Packit 032894
Packit 032894
	Dwarf_Die impl_die;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &impl_die) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("%s([%" PRIx64 "],%" PRId64 ") ", opname,
Packit 032894
		dwarf_dieoffset (&impl_die), expr->number2);
Packit 032894
Packit 032894
	if (dwarf_whatattr (&attrval) == DW_AT_const_value)
Packit 032894
	  printf ("<constant value>"); // Lookup type...
Packit 032894
	else
Packit 032894
	  {
Packit 032894
	    // Lookup the location description at the current address.
Packit 032894
	    Dwarf_Op *exprval;
Packit 032894
	    size_t exprval_len;
Packit 032894
	    int locs = dwarf_getlocation_addr (&attrval, addr,
Packit 032894
					       &exprval, &exprval_len, 1);
Packit 032894
	    if (locs == 0)
Packit 032894
	      printf ("<no location>"); // This means "optimized out".
Packit 032894
	    else if (locs == 1)
Packit 032894
	      print_expr_block (&attrval, exprval, exprval_len, addr, depth);
Packit 032894
	    else
Packit 032894
	      error (EXIT_FAILURE, 0,
Packit 032894
		     "dwarf_getlocation_addr attrval at addr 0x%" PRIx64
Packit 032894
		     ", locs (%d): %s", addr, locs, dwarf_errmsg (-1));
Packit 032894
	  }
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_GNU_variable_value:
Packit 032894
      /* Special, DIE offset. Referenced DIE has a location or const_value
Packit 032894
	 attribute. */
Packit 032894
      {
Packit 032894
	if (attr == NULL)
Packit 032894
	  error (EXIT_FAILURE, 0, "%s used in CFI", opname);
Packit 032894
Packit 032894
	Dwarf_Attribute attrval;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &attrval) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Die impl_die;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &impl_die) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("%s([%" PRIx64 "]) ", opname, dwarf_dieoffset (&impl_die));
Packit 032894
Packit 032894
	if (dwarf_whatattr (&attrval) == DW_AT_const_value)
Packit 032894
	  printf ("<constant value>"); // Lookup type...
Packit 032894
	else
Packit 032894
	  {
Packit 032894
	    // Lookup the location description at the current address.
Packit 032894
	    Dwarf_Op *exprval;
Packit 032894
	    size_t exprval_len;
Packit 032894
	    int locs = dwarf_getlocation_addr (&attrval, addr,
Packit 032894
					       &exprval, &exprval_len, 1);
Packit 032894
	    if (locs == 0)
Packit 032894
	      printf ("<no location>"); // This means "optimized out".
Packit 032894
	    else if (locs == 1)
Packit 032894
	      print_expr_block (&attrval, exprval, exprval_len, addr, depth);
Packit 032894
	    else
Packit 032894
	      error (EXIT_FAILURE, 0,
Packit 032894
		     "dwarf_getlocation_addr attrval at addr 0x%" PRIx64
Packit 032894
		     ", locs (%d): %s", addr, locs, dwarf_errmsg (-1));
Packit 032894
	  }
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_entry_value:
Packit 032894
    case DW_OP_GNU_entry_value:
Packit 032894
      /* Special, unsigned size plus expression block. All registers
Packit 032894
	 inside the block should be interpreted as they had on
Packit 032894
	 entering the function. dwarf_getlocation_attr will return an
Packit 032894
	 attribute containing the block as locexpr which can be
Packit 032894
	 retrieved with dwarf_getlocation.  */
Packit 032894
      {
Packit 032894
	Dwarf_Attribute entry_attr;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &entry_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Op *entry_ops;
Packit 032894
	size_t entry_len;
Packit 032894
	if (dwarf_getlocation (&entry_attr, &entry_ops, &entry_len) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation for entry: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("%s(%zd) ", opname, entry_len);
Packit 032894
	print_expr_block (attr, entry_ops, entry_len, addr, depth);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_GNU_parameter_ref:
Packit 032894
      /* Special, unsigned CU relative DIE offset pointing to a
Packit 032894
	 DW_TAG_formal_parameter. The value that parameter had at the
Packit 032894
	 call site of the current function will be put on the DWARF
Packit 032894
	 stack. The value can be retrieved by finding the
Packit 032894
	 DW_TAG_GNU_call_site_parameter which has as
Packit 032894
	 DW_AT_abstract_origin the same formal parameter DIE. */
Packit 032894
      {
Packit 032894
	Dwarf_Die param;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &param) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	// XXX actually lookup DW_TAG_GNU_call_site_parameter
Packit 032894
	printf ("%s[%" PRIx64 "]", opname, dwarf_dieoffset (&param));
Packit 032894
	assert (expr->number == dwarf_cuoffset (&param));
Packit 032894
	if (dwarf_tag (&param) != DW_TAG_formal_parameter)
Packit 032894
	  error (EXIT_FAILURE, 0, "Not a formal parameter");
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_convert:
Packit 032894
    case DW_OP_GNU_convert:
Packit 032894
    case DW_OP_reinterpret:
Packit 032894
    case DW_OP_GNU_reinterpret:
Packit 032894
      /* Special, unsigned CU relative DIE offset pointing to a
Packit 032894
	 DW_TAG_base_type. Pops a value, converts or reinterprets the
Packit 032894
	 value to the given type. When the argument is zero the value
Packit 032894
	 becomes untyped again. */
Packit 032894
      {
Packit 032894
	Dwarf_Die type;
Packit 032894
	Dwarf_Off off = expr->number;
Packit 032894
	if (off != 0)
Packit 032894
	  {
Packit 032894
	    if (dwarf_getlocation_die (attr, expr, &type) != 0)
Packit 032894
	      error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		     dwarf_errmsg (-1));
Packit 032894
	    off = dwarf_dieoffset (&type);
Packit 032894
	    assert (expr->number == dwarf_cuoffset (&type));
Packit 032894
	    printf ("%s", opname);
Packit 032894
	    print_base_type (&type);
Packit 032894
	  }
Packit 032894
	else
Packit 032894
	  printf ("%s[%" PRIu64 "]", opname, off);
Packit 032894
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_regval_type:
Packit 032894
    case DW_OP_GNU_regval_type:
Packit 032894
      /* Special, unsigned register number plus unsigned CU relative
Packit 032894
         DIE offset pointing to a DW_TAG_base_type. */
Packit 032894
      {
Packit 032894
	Dwarf_Die type;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &type) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	assert (expr->number2 == dwarf_cuoffset (&type));
Packit 032894
	// XXX check size against base_type size?
Packit 032894
	printf ("%s(reg%" PRIu64 ")", opname, expr->number);
Packit 032894
	print_base_type (&type);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_deref_type:
Packit 032894
    case DW_OP_GNU_deref_type:
Packit 032894
      /* Special, unsigned size plus unsigned CU relative DIE offset
Packit 032894
	 pointing to a DW_TAG_base_type. */
Packit 032894
      {
Packit 032894
	Dwarf_Die type;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &type) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	assert (expr->number2 == dwarf_cuoffset (&type));
Packit 032894
	// XXX check size against base_type size?
Packit 032894
	printf ("%s(%" PRIu64 ")", opname, expr->number);
Packit 032894
	print_base_type (&type);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_xderef_type:
Packit 032894
      /* Special, unsigned size plus unsigned DIE offset
Packit 032894
	 pointing to a DW_TAG_base_type. */
Packit 032894
      {
Packit 032894
	Dwarf_Die type;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &type) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	// XXX check size against base_type size?
Packit 032894
	printf ("%s(%" PRIu64 ")", opname, expr->number);
Packit 032894
	print_base_type (&type);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_const_type:
Packit 032894
    case DW_OP_GNU_const_type:
Packit 032894
      /* Special, unsigned CU relative DIE offset pointing to a
Packit 032894
	 DW_TAG_base_type, an unsigned size length plus a block with
Packit 032894
	 the constant value. */
Packit 032894
      {
Packit 032894
	Dwarf_Die type;
Packit 032894
	if (dwarf_getlocation_die (attr, expr, &type) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_die: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	assert (expr->number == dwarf_cuoffset (&type));
Packit 032894
Packit 032894
	Dwarf_Attribute const_attr;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &const_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for type: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
	  
Packit 032894
	Dwarf_Block block;
Packit 032894
	if (dwarf_formblock (&const_attr, &block) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_formblock for type: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("%s", opname);
Packit 032894
	print_base_type (&type);
Packit 032894
	printf ("(%" PRIu64 ")[", block.length);
Packit 032894
	for (size_t i = 0; i < block.length; i++)
Packit 032894
	  printf ("%02x", block.data[i]);
Packit 032894
	printf("]");
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_GNU_addr_index:
Packit 032894
    case DW_OP_addrx:
Packit 032894
      /* Address from the .debug_addr section (indexed based on CU).  */
Packit 032894
      {
Packit 032894
	Dwarf_Attribute addr_attr;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &addr_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for addr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Addr address;
Packit 032894
	if (dwarf_formaddr (&addr_attr, &address) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_formaddr address failed: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("addr: 0x%" PRIx64, address);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    case DW_OP_GNU_const_index:
Packit 032894
    case DW_OP_constx:
Packit 032894
      /* Constant from the .debug_addr section (indexed based on CU).  */
Packit 032894
      {
Packit 032894
	Dwarf_Attribute addr_attr;
Packit 032894
	if (dwarf_getlocation_attr (attr, expr, &addr_attr) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_getlocation_attr for addr: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	Dwarf_Word constant;
Packit 032894
	if (dwarf_formudata (&addr_attr, &constant) != 0)
Packit 032894
	  error (EXIT_FAILURE, 0, "dwarf_formudata constant failed: %s",
Packit 032894
		 dwarf_errmsg (-1));
Packit 032894
Packit 032894
	printf ("const: 0x%" PRIx64, constant);
Packit 032894
      }
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      error (EXIT_FAILURE, 0, "unhandled opcode: DW_OP_%s (0x%x)",
Packit 032894
	     opname, atom);
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
/* Get all variables and print their value expressions. */
Packit 032894
static void
Packit 032894
print_varlocs (Dwarf_Die *funcdie)
Packit 032894
{
Packit 032894
  // Display frame base for function if it exists.
Packit 032894
  // Should be used for DW_OP_fbreg.
Packit 032894
  has_frame_base = dwarf_hasattr (funcdie, DW_AT_frame_base);
Packit 032894
  if (has_frame_base)
Packit 032894
    {
Packit 032894
      Dwarf_Attribute fb_attr;
Packit 032894
      if (dwarf_attr (funcdie, DW_AT_frame_base, &fb_attr) == NULL)
Packit 032894
	error (EXIT_FAILURE, 0, "dwarf_attr fb: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
      Dwarf_Op *fb_expr;
Packit 032894
      size_t fb_exprlen;
Packit 032894
      if (dwarf_getlocation (&fb_attr, &fb_expr, &fb_exprlen) == 0)
Packit 032894
	{
Packit 032894
	  // Covers all of function.
Packit 032894
	  Dwarf_Addr entrypc;
Packit 032894
	  if (dwarf_entrypc (funcdie, &entrypc) != 0)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwarf_entrypc: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
	  printf ("    frame_base: ");
Packit 032894
	  if (entrypc == 0)
Packit 032894
	    printf ("XXX zero address"); // XXX bad DWARF?
Packit 032894
	  else
Packit 032894
	    print_expr_block (&fb_attr, fb_expr, fb_exprlen, entrypc, 0);
Packit 032894
	  printf ("\n");
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  Dwarf_Addr base, start, end;
Packit 032894
	  ptrdiff_t off = 0;
Packit 032894
	  printf ("    frame_base:\n");
Packit 032894
          while ((off = dwarf_getlocations (&fb_attr, off, &base,
Packit 032894
					    &start, &end,
Packit 032894
					    &fb_expr, &fb_exprlen)) > 0)
Packit 032894
	    {
Packit 032894
	      printf ("      (%" PRIx64 ",%" PRIx64 ") ", start, end);
Packit 032894
	      print_expr_block (&fb_attr, fb_expr, fb_exprlen, start, 0);
Packit 032894
	      printf ("\n");
Packit 032894
	    }
Packit 032894
Packit 032894
	  if (off < 0)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwarf_getlocations fb: %s",
Packit 032894
		   dwarf_errmsg (-1));
Packit 032894
	}
Packit 032894
    }
Packit 032894
  else if (dwarf_tag (funcdie) == DW_TAG_inlined_subroutine)
Packit 032894
    {
Packit 032894
      // See whether the subprogram we are inlined into has a frame
Packit 032894
      // base we should use.
Packit 032894
      Dwarf_Die *scopes;
Packit 032894
      int n = dwarf_getscopes_die (funcdie, &scopes);
Packit 032894
      if (n <= 0)
Packit 032894
	error (EXIT_FAILURE, 0, "dwarf_getscopes_die: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
      while (n-- > 0)
Packit 032894
	if (dwarf_tag (&scopes[n]) == DW_TAG_subprogram
Packit 032894
	    && dwarf_hasattr (&scopes[n], DW_AT_frame_base))
Packit 032894
	  {
Packit 032894
	    has_frame_base = true;
Packit 032894
	    break;
Packit 032894
	  }
Packit 032894
      free (scopes);
Packit 032894
    }
Packit 032894
Packit 032894
  if (! dwarf_haschildren (funcdie))
Packit 032894
    return;
Packit 032894
Packit 032894
  Dwarf_Die child;
Packit 032894
  int res = dwarf_child (funcdie, &child);
Packit 032894
  if (res < 0)
Packit 032894
    error (EXIT_FAILURE, 0, "dwarf_child: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
  /* We thought there was a child, but the child list was actually
Packit 032894
     empty. This isn't technically an error in the DWARF, but it is
Packit 032894
     certainly non-optimimal.  */
Packit 032894
  if (res == 1)
Packit 032894
    return;
Packit 032894
Packit 032894
  do
Packit 032894
    {
Packit 032894
      int tag = dwarf_tag (&child);
Packit 032894
      if (tag == DW_TAG_variable || tag == DW_TAG_formal_parameter)
Packit 032894
	{
Packit 032894
	  const char *what = tag == DW_TAG_variable ? "variable" : "parameter";
Packit 032894
	  print_die (&child, what, 2);
Packit 032894
Packit 032894
	  if (dwarf_hasattr (&child, DW_AT_location))
Packit 032894
	    {
Packit 032894
	      Dwarf_Attribute attr;
Packit 032894
	      if (dwarf_attr (&child, DW_AT_location, &attr) == NULL)
Packit 032894
		error (EXIT_FAILURE, 0, "dwarf_attr: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
	      Dwarf_Op *expr;
Packit 032894
	      size_t exprlen;
Packit 032894
	      if (dwarf_getlocation (&attr, &expr, &exprlen) == 0)
Packit 032894
		{
Packit 032894
		  // Covers all ranges of the function.
Packit 032894
		  // Evaluate the expression block for each range.
Packit 032894
		  ptrdiff_t offset = 0;
Packit 032894
		  Dwarf_Addr base, begin, end;
Packit 032894
		  do
Packit 032894
		    {
Packit 032894
		      offset = dwarf_ranges (funcdie, offset, &base,
Packit 032894
					     &begin, &end;;
Packit 032894
		      if (offset < 0)
Packit 032894
			error (EXIT_FAILURE, 0, "dwarf_ranges: %s",
Packit 032894
			       dwarf_errmsg (-1));
Packit 032894
Packit 032894
		      if (offset > 0)
Packit 032894
			{
Packit 032894
			  if (exprlen == 0)
Packit 032894
			    printf ("      (%"
Packit 032894
				    PRIx64 ",%" PRIx64
Packit 032894
				    ") <empty expression>\n", begin, end);
Packit 032894
			  else
Packit 032894
			    print_expr_block_addrs (&attr, begin, end,
Packit 032894
						    expr, exprlen);
Packit 032894
			}
Packit 032894
		    }
Packit 032894
		  while (offset > 0);
Packit 032894
Packit 032894
		  if (offset < 0)
Packit 032894
		    error (EXIT_FAILURE, 0, "dwarf_ranges: %s",
Packit 032894
			   dwarf_errmsg (-1));
Packit 032894
		}
Packit 032894
	      else
Packit 032894
		{
Packit 032894
		  Dwarf_Addr base, begin, end;
Packit 032894
		  ptrdiff_t offset = 0;
Packit 032894
		  while ((offset = dwarf_getlocations (&attr, offset,
Packit 032894
						       &base, &begin, &end,
Packit 032894
						       &expr, &exprlen)) > 0)
Packit 032894
		    if (begin >= end)
Packit 032894
		      printf ("      (%" PRIx64 ",%" PRIx64
Packit 032894
			      ") <empty range>\n", begin, end); // XXX report?
Packit 032894
		    else
Packit 032894
		      {
Packit 032894
			print_expr_block_addrs (&attr, begin, end,
Packit 032894
						expr, exprlen);
Packit 032894
Packit 032894
			// Extra sanity check for dwarf_getlocation_addr
Packit 032894
			// Must at least find one range for begin and end-1.
Packit 032894
			Dwarf_Op *expraddr;
Packit 032894
			size_t expraddr_len;
Packit 032894
			int locs = dwarf_getlocation_addr (&attr, begin,
Packit 032894
							   &expraddr,
Packit 032894
							   &expraddr_len, 1);
Packit 032894
			assert (locs == 1);
Packit 032894
			locs = dwarf_getlocation_addr (&attr, end - 1,
Packit 032894
						       &expraddr,
Packit 032894
						       &expraddr_len, 1);
Packit 032894
			assert (locs == 1);
Packit 032894
		      }
Packit 032894
Packit 032894
		  if (offset < 0)
Packit 032894
		    error (EXIT_FAILURE, 0, "dwarf_getlocations: %s",
Packit 032894
			   dwarf_errmsg (-1));
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	  else if (dwarf_hasattr (&child, DW_AT_const_value))
Packit 032894
	    {
Packit 032894
	      printf ("      <constant value>\n"); // Lookup type and print.
Packit 032894
	    }
Packit 032894
	  else
Packit 032894
	    {
Packit 032894
	      printf ("      <no value>\n");
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
  while (dwarf_siblingof (&child, &child) == 0);
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
handle_instance (Dwarf_Die *funcdie, void *arg __attribute__ ((unused)))
Packit 032894
{
Packit 032894
  print_die (funcdie, "inlined function", 1);
Packit 032894
  print_varlocs (funcdie);
Packit 032894
Packit 032894
  return DWARF_CB_OK;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
handle_function (Dwarf_Die *funcdie, void *arg __attribute__((unused)))
Packit 032894
{
Packit 032894
  if (dwarf_func_inline (funcdie) > 0)
Packit 032894
    {
Packit 032894
      // abstract inline definition, find all inlined instances.
Packit 032894
Packit 032894
      // Note this is convenient for listing all instances together
Packit 032894
      // so you can easily compare the location expressions describing
Packit 032894
      // the variables and parameters, but it isn't very efficient
Packit 032894
      // since it will walk the DIE tree multiple times.
Packit 032894
      if (dwarf_func_inline_instances (funcdie, &handle_instance, NULL) != 0)
Packit 032894
	error (EXIT_FAILURE, 0, "dwarf_func_inline_instances: %s",
Packit 032894
	       dwarf_errmsg (-1));
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      // Contains actual code, not just a declaration?
Packit 032894
      Dwarf_Addr entrypc;
Packit 032894
      if (dwarf_entrypc (funcdie, &entrypc) == 0)
Packit 032894
	{
Packit 032894
	  print_die (funcdie, "function", 1);
Packit 032894
	  print_varlocs (funcdie);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  return DWARF_CB_OK;
Packit 032894
}
Packit 032894
Packit 032894
struct attr_arg
Packit 032894
{
Packit 032894
  int depth;
Packit 032894
  Dwarf_Addr entrypc;
Packit 032894
};
Packit 032894
Packit 032894
static int
Packit 032894
handle_attr (Dwarf_Attribute *attr, void *arg)
Packit 032894
{
Packit 032894
  int depth = ((struct attr_arg *) arg)->depth;
Packit 032894
  Dwarf_Addr entrypc = ((struct attr_arg *) arg)->entrypc;
Packit 032894
Packit 032894
  unsigned int code = dwarf_whatattr (attr);
Packit 032894
  unsigned int form = dwarf_whatform (attr);
Packit 032894
Packit 032894
  printf ("%*s%s (%s)", depth * 2, "",
Packit 032894
	  dwarf_attr_string (code), dwarf_form_string (form));
Packit 032894
Packit 032894
  /* If we can get an DWARF expression (or location lists) from this
Packit 032894
     attribute we'll print it, otherwise we'll ignore it.  But if
Packit 032894
     there is an error while the attribute has the "correct" form then
Packit 032894
     we'll report an error (we can only really check DW_FORM_exprloc
Packit 032894
     other forms can be ambiguous).  */
Packit 032894
  Dwarf_Op *expr;
Packit 032894
  size_t exprlen;
Packit 032894
  bool printed = false;
Packit 032894
  int res = dwarf_getlocation (attr, &expr, &exprlen);
Packit 032894
  if (res == 0)
Packit 032894
    {
Packit 032894
      printf (" ");
Packit 032894
      print_expr_block (attr, expr, exprlen, entrypc, 0);
Packit 032894
      printf ("\n");
Packit 032894
      printed = true;
Packit 032894
    }
Packit 032894
  else if (form == DW_FORM_exprloc)
Packit 032894
    {
Packit 032894
      error (0, 0, "%s dwarf_getlocation failed: %s",
Packit 032894
	     dwarf_attr_string (code), dwarf_errmsg (-1));
Packit 032894
      return DWARF_CB_ABORT;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      Dwarf_Addr base, begin, end;
Packit 032894
      ptrdiff_t offset = 0;
Packit 032894
      while ((offset = dwarf_getlocations (attr, offset,
Packit 032894
					   &base, &begin, &end,
Packit 032894
					   &expr, &exprlen)) > 0)
Packit 032894
	{
Packit 032894
	  if (! printed)
Packit 032894
	    printf ("\n");
Packit 032894
	  printf ("%*s", depth * 2, "");
Packit 032894
	  print_expr_block_addrs (attr, begin, end, expr, exprlen);
Packit 032894
	  printed = true;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (! printed)
Packit 032894
    printf ("\n");
Packit 032894
Packit 032894
  return DWARF_CB_OK;
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
handle_die (Dwarf_Die *die, int depth, bool outer_has_frame_base,
Packit 032894
	    Dwarf_Addr outer_entrypc)
Packit 032894
{
Packit 032894
  /* CU DIE already printed.  */
Packit 032894
  if (depth > 0)
Packit 032894
    {
Packit 032894
      const char *name = dwarf_diename (die);
Packit 032894
      if (name != NULL)
Packit 032894
	printf ("%*s[%" PRIx64 "] %s \"%s\"\n", depth * 2, "",
Packit 032894
		dwarf_dieoffset (die), dwarf_tag_string (dwarf_tag (die)),
Packit 032894
		name);
Packit 032894
      else
Packit 032894
	printf ("%*s[%" PRIx64 "] %s\n", depth * 2, "",
Packit 032894
		dwarf_dieoffset (die), dwarf_tag_string (dwarf_tag (die)));
Packit 032894
    }
Packit 032894
Packit 032894
  struct attr_arg arg;
Packit 032894
  arg.depth = depth + 1;
Packit 032894
Packit 032894
  /* The (lowest) address to use for (looking up) operands that depend
Packit 032894
     on address.  */
Packit 032894
  Dwarf_Addr die_entrypc;
Packit 032894
  if (dwarf_entrypc (die, &die_entrypc) != 0 || die_entrypc == 0)
Packit 032894
    {
Packit 032894
      /* Try to get the lowest address of the first range covered.  */
Packit 032894
      Dwarf_Addr base, start, end;
Packit 032894
      if (dwarf_ranges (die, 0, &base, &start, &end) <= 0 || start == 0)
Packit 032894
	die_entrypc = outer_entrypc;
Packit 032894
      else
Packit 032894
	die_entrypc = start;
Packit 032894
    }
Packit 032894
  arg.entrypc = die_entrypc;
Packit 032894
Packit 032894
  /* Whether this or the any outer DIE has a frame base. Used as
Packit 032894
     sanity check when printing experssions that use DW_OP_fbreg.  */
Packit 032894
  bool die_has_frame_base = dwarf_hasattr (die, DW_AT_frame_base);
Packit 032894
  die_has_frame_base |= outer_has_frame_base;
Packit 032894
  has_frame_base = die_has_frame_base;
Packit 032894
Packit 032894
  /* Look through all attributes to find those that contain DWARF
Packit 032894
     expressions and print those.  We expect to handle all attributes,
Packit 032894
     anything else is an error.  */
Packit 032894
  if (dwarf_getattrs (die, handle_attr, &arg, 0) != 1)
Packit 032894
    error (EXIT_FAILURE, 0, "Couldn't get all attributes: %s",
Packit 032894
	   dwarf_errmsg (-1));
Packit 032894
Packit 032894
  /* Handle children and siblings recursively depth first.  */
Packit 032894
  Dwarf_Die child;
Packit 032894
  if (dwarf_haschildren (die) != 0 && dwarf_child (die, &child) == 0)
Packit 032894
    handle_die (&child, depth + 1, die_has_frame_base, die_entrypc);
Packit 032894
Packit 032894
  Dwarf_Die sibling;
Packit 032894
  if (dwarf_siblingof (die, &sibling) == 0)
Packit 032894
    handle_die (&sibling, depth, outer_has_frame_base, outer_entrypc);
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
main (int argc, char *argv[])
Packit 032894
{
Packit 032894
  /* With --exprlocs we process all DIEs looking for any attribute
Packit 032894
     which contains an DWARF expression (but not location lists) and
Packit 032894
     print those.  Otherwise we process all function DIEs and print
Packit 032894
     all DWARF expressions and location lists associated with
Packit 032894
     parameters and variables). It must be the first argument,
Packit 032894
     or the second, after --debug.  */
Packit 032894
  bool exprlocs = false;
Packit 032894
Packit 032894
  /* With --debug we ignore not being able to find .eh_frame.
Packit 032894
     It must come as first argument.  */
Packit 032894
  is_debug = false;
Packit 032894
  if (argc > 1)
Packit 032894
    {
Packit 032894
      if (strcmp ("--exprlocs", argv[1]) == 0)
Packit 032894
	{
Packit 032894
	  exprlocs = true;
Packit 032894
	  argv[1] = "";
Packit 032894
	}
Packit 032894
      else if (strcmp ("--debug", argv[1]) == 0)
Packit 032894
	{
Packit 032894
	  is_debug = true;
Packit 032894
	  argv[1] = "";
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (argc > 2)
Packit 032894
    {
Packit 032894
      if (strcmp ("--exprlocs", argv[2]) == 0)
Packit 032894
	{
Packit 032894
	  exprlocs = true;
Packit 032894
	  argv[2] = "";
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  int remaining;
Packit 032894
  Dwfl *dwfl;
Packit 032894
  (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining,
Packit 032894
                     &dwfl);
Packit 032894
  assert (dwfl != NULL);
Packit 032894
Packit 032894
  Dwarf_Die *cu = NULL;
Packit 032894
  Dwarf_Addr dwbias;
Packit 032894
  bool found_cu = false;
Packit 032894
  while ((cu = dwfl_nextcu (dwfl, cu, &dwbias)) != NULL)
Packit 032894
    {
Packit 032894
      /* Only walk actual compile units (not partial units) that
Packit 032894
	 contain code if we are only interested in the function variable
Packit 032894
	 locations.  */
Packit 032894
      Dwarf_Die cudie;
Packit 032894
      Dwarf_Die subdie;
Packit 032894
      uint8_t unit_type;
Packit 032894
      if (dwarf_cu_info (cu->cu, NULL, &unit_type, &cudie, &subdie,
Packit 032894
		         NULL, NULL, NULL) != 0)
Packit 032894
	error (EXIT_FAILURE, 0, "dwarf_cu_info: %s", dwarf_errmsg (-1));
Packit 032894
      if (unit_type == DW_UT_skeleton)
Packit 032894
	cudie = subdie;
Packit 032894
Packit 032894
      Dwarf_Addr cubase;
Packit 032894
      if (dwarf_tag (&cudie) == DW_TAG_compile_unit
Packit 032894
	  && (exprlocs || dwarf_lowpc (&cudie, &cubase) == 0))
Packit 032894
	{
Packit 032894
	  found_cu = true;
Packit 032894
Packit 032894
	  Dwfl_Module *mod = dwfl_cumodule (cu);
Packit 032894
	  Dwarf_Addr modbias;
Packit 032894
	  dw = dwfl_module_getdwarf (mod, &modbias);
Packit 032894
	  assert (dwbias == modbias);
Packit 032894
Packit 032894
	  const char *mainfile;
Packit 032894
	  const char *modname = dwfl_module_info (mod, NULL,
Packit 032894
						  NULL, NULL,
Packit 032894
						  NULL, NULL,
Packit 032894
						  &mainfile,
Packit 032894
						  NULL);
Packit 032894
	  if (modname == NULL)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwfl_module_info: %s", dwarf_errmsg (-1));
Packit 032894
Packit 032894
	  const char *name = (modname[0] != '\0'
Packit 032894
			      ? modname
Packit 032894
			      :  basename (mainfile));
Packit 032894
	  printf ("module '%s'\n", name);
Packit 032894
	  print_die (&cudie, "CU", 0);
Packit 032894
Packit 032894
	  Dwarf_Addr elfbias;
Packit 032894
	  Elf *elf = dwfl_module_getelf (mod, &elfbias);
Packit 032894
Packit 032894
	  // CFI. We need both since sometimes neither is complete.
Packit 032894
	  cfi_debug = dwfl_module_dwarf_cfi (mod, &cfi_debug_bias);
Packit 032894
	  cfi_eh = dwfl_module_eh_cfi (mod, &cfi_eh_bias);
Packit 032894
Packit 032894
	  // No bias needed, same file.
Packit 032894
	  assert (cfi_debug == NULL || cfi_debug_bias == 0);
Packit 032894
Packit 032894
	  // We are a bit forgiving for object files.  There might be
Packit 032894
	  // relocations we don't handle that are needed in some
Packit 032894
	  // places...
Packit 032894
	  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
Packit 032894
	  is_ET_REL = ehdr->e_type == ET_REL;
Packit 032894
Packit 032894
	  if (exprlocs)
Packit 032894
	    {
Packit 032894
	      Dwarf_Addr entrypc;
Packit 032894
	      if (dwarf_entrypc (&cudie, &entrypc) != 0)
Packit 032894
		entrypc = 0;
Packit 032894
Packit 032894
	      /* XXX - Passing true for has_frame_base is not really true.
Packit 032894
		 We do it because we want to resolve all DIEs and all
Packit 032894
		 attributes. Technically we should check that the DIE
Packit 032894
		 (types) are referenced from variables that are defined in
Packit 032894
		 a context (function) that has a frame base.  */
Packit 032894
	      handle_die (&cudie, 0, true /* Should be false */, entrypc);
Packit 032894
	    }
Packit 032894
	  else if (dwarf_getfuncs (&cudie, handle_function, NULL, 0) != 0)
Packit 032894
	    error (EXIT_FAILURE, 0, "dwarf_getfuncs %s",
Packit 032894
		   dwarf_errmsg (-1));
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (! found_cu)
Packit 032894
    error (EXIT_FAILURE, 0, "No DWARF CU found?");
Packit 032894
Packit 032894
  dwfl_end (dwfl);
Packit 032894
  return 0;
Packit 032894
}