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