diff options
Diffstat (limited to 'gdbserver/ax.c')
-rw-r--r-- | gdbserver/ax.c | 1376 |
1 files changed, 1376 insertions, 0 deletions
diff --git a/gdbserver/ax.c b/gdbserver/ax.c new file mode 100644 index 0000000..213db41 --- /dev/null +++ b/gdbserver/ax.c @@ -0,0 +1,1376 @@ +/* Agent expression code for remote server. + Copyright (C) 2009-2020 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 +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); +#ifdef IN_PROCESS_AGENT + fprintf (stderr, PROG "/ax: %s\n", buf); +#else + debug_printf (PROG "/ax: %s\n", buf); +#endif + 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 "gdbsupport/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 "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; + +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 (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 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 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 (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: + agent_mem_read (ctx, cnv.u8.bytes, (CORE_ADDR) top, 1); + top = cnv.u8.val; + break; + + case gdb_agent_op_ref16: + agent_mem_read (ctx, cnv.u16.bytes, (CORE_ADDR) top, 2); + top = cnv.u16.val; + break; + + case gdb_agent_op_ref32: + agent_mem_read (ctx, cnv.u32.bytes, (CORE_ADDR) top, 4); + top = cnv.u32.val; + break; + + case gdb_agent_op_ref64: + agent_mem_read (ctx, 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; + 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 (__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 (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)); + } +} |