]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Add missing files.
authorUlrich Drepper <drepper@redhat.com>
Fri, 29 Jul 2005 19:32:17 +0000 (19:32 +0000)
committerUlrich Drepper <drepper@redhat.com>
Fri, 29 Jul 2005 19:32:17 +0000 (19:32 +0000)
libdwfl/ChangeLog
libdwfl/Makefile.am
libdwfl/libdwfl_crc32.c [new file with mode: 0644]
libdwfl/libdwfl_crc32_file.c [new file with mode: 0644]
libdwfl/loc2c-runtime.h [new file with mode: 0644]
libdwfl/loc2c.c [new file with mode: 0644]
libdwfl/loc2c.h [new file with mode: 0644]
libdwfl/test2.c [new file with mode: 0644]

index dc5674806adde4786730d363b35fca5afe01a463..70fbf62845e5c0607f469f13f3d37e004cceefb6 100644 (file)
@@ -1,12 +1,3 @@
-2005-07-29  Roland McGrath  <roland@redhat.com>
-
-       * loc2c.c: File removed.
-       * loc2c.h: File removed.
-       * loc2c-runtime.h: File removed.
-       * test2.c: File removed.
-       * Makefile.am (EXTRA_DIST): Variable removed.
-       (noinst_HEADERS): Remove loc2c.h from here.
-
 2005-07-28  Ulrich Drepper  <drepper@redhat.com>
 
        * libdwfl.h: Add a few missing extern for function prototypes.
index 243f67f571736433e3f08c158b8a3ecee290ebb6..217cde2de098bcc82a94763505c9801fdeb9b97f 100644 (file)
@@ -25,6 +25,8 @@ INCLUDES = -I. -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
           -I$(srcdir)/../libdw -I.. -I$(srcdir)/../lib
 VERSION = 1
 
+EXTRA_DIST = test2.c loc2c.c
+
 noinst_LIBRARIES = libdwfl.a
 if !MUDFLAP
 noinst_LIBRARIES += libdwfl_pic.a
@@ -76,6 +78,6 @@ am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os)
        fi
 endif
 
-noinst_HEADERS = libdwflP.h
+noinst_HEADERS = libdwflP.h loc2c.h
 
 CLEANFILES = $(am_libdwfl_pic_a_OBJECTS)
diff --git a/libdwfl/libdwfl_crc32.c b/libdwfl/libdwfl_crc32.c
new file mode 100644 (file)
index 0000000..4a6cea7
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2002, 2005 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, version 2.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define crc32 attribute_hidden __libdwfl_crc32
+#define LIB_SYSTEM_H   1
+#include <libdwflP.h>
+#include "../lib/crc32.c"
diff --git a/libdwfl/libdwfl_crc32_file.c b/libdwfl/libdwfl_crc32_file.c
new file mode 100644 (file)
index 0000000..a791508
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright (C) 2002, 2005 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, version 2.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define crc32_file attribute_hidden __libdwfl_crc32_file
+#define crc32 __libdwfl_crc32
+#define LIB_SYSTEM_H   1
+#include <libdwflP.h>
+#include "../lib/crc32_file.c"
diff --git a/libdwfl/loc2c-runtime.h b/libdwfl/loc2c-runtime.h
new file mode 100644 (file)
index 0000000..af27f79
--- /dev/null
@@ -0,0 +1,125 @@
+/* target operations */
+
+#include <linux/types.h>
+#define intptr_t long
+#define uintptr_t unsigned long
+
+
+/* These three macro definitions are generic, just shorthands
+   used by the generated code.  */
+
+#define op_abs(x)      (x < 0 ? -x : x)
+
+#define fetch_bitfield(target, base, higherbits, nbits)                              \
+  target = (((base) >> (sizeof (base) * 8 - (higherbits) - (nbits)))         \
+           & (((__typeof (base)) 1 << (nbits)) - 1))
+
+#define store_bitfield(target, base, higherbits, nbits)                              \
+  target = (target                                                           \
+           &~ ((((__typeof (base)) 1 << (nbits)) - 1)                        \
+               << (sizeof (base) * 8 - (higherbits) - (nbits)))              \
+           | ((__typeof (base)) (value)                                      \
+              << (sizeof (base) * 8 - (higherbits) - (nbits))))
+
+
+/* These operations are target-specific.  */
+#include <asm/uaccess.h>
+
+#define fetch_register(regno) ((intptr_t) regs->dwarf_register_##regno)
+
+#if defined __i386__
+
+#define dwarf_register_0 eax
+#define dwarf_register_1 ecx
+#define dwarf_register_2 edx
+#define dwarf_register_3 ebx
+#define dwarf_register_4 esp
+#define dwarf_register_5 ebp
+#define dwarf_register_6 esi
+#define dwarf_register_7 edi
+
+#elif defined __x86_64__
+
+#define dwarf_register_0 eax
+#define dwarf_register_1 edx
+#define dwarf_register_2 ecx
+#define dwarf_register_3 ebx
+#define dwarf_register_4 esi
+#define dwarf_register_5 edi
+#define dwarf_register_6 ebp
+#define dwarf_register_7 esp
+#define dwarf_register_8 r8
+#define dwarf_register_9 r9
+#define dwarf_register_10 r10
+#define dwarf_register_11 r11
+#define dwarf_register_12 r12
+#define dwarf_register_13 r13
+#define dwarf_register_14 r14
+#define dwarf_register_15 r15
+
+#elif defined __powerpc__
+
+#undef fetch_register
+#define fetch_register(regno) ((intptr_t) regs->gpr[regno])
+
+#endif
+
+#if defined __i386__ || defined __x86_64__
+
+#define deref(size, addr)                                                    \
+  ({                                                                         \
+    int _bad = 0;                                                            \
+    u8 _b; u16 _w; u32 _l; u64 _q;                                           \
+    intptr_t _v;                                                             \
+    switch (size)                                                            \
+      {                                                                              \
+      case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break;    \
+      case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break;    \
+      case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break;     \
+      case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break;     \
+      default: _v = __get_user_bad();                                        \
+      }                                                                              \
+    if (_bad)                                                                \
+      goto deref_fault;                                                              \
+    _v;                                                                              \
+  })
+
+#elif defined __powerpc64__
+
+#define deref(size, addr)                                                    \
+  ({                                                                         \
+    int _bad = 0;                                                            \
+    intptr_t _v;                                                             \
+    switch (size)                                                            \
+      {                                                                              \
+      case 1: __get_user_asm(_v,addr,_bad,"lbz",1); break;                   \
+      case 2: __get_user_asm(_v,addr,_bad,"lhz",1); break;                   \
+      case 4: __get_user_asm(_v,addr,_bad,"lwz",1); break;                   \
+      case 8: __get_user_asm(_v,addr,_bad,"ld",1); break;                    \
+      default: _v = __get_user_bad();                                        \
+      }                                                                              \
+    if (_bad)                                                                \
+      goto deref_fault;                                                              \
+    _v;                                                                              \
+  })
+
+#elif defined __powerpc__
+
+#define deref(size, addr)                                                    \
+  ({                                                                         \
+    int _bad = 0;                                                            \
+    intptr_t _v;                                                             \
+    switch (size)                                                            \
+      {                                                                              \
+      case 1: __get_user_asm(_v,addr,_bad,"lbz"); break;                     \
+      case 2: __get_user_asm(_v,addr,_bad,"lhz"); break;                     \
+      case 4: __get_user_asm(_v,addr,_bad,"lwz"); break;                     \
+      case 8: __get_user_asm(_v,addr,_bad,"ld"); break;                              \
+      default: _v = __get_user_bad();                                        \
+      }                                                                              \
+    if (_bad)                                                                \
+      goto deref_fault;                                                              \
+    _v;                                                                              \
+  })
+
+#endif
diff --git a/libdwfl/loc2c.c b/libdwfl/loc2c.c
new file mode 100644 (file)
index 0000000..816eb06
--- /dev/null
@@ -0,0 +1,1398 @@
+#include <config.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <obstack.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <dwarf.h>
+#include <libdw.h>
+#include <assert.h>
+
+#define _(x) x
+
+#define STACK_TYPE     "intptr_t"  /* Must be the signed type.  */
+#define UTYPE          "uintptr_t" /* Must be the unsigned type.  */
+#define SFORMAT                "%" PRId64 "L"
+#define UFORMAT                "%" PRIu64 "UL"
+#define AFORMAT                "%#" PRIx64 "UL"
+#define STACKFMT       "s%u"
+
+struct location
+{
+  struct location *next;
+
+  const Dwarf_Loc *ops;
+  size_t nops;
+
+  Dwarf_Word byte_size;
+
+  enum { loc_address, loc_register, loc_noncontiguous, loc_final } type;
+  union
+  {
+    struct                     /* loc_address or loc_final */
+    {
+      char *program;           /* C fragment, leaves address in s0.  */
+      unsigned int stack_depth;        /* Temporaries "s0..<N>" used by it.  */
+      struct location *frame_base;
+      bool used_deref;         /* Program uses "deref" macro.  */
+    } address;
+    unsigned int regno;                /* loc_register */
+    struct location *pieces;   /* loc_noncontiguous */
+  };
+};
+
+\f
+static const char *
+dwarf_diename_integrate (Dwarf_Die *die)
+{
+  Dwarf_Attribute attr_mem;
+  return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem));
+}
+\f
+/* Synthesize a new loc_address using the program on the obstack.  */
+static struct location *
+new_synthetic_loc (struct obstack *pool, struct location *origin, bool deref)
+{
+  obstack_1grow (pool, '\0');
+  char *program = obstack_finish (pool);
+
+  struct location *loc = obstack_alloc (pool, sizeof *loc);
+  loc->next = NULL;
+  loc->byte_size = 0;
+  loc->type = loc_address;
+  loc->address.program = program;
+  loc->address.stack_depth = 0;
+  loc->address.frame_base = NULL;
+  loc->address.used_deref = deref;
+
+  if (origin->type == loc_register)
+    {
+      loc->ops = origin->ops;
+      loc->nops = origin->nops;
+    }
+  else
+    {
+      loc->ops = NULL;
+      loc->nops = 0;
+    }
+
+  return loc;
+}
+
+
+/* Die in the middle of an expression.  */
+static struct location *
+lose (const char *failure, const Dwarf_Loc *lexpr, size_t i)
+{
+  error (2, 0, _("%s in DWARF expression [%Zu] at %" PRIu64
+                " (%#x: %" PRId64 ", %" PRId64 ")"),
+        failure, i, lexpr[i].offset,
+        lexpr[i].atom, lexpr[i].number, lexpr[i].number2);
+  return NULL;
+}
+
+/* Translate a (constrained) DWARF expression into C code
+   emitted to the obstack POOL.  INDENT is the number of indentation levels.
+   ADDRBIAS is the difference between runtime and Dwarf info addresses.
+   INPUT is null or an expression to be initially pushed on the stack.
+   If NEED_FB is null, fail on DW_OP_fbreg, else set *NEED_FB to true
+   and emit "frame_base" for it.  On success, set *MAX_STACK to the number
+   of stack slots required.  On failure, set *LOSER to the index in EXPR
+   of the operation we could not handle.
+
+   Returns a failure message or null for success.  */
+
+static const char *
+translate (struct obstack *pool, int indent, Dwarf_Addr addrbias,
+          const Dwarf_Loc *expr, const size_t len,
+          struct location *input,
+          bool *need_fb, size_t *loser,
+          struct location *loc)
+{
+  loc->ops = expr;
+  loc->nops = len;
+
+#define DIE(msg) return (*loser = i, _(msg))
+
+#define emit(fmt, ...) obstack_printf (pool, fmt, ## __VA_ARGS__)
+
+  unsigned int stack_depth = 0, max_stack = 0;
+  inline void deepen (void)
+    {
+      if (stack_depth == max_stack)
+       ++max_stack;
+    }
+
+#define POP(var)                                                             \
+    if (stack_depth > 0)                                                     \
+      --stack_depth;                                                         \
+    else if (tos_register != -1)                                             \
+      fetch_tos_register ();                                                 \
+    else                                                                     \
+      goto underflow;                                                        \
+    int var = stack_depth
+#define PUSH           (deepen (), stack_depth++)
+#define STACK(idx)     (stack_depth - 1 - (idx))
+
+  /* Don't put stack operations in the arguments to this.  */
+#define push(fmt, ...) \
+  emit ("%*s" STACKFMT " = " fmt ";\n", indent * 2, "", PUSH, ## __VA_ARGS__)
+
+  int tos_register = -1;
+  inline void fetch_tos_register (void)
+    {
+      deepen ();
+      emit ("%*s" STACKFMT " = fetch_register (%d);\n",
+           indent * 2, "", stack_depth, tos_register);
+      tos_register = -1;
+    }
+
+  if (input != NULL)
+    switch (input->type)
+      {
+      case loc_address:
+       push ("addr");
+       break;
+
+      case loc_register:
+       tos_register = input->regno;
+       break;
+
+      default:
+       abort ();
+       break;
+      }
+
+  size_t i;
+
+  inline const char *finish (struct location *piece)
+    {
+      if (stack_depth > 1)
+       DIE ("multiple values left on stack");
+      if (stack_depth == 1)
+       {
+         obstack_1grow (pool, '\0');
+         char *program = obstack_finish (pool);
+         piece->type = loc_address;
+         piece->address.program = program;
+         piece->address.stack_depth = max_stack;
+         piece->address.frame_base = NULL;
+       }
+      else if (tos_register == -1)
+       DIE ("stack underflow");
+      else if (obstack_object_size (pool) != 0)
+       DIE ("register value must stand alone in location expression");
+      else
+       {
+         piece->type = loc_register;
+         piece->regno = tos_register;
+       }
+      return NULL;
+    }
+
+  struct location *pieces = NULL, **tailpiece = &pieces;
+  size_t piece_expr_start = 0;
+  for (i = 0; i < len; ++i)
+    {
+      unsigned int reg;
+      uint_fast8_t sp;
+      Dwarf_Word value;
+
+      switch (expr[i].atom)
+       {
+         /* Basic stack operations.  */
+       case DW_OP_nop:
+         break;
+
+       case DW_OP_dup:
+         if (stack_depth < 1)
+           goto underflow;
+         else
+           {
+             unsigned int tos = STACK (0);
+             push (STACKFMT, tos);
+           }
+         break;
+
+       case DW_OP_drop:
+         POP (ignore);
+         emit ("%*s/* drop " STACKFMT "*/\n", indent * 2, "", ignore);
+         break;
+
+       case DW_OP_pick:
+         sp = expr[i].number;
+       op_pick:
+         if (sp >= stack_depth)
+           goto underflow;
+         sp = STACK (sp);
+         push (STACKFMT, sp);
+         break;
+
+       case DW_OP_over:
+         sp = 1;
+         goto op_pick;
+
+       case DW_OP_swap:
+         if (stack_depth < 2)
+           goto underflow;
+         deepen ();            /* Use a temporary slot.  */
+         emit ("%*s"
+               STACKFMT " = " STACKFMT ", "
+               STACKFMT " = " STACKFMT ", "
+               STACKFMT " = " STACKFMT ";\n",
+               indent * 2, "",
+               STACK (-1), STACK (0),
+               STACK (0), STACK (1),
+               STACK (1), STACK (-1));
+         break;
+
+       case DW_OP_rot:
+         if (stack_depth < 3)
+           goto underflow;
+         deepen ();            /* Use a temporary slot.  */
+         emit ("%*s"
+               STACKFMT " = " STACKFMT ", "
+               STACKFMT " = " STACKFMT ", "
+               STACKFMT " = " STACKFMT ", "
+               STACKFMT " = " STACKFMT ";\n",
+               indent * 2, "",
+               STACK (-1), STACK (0),
+               STACK (0), STACK (1),
+               STACK (1), STACK (2),
+               STACK (3), STACK (-1));
+         break;
+
+
+         /* Control flow operations.  */
+       case DW_OP_skip:
+         {
+           Dwarf_Off target = expr[i].offset + 3 + expr[i].number;
+           while (i + 1 < len && expr[i + 1].offset < target)
+             ++i;
+           if (expr[i + 1].offset != target)
+             DIE ("invalid skip target");
+           break;
+         }
+
+       case DW_OP_bra:
+         DIE ("conditional branches not supported");
+         break;
+
+
+         /* Memory access.  */
+       case DW_OP_deref:
+         {
+           POP (addr);
+           push ("deref (sizeof (void *), " STACKFMT ")", addr);
+           loc->address.used_deref = true;
+         }
+         break;
+
+       case DW_OP_deref_size:
+         {
+           POP (addr);
+           push ("deref (" UFORMAT ", " STACKFMT ")",
+                 expr[i].number, addr);
+           loc->address.used_deref = true;
+         }
+         break;
+
+       case DW_OP_xderef:
+         {
+           POP (addr);
+           POP (as);
+           push ("xderef (sizeof (void *), " STACKFMT ", " STACKFMT ")",
+                 addr, as);
+           loc->address.used_deref = true;
+         }
+         break;
+
+       case DW_OP_xderef_size:
+         {
+           POP (addr);
+           POP (as);
+           push ("xderef (" UFORMAT ", " STACKFMT ", " STACKFMT ")",
+                 expr[i].number, addr, as);
+           loc->address.used_deref = true;
+         }
+         break;
+
+         /* Constant-value operations.  */
+
+       case DW_OP_addr:
+         push (AFORMAT, addrbias + expr[i].number);
+         break;
+
+       case DW_OP_lit0 ... DW_OP_lit31:
+         value = expr[i].atom - DW_OP_lit0;
+         goto op_const;
+
+       case DW_OP_const1u:
+       case DW_OP_const1s:
+       case DW_OP_const2u:
+       case DW_OP_const2s:
+       case DW_OP_const4u:
+       case DW_OP_const4s:
+       case DW_OP_const8u:
+       case DW_OP_const8s:
+       case DW_OP_constu:
+       case DW_OP_consts:
+         value = expr[i].number;
+       op_const:
+         push (SFORMAT, value);
+         break;
+
+         /* Arithmetic operations.  */
+#define UNOP(dw_op, c_op)                                                    \
+       case DW_OP_##dw_op:                                                   \
+         {                                                                   \
+           POP (tos);                                                        \
+           push ("%s (" STACKFMT ")", #c_op, tos);                           \
+         }                                                                   \
+         break
+#define BINOP(dw_op, c_op)                                                   \
+       case DW_OP_##dw_op:                                                   \
+         {                                                                   \
+           POP (b);                                                          \
+           POP (a);                                                          \
+           push (STACKFMT " %s " STACKFMT, a, #c_op, b);                     \
+         }                                                                   \
+         break
+
+         UNOP (abs, op_abs);
+         BINOP (and, &);
+         BINOP (div, /);
+         BINOP (minus, -);
+         BINOP (mod, %);
+         BINOP (mul, *);
+         UNOP (neg, -);
+         UNOP (not, ~);
+         BINOP (or, |);
+         BINOP (plus, +);
+         BINOP (shl, <<);
+         BINOP (shra, >>);
+         BINOP (xor, ^);
+
+         /* Comparisons are binary operators too.  */
+         BINOP (le, <=);
+         BINOP (ge, >=);
+         BINOP (eq, ==);
+         BINOP (lt, <);
+         BINOP (gt, >);
+         BINOP (ne, !=);
+
+#undef UNOP
+#undef BINOP
+
+       case DW_OP_shr:
+         {
+           POP (b);
+           POP (a);
+           push ("(%s) " STACKFMT " >> (%s)" STACKFMT,
+                 UTYPE, a, UTYPE, b);
+           break;
+         }
+
+       case DW_OP_plus_uconst:
+         {
+           POP (x);
+           push (STACKFMT " + " UFORMAT, x, expr[i].number);
+         }
+         break;
+
+
+         /* Register-relative addressing.  */
+       case DW_OP_breg0 ... DW_OP_breg31:
+         reg = expr[i].atom - DW_OP_breg0;
+         value = expr[i].number;
+         goto op_breg;
+
+       case DW_OP_bregx:
+         reg = expr[i].number;
+         value = expr[i].number2;
+       op_breg:
+         push ("fetch_register (%u) + " SFORMAT, reg, value);
+         break;
+
+       case DW_OP_fbreg:
+         if (need_fb == NULL)
+           DIE ("DW_OP_fbreg from DW_AT_frame_base");
+         *need_fb = true;
+         push ("frame_base + " SFORMAT, expr[i].number);
+         break;
+
+         /* Direct register contents.  */
+       case DW_OP_reg0 ... DW_OP_reg31:
+         reg = expr[i].atom - DW_OP_reg0;
+         goto op_reg;
+
+       case DW_OP_regx:
+         reg = expr[i].number;
+       op_reg:
+         tos_register = reg;
+         break;
+
+         /* Special magic.  */
+       case DW_OP_piece:
+         if (stack_depth > 1)
+           /* If this ever happens we could copy the program.  */
+           DIE ("DW_OP_piece left multiple values on stack");
+         else
+           {
+             struct location *piece = obstack_alloc (pool, sizeof *piece);
+             const char *failure = finish (piece);
+             if (unlikely (failure != NULL))
+               return failure;
+
+             piece->ops = &expr[piece_expr_start];
+             piece->nops = i - piece_expr_start;
+             piece_expr_start = i + 1;
+
+             piece->byte_size = expr[i].number;
+
+             *tailpiece = piece;
+             tailpiece = &piece->next;
+             piece->next = NULL;
+           }
+         break;
+
+       case DW_OP_push_object_address:
+         DIE ("XXX DW_OP_push_object_address");
+         break;
+
+       default:
+         DIE ("unrecognized operation");
+         break;
+       }
+    }
+
+  if (likely (pieces == NULL))
+    return finish (loc);
+
+  if (piece_expr_start != i)
+    DIE ("extra operations after last DW_OP_piece");
+
+  loc->type = loc_noncontiguous;
+  loc->pieces = pieces;
+
+  return NULL;
+
+ underflow:
+  DIE ("stack underflow");
+
+#undef emit
+#undef push
+#undef PUSH
+#undef POP
+#undef STACK
+#undef DIE
+}
+
+/* Translate a location starting from an address or nothing.  */
+static struct location *
+location_from_address (struct obstack *pool,
+                      int indent, Dwarf_Addr dwbias,
+                      const Dwarf_Loc *expr, size_t len, Dwarf_Addr address,
+                      struct location **input, Dwarf_Attribute *fb_attr)
+{
+  bool need_fb = false;
+  size_t loser;
+  struct location *loc = obstack_alloc (pool, sizeof *loc);
+  const char *failure = translate (pool, indent + 1, dwbias, expr, len,
+                                  *input, &need_fb, &loser, loc);
+  if (unlikely (failure != NULL))
+    return lose (failure, expr, loser);
+
+  loc->next = NULL;
+  if (need_fb)
+    {
+      /* The main expression uses DW_OP_fbreg, so we need to compute
+        the DW_AT_frame_base attribute expression's value first.  */
+
+      Dwarf_Loc *fb_expr;
+      size_t fb_len;
+      switch (dwarf_addrloclists (fb_attr, address - dwbias,
+                                 &fb_expr, &fb_len, 1))
+       {
+       case 1:                 /* Should always happen.  */
+         if (fb_len == 0)
+           goto fb_inaccessible;
+         break;
+
+       default:                /* Shouldn't happen.  */
+       case -1:
+         error (2, 0, "dwarf_addrloclists (form %#x): %s",
+                dwarf_whatform (fb_attr), dwarf_errmsg (-1));
+         return NULL;
+
+       case 0:                 /* Shouldn't happen.  */
+       fb_inaccessible:
+         error (2, 0, "DW_AT_frame_base not accessible at this address");
+         return NULL;
+       }
+
+      loc->address.frame_base = obstack_alloc (pool, sizeof *loc);
+      failure = translate (pool, indent + 1, dwbias, fb_expr, fb_len, NULL,
+                          NULL, &loser, loc->address.frame_base);
+      if (unlikely (failure != NULL))
+       return lose (failure, fb_expr, loser);
+    }
+
+  if (*input != NULL)
+    (*input)->next = loc;
+  *input = loc;
+
+  return loc;
+}
+
+/* Translate a location starting from a non-address "on the top of the
+   stack".  The *INPUT location is a register name or noncontiguous
+   object specification, and this expression wants to find the "address"
+   of an object relative to that "address".  */
+
+static struct location *
+location_relative (struct obstack *pool,
+                  int indent, Dwarf_Addr dwbias,
+                  const Dwarf_Loc *expr, size_t len, Dwarf_Addr address,
+                  struct location **input, Dwarf_Attribute *fb_attr)
+{
+  Dwarf_Sword *stack;
+  unsigned int stack_depth = 0, max_stack = 0;
+  inline void deepen (void)
+    {
+      if (stack_depth == max_stack)
+       {
+         ++max_stack;
+         obstack_blank (pool, sizeof stack[0]);
+         stack = (void *) obstack_base (pool);
+       }
+    }
+
+#define POP(var)                                                             \
+    if (stack_depth > 0)                                                     \
+      --stack_depth;                                                         \
+    else                                                                     \
+      goto underflow;                                                        \
+    int var = stack_depth
+#define PUSH           (deepen (), stack_depth++)
+#define STACK(idx)     (stack_depth - 1 - (idx))
+#define STACKWORD(idx) stack[STACK (idx)]
+
+  /* Don't put stack operations in the arguments to this.  */
+#define push(value) (stack[PUSH] = (value))
+
+  const char *failure = NULL;
+#define DIE(msg) do { failure = _(msg); goto fail; } while (0)
+
+  struct location *head = NULL;
+  size_t i;
+  for (i = 0; i < len; ++i)
+    {
+      uint_fast8_t sp;
+      Dwarf_Word value;
+
+      switch (expr[i].atom)
+       {
+         /* Basic stack operations.  */
+       case DW_OP_nop:
+         break;
+
+       case DW_OP_dup:
+         if (stack_depth < 1)
+           goto underflow;
+         else
+           {
+             unsigned int tos = STACK (0);
+             push (stack[tos]);
+           }
+         break;
+
+       case DW_OP_drop:
+         if (stack_depth > 0)
+           --stack_depth;
+         else if (*input != NULL)
+           /* Mark that we have consumed the input.  */
+           *input = NULL;
+         else
+           /* Hits if cleared above, or if we had no input at all.  */
+           goto underflow;
+         break;
+
+       case DW_OP_pick:
+         sp = expr[i].number;
+       op_pick:
+         if (sp >= stack_depth)
+           goto underflow;
+         sp = STACK (sp);
+         push (stack[sp]);
+         break;
+
+       case DW_OP_over:
+         sp = 1;
+         goto op_pick;
+
+       case DW_OP_swap:
+         if (stack_depth < 2)
+           goto underflow;
+         deepen ();            /* Use a temporary slot.  */
+         STACKWORD (-1) = STACKWORD (0);
+         STACKWORD (0) = STACKWORD (1);
+         STACKWORD (1) = STACKWORD (-1);
+         break;
+
+       case DW_OP_rot:
+         if (stack_depth < 3)
+           goto underflow;
+         deepen ();            /* Use a temporary slot.  */
+         STACKWORD (-1) = STACKWORD (0);
+         STACKWORD (0) = STACKWORD (1);
+         STACKWORD (2) = STACKWORD (2);
+         STACKWORD (2) = STACKWORD (-1);
+         break;
+
+
+         /* Control flow operations.  */
+       case DW_OP_bra:
+         {
+           POP (taken);
+           if (stack[taken] == 0)
+             break;
+         }
+         /*FALLTHROUGH*/
+
+       case DW_OP_skip:
+         {
+           Dwarf_Off target = expr[i].offset + 3 + expr[i].number;
+           while (i + 1 < len && expr[i + 1].offset < target)
+             ++i;
+           if (expr[i + 1].offset != target)
+             DIE ("invalid skip target");
+           break;
+         }
+
+         /* Memory access.  */
+       case DW_OP_deref:
+       case DW_OP_deref_size:
+       case DW_OP_xderef:
+       case DW_OP_xderef_size:
+
+         /* Register-relative addressing.  */
+       case DW_OP_breg0 ... DW_OP_breg31:
+       case DW_OP_bregx:
+       case DW_OP_fbreg:
+
+         /* This started from a register, but now it's following a pointer.
+            So we can do the translation starting from address here.  */
+         return location_from_address (pool, indent, dwbias,
+                                       expr, len, address, input, fb_attr);
+
+
+         /* Constant-value operations.  */
+       case DW_OP_addr:
+         push (dwbias + expr[i].number);
+         break;
+
+       case DW_OP_lit0 ... DW_OP_lit31:
+         value = expr[i].atom - DW_OP_lit0;
+         goto op_const;
+
+       case DW_OP_const1u:
+       case DW_OP_const1s:
+       case DW_OP_const2u:
+       case DW_OP_const2s:
+       case DW_OP_const4u:
+       case DW_OP_const4s:
+       case DW_OP_const8u:
+       case DW_OP_const8s:
+       case DW_OP_constu:
+       case DW_OP_consts:
+         value = expr[i].number;
+       op_const:
+         push (value);
+         break;
+
+         /* Arithmetic operations.  */
+#define UNOP(dw_op, c_op)                                                    \
+       case DW_OP_##dw_op:                                                   \
+         {                                                                   \
+           POP (tos);                                                        \
+           push (c_op (stack[tos]));                                         \
+         }                                                                   \
+         break
+#define BINOP(dw_op, c_op)                                                   \
+       case DW_OP_##dw_op:                                                   \
+         {                                                                   \
+           POP (b);                                                          \
+           POP (a);                                                          \
+           push (stack[a] c_op stack[b]);                                    \
+         }                                                                   \
+         break
+
+#define op_abs(x) (x < 0 ? -x : x)
+         UNOP (abs, op_abs);
+         BINOP (and, &);
+         BINOP (div, /);
+         BINOP (mod, %);
+         BINOP (mul, *);
+         UNOP (neg, -);
+         UNOP (not, ~);
+         BINOP (or, |);
+         BINOP (shl, <<);
+         BINOP (shra, >>);
+         BINOP (xor, ^);
+
+         /* Comparisons are binary operators too.  */
+         BINOP (le, <=);
+         BINOP (ge, >=);
+         BINOP (eq, ==);
+         BINOP (lt, <);
+         BINOP (gt, >);
+         BINOP (ne, !=);
+
+#undef UNOP
+#undef BINOP
+
+       case DW_OP_shr:
+         {
+           POP (b);
+           POP (a);
+           push ((Dwarf_Word) stack[a] >> (Dwarf_Word) stack[b]);
+           break;
+         }
+
+         /* Simple addition we may be able to handle relative to
+            the starting register name.  */
+       case DW_OP_minus:
+         {
+           POP (tos);
+           value = -stack[tos];
+           goto plus;
+         }
+       case DW_OP_plus:
+         {
+           POP (tos);
+           value = stack[tos];
+           goto plus;
+         }
+       case DW_OP_plus_uconst:
+         value = expr[i].number;
+       plus:
+         if (stack_depth > 0)
+           {
+             /* It's just private diddling after all.  */
+             POP (a);
+             push (stack[a] + value);
+             break;
+           }
+         if (*input == NULL)
+           goto underflow;
+
+         /* This is the primary real-world case: the expression takes
+            the input address and adds a constant offset.  */
+
+         while ((*input)->type == loc_noncontiguous)
+           {
+             /* We are starting from a noncontiguous object (DW_OP_piece).
+                Find the piece we want.  */
+
+             struct location *piece = (*input)->pieces;
+             while (piece != NULL && value >= piece->byte_size)
+               {
+                 value -= piece->byte_size;
+                 piece = piece->next;
+               }
+             if (piece == NULL)
+               DIE ("offset outside available pieces");
+
+             *input = piece;
+           }
+
+         switch ((*input)->type)
+           {
+           case loc_address:
+             {
+               /* The piece we want is actually in memory.  Use the same
+                  program to compute the address from the preceding input.  */
+
+               struct location *loc = obstack_alloc (pool, sizeof *loc);
+               *loc = **input;
+               if (head == NULL)
+                 head = loc;
+               (*input)->next = loc;
+               if (value == 0)
+                 {
+                   /* The piece addresses exactly where we want to go.  */
+                   loc->next = NULL;
+                   *input = loc;
+                 }
+               else
+                 {
+                   /* Add a second fragment to offset the piece address.  */
+                   obstack_printf (pool, "%*saddr += " SFORMAT "\n",
+                                   indent * 2, "", value);
+                   *input = loc->next = new_synthetic_loc (pool, *input,
+                                                           false);
+                 }
+
+               if (i + 1 < len)
+                 {
+                   /* This expression keeps going, but further
+                      computations now have an address to start with.
+                      So we can punt to the address computation generator.  */
+                   loc = location_from_address (pool, indent, dwbias,
+                                                &expr[i + 1], len - i - 1,
+                                                address, input, fb_attr);
+                   if (loc == NULL)
+                     return NULL;
+                 }
+
+               /* That's all she wrote.  */
+               return head;
+             }
+
+           case loc_register:
+             // XXX
+
+           default:
+             abort ();
+           }
+         break;
+
+         /* Direct register contents.  */
+       case DW_OP_reg0 ... DW_OP_reg31:
+       case DW_OP_regx:
+         DIE ("register");
+         break;
+
+         /* Special magic.  */
+       case DW_OP_piece:
+         DIE ("DW_OP_piece");
+         break;
+
+       case DW_OP_push_object_address:
+         DIE ("XXX DW_OP_push_object_address");
+         break;
+
+       default:
+         DIE ("unrecognized operation");
+         break;
+       }
+    }
+
+  if (stack_depth > 1)
+    DIE ("multiple values left on stack");
+
+  if (stack_depth > 0)         /* stack_depth == 1 */
+    {
+      if (*input != NULL)
+       DIE ("multiple values left on stack");
+
+      /* Could handle this if it ever actually happened.  */
+      DIE ("relative expression computed constant");
+    }
+
+  return head;
+
+ underflow:
+  if (*input == NULL)
+    DIE ("stack underflow");
+  else
+    DIE ("cannot handle location expression");
+
+ fail:
+  return lose (failure, expr, i);
+}
+
+
+/* Translate a C fragment for the location expression, using *INPUT
+   as the starting location, begin from scratch if *INPUT is null.
+   If DW_OP_fbreg is used, it may have a subfragment computing from
+   the FB_ATTR location expression.
+
+   On errors, exit and never return (XXX ?).  On success, return the
+   first fragment created, which is also chained onto (*INPUT)->next.
+   *INPUT is then updated with the new tail of that chain.  */
+
+struct location *
+c_translate_location (struct obstack *pool,
+                     int indent, Dwarf_Addr dwbias,
+                     Dwarf_Attribute *loc_attr, Dwarf_Addr address,
+                     struct location **input, Dwarf_Attribute *fb_attr)
+{
+  Dwarf_Loc *expr;
+  size_t len;
+  switch (dwarf_addrloclists (loc_attr, address - dwbias, &expr, &len, 1))
+    {
+    case 1:                    /* Should always happen.  */
+      if (len == 0)
+       goto inaccessible;
+      break;
+
+    default:                   /* Shouldn't happen.  */
+    case -1:
+      error (2, 0, "dwarf_addrloclists (form %#x): %s",
+            dwarf_whatform (fb_attr), dwarf_errmsg (-1));
+      return NULL;
+
+    case 0:                    /* Shouldn't happen.  */
+    inaccessible:
+      error (2, 0, "not accessible at this address");
+      return NULL;
+    }
+
+  ++indent;
+  switch (*input == NULL ? loc_address : (*input)->type)
+    {
+    case loc_address:
+      /* We have a previous address computation.
+        This expression will compute starting with that on the stack.  */
+      return location_from_address (pool, indent, dwbias, expr, len, address,
+                                   input, fb_attr);
+
+    case loc_noncontiguous:
+    case loc_register:
+      /* The starting point is not an address computation, but a
+        register.  We can only handle limited computations from here.  */
+      return location_relative (pool, indent, dwbias, expr, len, address,
+                               input, fb_attr);
+
+    case loc_final:            /* Bogus caller.  */
+    default:
+      abort ();
+      break;
+    }
+
+  return NULL;
+}
+
+
+/* Emit "uintNN_t TARGET = ...;".  */
+static bool
+emit_base_fetch (struct obstack *pool, Dwarf_Word byte_size,
+                const char *target, bool decl, struct location *loc)
+{
+  if (decl)
+    obstack_printf (pool, "uint%" PRIu64 "_t ", byte_size * 8);
+
+  switch (loc->type)
+    {
+    case loc_address:
+      if (byte_size != 0 && byte_size != (Dwarf_Word) -1)
+       obstack_printf (pool, "%s = deref (%" PRIu64 ", addr); ",
+                       target, byte_size);
+      else
+       obstack_printf (pool, "%s = deref (sizeof %s, addr); ",
+                       target, target);
+      return true;
+
+    case loc_register:
+      obstack_printf (pool, "%s = fetch_register (%u);", target, loc->regno);
+      break;
+
+    case loc_noncontiguous:
+      /* Could be handled if it ever happened.  */
+      error (2, 0, _("noncontiguous locations not supported"));
+      break;
+
+    default:
+      abort ();
+      break;
+    }
+
+  return false;
+}
+
+/* Translate a fragment to dereference the given pointer type,
+   where *INPUT is the location of the pointer with that type.
+
+   We chain on a loc_address program that yields this pointer value
+   (i.e. the location of what it points to).  */
+
+void
+c_translate_pointer (struct obstack *pool, int indent,
+                    Dwarf_Addr dwbias __attribute__ ((unused)),
+                    Dwarf_Die *typedie, struct location **input)
+{
+  obstack_printf (pool, "%*s{ ", (indent + 2) * 2, "");
+
+  bool deref = false;
+  Dwarf_Attribute attr_mem;
+  Dwarf_Word byte_size;
+  if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) == NULL)
+    {
+      obstack_printf (pool, "uintptr_t ");
+      emit_base_fetch (pool, 0, "tmp", false, *input);
+    }
+  else if (dwarf_formudata (&attr_mem, &byte_size) != 0)
+    error (2, 0,
+          _("cannot get byte_size attribute for type %s: %s"),
+          dwarf_diename_integrate (typedie) ?: "<anonymous>",
+          dwarf_errmsg (-1));
+  else
+    deref = emit_base_fetch (pool, byte_size, "tmp", true, *input);
+
+  obstack_printf (pool, " addr = tmp; }\n");
+
+  struct location *loc = new_synthetic_loc (pool, *input, deref);
+  (*input)->next = loc;
+  *input = loc;
+}
+
+/* Determine the byte size of a base type.  */
+static Dwarf_Word
+base_byte_size (Dwarf_Die *typedie)
+{
+  Dwarf_Attribute attr_mem;
+  Dwarf_Word size;
+  if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) != NULL
+      && dwarf_formudata (&attr_mem, &size) == 0)
+    return size;
+
+  error (2, 0,
+        _("cannot get byte_size attribute for type %s: %s"),
+        dwarf_diename_integrate (typedie) ?: "<anonymous>",
+        dwarf_errmsg (-1));
+  return -1;
+}
+
+/* Emit a code fragment like:
+       { uintNN_t tmp = ...; S1 S2 S3, tmp, B0, Bn); }
+*/
+static void
+emit_bitfield (struct obstack *pool, int indent,
+              Dwarf_Die *die, Dwarf_Word byte_size, struct location *loc,
+              const char *s1, const char *s2, const char *s3)
+{
+  Dwarf_Word bit_offset, bit_size;
+  Dwarf_Attribute attr_mem;
+  if (dwarf_attr_integrate (die, DW_AT_bit_offset, &attr_mem) == NULL
+      || dwarf_formudata (&attr_mem, &bit_offset) != 0
+      || dwarf_attr_integrate (die, DW_AT_bit_size, &attr_mem) == NULL
+      || dwarf_formudata (&attr_mem, &bit_size) != 0)
+    error (2, 0, _("cannot get bit field parameters: %s"),
+          dwarf_errmsg (-1));
+
+  /* Emit "{ uintNN_t tmp = ...;" to fetch the base type.  */
+
+  obstack_printf (pool, "%*s{ ", indent * 2, "");
+  emit_base_fetch (pool, byte_size, "tmp", true, loc);
+
+  obstack_printf (pool, "%s%s%s, tmp, %" PRIu64 ", %" PRIu64 "); }\n",
+                 s1, s2, s3, bit_offset, bit_size);
+}
+
+/* Translate a fragment to fetch the value of variable or member DIE
+   at the *INPUT location and store it in variable TARGET.  */
+
+void
+c_translate_fetch (struct obstack *pool, int indent,
+                  Dwarf_Addr dwbias __attribute__ ((unused)),
+                  Dwarf_Die *die, Dwarf_Attribute *typeattr,
+                  struct location **input, const char *target)
+{
+  ++indent;
+
+  Dwarf_Attribute size_attr;
+  Dwarf_Word byte_size;
+  if (dwarf_attr_integrate (die, DW_AT_byte_size, &size_attr) == NULL
+      || dwarf_formudata (&size_attr, &byte_size) != 0)
+    {
+      Dwarf_Die basedie;
+      if (dwarf_formref_die (typeattr, &basedie) == NULL)
+       error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
+      byte_size = base_byte_size (&basedie);
+    }
+
+  bool deref = false;
+  if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
+    /* This is a bit field.  */
+    emit_bitfield (pool, indent, die, byte_size, *input,
+                  "fetch_bitfield (", target, "");
+  else
+    switch (byte_size)
+      {
+      case 1:
+      case 2:
+      case 4:
+      case 8:
+       obstack_printf (pool, "%*s", indent * 2, "");
+       deref = emit_base_fetch (pool, byte_size, target, false, *input);
+       obstack_printf (pool, "\n");
+       break;
+
+      default:
+       /* Could handle this generating call to memcpy equivalent.  */
+       error (2, 0, _("fetch is larger than base integer types"));
+       break;
+      }
+
+  struct location *loc = new_synthetic_loc (pool, *input, deref);
+  loc->type = loc_final;
+  (*input)->next = loc;
+  *input = loc;
+}
+
+void
+c_translate_addressof (struct obstack *pool, int indent,
+                      Dwarf_Addr dwbias __attribute__ ((unused)),
+                      Dwarf_Die *die,
+                      Dwarf_Attribute *typeattr  __attribute__ ((unused)),
+                      struct location **input, const char *target)
+{
+  ++indent;
+
+  if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
+    error (2, 0, _("cannot take the address of a bit field"));
+
+  switch ((*input)->type)
+    {
+    case loc_address:
+      obstack_printf (pool, "%*s%s = addr;\n", indent * 2, "", target);
+      (*input)->next = new_synthetic_loc (pool, *input, false);
+      (*input)->next->type = loc_final;
+      break;
+
+    case loc_register:
+      error (2, 0, _("cannot take address of object in register"));
+      break;
+    case loc_noncontiguous:
+      error (2, 0, _("cannot take address of noncontiguous object"));
+      break;
+
+    default:
+      abort();
+      break;
+    }
+}
+
+
+/* Determine the element stride of an array type.  */
+static Dwarf_Word
+array_stride (Dwarf_Die *typedie)
+{
+  Dwarf_Attribute attr_mem;
+  if (dwarf_attr_integrate (typedie, DW_AT_stride_size, &attr_mem) != NULL)
+    {
+      Dwarf_Word stride;
+      if (dwarf_formudata (&attr_mem, &stride) == 0)
+       return stride;
+      error (2, 0, _("cannot get stride_size attribute array type %s: %s"),
+            dwarf_diename_integrate (typedie) ?: "<anonymous>",
+            dwarf_errmsg (-1));
+    }
+
+  Dwarf_Die die_mem;
+  if (dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem) == NULL
+      || dwarf_formref_die (&attr_mem, &die_mem) == NULL)
+    error (2, 0, _("cannot get element type of array type %s: %s"),
+          dwarf_diename_integrate (typedie) ?: "<anonymous>",
+          dwarf_errmsg (-1));
+
+  if (dwarf_attr_integrate (&die_mem, DW_AT_byte_size, &attr_mem) != NULL)
+    {
+      Dwarf_Word stride;
+      if (dwarf_formudata (&attr_mem, &stride) == 0)
+       return stride;
+      error (2, 0,
+            _("cannot get byte_size attribute for array element type %s: %s"),
+            dwarf_diename_integrate (&die_mem) ?: "<anonymous>",
+            dwarf_errmsg (-1));
+    }
+
+  error (2, 0, _("confused about array element size"));
+  return 0;
+}
+
+void
+c_translate_array (struct obstack *pool, int indent,
+                  Dwarf_Addr dwbias __attribute__ ((unused)),
+                  Dwarf_Die *typedie, struct location **input,
+                  const char *idx, Dwarf_Word const_idx)
+{
+  ++indent;
+
+  Dwarf_Word stride = array_stride (typedie);
+
+  struct location *loc = *input;
+  while (loc->type == loc_noncontiguous)
+    {
+      if (idx != NULL)
+       error (2, 0, _("cannot dynamically index noncontiguous array"));
+      else
+       {
+         Dwarf_Word offset = const_idx * stride;
+         struct location *piece = loc->pieces;
+         while (piece != NULL && offset >= piece->byte_size)
+           {
+             offset -= piece->byte_size;
+             piece = piece->next;
+           }
+         if (piece == NULL)
+           error (2, 0, _("constant index is outside noncontiguous array"));
+         if (offset % stride != 0)
+           error (2, 0, _("noncontiguous array splits elements"));
+         const_idx = offset / stride;
+         loc = piece;
+       }
+    }
+
+  switch (loc->type)
+    {
+    case loc_address:
+      ++indent;
+      if (idx != NULL)
+       obstack_printf (pool, "%*saddr += %s * " UFORMAT ";\n",
+                       indent * 2, "", idx, stride);
+      else
+       obstack_printf (pool, "%*saddr += " UFORMAT " * " UFORMAT ";\n",
+                       indent * 2, "", const_idx, stride);
+      loc = new_synthetic_loc (pool, loc, false);
+      break;
+
+    case loc_register:
+      error (2, 0, _("cannot index array stored in a register"));
+      break;
+
+    default:
+      abort();
+      break;
+    }
+
+  (*input)->next = loc;
+  *input = (*input)->next;
+}
+
+\f
+/* Emitting C code for finalized fragments.  */
+
+
+#define emit(fmt, ...) fprintf (out, fmt, ## __VA_ARGS__)
+
+/* Open a block with a comment giving the original DWARF expression.  */
+static void
+emit_header (FILE *out, struct location *loc, unsigned int hindent)
+{
+  if (loc->ops == NULL)
+    emit ("%*s{ // synthesized\n", hindent * 2, "");
+  else
+    {
+      emit ("%*s{ // DWARF expression:", hindent * 2, "");
+      for (size_t i = 0; i < loc->nops; ++i)
+       {
+         emit (" %#x", loc->ops[i].atom);
+         if (loc->ops[i].number2 == 0)
+           {
+             if (loc->ops[i].number != 0)
+               emit ("(%" PRId64 ")", loc->ops[i].number);
+           }
+         else
+           emit ("(%" PRId64 ",%" PRId64 ")",
+                 loc->ops[i].number, loc->ops[i].number2);
+       }
+      emit ("\n");
+    }
+}
+
+/* Emit a code fragment to assign the target variable to a register value.  */
+static void
+emit_loc_register (FILE *out, struct location *loc, unsigned int indent,
+                  const char *target)
+{
+  assert (loc->type == loc_register);
+
+  emit ("%*s%s = fetch_register (%u);\n",
+       indent * 2, "", target, loc->regno);
+}
+
+/* Emit a code fragment to assign the target variable to an address.  */
+static void
+emit_loc_address (FILE *out, struct location *loc, unsigned int indent,
+                 const char *target)
+{
+  assert (loc->type == loc_address);
+
+  if (loc->address.stack_depth == 0)
+    /* Synthetic program.  */
+    emit ("%s", loc->address.program);
+  else
+    {
+      emit ("%*s%s " STACKFMT, (indent + 1) * 2, "", STACK_TYPE, 0);
+      for (unsigned int i = 1; i < loc->address.stack_depth; ++i)
+       emit (", " STACKFMT, i);
+      emit (";\n");
+
+      emit ("%s%*s%s = " STACKFMT ";\n", loc->address.program,
+           (indent + 1) * 2, "", target, 0);
+    }
+}
+
+/* Emit a code fragment to declare the target variable and
+   assign it to an address-sized value.  */
+static void
+emit_loc_value (FILE *out, struct location *loc, unsigned int indent,
+               const char *target, bool declare)
+{
+  if (declare)
+    emit ("%*s%s %s;\n", indent * 2, "", STACK_TYPE, target);
+
+  emit_header (out, loc, indent);
+
+  switch (loc->type)
+    {
+    default:
+      abort ();
+      break;
+
+    case loc_register:
+      emit_loc_register (out, loc, indent, target);
+      break;
+
+    case loc_address:
+      if (loc->address.frame_base != NULL)
+       emit_loc_value (out, loc->address.frame_base, indent,
+                       "frame_base", true);
+      emit_loc_address (out, loc, indent, target);
+      break;
+    }
+
+  emit ("%*s}\n", indent * 2, "");
+}
+
+bool
+c_emit_location (FILE *out, struct location *loc, int indent)
+{
+  emit ("%*s{\n", indent * 2, "");
+
+  bool deref = false;
+  for (bool declare_addr = true; loc->next != NULL; loc = loc->next)
+    switch (loc->type)
+      {
+      case loc_address:
+       /* Emit the program fragment to calculate the address.  */
+       emit_loc_value (out, loc, indent + 1, "addr", declare_addr);
+       declare_addr = false;
+       deref = deref || loc->address.used_deref;
+       break;
+
+      case loc_register:
+      case loc_noncontiguous:
+       /* These don't produce any code directly.
+          The next address/final record incorporates the value.  */
+       break;
+
+      case loc_final:          /* Should be last in chain!  */
+      default:
+       abort ();
+       break;
+      }
+
+  if (loc->type != loc_final)  /* Unfinished chain.  */
+    abort ();
+
+  emit ("%s%*s}\n", loc->address.program, indent * 2, "");
+
+  return deref;
+}
+
+#undef emit
diff --git a/libdwfl/loc2c.h b/libdwfl/loc2c.h
new file mode 100644 (file)
index 0000000..15d8928
--- /dev/null
@@ -0,0 +1,63 @@
+#include <libdw.h>
+
+struct obstack;                        /* Use <obstack.h> */
+struct location;               /* Opaque */
+
+
+/* Translate a C fragment for the location expression, using *INPUT
+   as the starting location, begin from scratch if *INPUT is null.
+   If DW_OP_fbreg is used, it may have a subfragment computing from
+   the FB_ATTR location expression.
+
+   On errors, exit and never return (XXX ?).  On success, return the
+   first fragment created, which is also chained onto (*INPUT)->next.
+   *INPUT is then updated with the new tail of that chain.
+   *USED_DEREF is set to true iff the "deref" runtime operation
+   was used, otherwise it is not modified.  */
+struct location *c_translate_location (struct obstack *, int indent,
+                                      Dwarf_Addr bias,
+                                      Dwarf_Attribute *loc_attr,
+                                      Dwarf_Addr address,
+                                      struct location **input,
+                                      Dwarf_Attribute *fb_attr);
+
+/* Translate a fragment to dereference the given pointer type,
+   where *INPUT is the location of the pointer with that type.  */
+void c_translate_pointer (struct obstack *pool, int indent,
+                         Dwarf_Addr dwbias, Dwarf_Die *typedie,
+                         struct location **input);
+
+/* Translate a fragment to index an array (turning the location
+   of the array into the location of an element).  If IDX is non-null,
+   it's a string of C code to emit in the fragment as the array index.
+   If the index is a known constant, IDX should be null and CONST_IDX
+   is used instead (this case can handle local arrays in registers).  */
+void c_translate_array (struct obstack *pool, int indent,
+                       Dwarf_Addr dwbias, Dwarf_Die *typedie,
+                       struct location **input,
+                       const char *idx, Dwarf_Word const_idx);
+
+/* Translate a fragment to compute the address of the input location
+   and assign it to the variable TARGET.  This doesn't really do anything
+   (it always emits "TARGET = addr;"), but it will barf if the location
+   is a register or noncontiguous object.  */
+void c_translate_addressof (struct obstack *pool, int indent,
+                           Dwarf_Addr dwbias, Dwarf_Die *die,
+                           Dwarf_Attribute *typeattr,
+                           struct location **input, const char *target);
+
+/* Translate a fragment to fetch the value of variable or member DIE
+   at the *INPUT location and store it in variable TARGET.
+   This handles base integer types and bit fields.  */
+void c_translate_fetch (struct obstack *pool, int indent,
+                       Dwarf_Addr dwbias __attribute__ ((unused)),
+                       Dwarf_Die *die, Dwarf_Attribute *typeattr,
+                       struct location **input, const char *target);
+
+/* Emit the C fragment built up at LOC (i.e., the return value from the
+   first c_translate_location call made).  INDENT should match that
+   passed to c_translate_* previously.
+
+   Writes complete lines of C99, code forming a complete C block, to STREAM.
+   Return value is true iff that code uses the `deref' runtime macros.  */
+bool c_emit_location (FILE *stream, struct location *loc, int indent);
diff --git a/libdwfl/test2.c b/libdwfl/test2.c
new file mode 100644 (file)
index 0000000..4c1e7ad
--- /dev/null
@@ -0,0 +1,268 @@
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <locale.h>
+#include <argp.h>
+#include <libdwfl.h>
+#include <dwarf.h>
+#include "../libdw/libdwP.h"
+#include <obstack.h>
+
+#include "loc2c.h"
+
+static const char *
+dwarf_diename_integrate (Dwarf_Die *die)
+{
+  Dwarf_Attribute attr_mem;
+  return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem));
+}
+
+
+
+static void
+handle_variable (Dwarf_Die *scopes, int nscopes, int out,
+                Dwarf_Addr cubias, Dwarf_Die *vardie, Dwarf_Addr pc,
+                char **fields)
+{
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+  struct obstack pool;
+  obstack_init (&pool);
+
+   /* Figure out the appropriate frame base for accessing this variable.
+     XXX not handling nested functions
+     XXX inlines botched
+  */
+  Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
+  for (int inner = 0; inner < nscopes; ++inner)
+    {
+      switch (dwarf_tag (&scopes[inner]))
+       {
+       default:
+         continue;
+       case DW_TAG_subprogram:
+       case DW_TAG_entry_point:
+       case DW_TAG_inlined_subroutine: /* XXX */
+         if (inner >= out)
+           fb_attr = dwarf_attr_integrate (&scopes[inner],
+                                           DW_AT_frame_base,
+                                           &fb_attr_mem);
+         break;
+       }
+      break;
+    }
+
+  Dwarf_Attribute attr_mem;
+
+  if (dwarf_attr_integrate (vardie, DW_AT_location, &attr_mem) == NULL)
+    error (2, 0, _("cannot get location of variable: %s"),
+          dwarf_errmsg (-1));
+
+#define FIELD "addr"
+#define emit(fmt, ...) printf ("  addr = " fmt "\n", ## __VA_ARGS__)
+
+  struct location *head, *tail = NULL;
+  head = c_translate_location (&pool, 1, cubias, &attr_mem, pc,
+                              &tail, fb_attr);
+
+  if (dwarf_attr_integrate (vardie, DW_AT_type, &attr_mem) == NULL)
+    error (2, 0, _("cannot get type of variable: %s"),
+          dwarf_errmsg (-1));
+
+  Dwarf_Die die_mem, *die = vardie;
+  while (*fields != NULL)
+    {
+      die = dwarf_formref_die (&attr_mem, &die_mem);
+
+      const int typetag = dwarf_tag (die);
+      switch (typetag)
+       {
+       case DW_TAG_typedef:
+         /* Just iterate on the referent type.  */
+         break;
+
+       case DW_TAG_pointer_type:
+         if (**fields == '+')
+           goto subscript;
+         /* A "" field means explicit pointer dereference and we consume it.
+            Otherwise the next field implicitly gets the dereference.  */
+         if (**fields == '\0')
+           ++fields;
+         c_translate_pointer (&pool, 1, cubias, die, &tail);
+         break;
+
+       case DW_TAG_array_type:
+         if (**fields == '+')
+           {
+           subscript:;
+             char *endp = *fields + 1;
+             uintmax_t idx = strtoumax (*fields + 1, &endp, 0);
+             if (endp == NULL || endp == *fields || *endp != '\0')
+               c_translate_array (&pool, 1, cubias, die, &tail,
+                                  *fields + 1, 0);
+             else
+               c_translate_array (&pool, 1, cubias, die, &tail,
+                                  NULL, idx);
+             ++fields;
+           }
+         else
+           error (2, 0, _("bad field for array type: %s"), *fields);
+         break;
+
+       case DW_TAG_structure_type:
+       case DW_TAG_union_type:
+         switch (dwarf_child (die, &die_mem))
+           {
+           case 1:             /* No children.  */
+             error (2, 0, _("empty struct %s"),
+                    dwarf_diename_integrate (die) ?: "<anonymous>");
+             break;
+           case -1:            /* Error.  */
+           default:            /* Shouldn't happen */
+             error (2, 0, _("%s %s: %s"),
+                    typetag == DW_TAG_union_type ? "union" : "struct",
+                    dwarf_diename_integrate (die) ?: "<anonymous>",
+                    dwarf_errmsg (-1));
+             break;
+
+           case 0:
+             break;
+           }
+         while (dwarf_tag (die) != DW_TAG_member
+                || ({ const char *member = dwarf_diename_integrate (die);
+                      member == NULL || strcmp (member, *fields); }))
+           if (dwarf_siblingof (die, &die_mem) != 0)
+             error (2, 0, _("field name %s not found"), *fields);
+
+         if (dwarf_attr_integrate (die, DW_AT_data_member_location,
+                                   &attr_mem) == NULL)
+           {
+             /* Union members don't usually have a location,
+                but just use the containing union's location.  */
+             if (typetag != DW_TAG_union_type)
+               error (2, 0, _("no location for field %s: %s"),
+                      *fields, dwarf_errmsg (-1));
+           }
+         else
+           c_translate_location (&pool, 1, cubias, &attr_mem, pc,
+                                 &tail, NULL);
+         ++fields;
+         break;
+
+       case DW_TAG_base_type:
+         error (2, 0, _("field %s vs base type %s"),
+                *fields, dwarf_diename_integrate (die) ?: "<anonymous type>");
+         break;
+
+       case -1:
+         error (2, 0, _("cannot find type: %s"), dwarf_errmsg (-1));
+         break;
+
+       default:
+         error (2, 0, _("%s: unexpected type tag %#x"),
+                dwarf_diename_integrate (die) ?: "<anonymous type>",
+                dwarf_tag (die));
+         break;
+       }
+
+      /* Now iterate on the type in DIE's attribute.  */
+      if (dwarf_attr_integrate (die, DW_AT_type, &attr_mem) == NULL)
+       error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
+    }
+
+  c_translate_fetch (&pool, 1, cubias, die, &attr_mem, &tail, "value");
+
+  printf ("#define PROBEADDR %#" PRIx64 "ULL\n", pc);
+  puts ("static void print_value(struct pt_regs *regs)\n"
+       "{\n"
+       "  intptr_t value;");
+
+  bool deref = c_emit_location (stdout, head, 1);
+
+  puts ("  printk (\" ---> %ld\\n\", (unsigned long) value);\n"
+       "  return;");
+
+  if (deref)
+    puts ("\n"
+         " deref_fault:\n"
+         "  printk (\" => BAD FETCH\\n\");");
+
+  puts ("}");
+}
+
+int
+main (int argc, char **argv)
+{
+  /* We use no threads here which can interfere with handling a stream.  */
+  (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  Dwfl *dwfl = NULL;
+  int argi;
+  (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &argi, &dwfl);
+  assert (dwfl != NULL);
+
+  if (argi == argc)
+    error (2, 0, "need address argument");
+
+  char *endp;
+  uintmax_t pc = strtoumax (argv[argi], &endp, 0);
+  if (endp == argv[argi])
+    error (2, 0, "bad address argument");
+
+  Dwarf_Addr cubias;
+  Dwarf_Die *cudie = dwfl_addrdie (dwfl, pc, &cubias);
+  if (cudie == NULL)
+    error (EXIT_FAILURE, 0, "dwfl_addrdie: %s", dwfl_errmsg (-1));
+
+  Dwarf_Die *scopes;
+  int n = dwarf_getscopes (cudie, pc - cubias, &scopes);
+  if (n < 0)
+    error (EXIT_FAILURE, 0, "dwarf_getscopes: %s", dwarf_errmsg (-1));
+  else if (n == 0)
+    error (EXIT_FAILURE, 0, "%#" PRIx64 ": not in any scope\n", pc);
+
+  if (++argi == argc)
+    error (2, 0, "need variable arguments");
+
+  char *spec = argv[argi++];
+
+  int lineno = 0, colno = 0, shadow = 0;
+  char *at = strchr (spec, '@');
+  if (at != NULL)
+    {
+      *at++ = '\0';
+      if (sscanf (at, "%*[^:]:%i:%i", &lineno, &colno) < 1)
+       lineno = 0;
+    }
+  else
+    {
+      int len;
+      if (sscanf (spec, "%*[^+]%n+%i", &len, &shadow) == 2)
+       spec[len] = '\0';
+    }
+
+  Dwarf_Die vardie;
+  int out = dwarf_getscopevar (scopes, n, spec, shadow, at, lineno, colno,
+                              &vardie);
+  if (out == -2)
+    error (0, 0, "no match for %s (+%d, %s:%d:%d)",
+          spec, shadow, at, lineno, colno);
+  else if (out < 0)
+    error (0, 0, "dwarf_getscopevar: %s (+%d, %s:%d:%d): %s",
+          spec, shadow, at, lineno, colno, dwarf_errmsg (-1));
+  else
+    handle_variable (scopes, n, out, cubias, &vardie, pc, &argv[argi]);
+
+  dwfl_end (dwfl);
+
+  return 0;
+}