diff options
author | Luis Machado <luisgpm@br.ibm.com> | 2012-02-24 15:14:47 +0000 |
---|---|---|
committer | Luis Machado <luisgpm@br.ibm.com> | 2012-02-24 15:14:47 +0000 |
commit | 9f14eebc6a9d99804ad44f4f6084b7f279f488df (patch) | |
tree | 0a84eed27c1f9263ef37218f4746f8e48034e315 /gdb | |
parent | 5e1dc496746ac407cad3b99d2a89e776d08ee326 (diff) | |
download | gdb-9f14eebc6a9d99804ad44f4f6084b7f279f488df.zip gdb-9f14eebc6a9d99804ad44f4f6084b7f279f488df.tar.gz gdb-9f14eebc6a9d99804ad44f4f6084b7f279f488df.tar.bz2 |
Add missing files from previous commit.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/gdbserver/ax.c | 1191 | ||||
-rw-r--r-- | gdb/gdbserver/ax.h | 74 |
2 files changed, 1265 insertions, 0 deletions
diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c new file mode 100644 index 0000000..4eba27b --- /dev/null +++ b/gdb/gdbserver/ax.c @@ -0,0 +1,1191 @@ +/* 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" + +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 + +/* 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; + + /* 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, pulongest (top)); + } +} diff --git a/gdb/gdbserver/ax.h b/gdb/gdbserver/ax.h new file mode 100644 index 0000000..3ada3dd --- /dev/null +++ b/gdb/gdbserver/ax.h @@ -0,0 +1,74 @@ +/* Data structures and functions associated with agent expressions in GDB. + 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/>. */ + +#if !defined (AX_H) +#define AX_H 1 + +#include "server.h" +#include "regcache.h" + +#ifdef IN_PROCESS_AGENT +extern int debug_agent; +#define debug_threads debug_agent +#endif + +struct traceframe; + +/* Enumeration of the different kinds of things that can happen during + agent expression evaluation. */ + +enum eval_result_type + { + expr_eval_no_error, + expr_eval_empty_expression, + expr_eval_empty_stack, + expr_eval_stack_overflow, + expr_eval_stack_underflow, + expr_eval_unhandled_opcode, + expr_eval_unrecognized_opcode, + expr_eval_divide_by_zero, + expr_eval_invalid_goto + }; + +struct agent_expr +{ + int length; + + unsigned char *bytes; +}; + +#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); + +/* 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); +void emit_prologue (void); +void emit_epilogue (void); +enum eval_result_type compile_bytecodes (struct agent_expr *aexpr); +#endif + +enum eval_result_type gdb_eval_agent_expr (struct regcache *regcache, + struct traceframe *tframe, + struct agent_expr *aexpr, + ULONGEST *rslt); +#endif /* AX_H */ |