Add missing files from previous commit.
authorLuis Machado <luisgpm@br.ibm.com>
Fri, 24 Feb 2012 15:14:47 +0000 (15:14 +0000)
committerLuis Machado <luisgpm@br.ibm.com>
Fri, 24 Feb 2012 15:14:47 +0000 (15:14 +0000)
gdb/gdbserver/ax.c [new file with mode: 0644]
gdb/gdbserver/ax.h [new file with mode: 0644]

diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c
new file mode 100644 (file)
index 0000000..4eba27b
--- /dev/null
@@ -0,0 +1,1191 @@
+/* Agent expression code for remote server.
+   Copyright (C) 2009-2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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; either version 3 of the License, or
+   (at your option) any later version.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#include "server.h"
+#include "ax.h"
+
+static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
+
+#ifdef IN_PROCESS_AGENT
+int debug_agent = 0;
+#endif
+
+static void
+ax_vdebug (const char *fmt, ...)
+{
+  char buf[1024];
+  va_list ap;
+
+  va_start (ap, fmt);
+  vsprintf (buf, fmt, ap);
+  fprintf (stderr, PROG "/ax: %s\n", buf);
+  va_end (ap);
+}
+
+#define ax_debug_1(level, fmt, args...)        \
+  do {                                         \
+    if (level <= debug_threads)                        \
+      ax_vdebug ((fmt), ##args);               \
+  } while (0)
+
+#define ax_debug(FMT, args...)         \
+  ax_debug_1 (1, FMT, ##args)
+
+/* This enum must exactly match what is documented in
+   gdb/doc/agentexpr.texi, including all the numerical values.  */
+
+enum gdb_agent_op
+  {
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE)  \
+    gdb_agent_op_ ## NAME = VALUE,
+#include "ax.def"
+#undef DEFOP
+    gdb_agent_op_last
+  };
+
+static const char *gdb_agent_op_names [gdb_agent_op_last] =
+  {
+    "?undef?"
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE)  , # NAME
+#include "ax.def"
+#undef DEFOP
+  };
+
+static const unsigned char gdb_agent_op_sizes [gdb_agent_op_last] =
+  {
+    0
+#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE)  , SIZE
+#include "ax.def"
+#undef DEFOP
+  };
+
+/* A wrapper for gdb_agent_op_names that does some bounds-checking.  */
+
+static const char *
+gdb_agent_op_name (int op)
+{
+  if (op < 0 || op >= gdb_agent_op_last || gdb_agent_op_names[op] == NULL)
+    return "?undef?";
+  return gdb_agent_op_names[op];
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* The packet form of an agent expression consists of an 'X', number
+   of bytes in expression, a comma, and then the bytes.  */
+
+struct agent_expr *
+gdb_parse_agent_expr (char **actparm)
+{
+  char *act = *actparm;
+  ULONGEST xlen;
+  struct agent_expr *aexpr;
+
+  ++act;  /* skip the X */
+  act = unpack_varlen_hex (act, &xlen);
+  ++act;  /* skip a comma */
+  aexpr = xmalloc (sizeof (struct agent_expr));
+  aexpr->length = xlen;
+  aexpr->bytes = xmalloc (xlen);
+  convert_ascii_to_int (act, aexpr->bytes, xlen);
+  *actparm = act + (xlen * 2);
+  return aexpr;
+}
+
+/* Convert the bytes of an agent expression back into hex digits, so
+   they can be printed or uploaded.  This allocates the buffer,
+   callers should free when they are done with it.  */
+
+char *
+gdb_unparse_agent_expr (struct agent_expr *aexpr)
+{
+  char *rslt;
+
+  rslt = xmalloc (2 * aexpr->length + 1);
+  convert_int_to_ascii (aexpr->bytes, rslt, aexpr->length);
+  return rslt;
+}
+
+/* Bytecode compilation.  */
+
+CORE_ADDR current_insn_ptr;
+
+int emit_error;
+
+struct bytecode_address
+{
+  int pc;
+  CORE_ADDR address;
+  int goto_pc;
+  /* Offset and size of field to be modified in the goto block.  */
+  int from_offset, from_size;
+  struct bytecode_address *next;
+} *bytecode_address_table;
+
+void
+emit_prologue (void)
+{
+  target_emit_ops ()->emit_prologue ();
+}
+
+void
+emit_epilogue (void)
+{
+  target_emit_ops ()->emit_epilogue ();
+}
+
+static void
+emit_add (void)
+{
+  target_emit_ops ()->emit_add ();
+}
+
+static void
+emit_sub (void)
+{
+  target_emit_ops ()->emit_sub ();
+}
+
+static void
+emit_mul (void)
+{
+  target_emit_ops ()->emit_mul ();
+}
+
+static void
+emit_lsh (void)
+{
+  target_emit_ops ()->emit_lsh ();
+}
+
+static void
+emit_rsh_signed (void)
+{
+  target_emit_ops ()->emit_rsh_signed ();
+}
+
+static void
+emit_rsh_unsigned (void)
+{
+  target_emit_ops ()->emit_rsh_unsigned ();
+}
+
+static void
+emit_ext (int arg)
+{
+  target_emit_ops ()->emit_ext (arg);
+}
+
+static void
+emit_log_not (void)
+{
+  target_emit_ops ()->emit_log_not ();
+}
+
+static void
+emit_bit_and (void)
+{
+  target_emit_ops ()->emit_bit_and ();
+}
+
+static void
+emit_bit_or (void)
+{
+  target_emit_ops ()->emit_bit_or ();
+}
+
+static void
+emit_bit_xor (void)
+{
+  target_emit_ops ()->emit_bit_xor ();
+}
+
+static void
+emit_bit_not (void)
+{
+  target_emit_ops ()->emit_bit_not ();
+}
+
+static void
+emit_equal (void)
+{
+  target_emit_ops ()->emit_equal ();
+}
+
+static void
+emit_less_signed (void)
+{
+  target_emit_ops ()->emit_less_signed ();
+}
+
+static void
+emit_less_unsigned (void)
+{
+  target_emit_ops ()->emit_less_unsigned ();
+}
+
+static void
+emit_ref (int size)
+{
+  target_emit_ops ()->emit_ref (size);
+}
+
+static void
+emit_if_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_if_goto (offset_p, size_p);
+}
+
+static void
+emit_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_goto (offset_p, size_p);
+}
+
+static void
+write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+  target_emit_ops ()->write_goto_address (from, to, size);
+}
+
+static void
+emit_const (LONGEST num)
+{
+  target_emit_ops ()->emit_const (num);
+}
+
+static void
+emit_reg (int reg)
+{
+  target_emit_ops ()->emit_reg (reg);
+}
+
+static void
+emit_pop (void)
+{
+  target_emit_ops ()->emit_pop ();
+}
+
+static void
+emit_stack_flush (void)
+{
+  target_emit_ops ()->emit_stack_flush ();
+}
+
+static void
+emit_zero_ext (int arg)
+{
+  target_emit_ops ()->emit_zero_ext (arg);
+}
+
+static void
+emit_swap (void)
+{
+  target_emit_ops ()->emit_swap ();
+}
+
+static void
+emit_stack_adjust (int n)
+{
+  target_emit_ops ()->emit_stack_adjust (n);
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'.  */
+
+static void
+emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+  target_emit_ops ()->emit_int_call_1 (fn, arg1);
+}
+
+/* FN's prototype is `void(*fn)(int,LONGEST)'.  */
+
+static void
+emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+  target_emit_ops ()->emit_void_call_2 (fn, arg1);
+}
+
+static void
+emit_eq_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_eq_goto (offset_p, size_p);
+}
+
+static void
+emit_ne_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_ne_goto (offset_p, size_p);
+}
+
+static void
+emit_lt_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_lt_goto (offset_p, size_p);
+}
+
+static void
+emit_ge_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_ge_goto (offset_p, size_p);
+}
+
+static void
+emit_gt_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_gt_goto (offset_p, size_p);
+}
+
+static void
+emit_le_goto (int *offset_p, int *size_p)
+{
+  target_emit_ops ()->emit_le_goto (offset_p, size_p);
+}
+
+/* Scan an agent expression for any evidence that the given PC is the
+   target of a jump bytecode in the expression.  */
+
+int
+is_goto_target (struct agent_expr *aexpr, int pc)
+{
+  int i;
+  unsigned char op;
+
+  for (i = 0; i < aexpr->length; i += 1 + gdb_agent_op_sizes[op])
+    {
+      op = aexpr->bytes[i];
+
+      if (op == gdb_agent_op_goto || op == gdb_agent_op_if_goto)
+       {
+         int target = (aexpr->bytes[i + 1] << 8) + aexpr->bytes[i + 2];
+         if (target == pc)
+           return 1;
+       }
+    }
+
+  return 0;
+}
+
+/* Given an agent expression, turn it into native code.  */
+
+enum eval_result_type
+compile_bytecodes (struct agent_expr *aexpr)
+{
+  int pc = 0;
+  int done = 0;
+  unsigned char op, next_op;
+  int arg;
+  /* This is only used to build 64-bit value for constants.  */
+  ULONGEST top;
+  struct bytecode_address *aentry, *aentry2;
+
+#define UNHANDLED                                      \
+  do                                                   \
+    {                                                  \
+      ax_debug ("Cannot compile op 0x%x\n", op);       \
+      return expr_eval_unhandled_opcode;               \
+    } while (0)
+
+  if (aexpr->length == 0)
+    {
+      ax_debug ("empty agent expression\n");
+      return expr_eval_empty_expression;
+    }
+
+  bytecode_address_table = NULL;
+
+  while (!done)
+    {
+      op = aexpr->bytes[pc];
+
+      ax_debug ("About to compile op 0x%x, pc=%d\n", op, pc);
+
+      /* Record the compiled-code address of the bytecode, for use by
+        jump instructions.  */
+      aentry = xmalloc (sizeof (struct bytecode_address));
+      aentry->pc = pc;
+      aentry->address = current_insn_ptr;
+      aentry->goto_pc = -1;
+      aentry->from_offset = aentry->from_size = 0;
+      aentry->next = bytecode_address_table;
+      bytecode_address_table = aentry;
+
+      ++pc;
+
+      emit_error = 0;
+
+      switch (op)
+       {
+       case gdb_agent_op_add:
+         emit_add ();
+         break;
+
+       case gdb_agent_op_sub:
+         emit_sub ();
+         break;
+
+       case gdb_agent_op_mul:
+         emit_mul ();
+         break;
+
+       case gdb_agent_op_div_signed:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_div_unsigned:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_rem_signed:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_rem_unsigned:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_lsh:
+         emit_lsh ();
+         break;
+
+       case gdb_agent_op_rsh_signed:
+         emit_rsh_signed ();
+         break;
+
+       case gdb_agent_op_rsh_unsigned:
+         emit_rsh_unsigned ();
+         break;
+
+       case gdb_agent_op_trace:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_trace_quick:
+         UNHANDLED;
+         break;
+
+       case gdb_agent_op_log_not:
+         emit_log_not ();
+         break;
+
+       case gdb_agent_op_bit_and:
+         emit_bit_and ();
+         break;
+
+       case gdb_agent_op_bit_or:
+         emit_bit_or ();
+         break;
+
+       case gdb_agent_op_bit_xor:
+         emit_bit_xor ();
+         break;
+
+       case gdb_agent_op_bit_not:
+         emit_bit_not ();
+         break;
+
+       case gdb_agent_op_equal:
+         next_op = aexpr->bytes[pc];
+         if (next_op == gdb_agent_op_if_goto
+             && !is_goto_target (aexpr, pc)
+             && target_emit_ops ()->emit_eq_goto)
+           {
+             ax_debug ("Combining equal & if_goto");
+             pc += 1;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_eq_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else if (next_op == gdb_agent_op_log_not
+                  && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+                  && !is_goto_target (aexpr, pc + 1)
+                  && target_emit_ops ()->emit_ne_goto)
+           {
+             ax_debug ("Combining equal & log_not & if_goto");
+             pc += 2;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_ne_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else
+           emit_equal ();
+         break;
+
+       case gdb_agent_op_less_signed:
+         next_op = aexpr->bytes[pc];
+         if (next_op == gdb_agent_op_if_goto
+             && !is_goto_target (aexpr, pc))
+           {
+             ax_debug ("Combining less_signed & if_goto");
+             pc += 1;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_lt_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else if (next_op == gdb_agent_op_log_not
+                  && !is_goto_target (aexpr, pc)
+                  && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+                  && !is_goto_target (aexpr, pc + 1))
+           {
+             ax_debug ("Combining less_signed & log_not & if_goto");
+             pc += 2;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_ge_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else
+           emit_less_signed ();
+         break;
+
+       case gdb_agent_op_less_unsigned:
+         emit_less_unsigned ();
+         break;
+
+       case gdb_agent_op_ext:
+         arg = aexpr->bytes[pc++];
+         if (arg < (sizeof (LONGEST) * 8))
+           emit_ext (arg);
+         break;
+
+       case gdb_agent_op_ref8:
+         emit_ref (1);
+         break;
+
+       case gdb_agent_op_ref16:
+         emit_ref (2);
+         break;
+
+       case gdb_agent_op_ref32:
+         emit_ref (4);
+         break;
+
+       case gdb_agent_op_ref64:
+         emit_ref (8);
+         break;
+
+       case gdb_agent_op_if_goto:
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         aentry->goto_pc = arg;
+         emit_if_goto (&(aentry->from_offset), &(aentry->from_size));
+         break;
+
+       case gdb_agent_op_goto:
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         aentry->goto_pc = arg;
+         emit_goto (&(aentry->from_offset), &(aentry->from_size));
+         break;
+
+       case gdb_agent_op_const8:
+         emit_stack_flush ();
+         top = aexpr->bytes[pc++];
+         emit_const (top);
+         break;
+
+       case gdb_agent_op_const16:
+         emit_stack_flush ();
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         emit_const (top);
+         break;
+
+       case gdb_agent_op_const32:
+         emit_stack_flush ();
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         emit_const (top);
+         break;
+
+       case gdb_agent_op_const64:
+         emit_stack_flush ();
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         emit_const (top);
+         break;
+
+       case gdb_agent_op_reg:
+         emit_stack_flush ();
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         emit_reg (arg);
+         break;
+
+       case gdb_agent_op_end:
+         ax_debug ("At end of expression\n");
+
+         /* Assume there is one stack element left, and that it is
+            cached in "top" where emit_epilogue can get to it.  */
+         emit_stack_adjust (1);
+
+         done = 1;
+         break;
+
+       case gdb_agent_op_dup:
+         /* In our design, dup is equivalent to stack flushing.  */
+         emit_stack_flush ();
+         break;
+
+       case gdb_agent_op_pop:
+         emit_pop ();
+         break;
+
+       case gdb_agent_op_zero_ext:
+         arg = aexpr->bytes[pc++];
+         if (arg < (sizeof (LONGEST) * 8))
+           emit_zero_ext (arg);
+         break;
+
+       case gdb_agent_op_swap:
+         next_op = aexpr->bytes[pc];
+         /* Detect greater-than comparison sequences.  */
+         if (next_op == gdb_agent_op_less_signed
+             && !is_goto_target (aexpr, pc)
+             && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto)
+             && !is_goto_target (aexpr, pc + 1))
+           {
+             ax_debug ("Combining swap & less_signed & if_goto");
+             pc += 2;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_gt_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else if (next_op == gdb_agent_op_less_signed
+                  && !is_goto_target (aexpr, pc)
+                  && (aexpr->bytes[pc + 1] == gdb_agent_op_log_not)
+                  && !is_goto_target (aexpr, pc + 1)
+                  && (aexpr->bytes[pc + 2] == gdb_agent_op_if_goto)
+                  && !is_goto_target (aexpr, pc + 2))
+           {
+             ax_debug ("Combining swap & less_signed & log_not & if_goto");
+             pc += 3;
+             aentry->pc = pc;
+             arg = aexpr->bytes[pc++];
+             arg = (arg << 8) + aexpr->bytes[pc++];
+             aentry->goto_pc = arg;
+             emit_le_goto (&(aentry->from_offset), &(aentry->from_size));
+           }
+         else
+           emit_swap ();
+         break;
+
+       case gdb_agent_op_getv:
+         emit_stack_flush ();
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         emit_int_call_1 (get_get_tsv_func_addr (),
+                          arg);
+         break;
+
+       case gdb_agent_op_setv:
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         emit_void_call_2 (get_set_tsv_func_addr (),
+                           arg);
+         break;
+
+       case gdb_agent_op_tracev:
+         UNHANDLED;
+         break;
+
+         /* GDB never (currently) generates any of these ops.  */
+       case gdb_agent_op_float:
+       case gdb_agent_op_ref_float:
+       case gdb_agent_op_ref_double:
+       case gdb_agent_op_ref_long_double:
+       case gdb_agent_op_l_to_d:
+       case gdb_agent_op_d_to_l:
+       case gdb_agent_op_trace16:
+         UNHANDLED;
+         break;
+
+       default:
+         ax_debug ("Agent expression op 0x%x not recognized\n", op);
+         /* Don't struggle on, things will just get worse.  */
+         return expr_eval_unrecognized_opcode;
+       }
+
+      /* This catches errors that occur in target-specific code
+        emission.  */
+      if (emit_error)
+       {
+         ax_debug ("Error %d while emitting code for %s\n",
+                   emit_error, gdb_agent_op_name (op));
+         return expr_eval_unhandled_opcode;
+       }
+
+      ax_debug ("Op %s compiled\n", gdb_agent_op_name (op));
+    }
+
+  /* Now fill in real addresses as goto destinations.  */
+  for (aentry = bytecode_address_table; aentry; aentry = aentry->next)
+    {
+      int written = 0;
+
+      if (aentry->goto_pc < 0)
+       continue;
+
+      /* Find the location that we are going to, and call back into
+        target-specific code to write the actual address or
+        displacement.  */
+      for (aentry2 = bytecode_address_table; aentry2; aentry2 = aentry2->next)
+       {
+         if (aentry2->pc == aentry->goto_pc)
+           {
+             ax_debug ("Want to jump from %s to %s\n",
+                       paddress (aentry->address),
+                       paddress (aentry2->address));
+             write_goto_address (aentry->address + aentry->from_offset,
+                                 aentry2->address, aentry->from_size);
+             written = 1;
+             break;
+           }
+       }
+
+      /* Error out if we didn't find a destination.  */
+      if (!written)
+       {
+         ax_debug ("Destination of goto %d not found\n",
+                   aentry->goto_pc);
+         return expr_eval_invalid_goto;
+       }
+    }
+
+  return expr_eval_no_error;
+}
+
+#endif
+
+/* The agent expression evaluator, as specified by the GDB docs. It
+   returns 0 if everything went OK, and a nonzero error code
+   otherwise.  */
+
+enum eval_result_type
+gdb_eval_agent_expr (struct regcache *regcache,
+                    struct traceframe *tframe,
+                    struct agent_expr *aexpr,
+                    ULONGEST *rslt)
+{
+  int pc = 0;
+#define STACK_MAX 100
+  ULONGEST stack[STACK_MAX], top;
+  int sp = 0;
+  unsigned char op;
+  int arg;
+
+  /* This union is a convenient way to convert representations.  For
+     now, assume a standard architecture where the hardware integer
+     types have 8, 16, 32, 64 bit types.  A more robust solution would
+     be to import stdint.h from gnulib.  */
+  union
+  {
+    union
+    {
+      unsigned char bytes[1];
+      unsigned char val;
+    } u8;
+    union
+    {
+      unsigned char bytes[2];
+      unsigned short val;
+    } u16;
+    union
+    {
+      unsigned char bytes[4];
+      unsigned int val;
+    } u32;
+    union
+    {
+      unsigned char bytes[8];
+      ULONGEST val;
+    } u64;
+  } cnv;
+
+  if (aexpr->length == 0)
+    {
+      ax_debug ("empty agent expression");
+      return expr_eval_empty_expression;
+    }
+
+  /* Cache the stack top in its own variable. Much of the time we can
+     operate on this variable, rather than dinking with the stack. It
+     needs to be copied to the stack when sp changes.  */
+  top = 0;
+
+  while (1)
+    {
+      op = aexpr->bytes[pc++];
+
+      ax_debug ("About to interpret byte 0x%x", op);
+
+      switch (op)
+       {
+       case gdb_agent_op_add:
+         top += stack[--sp];
+         break;
+
+       case gdb_agent_op_sub:
+         top = stack[--sp] - top;
+         break;
+
+       case gdb_agent_op_mul:
+         top *= stack[--sp];
+         break;
+
+       case gdb_agent_op_div_signed:
+         if (top == 0)
+           {
+             ax_debug ("Attempted to divide by zero");
+             return expr_eval_divide_by_zero;
+           }
+         top = ((LONGEST) stack[--sp]) / ((LONGEST) top);
+         break;
+
+       case gdb_agent_op_div_unsigned:
+         if (top == 0)
+           {
+             ax_debug ("Attempted to divide by zero");
+             return expr_eval_divide_by_zero;
+           }
+         top = stack[--sp] / top;
+         break;
+
+       case gdb_agent_op_rem_signed:
+         if (top == 0)
+           {
+             ax_debug ("Attempted to divide by zero");
+             return expr_eval_divide_by_zero;
+           }
+         top = ((LONGEST) stack[--sp]) % ((LONGEST) top);
+         break;
+
+       case gdb_agent_op_rem_unsigned:
+         if (top == 0)
+           {
+             ax_debug ("Attempted to divide by zero");
+             return expr_eval_divide_by_zero;
+           }
+         top = stack[--sp] % top;
+         break;
+
+       case gdb_agent_op_lsh:
+         top = stack[--sp] << top;
+         break;
+
+       case gdb_agent_op_rsh_signed:
+         top = ((LONGEST) stack[--sp]) >> top;
+         break;
+
+       case gdb_agent_op_rsh_unsigned:
+         top = stack[--sp] >> top;
+         break;
+
+       case gdb_agent_op_trace:
+         agent_mem_read (tframe,
+                         NULL, (CORE_ADDR) stack[--sp], (ULONGEST) top);
+         if (--sp >= 0)
+           top = stack[sp];
+         break;
+
+       case gdb_agent_op_trace_quick:
+         arg = aexpr->bytes[pc++];
+         agent_mem_read (tframe, NULL, (CORE_ADDR) top, (ULONGEST) arg);
+         break;
+
+       case gdb_agent_op_log_not:
+         top = !top;
+         break;
+
+       case gdb_agent_op_bit_and:
+         top &= stack[--sp];
+         break;
+
+       case gdb_agent_op_bit_or:
+         top |= stack[--sp];
+         break;
+
+       case gdb_agent_op_bit_xor:
+         top ^= stack[--sp];
+         break;
+
+       case gdb_agent_op_bit_not:
+         top = ~top;
+         break;
+
+       case gdb_agent_op_equal:
+         top = (stack[--sp] == top);
+         break;
+
+       case gdb_agent_op_less_signed:
+         top = (((LONGEST) stack[--sp]) < ((LONGEST) top));
+         break;
+
+       case gdb_agent_op_less_unsigned:
+         top = (stack[--sp] < top);
+         break;
+
+       case gdb_agent_op_ext:
+         arg = aexpr->bytes[pc++];
+         if (arg < (sizeof (LONGEST) * 8))
+           {
+             LONGEST mask = 1 << (arg - 1);
+             top &= ((LONGEST) 1 << arg) - 1;
+             top = (top ^ mask) - mask;
+           }
+         break;
+
+       case gdb_agent_op_ref8:
+         agent_mem_read (tframe, cnv.u8.bytes, (CORE_ADDR) top, 1);
+         top = cnv.u8.val;
+         break;
+
+       case gdb_agent_op_ref16:
+         agent_mem_read (tframe, cnv.u16.bytes, (CORE_ADDR) top, 2);
+         top = cnv.u16.val;
+         break;
+
+       case gdb_agent_op_ref32:
+         agent_mem_read (tframe, cnv.u32.bytes, (CORE_ADDR) top, 4);
+         top = cnv.u32.val;
+         break;
+
+       case gdb_agent_op_ref64:
+         agent_mem_read (tframe, cnv.u64.bytes, (CORE_ADDR) top, 8);
+         top = cnv.u64.val;
+         break;
+
+       case gdb_agent_op_if_goto:
+         if (top)
+           pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
+         else
+           pc += 2;
+         if (--sp >= 0)
+           top = stack[sp];
+         break;
+
+       case gdb_agent_op_goto:
+         pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
+         break;
+
+       case gdb_agent_op_const8:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         top = aexpr->bytes[pc++];
+         break;
+
+       case gdb_agent_op_const16:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         break;
+
+       case gdb_agent_op_const32:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         break;
+
+       case gdb_agent_op_const64:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         top = aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         top = (top << 8) + aexpr->bytes[pc++];
+         break;
+
+       case gdb_agent_op_reg:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         {
+           int regnum = arg;
+
+           switch (register_size (regnum))
+             {
+             case 8:
+               collect_register (regcache, regnum, cnv.u64.bytes);
+               top = cnv.u64.val;
+               break;
+             case 4:
+               collect_register (regcache, regnum, cnv.u32.bytes);
+               top = cnv.u32.val;
+               break;
+             case 2:
+               collect_register (regcache, regnum, cnv.u16.bytes);
+               top = cnv.u16.val;
+               break;
+             case 1:
+               collect_register (regcache, regnum, cnv.u8.bytes);
+               top = cnv.u8.val;
+               break;
+             default:
+               internal_error (__FILE__, __LINE__,
+                               "unhandled register size");
+             }
+         }
+         break;
+
+       case gdb_agent_op_end:
+         ax_debug ("At end of expression, sp=%d, stack top cache=0x%s",
+                   sp, pulongest (top));
+         if (rslt)
+           {
+             if (sp <= 0)
+               {
+                 /* This should be an error */
+                 ax_debug ("Stack is empty, nothing to return");
+                 return expr_eval_empty_stack;
+               }
+             *rslt = top;
+           }
+         return expr_eval_no_error;
+
+       case gdb_agent_op_dup:
+         stack[sp++] = top;
+         break;
+
+       case gdb_agent_op_pop:
+         if (--sp >= 0)
+           top = stack[sp];
+         break;
+
+       case gdb_agent_op_pick:
+         arg = aexpr->bytes[pc++];
+         stack[sp] = top;
+         top = stack[sp - arg];
+         ++sp;
+         break;
+
+       case gdb_agent_op_rot:
+         {
+           ULONGEST tem = stack[sp - 1];
+
+           stack[sp - 1] = stack[sp - 2];
+           stack[sp - 2] = top;
+           top = tem;
+         }
+         break;
+
+       case gdb_agent_op_zero_ext:
+         arg = aexpr->bytes[pc++];
+         if (arg < (sizeof (LONGEST) * 8))
+           top &= ((LONGEST) 1 << arg) - 1;
+         break;
+
+       case gdb_agent_op_swap:
+         /* Interchange top two stack elements, making sure top gets
+            copied back onto stack.  */
+         stack[sp] = top;
+         top = stack[sp - 1];
+         stack[sp - 1] = stack[sp];
+         break;
+
+       case gdb_agent_op_getv:
+         /* Flush the cached stack top.  */
+         stack[sp++] = top;
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         top = agent_get_trace_state_variable_value (arg);
+         break;
+
+       case gdb_agent_op_setv:
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         agent_set_trace_state_variable_value (arg, top);
+         /* Note that we leave the value on the stack, for the
+            benefit of later/enclosing expressions.  */
+         break;
+
+       case gdb_agent_op_tracev:
+         arg = aexpr->bytes[pc++];
+         arg = (arg << 8) + aexpr->bytes[pc++];
+         agent_tsv_read (tframe, arg);
+         break;
+
+       case gdb_agent_op_tracenz:
+         agent_mem_read_string (tframe, NULL, (CORE_ADDR) stack[--sp],
+                                (ULONGEST) top);
+         if (--sp >= 0)
+           top = stack[sp];
+         break;
+
+         /* GDB never (currently) generates any of these ops.  */
+       case gdb_agent_op_float:
+       case gdb_agent_op_ref_float:
+       case gdb_agent_op_ref_double:
+       case gdb_agent_op_ref_long_double:
+       case gdb_agent_op_l_to_d:
+       case gdb_agent_op_d_to_l:
+       case gdb_agent_op_trace16:
+         ax_debug ("Agent expression op 0x%x valid, but not handled",
+                   op);
+         /* If ever GDB generates any of these, we don't have the
+            option of ignoring.  */
+         return 1;
+
+       default:
+         ax_debug ("Agent expression op 0x%x not recognized", op);
+         /* Don't struggle on, things will just get worse.  */
+         return expr_eval_unrecognized_opcode;
+       }
+
+      /* Check for stack badness.  */
+      if (sp >= (STACK_MAX - 1))
+       {
+         ax_debug ("Expression stack overflow");
+         return expr_eval_stack_overflow;
+       }
+
+      if (sp < 0)
+       {
+         ax_debug ("Expression stack underflow");
+         return expr_eval_stack_underflow;
+       }
+
+      ax_debug ("Op %s -> sp=%d, top=0x%s",
+               gdb_agent_op_name (op), sp, pulongest (top));
+    }
+}
diff --git a/gdb/gdbserver/ax.h b/gdb/gdbserver/ax.h
new file mode 100644 (file)
index 0000000..3ada3dd
--- /dev/null
@@ -0,0 +1,74 @@
+/* Data structures and functions associated with agent expressions in GDB.
+   Copyright (C) 2009-2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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; either version 3 of the License, or
+   (at your option) any later version.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#if !defined (AX_H)
+#define AX_H 1
+
+#include "server.h"
+#include "regcache.h"
+
+#ifdef IN_PROCESS_AGENT
+extern int debug_agent;
+#define debug_threads debug_agent
+#endif
+
+struct traceframe;
+
+/* Enumeration of the different kinds of things that can happen during
+   agent expression evaluation.  */
+
+enum eval_result_type
+  {
+    expr_eval_no_error,
+    expr_eval_empty_expression,
+    expr_eval_empty_stack,
+    expr_eval_stack_overflow,
+    expr_eval_stack_underflow,
+    expr_eval_unhandled_opcode,
+    expr_eval_unrecognized_opcode,
+    expr_eval_divide_by_zero,
+    expr_eval_invalid_goto
+  };
+
+struct agent_expr
+{
+  int length;
+
+  unsigned char *bytes;
+};
+
+#ifndef IN_PROCESS_AGENT
+
+/* The packet form of an agent expression consists of an 'X', number
+   of bytes in expression, a comma, and then the bytes.  */
+struct agent_expr *gdb_parse_agent_expr (char **actparm);
+
+/* Convert the bytes of an agent expression back into hex digits, so
+   they can be printed or uploaded.  This allocates the buffer,
+   callers should free when they are done with it.  */
+char *gdb_unparse_agent_expr (struct agent_expr *aexpr);
+void emit_prologue (void);
+void emit_epilogue (void);
+enum eval_result_type compile_bytecodes (struct agent_expr *aexpr);
+#endif
+
+enum eval_result_type gdb_eval_agent_expr (struct regcache *regcache,
+                                          struct traceframe *tframe,
+                                          struct agent_expr *aexpr,
+                                          ULONGEST *rslt);
+#endif /* AX_H */