diff options
Diffstat (limited to 'gdb/compile/compile-loc2c.c')
-rw-r--r-- | gdb/compile/compile-loc2c.c | 1148 |
1 files changed, 1148 insertions, 0 deletions
diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c new file mode 100644 index 0000000..e31d44d --- /dev/null +++ b/gdb/compile/compile-loc2c.c @@ -0,0 +1,1148 @@ +/* Convert a DWARF location expression to C + + Copyright (C) 2014 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 "defs.h" +#include "dwarf2.h" +#include "dwarf2expr.h" +#include "dwarf2loc.h" +#include "ui-file.h" +#include "utils.h" +#include "compile-internal.h" +#include "compile.h" +#include "block.h" +#include "dwarf2-frame.h" +#include "gdb_vecs.h" +#include "value.h" + + + +/* Information about a given instruction. */ + +struct insn_info +{ + /* Stack depth at entry. */ + + unsigned int depth; + + /* Whether this instruction has been visited. */ + + unsigned int visited : 1; + + /* Whether this instruction needs a label. */ + + unsigned int label : 1; + + /* Whether this instruction is DW_OP_GNU_push_tls_address. This is + a hack until we can add a feature to glibc to let us properly + generate code for TLS. */ + + unsigned int is_tls : 1; +}; + +/* A helper function for compute_stack_depth that does the work. This + examines the DWARF expression starting from START and computes + stack effects. + + NEED_TEMPVAR is an out parameter which is set if this expression + needs a special temporary variable to be emitted (see the code + generator). + INFO is an array of insn_info objects, indexed by offset from the + start of the DWARF expression. + TO_DO is a list of bytecodes which must be examined; it may be + added to by this function. + BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way. + OP_PTR and OP_END are the bounds of the DWARF expression. */ + +static void +compute_stack_depth_worker (int start, int *need_tempvar, + struct insn_info *info, + VEC (int) **to_do, + enum bfd_endian byte_order, unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end) +{ + const gdb_byte * const base = op_ptr; + int stack_depth; + + op_ptr += start; + gdb_assert (info[start].visited); + stack_depth = info[start].depth; + + while (op_ptr < op_end) + { + enum dwarf_location_atom op = *op_ptr; + uint64_t reg; + int64_t offset; + int ndx = op_ptr - base; + +#define SET_CHECK_DEPTH(WHERE) \ + if (info[WHERE].visited) \ + { \ + if (info[WHERE].depth != stack_depth) \ + error (_("inconsistent stack depths")); \ + } \ + else \ + { \ + /* Stack depth not set, so set it. */ \ + info[WHERE].visited = 1; \ + info[WHERE].depth = stack_depth; \ + } + + SET_CHECK_DEPTH (ndx); + + ++op_ptr; + + switch (op) + { + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + ++stack_depth; + break; + + case DW_OP_addr: + op_ptr += addr_size; + ++stack_depth; + break; + + case DW_OP_const1u: + case DW_OP_const1s: + op_ptr += 1; + ++stack_depth; + break; + case DW_OP_const2u: + case DW_OP_const2s: + op_ptr += 2; + ++stack_depth; + break; + case DW_OP_const4u: + case DW_OP_const4s: + op_ptr += 4; + ++stack_depth; + break; + case DW_OP_const8u: + case DW_OP_const8s: + op_ptr += 8; + ++stack_depth; + break; + case DW_OP_constu: + case DW_OP_consts: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + ++stack_depth; + break; + + case DW_OP_regx: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + ++stack_depth; + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + case DW_OP_bregx: + { + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + } + break; + case DW_OP_fbreg: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + + case DW_OP_dup: + ++stack_depth; + break; + + case DW_OP_drop: + --stack_depth; + break; + + case DW_OP_pick: + ++op_ptr; + ++stack_depth; + break; + + case DW_OP_rot: + case DW_OP_swap: + *need_tempvar = 1; + break; + + case DW_OP_over: + ++stack_depth; + break; + + case DW_OP_abs: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_deref: + break; + + case DW_OP_deref_size: + ++op_ptr; + break; + + case DW_OP_plus_uconst: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + break; + + case DW_OP_div: + case DW_OP_shra: + case DW_OP_and: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_xor: + case DW_OP_le: + case DW_OP_ge: + case DW_OP_eq: + case DW_OP_lt: + case DW_OP_gt: + case DW_OP_ne: + --stack_depth; + break; + + case DW_OP_call_frame_cfa: + ++stack_depth; + break; + + case DW_OP_GNU_push_tls_address: + info[ndx].is_tls = 1; + break; + + case DW_OP_skip: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + offset = op_ptr + offset - base; + /* If the destination has not been seen yet, add it to the + to-do list. */ + if (!info[offset].visited) + VEC_safe_push (int, *to_do, offset); + SET_CHECK_DEPTH (offset); + info[offset].label = 1; + /* We're done with this line of code. */ + return; + + case DW_OP_bra: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + offset = op_ptr + offset - base; + --stack_depth; + /* If the destination has not been seen yet, add it to the + to-do list. */ + if (!info[offset].visited) + VEC_safe_push (int, *to_do, offset); + SET_CHECK_DEPTH (offset); + info[offset].label = 1; + break; + + case DW_OP_nop: + break; + + default: + error (_("unhandled DWARF op: %s"), get_DW_OP_name (op)); + } + } + + gdb_assert (op_ptr == op_end); + +#undef SET_CHECK_DEPTH +} + +/* Compute the maximum needed stack depth of a DWARF expression, and + some other information as well. + + BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way. + NEED_TEMPVAR is an out parameter which is set if this expression + needs a special temporary variable to be emitted (see the code + generator). + IS_TLS is an out parameter which is set if this expression refers + to a TLS variable. + OP_PTR and OP_END are the bounds of the DWARF expression. + INITIAL_DEPTH is the initial depth of the DWARF expression stack. + INFO is an array of insn_info objects, indexed by offset from the + start of the DWARF expression. + + This returns the maximum stack depth. */ + +static int +compute_stack_depth (enum bfd_endian byte_order, unsigned int addr_size, + int *need_tempvar, int *is_tls, + const gdb_byte *op_ptr, const gdb_byte *op_end, + int initial_depth, + struct insn_info **info) +{ + unsigned char *set; + struct cleanup *outer_cleanup, *cleanup; + VEC (int) *to_do = NULL; + int stack_depth, i; + + *info = XCNEWVEC (struct insn_info, op_end - op_ptr); + outer_cleanup = make_cleanup (xfree, *info); + + cleanup = make_cleanup (VEC_cleanup (int), &to_do); + + VEC_safe_push (int, to_do, 0); + (*info)[0].depth = initial_depth; + (*info)[0].visited = 1; + + while (!VEC_empty (int, to_do)) + { + int ndx = VEC_pop (int, to_do); + + compute_stack_depth_worker (ndx, need_tempvar, *info, &to_do, + byte_order, addr_size, + op_ptr, op_end); + } + + stack_depth = 0; + *is_tls = 0; + for (i = 0; i < op_end - op_ptr; ++i) + { + if ((*info)[i].depth > stack_depth) + stack_depth = (*info)[i].depth; + if ((*info)[i].is_tls) + *is_tls = 1; + } + + do_cleanups (cleanup); + discard_cleanups (outer_cleanup); + return stack_depth + 1; +} + + + +#define GCC_UINTPTR "__gdb_uintptr" +#define GCC_INTPTR "__gdb_intptr" + +/* Emit code to push a constant. */ + +static void +push (int indent, struct ui_file *stream, ULONGEST l) +{ + fprintfi_filtered (indent, stream, "__gdb_stack[++__gdb_tos] = %s;\n", + hex_string (l)); +} + +/* Emit code to push an arbitrary expression. This works like + printf. */ + +static void +pushf (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos + 1] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); + + fprintfi_filtered (indent, stream, "++__gdb_tos;\n"); +} + +/* Emit code for a unary expression -- one which operates in-place on + the top-of-stack. This works like printf. */ + +static void +unary (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); +} + +/* Emit code for a unary expression -- one which uses the top two + stack items, popping the topmost one. This works like printf. */ + +static void +binary (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 1] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); + fprintfi_filtered (indent, stream, "--__gdb_tos;\n"); +} + +/* Print the name of a label given its "SCOPE", an arbitrary integer + used for uniqueness, and its TARGET, the bytecode offset + corresponding to the label's point of definition. */ + +static void +print_label (struct ui_file *stream, unsigned int scope, int target) +{ + fprintf_filtered (stream, "__label_%u_%s", + scope, pulongest (target)); +} + +/* Emit code that pushes a register's address on the stack. + REGISTERS_USED is an out parameter which is updated to note which + register was needed by this expression. */ + +static void +pushf_register_address (int indent, struct ui_file *stream, + unsigned char *registers_used, + struct gdbarch *gdbarch, int regnum) +{ + char *regname = compile_register_name_mangled (gdbarch, regnum); + struct cleanup *cleanups = make_cleanup (xfree, regname); + + registers_used[regnum] = 1; + pushf (indent, stream, "&" COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", + regname); + + do_cleanups (cleanups); +} + +/* Emit code that pushes a register's value on the stack. + REGISTERS_USED is an out parameter which is updated to note which + register was needed by this expression. OFFSET is added to the + register's value before it is pushed. */ + +static void +pushf_register (int indent, struct ui_file *stream, + unsigned char *registers_used, + struct gdbarch *gdbarch, int regnum, uint64_t offset) +{ + char *regname = compile_register_name_mangled (gdbarch, regnum); + struct cleanup *cleanups = make_cleanup (xfree, regname); + + registers_used[regnum] = 1; + if (offset == 0) + pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", + regname); + else + pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s + %s", + regname, hex_string (offset)); + + do_cleanups (cleanups); +} + +/* Compile a DWARF expression to C code. + + INDENT is the indentation level to use. + STREAM is the stream where the code should be written. + + TYPE_NAME names the type of the result of the DWARF expression. + For locations this is "void *" but for array bounds it will be an + integer type. + + RESULT_NAME is the name of a variable in the resulting C code. The + result of the expression will be assigned to this variable. + + SYM is the symbol corresponding to this expression. + PC is the location at which the expression is being evaluated. + ARCH is the architecture to use. + + REGISTERS_USED is an out parameter which is updated to note which + registers were needed by this expression. + + ADDR_SIZE is the DWARF address size to use. + + OPT_PTR and OP_END are the bounds of the DWARF expression. + + If non-NULL, INITIAL points to an initial value to write to the + stack. If NULL, no initial value is written. + + PER_CU is the per-CU object used for looking up various other + things. */ + +static void +do_compile_dwarf_expr_to_c (int indent, struct ui_file *stream, + const char *type_name, + const char *result_name, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, + unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + CORE_ADDR *initial, + struct dwarf2_per_cu_data *per_cu) +{ + /* We keep a counter so that labels and other objects we create have + unique names. */ + static unsigned int scope; + + enum bfd_endian byte_order = gdbarch_byte_order (arch); + const gdb_byte * const base = op_ptr; + int need_tempvar = 0; + int is_tls = 0; + struct cleanup *cleanup; + struct insn_info *info; + int stack_depth; + + ++scope; + + fprintfi_filtered (indent, stream, "%s%s;\n", type_name, result_name); + fprintfi_filtered (indent, stream, "{\n"); + indent += 2; + + stack_depth = compute_stack_depth (byte_order, addr_size, + &need_tempvar, &is_tls, + op_ptr, op_end, initial != NULL, + &info); + cleanup = make_cleanup (xfree, info); + + /* This is a hack until we can add a feature to glibc to let us + properly generate code for TLS. You might think we could emit + the address in the ordinary course of translating + DW_OP_GNU_push_tls_address, but since the operand appears on the + stack, it is relatively hard to find, and the idea of calling + target_translate_tls_address with OFFSET==0 and then adding the + offset by hand seemed too hackish. */ + if (is_tls) + { + struct frame_info *frame = get_selected_frame (NULL); + struct value *val; + + if (frame == NULL) + error (_("Symbol \"%s\" cannot be used because " + "there is no selected frame"), + SYMBOL_PRINT_NAME (sym)); + + val = read_var_value (sym, frame); + if (VALUE_LVAL (val) != lval_memory) + error (_("Symbol \"%s\" cannot be used for compilation evaluation " + "as its address has not been found."), + SYMBOL_PRINT_NAME (sym)); + + warning (_("Symbol \"%s\" is thread-local and currently can only " + "be referenced from the current thread in " + "compiled code."), + SYMBOL_PRINT_NAME (sym)); + + fprintfi_filtered (indent, stream, "%s = %s;\n", + result_name, + core_addr_to_string (value_address (val))); + fprintfi_filtered (indent - 2, stream, "}\n"); + do_cleanups (cleanup); + return; + } + + fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_stack[%d];\n", + stack_depth); + + if (need_tempvar) + fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_tmp;\n"); + fprintfi_filtered (indent, stream, "int __gdb_tos = -1;\n"); + + if (initial != NULL) + pushf (indent, stream, core_addr_to_string (*initial)); + + while (op_ptr < op_end) + { + enum dwarf_location_atom op = *op_ptr; + uint64_t uoffset, reg; + int64_t offset; + + print_spaces (indent - 2, stream); + if (info[op_ptr - base].label) + { + print_label (stream, scope, op_ptr - base); + fprintf_filtered (stream, ":;"); + } + fprintf_filtered (stream, "/* %s */\n", get_DW_OP_name (op)); + + /* This is handy for debugging the generated code: + fprintf_filtered (stream, "if (__gdb_tos != %d) abort ();\n", + (int) info[op_ptr - base].depth - 1); + */ + + ++op_ptr; + + switch (op) + { + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + push (indent, stream, op - DW_OP_lit0); + break; + + case DW_OP_addr: + op_ptr += addr_size; + /* Some versions of GCC emit DW_OP_addr before + DW_OP_GNU_push_tls_address. In this case the value is an + index, not an address. We don't support things like + branching between the address and the TLS op. */ + if (op_ptr >= op_end || *op_ptr != DW_OP_GNU_push_tls_address) + uoffset += dwarf2_per_cu_text_offset (per_cu); + push (indent, stream, uoffset); + break; + + case DW_OP_const1u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 1, byte_order)); + op_ptr += 1; + break; + case DW_OP_const1s: + push (indent, stream, + extract_signed_integer (op_ptr, 1, byte_order)); + op_ptr += 1; + break; + case DW_OP_const2u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 2, byte_order)); + op_ptr += 2; + break; + case DW_OP_const2s: + push (indent, stream, + extract_signed_integer (op_ptr, 2, byte_order)); + op_ptr += 2; + break; + case DW_OP_const4u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 4, byte_order)); + op_ptr += 4; + break; + case DW_OP_const4s: + push (indent, stream, + extract_signed_integer (op_ptr, 4, byte_order)); + op_ptr += 4; + break; + case DW_OP_const8u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 8, byte_order)); + op_ptr += 8; + break; + case DW_OP_const8s: + push (indent, stream, + extract_signed_integer (op_ptr, 8, byte_order)); + op_ptr += 8; + break; + case DW_OP_constu: + op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset); + push (indent, stream, uoffset); + break; + case DW_OP_consts: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + push (indent, stream, offset); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx"); + pushf_register_address (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, + op - DW_OP_reg0)); + break; + + case DW_OP_regx: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx"); + pushf_register_address (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, reg)); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + pushf_register (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, + op - DW_OP_breg0), + offset); + break; + case DW_OP_bregx: + { + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + pushf_register (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, reg), offset); + } + break; + case DW_OP_fbreg: + { + const gdb_byte *datastart; + size_t datalen; + const struct block *b; + struct symbol *framefunc; + char fb_name[50]; + + b = block_for_pc (pc); + + if (!b) + error (_("No block found for address")); + + framefunc = block_linkage_function (b); + + if (!framefunc) + error (_("No function found for block")); + + func_get_frame_base_dwarf_block (framefunc, pc, + &datastart, &datalen); + + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + + /* Generate a unique-enough name, in case the frame base + is computed multiple times in this expression. */ + xsnprintf (fb_name, sizeof (fb_name), "__frame_base_%ld", + (long) (op_ptr - base)); + + do_compile_dwarf_expr_to_c (indent, stream, + "void *", fb_name, + sym, pc, + arch, registers_used, addr_size, + datastart, datastart + datalen, + NULL, per_cu); + + pushf (indent, stream, "%s + %s", fb_name, hex_string (offset)); + } + break; + + case DW_OP_dup: + pushf (indent, stream, "__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_drop: + fprintfi_filtered (indent, stream, "--__gdb_tos;\n"); + break; + + case DW_OP_pick: + offset = *op_ptr++; + pushf (indent, stream, "__gdb_stack[__gdb_tos - %d]", offset); + break; + + case DW_OP_swap: + fprintfi_filtered (indent, stream, + "__gdb_tmp = __gdb_stack[__gdb_tos - 1];\n"); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos - 1] = " + "__gdb_stack[__gdb_tos];\n"); + fprintfi_filtered (indent, stream, ("__gdb_stack[__gdb_tos] = " + "__gdb_tmp;\n")); + break; + + case DW_OP_over: + pushf (indent, stream, "__gdb_stack[__gdb_tos - 1]"); + break; + + case DW_OP_rot: + fprintfi_filtered (indent, stream, ("__gdb_tmp = " + "__gdb_stack[__gdb_tos];\n")); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos] = " + "__gdb_stack[__gdb_tos - 1];\n"); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos - 1] = " + "__gdb_stack[__gdb_tos -2];\n"); + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 2] = " + "__gdb_tmp;\n"); + break; + + case DW_OP_deref: + case DW_OP_deref_size: + { + int size; + const char *mode; + + if (op == DW_OP_deref_size) + size = *op_ptr++; + else + size = addr_size; + + mode = c_get_mode_for_size (size); + if (mode == NULL) + error (_("Unsupported size %d in %s"), + size, get_DW_OP_name (op)); + + /* Cast to a pointer of the desired type, then + dereference. */ + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos] = " + "*((__gdb_int_%s *) " + "__gdb_stack[__gdb_tos]);\n", + mode); + } + break; + + case DW_OP_abs: + unary (indent, stream, + "((" GCC_INTPTR ") __gdb_stack[__gdb_tos]) < 0 ? " + "-__gdb_stack[__gdb_tos] : __gdb_stack[__gdb_tos]"); + break; + + case DW_OP_neg: + unary (indent, stream, "-__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_not: + unary (indent, stream, "~__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_plus_uconst: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + unary (indent, stream, "__gdb_stack[__gdb_tos] + %s", + hex_string (reg)); + break; + + case DW_OP_div: + binary (indent, stream, ("((" GCC_INTPTR + ") __gdb_stack[__gdb_tos-1]) / ((" + GCC_INTPTR ") __gdb_stack[__gdb_tos])")); + break; + + case DW_OP_shra: + binary (indent, stream, + "((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) >> " + "__gdb_stack[__gdb_tos]"); + break; + +#define BINARY(OP) \ + binary (indent, stream, ("__gdb_stack[__gdb_tos-1] " #OP \ + " __gdb_stack[__gdb_tos]")); \ + break + + case DW_OP_and: + BINARY (&); + case DW_OP_minus: + BINARY (-); + case DW_OP_mod: + BINARY (%); + case DW_OP_mul: + BINARY (*); + case DW_OP_or: + BINARY (|); + case DW_OP_plus: + BINARY (+); + case DW_OP_shl: + BINARY (<<); + case DW_OP_shr: + BINARY (>>); + case DW_OP_xor: + BINARY (^); +#undef BINARY + +#define COMPARE(OP) \ + binary (indent, stream, \ + "(((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) " #OP \ + " ((" GCC_INTPTR \ + ") __gdb_stack[__gdb_tos]))"); \ + break + + case DW_OP_le: + COMPARE (<=); + case DW_OP_ge: + COMPARE (>=); + case DW_OP_eq: + COMPARE (==); + case DW_OP_lt: + COMPARE (<); + case DW_OP_gt: + COMPARE (>); + case DW_OP_ne: + COMPARE (!=); +#undef COMPARE + + case DW_OP_call_frame_cfa: + { + int regnum; + CORE_ADDR text_offset; + LONGEST off; + const gdb_byte *cfa_start, *cfa_end; + + if (dwarf2_fetch_cfa_info (arch, pc, per_cu, + ®num, &off, + &text_offset, &cfa_start, &cfa_end)) + { + /* Register. */ + pushf_register (indent, stream, registers_used, arch, regnum, + off); + } + else + { + /* Another expression. */ + char cfa_name[50]; + + /* Generate a unique-enough name, in case the CFA is + computed multiple times in this expression. */ + xsnprintf (cfa_name, sizeof (cfa_name), + "__cfa_%ld", (long) (op_ptr - base)); + + do_compile_dwarf_expr_to_c (indent, stream, + "void *", cfa_name, + sym, pc, arch, registers_used, + addr_size, + cfa_start, cfa_end, + &text_offset, per_cu); + pushf (indent, stream, cfa_name); + } + } + + break; + + case DW_OP_skip: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + fprintfi_filtered (indent, stream, "goto "); + print_label (stream, scope, op_ptr + offset - base); + fprintf_filtered (stream, ";\n"); + break; + + case DW_OP_bra: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + fprintfi_filtered (indent, stream, + "if ((( " GCC_INTPTR + ") __gdb_stack[__gdb_tos--]) != 0) goto "); + print_label (stream, scope, op_ptr + offset - base); + fprintf_filtered (stream, ";\n"); + break; + + case DW_OP_nop: + break; + + default: + error (_("unhandled DWARF op: %s"), get_DW_OP_name (op)); + } + } + + fprintfi_filtered (indent, stream, "%s = (%s) __gdb_stack[__gdb_tos];\n", + result_name, type_name); + fprintfi_filtered (indent - 2, stream, "}\n"); + + do_cleanups (cleanup); +} + +/* See compile.h. */ + +void +compile_dwarf_expr_to_c (struct ui_file *stream, const char *result_name, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu) +{ + do_compile_dwarf_expr_to_c (2, stream, "void *", result_name, sym, pc, + arch, registers_used, addr_size, op_ptr, op_end, + NULL, per_cu); +} + +/* See compile.h. */ + +void +compile_dwarf_bounds_to_c (struct ui_file *stream, + const char *result_name, + const struct dynamic_prop *prop, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu) +{ + do_compile_dwarf_expr_to_c (2, stream, "unsigned long ", result_name, + sym, pc, arch, registers_used, + addr_size, op_ptr, op_end, NULL, per_cu); +} |