| /* Agent expression code for remote server. | 
 |    Copyright (C) 2009-2024 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 "ax.h" | 
 | #include "gdbsupport/format.h" | 
 | #include "tracepoint.h" | 
 | #include "gdbsupport/rsp-low.h" | 
 |  | 
 | static void ax_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2); | 
 |  | 
 | #ifdef IN_PROCESS_AGENT | 
 | bool 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); | 
 | #ifdef IN_PROCESS_AGENT | 
 |   fprintf (stderr, PROG "/ax: %s\n", buf); | 
 | #else | 
 |   threads_debug_printf (PROG "/ax: %s", buf); | 
 | #endif | 
 |   va_end (ap); | 
 | } | 
 |  | 
 | #define ax_debug(fmt, args...) \ | 
 |   do {						\ | 
 |     if (debug_threads)			\ | 
 |       ax_vdebug ((fmt), ##args);		\ | 
 |   } while (0) | 
 |  | 
 | /* 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 "gdbsupport/ax.def" | 
 | #undef DEFOP | 
 |     gdb_agent_op_last | 
 |   }; | 
 |  | 
 | static const char * const gdb_agent_op_names [gdb_agent_op_last] = | 
 |   { | 
 |     "?undef?" | 
 | #define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE)  , # NAME | 
 | #include "gdbsupport/ax.def" | 
 | #undef DEFOP | 
 |   }; | 
 |  | 
 | #ifndef IN_PROCESS_AGENT | 
 | static const unsigned char gdb_agent_op_sizes [gdb_agent_op_last] = | 
 |   { | 
 |     0 | 
 | #define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE)  , SIZE | 
 | #include "gdbsupport/ax.def" | 
 | #undef DEFOP | 
 |   }; | 
 | #endif | 
 |  | 
 | /* 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 (const char **actparm) | 
 | { | 
 |   const char *act = *actparm; | 
 |   ULONGEST xlen; | 
 |   struct agent_expr *aexpr; | 
 |  | 
 |   ++act;  /* skip the X */ | 
 |   act = unpack_varlen_hex (act, &xlen); | 
 |   ++act;  /* skip a comma */ | 
 |   aexpr = XNEW (struct agent_expr); | 
 |   aexpr->length = xlen; | 
 |   aexpr->bytes = (unsigned char *) xmalloc (xlen); | 
 |   hex2bin (act, aexpr->bytes, xlen); | 
 |   *actparm = act + (xlen * 2); | 
 |   return aexpr; | 
 | } | 
 |  | 
 | void | 
 | gdb_free_agent_expr (struct agent_expr *aexpr) | 
 | { | 
 |   if (aexpr != NULL) | 
 |     { | 
 |       free (aexpr->bytes); | 
 |       free (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 = (char *) xmalloc (2 * aexpr->length + 1); | 
 |   bin2hex (aexpr->bytes, rslt, aexpr->length); | 
 |   return rslt; | 
 | } | 
 |  | 
 | /* Bytecode compilation.  */ | 
 |  | 
 | CORE_ADDR current_insn_ptr; | 
 |  | 
 | int emit_error; | 
 |  | 
 | static 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.  */ | 
 |  | 
 | static 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 = XNEW (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 | 
 |  | 
 | /* Make printf-type calls using arguments supplied from the host.  We | 
 |    need to parse the format string ourselves, and call the formatting | 
 |    function with one argument at a time, partly because there is no | 
 |    safe portable way to construct a varargs call, and partly to serve | 
 |    as a security barrier against bad format strings that might get | 
 |    in.  */ | 
 |  | 
 | static void | 
 | ax_printf (CORE_ADDR fn, CORE_ADDR chan, const char *format, | 
 | 	   int nargs, ULONGEST *args) | 
 | { | 
 |   const char *f = format; | 
 |   int i; | 
 |   const char *current_substring; | 
 |   int nargs_wanted; | 
 |  | 
 |   ax_debug ("Printf of \"%s\" with %d args", format, nargs); | 
 |  | 
 |   format_pieces fpieces (&f); | 
 |  | 
 |   nargs_wanted = 0; | 
 |   for (auto &&piece : fpieces) | 
 |     if (piece.argclass != literal_piece) | 
 |       ++nargs_wanted; | 
 |  | 
 |   if (nargs != nargs_wanted) | 
 |     error (_("Wrong number of arguments for specified format-string")); | 
 |  | 
 |   i = 0; | 
 |   for (auto &&piece : fpieces) | 
 |     { | 
 |       current_substring = piece.string; | 
 |       ax_debug ("current substring is '%s', class is %d", | 
 | 		current_substring, piece.argclass); | 
 |       switch (piece.argclass) | 
 | 	{ | 
 | 	case string_arg: | 
 | 	  { | 
 | 	    gdb_byte *str; | 
 | 	    CORE_ADDR tem; | 
 | 	    int j; | 
 |  | 
 | 	    tem = args[i]; | 
 | 	    if (tem == 0) | 
 | 	      { | 
 | 		printf (current_substring, "(null)"); | 
 | 		break; | 
 | 	      } | 
 |  | 
 | 	    /* This is a %s argument.  Find the length of the string.  */ | 
 | 	    for (j = 0;; j++) | 
 | 	      { | 
 | 		gdb_byte c; | 
 |  | 
 | 		read_inferior_memory (tem + j, &c, 1); | 
 | 		if (c == 0) | 
 | 		  break; | 
 | 	      } | 
 |  | 
 | 	      /* Copy the string contents into a string inside GDB.  */ | 
 | 	      str = (gdb_byte *) alloca (j + 1); | 
 | 	      if (j != 0) | 
 | 		read_inferior_memory (tem, str, j); | 
 | 	      str[j] = 0; | 
 |  | 
 | 	      printf (current_substring, (char *) str); | 
 | 	    } | 
 | 	    break; | 
 |  | 
 | 	  case long_long_arg: | 
 | #if defined (PRINTF_HAS_LONG_LONG) | 
 | 	    { | 
 | 	      long long val = args[i]; | 
 |  | 
 | 	      printf (current_substring, val); | 
 | 	      break; | 
 | 	    } | 
 | #else | 
 | 	    error (_("long long not supported in agent printf")); | 
 | #endif | 
 | 	case int_arg: | 
 | 	  { | 
 | 	    int val = args[i]; | 
 |  | 
 | 	    printf (current_substring, val); | 
 | 	    break; | 
 | 	  } | 
 |  | 
 | 	case long_arg: | 
 | 	  { | 
 | 	    long val = args[i]; | 
 |  | 
 | 	    printf (current_substring, val); | 
 | 	    break; | 
 | 	  } | 
 |  | 
 | 	case size_t_arg: | 
 | 	  { | 
 | 	    size_t val = args[i]; | 
 |  | 
 | 	    printf (current_substring, val); | 
 | 	    break; | 
 | 	  } | 
 |  | 
 | 	case literal_piece: | 
 | 	  /* Print a portion of the format string that has no | 
 | 	     directives.  Note that this will not include any | 
 | 	     ordinary %-specs, but it might include "%%".  That is | 
 | 	     why we use printf_filtered and not puts_filtered here. | 
 | 	     Also, we pass a dummy argument because some platforms | 
 | 	     have modified GCC to include -Wformat-security by | 
 | 	     default, which will warn here if there is no | 
 | 	     argument.  */ | 
 | 	  printf (current_substring, 0); | 
 | 	  break; | 
 |  | 
 | 	default: | 
 | 	  error (_("Format directive in '%s' not supported in agent printf"), | 
 | 		 current_substring); | 
 | 	} | 
 |  | 
 |       /* Maybe advance to the next argument.  */ | 
 |       if (piece.argclass != literal_piece) | 
 | 	++i; | 
 |     } | 
 |  | 
 |   fflush (stdout); | 
 | } | 
 |  | 
 | /* 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 eval_agent_expr_context *ctx, | 
 | 		     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 syncing 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 (ctx, 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 (ctx, 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: | 
 | 	  if (agent_mem_read (ctx, cnv.u8.bytes, (CORE_ADDR) top, 1) != 0) | 
 | 	    return expr_eval_invalid_memory_access; | 
 | 	  top = cnv.u8.val; | 
 | 	  break; | 
 |  | 
 | 	case gdb_agent_op_ref16: | 
 | 	  if (agent_mem_read (ctx, cnv.u16.bytes, (CORE_ADDR) top, 2) != 0) | 
 | 	    return expr_eval_invalid_memory_access; | 
 | 	  top = cnv.u16.val; | 
 | 	  break; | 
 |  | 
 | 	case gdb_agent_op_ref32: | 
 | 	  if (agent_mem_read (ctx, cnv.u32.bytes, (CORE_ADDR) top, 4) != 0) | 
 | 	    return expr_eval_invalid_memory_access; | 
 | 	  top = cnv.u32.val; | 
 | 	  break; | 
 |  | 
 | 	case gdb_agent_op_ref64: | 
 | 	  if (agent_mem_read (ctx, cnv.u64.bytes, (CORE_ADDR) top, 8) != 0) | 
 | 	    return expr_eval_invalid_memory_access; | 
 | 	  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; | 
 | 	    struct regcache *regcache = ctx->regcache; | 
 |  | 
 | 	    switch (register_size (regcache->tdesc, 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 ("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 (ctx, arg); | 
 | 	  break; | 
 |  | 
 | 	case gdb_agent_op_tracenz: | 
 | 	  agent_mem_read_string (ctx, NULL, (CORE_ADDR) stack[--sp], | 
 | 				 (ULONGEST) top); | 
 | 	  if (--sp >= 0) | 
 | 	    top = stack[sp]; | 
 | 	  break; | 
 |  | 
 | 	case gdb_agent_op_printf: | 
 | 	  { | 
 | 	    int nargs, slen, i; | 
 | 	    CORE_ADDR fn = 0, chan = 0; | 
 | 	    /* Can't have more args than the entire size of the stack.  */ | 
 | 	    ULONGEST args[STACK_MAX]; | 
 | 	    char *format; | 
 |  | 
 | 	    nargs = aexpr->bytes[pc++]; | 
 | 	    slen = aexpr->bytes[pc++]; | 
 | 	    slen = (slen << 8) + aexpr->bytes[pc++]; | 
 | 	    format = (char *) &(aexpr->bytes[pc]); | 
 | 	    pc += slen; | 
 | 	    /* Pop function and channel.  */ | 
 | 	    fn = top; | 
 | 	    if (--sp >= 0) | 
 | 	      top = stack[sp]; | 
 | 	    chan = top; | 
 | 	    if (--sp >= 0) | 
 | 	      top = stack[sp]; | 
 | 	    /* Pop arguments into a dedicated array.  */ | 
 | 	    for (i = 0; i < nargs; ++i) | 
 | 	      { | 
 | 		args[i] = top; | 
 | 		if (--sp >= 0) | 
 | 		  top = stack[sp]; | 
 | 	      } | 
 |  | 
 | 	    /* A bad format string means something is very wrong; give | 
 | 	       up immediately.  */ | 
 | 	    if (format[slen - 1] != '\0') | 
 | 	      error (_("Unterminated format string in printf bytecode")); | 
 |  | 
 | 	    ax_printf (fn, chan, format, nargs, args); | 
 | 	  } | 
 | 	  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 expr_eval_unhandled_opcode; | 
 |  | 
 | 	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, phex_nz (top, 0)); | 
 |     } | 
 | } |