|  | /* Convert a DWARF location expression to C | 
|  |  | 
|  | Copyright (C) 2014-2021 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 "objfiles.h" | 
|  | #include "dwarf2/expr.h" | 
|  | #include "dwarf2/loc.h" | 
|  | #include "dwarf2/read.h" | 
|  | #include "ui-file.h" | 
|  | #include "utils.h" | 
|  | #include "compile-internal.h" | 
|  | #include "compile-c.h" | 
|  | #include "compile.h" | 
|  | #include "block.h" | 
|  | #include "dwarf2/frame.h" | 
|  | #include "gdbsupport/gdb_vecs.h" | 
|  | #include "value.h" | 
|  | #include "gdbarch.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 or | 
|  | DW_OP_form_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 a vector 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, | 
|  | std::vector<struct insn_info> *info, | 
|  | std::vector<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 = (enum dwarf_location_atom) *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: | 
|  | case DW_OP_form_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) | 
|  | to_do->push_back (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) | 
|  | to_do->push_back (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, | 
|  | std::vector<struct insn_info> *info) | 
|  | { | 
|  | std::vector<int> to_do; | 
|  | int stack_depth, i; | 
|  |  | 
|  | info->resize (op_end - op_ptr); | 
|  |  | 
|  | to_do.push_back (0); | 
|  | (*info)[0].depth = initial_depth; | 
|  | (*info)[0].visited = 1; | 
|  |  | 
|  | while (!to_do.empty ()) | 
|  | { | 
|  | int ndx = to_do.back (); | 
|  | to_do.pop_back (); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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, string_file *stream, ULONGEST l) | 
|  | { | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_stack[++__gdb_tos] = (" GCC_UINTPTR ") %s;\n", | 
|  | indent, "", hex_string (l)); | 
|  | } | 
|  |  | 
|  | /* Emit code to push an arbitrary expression.  This works like | 
|  | printf.  */ | 
|  |  | 
|  | static void pushf (int indent, string_file *stream, const char *format, ...) | 
|  | ATTRIBUTE_PRINTF (3, 4); | 
|  |  | 
|  | static void | 
|  | pushf (int indent, string_file *stream, const char *format, ...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | fprintf_filtered (stream, "%*s__gdb_stack[__gdb_tos + 1] = ", indent, ""); | 
|  | va_start (args, format); | 
|  | stream->vprintf (format, args); | 
|  | va_end (args); | 
|  | stream->puts (";\n"); | 
|  |  | 
|  | fprintf_filtered (stream, "%*s++__gdb_tos;\n", indent, ""); | 
|  | } | 
|  |  | 
|  | /* 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, string_file *stream, const char *format, ...) | 
|  | ATTRIBUTE_PRINTF (3, 4); | 
|  |  | 
|  | static void | 
|  | unary (int indent, string_file *stream, const char *format, ...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | fprintf_filtered (stream, "%*s__gdb_stack[__gdb_tos] = ", indent, ""); | 
|  | va_start (args, format); | 
|  | stream->vprintf (format, args); | 
|  | va_end (args); | 
|  | stream->puts (";\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, string_file *stream, const char *format, ...) | 
|  | ATTRIBUTE_PRINTF (3, 4); | 
|  |  | 
|  | static void | 
|  | binary (int indent, string_file *stream, const char *format, ...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | fprintf_filtered (stream, "%*s__gdb_stack[__gdb_tos - 1] = ", indent, ""); | 
|  | va_start (args, format); | 
|  | stream->vprintf (format, args); | 
|  | va_end (args); | 
|  | stream->puts (";\n"); | 
|  | fprintf_filtered (stream, "%*s--__gdb_tos;\n", indent, ""); | 
|  | } | 
|  |  | 
|  | /* 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 (string_file *stream, unsigned int scope, int target) | 
|  | { | 
|  | stream->printf ("__label_%u_%s", scope, pulongest (target)); | 
|  | } | 
|  |  | 
|  | /* Note that a register was used.  */ | 
|  |  | 
|  | static void | 
|  | note_register (int regnum, std::vector<bool> ®isters_used) | 
|  | { | 
|  | gdb_assert (regnum >= 0); | 
|  | /* If the expression uses a cooked register, then we currently can't | 
|  | compile it.  We would need a gdbarch method to handle this | 
|  | situation.  */ | 
|  | if (regnum >= registers_used.size ()) | 
|  | error (_("Expression uses \"cooked\" register and cannot be compiled.")); | 
|  | registers_used[regnum] = true; | 
|  | } | 
|  |  | 
|  | /* 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, string_file *stream, | 
|  | std::vector<bool> ®isters_used, | 
|  | struct gdbarch *gdbarch, int regnum) | 
|  | { | 
|  | std::string regname = compile_register_name_mangled (gdbarch, regnum); | 
|  |  | 
|  | note_register (regnum, registers_used); | 
|  | pushf (indent, stream, | 
|  | "(" GCC_UINTPTR ") &" COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", | 
|  | regname.c_str ()); | 
|  | } | 
|  |  | 
|  | /* 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, string_file *stream, | 
|  | std::vector<bool> ®isters_used, | 
|  | struct gdbarch *gdbarch, int regnum, uint64_t offset) | 
|  | { | 
|  | std::string regname = compile_register_name_mangled (gdbarch, regnum); | 
|  |  | 
|  | note_register (regnum, registers_used); | 
|  | if (offset == 0) | 
|  | pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", | 
|  | regname.c_str ()); | 
|  | else | 
|  | pushf (indent, stream, | 
|  | COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s + (" GCC_UINTPTR ") %s", | 
|  | regname.c_str (), hex_string (offset)); | 
|  | } | 
|  |  | 
|  | /* 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, string_file *stream, | 
|  | const char *type_name, | 
|  | const char *result_name, | 
|  | struct symbol *sym, CORE_ADDR pc, | 
|  | struct gdbarch *arch, | 
|  | std::vector<bool> ®isters_used, | 
|  | unsigned int addr_size, | 
|  | const gdb_byte *op_ptr, const gdb_byte *op_end, | 
|  | CORE_ADDR *initial, | 
|  | dwarf2_per_cu_data *per_cu, | 
|  | dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | /* 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; | 
|  | std::vector<struct insn_info> info; | 
|  | int stack_depth; | 
|  |  | 
|  | ++scope; | 
|  |  | 
|  | fprintf_filtered (stream, "%*s__attribute__ ((unused)) %s %s;\n", | 
|  | indent, "", type_name, result_name); | 
|  | fprintf_filtered (stream, "%*s{\n", indent, ""); | 
|  | indent += 2; | 
|  |  | 
|  | stack_depth = compute_stack_depth (byte_order, addr_size, | 
|  | &need_tempvar, &is_tls, | 
|  | op_ptr, op_end, initial != NULL, | 
|  | &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"), | 
|  | sym->print_name ()); | 
|  |  | 
|  | val = read_var_value (sym, NULL, frame); | 
|  | if (VALUE_LVAL (val) != lval_memory) | 
|  | error (_("Symbol \"%s\" cannot be used for compilation evaluation " | 
|  | "as its address has not been found."), | 
|  | sym->print_name ()); | 
|  |  | 
|  | warning (_("Symbol \"%s\" is thread-local and currently can only " | 
|  | "be referenced from the current thread in " | 
|  | "compiled code."), | 
|  | sym->print_name ()); | 
|  |  | 
|  | fprintf_filtered (stream, "%*s%s = %s;\n", | 
|  | indent, "", result_name, | 
|  | core_addr_to_string (value_address (val))); | 
|  | fprintf_filtered (stream, "%*s}\n", indent - 2, ""); | 
|  | return; | 
|  | } | 
|  |  | 
|  | fprintf_filtered (stream, "%*s" GCC_UINTPTR " __gdb_stack[%d];\n", | 
|  | indent, "", stack_depth); | 
|  |  | 
|  | if (need_tempvar) | 
|  | fprintf_filtered (stream, "%*s" GCC_UINTPTR " __gdb_tmp;\n", indent, ""); | 
|  | fprintf_filtered (stream, "%*sint __gdb_tos = -1;\n", indent, ""); | 
|  |  | 
|  | if (initial != NULL) | 
|  | pushf (indent, stream, "%s", core_addr_to_string (*initial)); | 
|  |  | 
|  | while (op_ptr < op_end) | 
|  | { | 
|  | enum dwarf_location_atom op = (enum dwarf_location_atom) *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); | 
|  | stream->puts (":;"); | 
|  | } | 
|  | stream->printf ("/* %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: | 
|  | uoffset = extract_unsigned_integer (op_ptr, addr_size, byte_order); | 
|  | 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 += per_objfile->objfile->text_section_offset (); | 
|  | 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, | 
|  | dwarf_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, | 
|  | dwarf_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, | 
|  | dwarf_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, | 
|  | dwarf_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, | 
|  | GCC_UINTPTR, fb_name, | 
|  | sym, pc, | 
|  | arch, registers_used, addr_size, | 
|  | datastart, datastart + datalen, | 
|  | NULL, per_cu, per_objfile); | 
|  |  | 
|  | 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: | 
|  | fprintf_filtered (stream, "%*s--__gdb_tos;\n", indent, ""); | 
|  | break; | 
|  |  | 
|  | case DW_OP_pick: | 
|  | offset = *op_ptr++; | 
|  | pushf (indent, stream, "__gdb_stack[__gdb_tos - %s]", | 
|  | plongest (offset)); | 
|  | break; | 
|  |  | 
|  | case DW_OP_swap: | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_tmp = __gdb_stack[__gdb_tos - 1];\n", | 
|  | indent, ""); | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_stack[__gdb_tos - 1] = " | 
|  | "__gdb_stack[__gdb_tos];\n", | 
|  | indent, ""); | 
|  | fprintf_filtered (stream, ("%*s__gdb_stack[__gdb_tos] = " | 
|  | "__gdb_tmp;\n"), | 
|  | indent, ""); | 
|  | break; | 
|  |  | 
|  | case DW_OP_over: | 
|  | pushf (indent, stream, "__gdb_stack[__gdb_tos - 1]"); | 
|  | break; | 
|  |  | 
|  | case DW_OP_rot: | 
|  | fprintf_filtered (stream, ("%*s__gdb_tmp = " | 
|  | "__gdb_stack[__gdb_tos];\n"), | 
|  | indent, ""); | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_stack[__gdb_tos] = " | 
|  | "__gdb_stack[__gdb_tos - 1];\n", | 
|  | indent, ""); | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_stack[__gdb_tos - 1] = " | 
|  | "__gdb_stack[__gdb_tos -2];\n", | 
|  | indent, ""); | 
|  | fprintf_filtered (stream, "%*s__gdb_stack[__gdb_tos - 2] = " | 
|  | "__gdb_tmp;\n", | 
|  | indent, ""); | 
|  | 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.  */ | 
|  | fprintf_filtered (stream, | 
|  | "%*s__gdb_stack[__gdb_tos] = " | 
|  | "*((__gdb_int_%s *) " | 
|  | "__gdb_stack[__gdb_tos]);\n", | 
|  | indent, "", 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, "%s", "__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, | 
|  | GCC_UINTPTR, cfa_name, | 
|  | sym, pc, arch, registers_used, | 
|  | addr_size, | 
|  | cfa_start, cfa_end, | 
|  | &text_offset, per_cu, per_objfile); | 
|  | pushf (indent, stream, "%s", cfa_name); | 
|  | } | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case DW_OP_skip: | 
|  | offset = extract_signed_integer (op_ptr, 2, byte_order); | 
|  | op_ptr += 2; | 
|  | fprintf_filtered (stream, "%*sgoto ", indent, ""); | 
|  | print_label (stream, scope, op_ptr + offset - base); | 
|  | stream->puts (";\n"); | 
|  | break; | 
|  |  | 
|  | case DW_OP_bra: | 
|  | offset = extract_signed_integer (op_ptr, 2, byte_order); | 
|  | op_ptr += 2; | 
|  | fprintf_filtered (stream, | 
|  | "%*sif ((( " GCC_INTPTR | 
|  | ") __gdb_stack[__gdb_tos--]) != 0) goto ", | 
|  | indent, ""); | 
|  | print_label (stream, scope, op_ptr + offset - base); | 
|  | stream->puts (";\n"); | 
|  | break; | 
|  |  | 
|  | case DW_OP_nop: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | error (_("unhandled DWARF op: %s"), get_DW_OP_name (op)); | 
|  | } | 
|  | } | 
|  |  | 
|  | fprintf_filtered (stream, "%*s%s = __gdb_stack[__gdb_tos];\n", | 
|  | indent, "", result_name); | 
|  | fprintf_filtered (stream, "%*s}\n", indent - 2, ""); | 
|  | } | 
|  |  | 
|  | /* See compile.h.  */ | 
|  |  | 
|  | void | 
|  | compile_dwarf_expr_to_c (string_file *stream, const char *result_name, | 
|  | struct symbol *sym, CORE_ADDR pc, | 
|  | struct gdbarch *arch, | 
|  | std::vector<bool> ®isters_used, | 
|  | unsigned int addr_size, | 
|  | const gdb_byte *op_ptr, const gdb_byte *op_end, | 
|  | dwarf2_per_cu_data *per_cu, | 
|  | dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | do_compile_dwarf_expr_to_c (2, stream, GCC_UINTPTR, result_name, sym, pc, | 
|  | arch, registers_used, addr_size, op_ptr, op_end, | 
|  | NULL, per_cu, per_objfile); | 
|  | } | 
|  |  | 
|  | /* See compile.h.  */ | 
|  |  | 
|  | void | 
|  | compile_dwarf_bounds_to_c (string_file *stream, | 
|  | const char *result_name, | 
|  | const struct dynamic_prop *prop, | 
|  | struct symbol *sym, CORE_ADDR pc, | 
|  | struct gdbarch *arch, | 
|  | std::vector<bool> ®isters_used, | 
|  | unsigned int addr_size, | 
|  | const gdb_byte *op_ptr, const gdb_byte *op_end, | 
|  | dwarf2_per_cu_data *per_cu, | 
|  | dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | 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, | 
|  | per_objfile); | 
|  | } |