| /* Functions for manipulating expressions designed to be executed on the agent | 
 |    Copyright (C) 1998-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/>.  */ | 
 |  | 
 | /* Despite what the above comment says about this file being part of | 
 |    GDB, we would like to keep these functions free of GDB | 
 |    dependencies, since we want to be able to use them in contexts | 
 |    outside of GDB (test suites, the stub, etc.)  */ | 
 |  | 
 | #include "ax.h" | 
 | #include "gdbarch.h" | 
 |  | 
 | #include "value.h" | 
 | #include "user-regs.h" | 
 |  | 
 | static void append_const (struct agent_expr *x, LONGEST val, int n); | 
 |  | 
 | static LONGEST read_const (struct agent_expr *x, int o, int n); | 
 |  | 
 | static void generic_ext (struct agent_expr *x, enum agent_op op, int n); | 
 |  | 
 | /* Functions for building expressions.  */ | 
 |  | 
 | /* Append the low N bytes of VAL as an N-byte integer to the | 
 |    expression X, in big-endian order.  */ | 
 | static void | 
 | append_const (struct agent_expr *x, LONGEST val, int n) | 
 | { | 
 |   size_t len = x->buf.size (); | 
 |   x->buf.resize (len + n); | 
 |   for (int i = n - 1; i >= 0; i--) | 
 |     { | 
 |       x->buf[len + i] = val & 0xff; | 
 |       val >>= 8; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /* Extract an N-byte big-endian unsigned integer from expression X at | 
 |    offset O.  */ | 
 | static LONGEST | 
 | read_const (struct agent_expr *x, int o, int n) | 
 | { | 
 |   int i; | 
 |   LONGEST accum = 0; | 
 |  | 
 |   /* Make sure we're not reading off the end of the expression.  */ | 
 |   if (o + n > x->buf.size ()) | 
 |     error (_("GDB bug: ax-general.c (read_const): incomplete constant")); | 
 |  | 
 |   for (i = 0; i < n; i++) | 
 |     accum = (accum << 8) | x->buf[o + i]; | 
 |  | 
 |   return accum; | 
 | } | 
 |  | 
 | /* See ax.h.  */ | 
 |  | 
 | void | 
 | ax_raw_byte (struct agent_expr *x, gdb_byte byte) | 
 | { | 
 |   x->buf.push_back (byte); | 
 | } | 
 |  | 
 | /* Append a simple operator OP to EXPR.  */ | 
 | void | 
 | ax_simple (struct agent_expr *x, enum agent_op op) | 
 | { | 
 |   ax_raw_byte (x, op); | 
 | } | 
 |  | 
 | /* Append a pick operator to EXPR.  DEPTH is the stack item to pick, | 
 |    with 0 being top of stack.  */ | 
 |  | 
 | void | 
 | ax_pick (struct agent_expr *x, int depth) | 
 | { | 
 |   if (depth < 0 || depth > 255) | 
 |     error (_("GDB bug: ax-general.c (ax_pick): stack depth out of range")); | 
 |   ax_simple (x, aop_pick); | 
 |   append_const (x, 1, depth); | 
 | } | 
 |  | 
 |  | 
 | /* Append a sign-extension or zero-extension instruction to EXPR, to | 
 |    extend an N-bit value.  */ | 
 | static void | 
 | generic_ext (struct agent_expr *x, enum agent_op op, int n) | 
 | { | 
 |   /* N must fit in a byte.  */ | 
 |   if (n < 0 || n > 255) | 
 |     error (_("GDB bug: ax-general.c (generic_ext): bit count out of range")); | 
 |   /* That had better be enough range.  */ | 
 |   if (sizeof (LONGEST) * 8 > 255) | 
 |     error (_("GDB bug: ax-general.c (generic_ext): " | 
 | 	     "opcode has inadequate range")); | 
 |  | 
 |   x->buf.push_back (op); | 
 |   x->buf.push_back (n); | 
 | } | 
 |  | 
 |  | 
 | /* Append a sign-extension instruction to EXPR, to extend an N-bit value.  */ | 
 | void | 
 | ax_ext (struct agent_expr *x, int n) | 
 | { | 
 |   generic_ext (x, aop_ext, n); | 
 | } | 
 |  | 
 |  | 
 | /* Append a zero-extension instruction to EXPR, to extend an N-bit value.  */ | 
 | void | 
 | ax_zero_ext (struct agent_expr *x, int n) | 
 | { | 
 |   generic_ext (x, aop_zero_ext, n); | 
 | } | 
 |  | 
 |  | 
 | /* Append a trace_quick instruction to EXPR, to record N bytes.  */ | 
 | void | 
 | ax_trace_quick (struct agent_expr *x, int n) | 
 | { | 
 |   /* N must fit in a byte.  */ | 
 |   if (n < 0 || n > 255) | 
 |     error (_("GDB bug: ax-general.c (ax_trace_quick): " | 
 | 	     "size out of range for trace_quick")); | 
 |  | 
 |   x->buf.push_back (aop_trace_quick); | 
 |   x->buf.push_back (n); | 
 | } | 
 |  | 
 |  | 
 | /* Append a goto op to EXPR.  OP is the actual op (must be aop_goto or | 
 |    aop_if_goto).  We assume we don't know the target offset yet, | 
 |    because it's probably a forward branch, so we leave space in EXPR | 
 |    for the target, and return the offset in EXPR of that space, so we | 
 |    can backpatch it once we do know the target offset.  Use ax_label | 
 |    to do the backpatching.  */ | 
 | int | 
 | ax_goto (struct agent_expr *x, enum agent_op op) | 
 | { | 
 |   x->buf.push_back (op); | 
 |   x->buf.push_back (0xff); | 
 |   x->buf.push_back (0xff); | 
 |   return x->buf.size () - 2; | 
 | } | 
 |  | 
 | /* Suppose a given call to ax_goto returns some value PATCH.  When you | 
 |    know the offset TARGET that goto should jump to, call | 
 |    ax_label (EXPR, PATCH, TARGET) | 
 |    to patch TARGET into the ax_goto instruction.  */ | 
 | void | 
 | ax_label (struct agent_expr *x, int patch, int target) | 
 | { | 
 |   /* Make sure the value is in range.  Don't accept 0xffff as an | 
 |      offset; that's our magic sentinel value for unpatched branches.  */ | 
 |   if (target < 0 || target >= 0xffff) | 
 |     error (_("GDB bug: ax-general.c (ax_label): label target out of range")); | 
 |  | 
 |   x->buf[patch] = (target >> 8) & 0xff; | 
 |   x->buf[patch + 1] = target & 0xff; | 
 | } | 
 |  | 
 |  | 
 | /* Assemble code to push a constant on the stack.  */ | 
 | void | 
 | ax_const_l (struct agent_expr *x, LONGEST l) | 
 | { | 
 |   static enum agent_op ops[] | 
 |   = | 
 |   {aop_const8, aop_const16, aop_const32, aop_const64}; | 
 |   int size; | 
 |   int op; | 
 |  | 
 |   /* How big is the number?  'op' keeps track of which opcode to use. | 
 |      Notice that we don't really care whether the original number was | 
 |      signed or unsigned; we always reproduce the value exactly, and | 
 |      use the shortest representation.  */ | 
 |   for (op = 0, size = 8; size < 64; size *= 2, op++) | 
 |     { | 
 |       LONGEST lim = ((LONGEST) 1) << (size - 1); | 
 |  | 
 |       if (-lim <= l && l <= lim - 1) | 
 | 	break; | 
 |     } | 
 |  | 
 |   /* Emit the right opcode...  */ | 
 |   ax_simple (x, ops[op]); | 
 |  | 
 |   /* Emit the low SIZE bytes as an unsigned number.  We know that | 
 |      sign-extending this will yield l.  */ | 
 |   append_const (x, l, size / 8); | 
 |  | 
 |   /* Now, if it was negative, and not full-sized, sign-extend it.  */ | 
 |   if (l < 0 && size < 64) | 
 |     ax_ext (x, size); | 
 | } | 
 |  | 
 |  | 
 | void | 
 | ax_const_d (struct agent_expr *x, LONGEST d) | 
 | { | 
 |   /* FIXME: floating-point support not present yet.  */ | 
 |   error (_("GDB bug: ax-general.c (ax_const_d): " | 
 | 	   "floating point not supported yet")); | 
 | } | 
 |  | 
 |  | 
 | /* Assemble code to push the value of register number REG on the | 
 |    stack.  */ | 
 | void | 
 | ax_reg (struct agent_expr *x, int reg) | 
 | { | 
 |   if (reg >= gdbarch_num_regs (x->gdbarch)) | 
 |     { | 
 |       /* This is a pseudo-register.  */ | 
 |       if (!gdbarch_ax_pseudo_register_push_stack_p (x->gdbarch)) | 
 | 	error (_("'%s' is a pseudo-register; " | 
 | 		 "GDB cannot yet trace its contents."), | 
 | 	       user_reg_map_regnum_to_name (x->gdbarch, reg)); | 
 |       if (gdbarch_ax_pseudo_register_push_stack (x->gdbarch, x, reg)) | 
 | 	error (_("Trace '%s' failed."), | 
 | 	       user_reg_map_regnum_to_name (x->gdbarch, reg)); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Get the remote register number.  */ | 
 |       reg = gdbarch_remote_register_number (x->gdbarch, reg); | 
 |  | 
 |       /* Make sure the register number is in range.  */ | 
 |       if (reg < 0 || reg > 0xffff) | 
 | 	error (_("GDB bug: ax-general.c (ax_reg): " | 
 | 		 "register number out of range")); | 
 |       x->buf.push_back (aop_reg); | 
 |       x->buf.push_back ((reg >> 8) & 0xff); | 
 |       x->buf.push_back ((reg) & 0xff); | 
 |     } | 
 | } | 
 |  | 
 | /* Assemble code to operate on a trace state variable.  */ | 
 |  | 
 | void | 
 | ax_tsv (struct agent_expr *x, enum agent_op op, int num) | 
 | { | 
 |   /* Make sure the tsv number is in range.  */ | 
 |   if (num < 0 || num > 0xffff) | 
 |     internal_error (_("ax-general.c (ax_tsv): variable " | 
 | 		      "number is %d, out of range"), num); | 
 |  | 
 |   x->buf.push_back (op); | 
 |   x->buf.push_back ((num >> 8) & 0xff); | 
 |   x->buf.push_back ((num) & 0xff); | 
 | } | 
 |  | 
 | /* Append a string to the expression.  Note that the string is going | 
 |    into the bytecodes directly, not on the stack.  As a precaution, | 
 |    include both length as prefix, and terminate with a NUL.  (The NUL | 
 |    is counted in the length.)  */ | 
 |  | 
 | void | 
 | ax_string (struct agent_expr *x, const char *str, int slen) | 
 | { | 
 |   int i; | 
 |  | 
 |   /* Make sure the string length is reasonable.  */ | 
 |   if (slen < 0 || slen > 0xffff) | 
 |     internal_error (_("ax-general.c (ax_string): string " | 
 | 		      "length is %d, out of allowed range"), slen); | 
 |  | 
 |   x->buf.push_back (((slen + 1) >> 8) & 0xff); | 
 |   x->buf.push_back ((slen + 1) & 0xff); | 
 |   for (i = 0; i < slen; ++i) | 
 |     x->buf.push_back (str[i]); | 
 |   x->buf.push_back ('\0'); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* Functions for disassembling agent expressions, and otherwise | 
 |    debugging the expression compiler.  */ | 
 |  | 
 | /* An entry in the opcode map.  */ | 
 | struct aop_map | 
 |   { | 
 |  | 
 |     /* The name of the opcode.  Null means that this entry is not a | 
 |        valid opcode --- a hole in the opcode space.  */ | 
 |     const char *name; | 
 |  | 
 |     /* All opcodes take no operands from the bytecode stream, or take | 
 |        unsigned integers of various sizes.  If this is a positive number | 
 |        n, then the opcode is followed by an n-byte operand, which should | 
 |        be printed as an unsigned integer.  If this is zero, then the | 
 |        opcode takes no operands from the bytecode stream. | 
 |  | 
 |        If we get more complicated opcodes in the future, don't add other | 
 |        magic values of this; that's a crock.  Add an `enum encoding' | 
 |        field to this, or something like that.  */ | 
 |     int op_size; | 
 |  | 
 |     /* The size of the data operated upon, in bits, for bytecodes that | 
 |        care about that (ref and const).  Zero for all others.  */ | 
 |     int data_size; | 
 |  | 
 |     /* Number of stack elements consumed, and number produced.  */ | 
 |     int consumed, produced; | 
 |   }; | 
 |  | 
 | /* Map of the bytecodes, indexed by bytecode number.  */ | 
 |  | 
 | static struct aop_map aop_map[] = | 
 | { | 
 |   {0, 0, 0, 0, 0} | 
 | #define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) \ | 
 |   , { # NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED } | 
 | #include "gdbsupport/ax.def" | 
 | #undef DEFOP | 
 | }; | 
 |  | 
 |  | 
 | /* Disassemble the expression EXPR, writing to F.  */ | 
 | void | 
 | ax_print (struct ui_file *f, struct agent_expr *x) | 
 | { | 
 |   int i; | 
 |  | 
 |   gdb_printf (f, _("Scope: %s\n"), paddress (x->gdbarch, x->scope)); | 
 |   gdb_printf (f, _("Reg mask:")); | 
 |   for (i = 0; i < x->reg_mask.size (); ++i) | 
 |     { | 
 |       if ((i % 8) == 0) | 
 | 	gdb_printf (f, " "); | 
 |       gdb_printf (f, _("%d"), (int) x->reg_mask[i]); | 
 |     } | 
 |   gdb_printf (f, _("\n")); | 
 |  | 
 |   for (i = 0; i < x->buf.size ();) | 
 |     { | 
 |       enum agent_op op = (enum agent_op) x->buf[i]; | 
 |  | 
 |       if (op >= ARRAY_SIZE (aop_map) || aop_map[op].name == nullptr) | 
 | 	{ | 
 | 	  gdb_printf (f, _("%3d  <bad opcode %02x>\n"), i, op); | 
 | 	  i++; | 
 | 	  continue; | 
 | 	} | 
 |       if (i + 1 + aop_map[op].op_size > x->buf.size ()) | 
 | 	{ | 
 | 	  gdb_printf (f, _("%3d  <incomplete opcode %s>\n"), | 
 | 		      i, aop_map[op].name); | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       gdb_printf (f, "%3d  %s", i, aop_map[op].name); | 
 |       if (aop_map[op].op_size > 0) | 
 | 	{ | 
 | 	  gdb_puts (" ", f); | 
 |  | 
 | 	  print_longest (f, 'd', 0, | 
 | 			 read_const (x, i + 1, aop_map[op].op_size)); | 
 | 	} | 
 |       /* Handle the complicated printf arguments specially.  */ | 
 |       else if (op == aop_printf) | 
 | 	{ | 
 | 	  int slen, nargs; | 
 |  | 
 | 	  i++; | 
 | 	  nargs = x->buf[i++]; | 
 | 	  slen = x->buf[i++]; | 
 | 	  slen = slen * 256 + x->buf[i++]; | 
 | 	  gdb_printf (f, _(" \"%s\", %d args"), | 
 | 		      &(x->buf[i]), nargs); | 
 | 	  i += slen - 1; | 
 | 	} | 
 |       gdb_printf (f, "\n"); | 
 |       i += 1 + aop_map[op].op_size; | 
 |     } | 
 | } | 
 |  | 
 | /* Add register REG to the register mask for expression AX.  */ | 
 | void | 
 | ax_reg_mask (struct agent_expr *ax, int reg) | 
 | { | 
 |   if (reg >= gdbarch_num_regs (ax->gdbarch)) | 
 |     { | 
 |       /* This is a pseudo-register.  */ | 
 |       if (!gdbarch_ax_pseudo_register_collect_p (ax->gdbarch)) | 
 | 	error (_("'%s' is a pseudo-register; " | 
 | 		 "GDB cannot yet trace its contents."), | 
 | 	       user_reg_map_regnum_to_name (ax->gdbarch, reg)); | 
 |       if (gdbarch_ax_pseudo_register_collect (ax->gdbarch, ax, reg)) | 
 | 	error (_("Trace '%s' failed."), | 
 | 	       user_reg_map_regnum_to_name (ax->gdbarch, reg)); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Get the remote register number.  */ | 
 |       reg = gdbarch_remote_register_number (ax->gdbarch, reg); | 
 |  | 
 |       /* Grow the bit mask if necessary.  */ | 
 |       if (reg >= ax->reg_mask.size ()) | 
 | 	ax->reg_mask.resize (reg + 1); | 
 |  | 
 |       ax->reg_mask[reg] = true; | 
 |     } | 
 | } | 
 |  | 
 | /* Given an agent expression AX, fill in requirements and other descriptive | 
 |    bits.  */ | 
 | void | 
 | ax_reqs (struct agent_expr *ax) | 
 | { | 
 |   int i; | 
 |   int height; | 
 |  | 
 |   /* Jump target table.  targets[i] is non-zero iff we have found a | 
 |      jump to offset i.  */ | 
 |   char *targets = (char *) alloca (ax->buf.size () * sizeof (targets[0])); | 
 |  | 
 |   /* Instruction boundary table.  boundary[i] is non-zero iff our scan | 
 |      has reached an instruction starting at offset i.  */ | 
 |   char *boundary = (char *) alloca (ax->buf.size () * sizeof (boundary[0])); | 
 |  | 
 |   /* Stack height record.  If either targets[i] or boundary[i] is | 
 |      non-zero, heights[i] is the height the stack should have before | 
 |      executing the bytecode at that point.  */ | 
 |   int *heights = (int *) alloca (ax->buf.size () * sizeof (heights[0])); | 
 |  | 
 |   /* Pointer to a description of the present op.  */ | 
 |   struct aop_map *op; | 
 |  | 
 |   memset (targets, 0, ax->buf.size () * sizeof (targets[0])); | 
 |   memset (boundary, 0, ax->buf.size () * sizeof (boundary[0])); | 
 |  | 
 |   ax->max_height = ax->min_height = height = 0; | 
 |   ax->flaw = agent_flaw_none; | 
 |   ax->max_data_size = 0; | 
 |  | 
 |   for (i = 0; i < ax->buf.size (); i += 1 + op->op_size) | 
 |     { | 
 |       if (ax->buf[i] >= ARRAY_SIZE (aop_map)) | 
 | 	{ | 
 | 	  ax->flaw = agent_flaw_bad_instruction; | 
 | 	  return; | 
 | 	} | 
 |  | 
 |       op = &aop_map[ax->buf[i]]; | 
 |  | 
 |       if (!op->name) | 
 | 	{ | 
 | 	  ax->flaw = agent_flaw_bad_instruction; | 
 | 	  return; | 
 | 	} | 
 |  | 
 |       if (i + 1 + op->op_size > ax->buf.size ()) | 
 | 	{ | 
 | 	  ax->flaw = agent_flaw_incomplete_instruction; | 
 | 	  return; | 
 | 	} | 
 |  | 
 |       /* If this instruction is a forward jump target, does the | 
 | 	 current stack height match the stack height at the jump | 
 | 	 source?  */ | 
 |       if (targets[i] && (heights[i] != height)) | 
 | 	{ | 
 | 	  ax->flaw = agent_flaw_height_mismatch; | 
 | 	  return; | 
 | 	} | 
 |  | 
 |       boundary[i] = 1; | 
 |       heights[i] = height; | 
 |  | 
 |       height -= op->consumed; | 
 |       if (height < ax->min_height) | 
 | 	ax->min_height = height; | 
 |       height += op->produced; | 
 |       if (height > ax->max_height) | 
 | 	ax->max_height = height; | 
 |  | 
 |       if (op->data_size > ax->max_data_size) | 
 | 	ax->max_data_size = op->data_size; | 
 |  | 
 |       /* For jump instructions, check that the target is a valid | 
 | 	 offset.  If it is, record the fact that that location is a | 
 | 	 jump target, and record the height we expect there.  */ | 
 |       if (aop_goto == op - aop_map | 
 | 	  || aop_if_goto == op - aop_map) | 
 | 	{ | 
 | 	  int target = read_const (ax, i + 1, 2); | 
 | 	  if (target < 0 || target >= ax->buf.size ()) | 
 | 	    { | 
 | 	      ax->flaw = agent_flaw_bad_jump; | 
 | 	      return; | 
 | 	    } | 
 |  | 
 | 	  /* Do we have any information about what the stack height | 
 | 	     should be at the target?  */ | 
 | 	  if (targets[target] || boundary[target]) | 
 | 	    { | 
 | 	      if (heights[target] != height) | 
 | 		{ | 
 | 		  ax->flaw = agent_flaw_height_mismatch; | 
 | 		  return; | 
 | 		} | 
 | 	    } | 
 |  | 
 | 	  /* Record the target, along with the stack height we expect.  */ | 
 | 	  targets[target] = 1; | 
 | 	  heights[target] = height; | 
 | 	} | 
 |  | 
 |       /* For unconditional jumps with a successor, check that the | 
 | 	 successor is a target, and pick up its stack height.  */ | 
 |       if (aop_goto == op - aop_map | 
 | 	  && i + 3 < ax->buf.size ()) | 
 | 	{ | 
 | 	  if (!targets[i + 3]) | 
 | 	    { | 
 | 	      ax->flaw = agent_flaw_hole; | 
 | 	      return; | 
 | 	    } | 
 |  | 
 | 	  height = heights[i + 3]; | 
 | 	} | 
 |  | 
 |       /* For reg instructions, record the register in the bit mask.  */ | 
 |       if (aop_reg == op - aop_map) | 
 | 	{ | 
 | 	  int reg = read_const (ax, i + 1, 2); | 
 |  | 
 | 	  ax_reg_mask (ax, reg); | 
 | 	} | 
 |     } | 
 |  | 
 |   /* Check that all the targets are on boundaries.  */ | 
 |   for (i = 0; i < ax->buf.size (); i++) | 
 |     if (targets[i] && !boundary[i]) | 
 |       { | 
 | 	ax->flaw = agent_flaw_bad_jump; | 
 | 	return; | 
 |       } | 
 |  | 
 |   ax->final_height = height; | 
 | } |