|  | /* Agent expression code for remote server. | 
|  | Copyright (C) 2009-2023 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" | 
|  | #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)); | 
|  | } | 
|  | } |