Rename: src/dwarf/Gexpr-dwarf.c -> src/dwarf/Gexpr.c

(Logical change 1.241)
diff --git a/src/dwarf/Gexpr.c b/src/dwarf/Gexpr.c
index e69de29..b1d9464 100644
--- a/src/dwarf/Gexpr.c
+++ b/src/dwarf/Gexpr.c
@@ -0,0 +1,578 @@
+/* libunwind - a platform-independent unwind library
+   Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
+	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#include "dwarf_i.h"
+#include "tdep.h"
+
+/* The "pick" operator provides an index range of 0..255 indicating
+   that the stack could at least have a depth of up to 256 elements,
+   but the GCC unwinder restricts the depth to 64, which seems
+   reasonable so we use the same value here.  */
+#define MAX_EXPR_STACK_SIZE	64
+
+#define NUM_OPERANDS(signature)	(((signature) >> 6) & 0x3)
+#define OPND1_TYPE(signature)	(((signature) >> 3) & 0x7)
+#define OPND2_TYPE(signature)	(((signature) >> 0) & 0x7)
+
+#define OPND_SIGNATURE(n, t1, t2) (((n) << 6) | ((t1) << 3) | ((t2) << 0))
+#define OPND1(t1)		OPND_SIGNATURE(1, t1, 0)
+#define OPND2(t1, t2)		OPND_SIGNATURE(2, t1, t2)
+
+#define VAL8	0x0
+#define VAL16	0x1
+#define VAL32	0x2
+#define VAL64	0x3
+#define ULEB128	0x4
+#define SLEB128	0x5
+#define OFFSET	0x6	/* 32-bit offset for 32-bit DWARF, 64-bit otherwise */
+
+static uint8_t operands[256] =
+  {
+    [DW_OP_addr] =	OPND1 (sizeof (unw_word_t) == 4 ? VAL32 : VAL64),
+    [DW_OP_const1u] =		OPND1 (VAL8),
+    [DW_OP_const1s] =		OPND1 (VAL8),
+    [DW_OP_const2u] =		OPND1 (VAL16),
+    [DW_OP_const2s] =		OPND1 (VAL16),
+    [DW_OP_const4u] =		OPND1 (VAL32),
+    [DW_OP_const4s] =		OPND1 (VAL32),
+    [DW_OP_const8u] =		OPND1 (VAL64),
+    [DW_OP_const8s] =		OPND1 (VAL64),
+    [DW_OP_pick] =		OPND1 (VAL8),
+    [DW_OP_plus_uconst] =	OPND1 (ULEB128),
+    [DW_OP_skip] =		OPND1 (VAL16),
+    [DW_OP_bra] =		OPND1 (VAL16),
+    [DW_OP_breg0 +  0] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  1] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  2] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  3] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  4] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  5] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  6] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  7] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  8] =	OPND1 (SLEB128),
+    [DW_OP_breg0 +  9] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 10] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 11] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 12] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 13] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 14] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 15] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 16] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 17] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 18] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 19] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 20] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 21] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 22] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 23] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 24] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 25] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 26] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 27] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 28] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 29] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 30] =	OPND1 (SLEB128),
+    [DW_OP_breg0 + 31] =	OPND1 (SLEB128),
+    [DW_OP_regx] =		OPND1 (ULEB128),
+    [DW_OP_fbreg] =		OPND1 (SLEB128),
+    [DW_OP_bregx] =		OPND2 (ULEB128, SLEB128),
+    [DW_OP_piece] =		OPND1 (ULEB128),
+    [DW_OP_deref_size] =	OPND1 (VAL8),
+    [DW_OP_xderef_size] =	OPND1 (VAL8),
+    [DW_OP_call2] =		OPND1 (VAL16),
+    [DW_OP_call4] =		OPND1 (VAL32),
+    [DW_OP_call_ref] =		OPND1 (OFFSET)
+  };
+
+static inline unw_word_t
+sword (unw_word_t val)
+{
+  switch (sizeof (unw_word_t))
+    {
+    case 1: return (int8_t) val;
+    case 2: return (int16_t) val;
+    case 4: return (int32_t) val;
+    case 8: return (int64_t) val;
+    default: abort ();
+    }
+}
+
+static inline unw_word_t
+read_operand (unw_addr_space_t as, unw_accessors_t *a,
+	      unw_word_t *addr, int operand_type, unw_word_t *val, void *arg)
+{
+  uint8_t u8;
+  uint16_t u16;
+  uint32_t u32;
+  uint64_t u64;
+  int ret;
+
+  switch (operand_type)
+    {
+    case VAL8:
+      ret = dwarf_readu8 (as, a, addr, &u8, arg);
+      *val = u8;
+      break;
+
+    case VAL16:
+      ret = dwarf_readu16 (as, a, addr, &u16, arg);
+      *val = u16;
+      break;
+
+    case VAL32:
+      ret = dwarf_readu32 (as, a, addr, &u32, arg);
+      *val = u32;
+      break;
+
+    case VAL64:
+      ret = dwarf_readu64 (as, a, addr, &u64, arg);
+      *val = u64;
+      break;
+
+    case ULEB128:
+      ret = dwarf_read_uleb128 (as, a, addr, val, arg);
+      break;
+
+    case SLEB128:
+      ret = dwarf_read_sleb128 (as, a, addr, val, arg);
+      break;
+
+    case OFFSET: /* only used by DW_OP_call_ref, which we don't implement */
+    default:
+      Debug (1, "Unexpected operand type %d", operand_type);
+      ret = -UNW_EINVAL;
+    }
+  return ret;
+}
+
+HIDDEN int
+dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, unw_word_t len,
+		 unw_word_t *valp, int *is_register)
+{
+  unw_word_t operand1 = 0, operand2 = 0, tmp1, tmp2, tmp3, end_addr;
+  uint8_t opcode, operands_signature, u8;
+  unw_addr_space_t as;
+  unw_accessors_t *a;
+  void *arg;
+  unw_word_t stack[MAX_EXPR_STACK_SIZE];
+  unsigned int tos = 0;
+  uint16_t u16;
+  uint32_t u32;
+  uint64_t u64;
+  int ret;
+# define pop()					\
+({						\
+  if (tos >= MAX_EXPR_STACK_SIZE)		\
+    {						\
+      Debug (1, "Stack underflow\n");		\
+      return -UNW_EINVAL;			\
+    }						\
+  stack[tos--];					\
+})
+# define push(x)				\
+do {						\
+  if (tos >= MAX_EXPR_STACK_SIZE)		\
+    {						\
+      Debug (1, "Stack overflow\n");		\
+      return -UNW_EINVAL;			\
+    }						\
+  stack[tos++] = (x);				\
+} while (0)
+# define pick(n)				\
+({						\
+  unsigned int _index = tos - (n);		\
+  if (_index >= MAX_EXPR_STACK_SIZE)		\
+    {						\
+      Debug (1, "Out-of-stack pick\n");		\
+      return -UNW_EINVAL;			\
+    }						\
+  stack[_index];				\
+})
+
+  as = c->as;
+  arg = c->as_arg;
+  a = unw_get_accessors (as);
+  end_addr = *addr + len;
+
+  push (c->cfa);	/* push current CFA as required by DWARF spec */
+
+  while (*addr < len)
+    {
+      if ((ret = dwarf_readu8 (as, a, addr, &opcode, arg)) < 0)
+	return ret;
+
+      operands_signature = operands[opcode];
+
+      if (unlikely (NUM_OPERANDS (operands_signature) > 0))
+	{
+	  if ((ret = read_operand (as, a, addr,
+				   OPND1_TYPE (operands_signature),
+				   &operand1, arg)) < 0)
+	    return ret;
+	  if (NUM_OPERANDS (operands_signature > 1))
+	    if ((ret = read_operand (as, a, addr,
+				     OPND2_TYPE (operands_signature),
+				     &operand2, arg)) < 0)
+	      return ret;
+	}
+
+      switch ((dwarf_expr_op_t) opcode)
+	{
+	case DW_OP_lit0:  case DW_OP_lit1:  case DW_OP_lit2:
+	case DW_OP_lit3:  case DW_OP_lit4:  case DW_OP_lit5:
+	case DW_OP_lit6:  case DW_OP_lit7:  case DW_OP_lit8:
+	case DW_OP_lit9:  case DW_OP_lit10: case DW_OP_lit11:
+	case DW_OP_lit12: case DW_OP_lit13: case DW_OP_lit14:
+	case DW_OP_lit15: case DW_OP_lit16: case DW_OP_lit17:
+	case DW_OP_lit18: case DW_OP_lit19: case DW_OP_lit20:
+	case DW_OP_lit21: case DW_OP_lit22: case DW_OP_lit23:
+	case DW_OP_lit24: case DW_OP_lit25: case DW_OP_lit26:
+	case DW_OP_lit27: case DW_OP_lit28: case DW_OP_lit29:
+	case DW_OP_lit30: case DW_OP_lit31:
+	  push (opcode = DW_OP_lit0);
+	  break;
+
+	case DW_OP_breg0:  case DW_OP_breg1:  case DW_OP_breg2:
+	case DW_OP_breg3:  case DW_OP_breg4:  case DW_OP_breg5:
+	case DW_OP_breg6:  case DW_OP_breg7:  case DW_OP_breg8:
+	case DW_OP_breg9:  case DW_OP_breg10: case DW_OP_breg11:
+	case DW_OP_breg12: case DW_OP_breg13: case DW_OP_breg14:
+	case DW_OP_breg15: case DW_OP_breg16: case DW_OP_breg17:
+	case DW_OP_breg18: case DW_OP_breg19: case DW_OP_breg20:
+	case DW_OP_breg21: case DW_OP_breg22: case DW_OP_breg23:
+	case DW_OP_breg24: case DW_OP_breg25: case DW_OP_breg26:
+	case DW_OP_breg27: case DW_OP_breg28: case DW_OP_breg29:
+	case DW_OP_breg30: case DW_OP_breg31:
+	  if ((ret = unw_get_reg (dwarf_to_cursor (c),
+				  dwarf_to_unw_regnum (opcode - DW_OP_breg0),
+				  &tmp1)) < 0)
+	    return ret;
+	  push (tmp1 + operand1);
+	  break;
+
+	case DW_OP_bregx:
+	  if ((ret = unw_get_reg (dwarf_to_cursor (c),
+				  dwarf_to_unw_regnum (operand1), &tmp1)) < 0)
+	    return ret;
+	  push (tmp1 + operand2);
+	  break;
+
+	case DW_OP_reg0:  case DW_OP_reg1:  case DW_OP_reg2:
+	case DW_OP_reg3:  case DW_OP_reg4:  case DW_OP_reg5:
+	case DW_OP_reg6:  case DW_OP_reg7:  case DW_OP_reg8:
+	case DW_OP_reg9:  case DW_OP_reg10: case DW_OP_reg11:
+	case DW_OP_reg12: case DW_OP_reg13: case DW_OP_reg14:
+	case DW_OP_reg15: case DW_OP_reg16: case DW_OP_reg17:
+	case DW_OP_reg18: case DW_OP_reg19: case DW_OP_reg20:
+	case DW_OP_reg21: case DW_OP_reg22: case DW_OP_reg23:
+	case DW_OP_reg24: case DW_OP_reg25: case DW_OP_reg26:
+	case DW_OP_reg27: case DW_OP_reg28: case DW_OP_reg29:
+	case DW_OP_reg30: case DW_OP_reg31:
+	  *valp = dwarf_to_unw_regnum (opcode - DW_OP_reg0);
+	  *is_register = 1;
+	  return 0;
+
+	case DW_OP_regx:
+	  *valp = dwarf_to_unw_regnum (operand1);
+	  *is_register = 1;
+	  return 0;
+
+	case DW_OP_addr:
+	case DW_OP_const1u:
+	case DW_OP_const2u:
+	case DW_OP_const4u:
+	case DW_OP_const8u:
+	case DW_OP_constu:
+	case DW_OP_const8s:
+	case DW_OP_consts:
+	  push (operand1);
+	  break;
+
+	case DW_OP_const1s:
+	  if (operand1 & 0x80)
+	    operand1 |= ((unw_word_t) -1) << 8;
+	  push (operand1);
+	  break;
+
+	case DW_OP_const2s:
+	  if (operand1 & 0x8000)
+	    operand1 |= ((unw_word_t) -1) << 16;
+	  push (operand1);
+	  break;
+
+	case DW_OP_const4s:
+	  if (operand1 & 0x80000000)
+	    operand1 |= (((unw_word_t) -1) << 16) << 16;
+	  push (operand1);
+	  break;
+
+	case DW_OP_deref:
+	  tmp1 = pop ();
+	  if ((ret = dwarf_readw (as, a, &tmp1, &tmp2, arg)) < 0)
+	    return ret;
+	  push (tmp2);
+	  break;
+
+	case DW_OP_deref_size:
+	  tmp1 = pop ();
+	  switch (operand1)
+	    {
+	    case 0:
+	      break;
+
+	    case 1:
+	      if ((ret = dwarf_readu8 (as, a, &tmp1, &u8, arg)) < 0)
+		return ret;
+	      tmp2 = u8;
+	      break;
+
+	    case 2:
+	      if ((ret = dwarf_readu16 (as, a, &tmp1, &u16, arg)) < 0)
+		return ret;
+	      tmp2 = u16;
+	      break;
+
+	    case 3:
+	    case 4:
+	      if ((ret = dwarf_readu32 (as, a, &tmp1, &u32, arg)) < 0)
+		return ret;
+	      tmp2 = u32;
+	      if (operand1 == 3)
+		{
+		  if (dwarf_is_big_endian (as))
+		    tmp2 >>= 8;
+		  else
+		    tmp2 &= 0xffffff;
+		}
+	      break;
+	    case 5:
+	    case 6:
+	    case 7:
+	    case 8:
+	      if ((ret = dwarf_readu64 (as, a, &tmp1, &u64, arg)) < 0)
+		return ret;
+	      tmp2 = u16;
+	      if (operand1 != 8)
+		{
+		  if (dwarf_is_big_endian (as))
+		    tmp2 >>= 64 - 8 * operand1;
+		  else
+		    tmp2 &= (~ (unw_word_t) 0) << (8 * operand1);
+		}
+	      break;
+	    }
+	  push (tmp2);
+	  break;
+
+	case DW_OP_dup:
+	  push (pick (0));
+	  break;
+
+	case DW_OP_drop:
+	  pop ();
+	  break;
+
+	case DW_OP_pick:
+	  push (pick (operand1));
+	  break;
+
+	case DW_OP_over:
+	  push (pick (1));
+	  break;
+
+	case DW_OP_swap:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp1);
+	  push (tmp2);
+	  break;
+
+	case DW_OP_rot:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  tmp3 = pop ();
+	  push (tmp1);
+	  push (tmp3);
+	  push (tmp2);
+	  break;
+
+	case DW_OP_abs:
+	  tmp1 = pop ();
+	  if (tmp1 & ((unw_word_t) 1 << (8 * sizeof (unw_word_t) - 1)))
+	    tmp1 = -tmp1;
+	  push (tmp1);
+	  break;
+
+	case DW_OP_and:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp1 & tmp2);
+	  break;
+
+	case DW_OP_div:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  if (tmp1)
+	    tmp1 = sword (tmp2) / sword (tmp1);
+	  push (tmp1);
+	  break;
+
+	case DW_OP_minus:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  tmp1 = tmp2 - tmp1;
+	  push (tmp1);
+	  break;
+
+	case DW_OP_mod:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  if (tmp1)
+	    tmp1 = tmp2 % tmp1;
+	  push (tmp1);
+	  break;
+
+	case DW_OP_mul:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  if (tmp1)
+	    tmp1 = tmp2 * tmp1;
+	  push (tmp1);
+	  break;
+
+	case DW_OP_neg:
+	  push (-pop ());
+	  break;
+
+	case DW_OP_not:
+	  push (~pop ());
+	  break;
+
+	case DW_OP_or:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp1 | tmp2);
+	  break;
+
+	case DW_OP_plus:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp1 + tmp2);
+	  break;
+
+	case DW_OP_plus_uconst:
+	  tmp1 = pop ();
+	  push (tmp1 + operand1);
+	  break;
+
+	case DW_OP_shl:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp2 << tmp1);
+	  break;
+
+	case DW_OP_shr:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp2 >> tmp1);
+	  break;
+
+	case DW_OP_shra:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp2) >> tmp1);
+	  break;
+
+	case DW_OP_xor:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (tmp1 ^ tmp2);
+	  break;
+
+	case DW_OP_le:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) <= sword (tmp2));
+	  break;
+
+	case DW_OP_ge:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) >= sword (tmp2));
+	  break;
+
+	case DW_OP_eq:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) == sword (tmp2));
+	  break;
+
+	case DW_OP_lt:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) < sword (tmp2));
+	  break;
+
+	case DW_OP_gt:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) > sword (tmp2));
+	  break;
+
+	case DW_OP_ne:
+	  tmp1 = pop ();
+	  tmp2 = pop ();
+	  push (sword (tmp1) != sword (tmp2));
+	  break;
+
+	case DW_OP_skip:
+	  *addr += (int16_t) operand1;
+	  break;
+
+	case DW_OP_bra:
+	  tmp1 = pop ();
+	  if (tmp1)
+	    *addr += (int16_t) operand1;
+	  break;
+
+	case DW_OP_nop:
+	  break;
+
+	case DW_OP_call2:
+	case DW_OP_call4:
+	case DW_OP_call_ref:
+	case DW_OP_fbreg:
+	case DW_OP_piece:
+	case DW_OP_push_object_address:
+	case DW_OP_xderef:
+	case DW_OP_xderef_size:
+	default:
+	  Debug (1, "Unexpected opcode 0x%x\n", opcode);
+	  return -UNW_EINVAL;
+	}
+    }
+  *valp = pop ();
+  return 0;
+}