data, data + size, per_cu, per_objfile);
}
-\f
-/* Helper functions and baton for dwarf2_loc_desc_get_symbol_read_needs. */
+/* Compute the correct symbol_needs_kind value for the location
+ expression in EXPR. */
-class symbol_needs_eval_context : public dwarf_expr_context
+static enum symbol_needs_kind
+dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
+ dwarf2_per_cu_data *per_cu,
+ dwarf2_per_objfile *per_objfile,
+ bfd_endian byte_order,
+ int addr_size,
+ int ref_addr_size,
+ int depth = 0)
{
-public:
- symbol_needs_eval_context (dwarf2_per_objfile *per_objfile)
- : dwarf_expr_context (per_objfile)
- {}
+ const gdb_byte *expr_end = expr.data () + expr.size ();
+ const gdb_byte *op_ptr = expr.data ();
+ enum symbol_needs_kind symbol_needs = SYMBOL_NEEDS_NONE;
+ const int max_depth = 256;
- enum symbol_needs_kind needs;
- struct dwarf2_per_cu_data *per_cu;
+ if (depth > max_depth)
+ error (_("DWARF-2 expression error: Loop detected (%d)."), depth);
- /* Reads from registers do require a frame. */
- CORE_ADDR read_addr_from_reg (int regnum) override
- {
- needs = SYMBOL_NEEDS_FRAME;
- return 1;
- }
+ depth++;
- /* "get_reg_value" callback: Reads from registers do require a
- frame. */
+ while (op_ptr < expr_end)
+ {
+ enum dwarf_location_atom op
+ = (enum dwarf_location_atom) *op_ptr++;
+ uint64_t uoffset;
+ int64_t offset;
- struct value *get_reg_value (struct type *type, int regnum) override
- {
- needs = SYMBOL_NEEDS_FRAME;
- return value_zero (type, not_lval);
- }
+ /* The DWARF expression might have a bug causing an infinite
+ loop. In that case, quitting is the only way out. */
+ QUIT;
- /* Reads from memory do not require a frame. */
- void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
- {
- memset (buf, 0, len);
- }
+ 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:
+ case DW_OP_stack_value:
+ case DW_OP_dup:
+ case DW_OP_drop:
+ case DW_OP_swap:
+ case DW_OP_over:
+ case DW_OP_rot:
+ case DW_OP_deref:
+ case DW_OP_abs:
+ case DW_OP_neg:
+ case DW_OP_not:
+ case DW_OP_and:
+ case DW_OP_div:
+ 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_shra:
+ 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:
+ case DW_OP_GNU_push_tls_address:
+ case DW_OP_nop:
+ case DW_OP_GNU_uninit:
+ case DW_OP_push_object_address:
+ break;
- /* Frame-relative accesses do require a frame. */
- void get_frame_base (const gdb_byte **start, size_t *length) override
- {
- static gdb_byte lit0 = DW_OP_lit0;
+ case DW_OP_form_tls_address:
+ if (symbol_needs <= SYMBOL_NEEDS_REGISTERS)
+ symbol_needs = SYMBOL_NEEDS_REGISTERS;
+ break;
- *start = &lit0;
- *length = 1;
+ case DW_OP_convert:
+ case DW_OP_GNU_convert:
+ case DW_OP_reinterpret:
+ case DW_OP_GNU_reinterpret:
+ case DW_OP_addrx:
+ case DW_OP_GNU_addr_index:
+ case DW_OP_GNU_const_index:
+ case DW_OP_constu:
+ case DW_OP_plus_uconst:
+ case DW_OP_piece:
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ break;
- needs = SYMBOL_NEEDS_FRAME;
- }
+ case DW_OP_consts:
+ op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
+ break;
- /* CFA accesses require a frame. */
- CORE_ADDR get_frame_cfa () override
- {
- needs = SYMBOL_NEEDS_FRAME;
- return 1;
- }
+ case DW_OP_bit_piece:
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ break;
- CORE_ADDR get_frame_pc () override
- {
- needs = SYMBOL_NEEDS_FRAME;
- return 1;
- }
+ case DW_OP_deref_type:
+ case DW_OP_GNU_deref_type:
+ op_ptr++;
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ break;
- /* Thread-local accesses require registers, but not a frame. */
- CORE_ADDR get_tls_address (CORE_ADDR offset) override
- {
- if (needs <= SYMBOL_NEEDS_REGISTERS)
- needs = SYMBOL_NEEDS_REGISTERS;
- return 1;
- }
+ case DW_OP_addr:
+ op_ptr += addr_size;
+ break;
- /* Helper interface of per_cu_dwarf_call for
- dwarf2_loc_desc_get_symbol_read_needs. */
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ op_ptr += 1;
+ break;
- void dwarf_call (cu_offset die_offset) override
- {
- per_cu_dwarf_call (this, die_offset, per_cu, per_objfile);
- }
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ op_ptr += 2;
+ break;
- /* Helper interface of sect_variable_value for
- dwarf2_loc_desc_get_symbol_read_needs. */
+ case DW_OP_const4s:
+ case DW_OP_const4u:
+ op_ptr += 4;
+ break;
- struct value *dwarf_variable_value (sect_offset sect_off) override
- {
- return sect_variable_value (this, sect_off, per_cu, per_objfile);
- }
+ case DW_OP_const8s:
+ case DW_OP_const8u:
+ op_ptr += 8;
+ break;
- /* DW_OP_entry_value accesses require a caller, therefore a
- frame. */
+ 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:
+ case DW_OP_regx:
+ 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:
+ case DW_OP_bregx:
+ case DW_OP_fbreg:
+ case DW_OP_call_frame_cfa:
+ case DW_OP_entry_value:
+ case DW_OP_GNU_entry_value:
+ case DW_OP_GNU_parameter_ref:
+ case DW_OP_regval_type:
+ case DW_OP_GNU_regval_type:
+ symbol_needs = SYMBOL_NEEDS_FRAME;
+ break;
- void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
- union call_site_parameter_u kind_u,
- int deref_size) override
- {
- needs = SYMBOL_NEEDS_FRAME;
+ case DW_OP_implicit_value:
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ op_ptr += uoffset;
+ break;
- /* The expression may require some stub values on DWARF stack. */
- push_address (0, 0);
- }
+ case DW_OP_implicit_pointer:
+ case DW_OP_GNU_implicit_pointer:
+ op_ptr += ref_addr_size;
+ op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
+ break;
+
+ case DW_OP_deref_size:
+ case DW_OP_pick:
+ op_ptr++;
+ break;
- /* DW_OP_addrx and DW_OP_GNU_addr_index doesn't require a frame. */
+ case DW_OP_skip:
+ op_ptr += 2;
+ break;
- CORE_ADDR get_addr_index (unsigned int index) override
- {
- /* Nothing to do. */
- return 1;
- }
+ case DW_OP_bra:
+ op_ptr += 2;
+ break;
- /* DW_OP_push_object_address has a frame already passed through. */
+ case DW_OP_call2:
+ {
+ cu_offset cu_off
+ = (cu_offset) extract_unsigned_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
- CORE_ADDR get_object_address () override
- {
- /* Nothing to do. */
- return 1;
- }
-};
+ auto get_frame_pc = [&symbol_needs] ()
+ {
+ symbol_needs = SYMBOL_NEEDS_FRAME;
+ return 0;
+ };
-/* Compute the correct symbol_needs_kind value for the location
- expression at DATA (length SIZE). */
+ struct dwarf2_locexpr_baton baton
+ = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
+ per_objfile,
+ get_frame_pc);
-static enum symbol_needs_kind
-dwarf2_loc_desc_get_symbol_read_needs (const gdb_byte *data, size_t size,
- dwarf2_per_cu_data *per_cu,
- dwarf2_per_objfile *per_objfile)
-{
- scoped_value_mark free_values;
+ /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+ we dont have to check the baton content. */
+ if (symbol_needs != SYMBOL_NEEDS_FRAME)
+ {
+ gdbarch *arch = baton.per_objfile->objfile->arch ();
+ gdb::array_view<const gdb_byte> sub_expr (baton.data,
+ baton.size);
+ symbol_needs
+ = dwarf2_get_symbol_read_needs (sub_expr,
+ baton.per_cu,
+ baton.per_objfile,
+ gdbarch_byte_order (arch),
+ baton.per_cu->addr_size (),
+ baton.per_cu->ref_addr_size (),
+ depth);
+ }
+ break;
+ }
- symbol_needs_eval_context ctx (per_objfile);
+ case DW_OP_call4:
+ {
+ cu_offset cu_off
+ = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order);
+ op_ptr += 4;
- ctx.needs = SYMBOL_NEEDS_NONE;
- ctx.per_cu = per_cu;
- ctx.gdbarch = per_objfile->objfile->arch ();
- ctx.addr_size = per_cu->addr_size ();
- ctx.ref_addr_size = per_cu->ref_addr_size ();
+ auto get_frame_pc = [&symbol_needs] ()
+ {
+ symbol_needs = SYMBOL_NEEDS_FRAME;
+ return 0;
+ };
+
+ struct dwarf2_locexpr_baton baton
+ = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
+ per_objfile,
+ get_frame_pc);
+
+ /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+ we dont have to check the baton content. */
+ if (symbol_needs != SYMBOL_NEEDS_FRAME)
+ {
+ gdbarch *arch = baton.per_objfile->objfile->arch ();
+ gdb::array_view<const gdb_byte> sub_expr (baton.data,
+ baton.size);
+ symbol_needs
+ = dwarf2_get_symbol_read_needs (sub_expr,
+ baton.per_cu,
+ baton.per_objfile,
+ gdbarch_byte_order (arch),
+ baton.per_cu->addr_size (),
+ baton.per_cu->ref_addr_size (),
+ depth);
+ }
+ break;
+ }
- ctx.eval (data, size);
+ case DW_OP_GNU_variable_value:
+ {
+ sect_offset sect_off
+ = (sect_offset) extract_unsigned_integer (op_ptr,
+ ref_addr_size,
+ byte_order);
+ op_ptr += ref_addr_size;
+
+ struct type *die_type
+ = dwarf2_fetch_die_type_sect_off (sect_off, per_cu,
+ per_objfile);
+
+ if (die_type == NULL)
+ error (_("Bad DW_OP_GNU_variable_value DIE."));
+
+ /* Note: Things still work when the following test is
+ removed. This test and error is here to conform to the
+ proposed specification. */
+ if (die_type->code () != TYPE_CODE_INT
+ && die_type->code () != TYPE_CODE_PTR)
+ error (_("Type of DW_OP_GNU_variable_value DIE must be "
+ "an integer or pointer."));
+
+ auto get_frame_pc = [&symbol_needs] ()
+ {
+ symbol_needs = SYMBOL_NEEDS_FRAME;
+ return 0;
+ };
- bool in_reg = ctx.location == DWARF_VALUE_REGISTER;
+ struct dwarf2_locexpr_baton baton
+ = dwarf2_fetch_die_loc_sect_off (sect_off, per_cu,
+ per_objfile,
+ get_frame_pc, true);
- /* If the location has several pieces, and any of them are in
- registers, then we will need a frame to fetch them from. */
- for (dwarf_expr_piece &p : ctx.pieces)
- if (p.location == DWARF_VALUE_REGISTER)
- in_reg = true;
+ /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+ we dont have to check the baton content. */
+ if (symbol_needs != SYMBOL_NEEDS_FRAME)
+ {
+ gdbarch *arch = baton.per_objfile->objfile->arch ();
+ gdb::array_view<const gdb_byte> sub_expr (baton.data,
+ baton.size);
+ symbol_needs
+ = dwarf2_get_symbol_read_needs (sub_expr,
+ baton.per_cu,
+ baton.per_objfile,
+ gdbarch_byte_order (arch),
+ baton.per_cu->addr_size (),
+ baton.per_cu->ref_addr_size (),
+ depth);
+ }
+ break;
+ }
- if (in_reg)
- ctx.needs = SYMBOL_NEEDS_FRAME;
+ case DW_OP_const_type:
+ case DW_OP_GNU_const_type:
+ op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+ offset = *op_ptr++;
+ op_ptr += offset;
+ break;
+
+ default:
+ error (_("Unhandled DWARF expression opcode 0x%x"), op);
+ }
+ /* If it is known that a frame information is
+ needed we can stop parsing the expression. */
+ if (symbol_needs == SYMBOL_NEEDS_FRAME)
+ break;
+ }
- return ctx.needs;
+ return symbol_needs;
}
/* A helper function that throws an unimplemented error mentioning a
struct dwarf2_locexpr_baton *dlbaton
= (struct dwarf2_locexpr_baton *) SYMBOL_LOCATION_BATON (symbol);
- return dwarf2_loc_desc_get_symbol_read_needs (dlbaton->data, dlbaton->size,
- dlbaton->per_cu,
- dlbaton->per_objfile);
+ gdbarch *arch = dlbaton->per_objfile->objfile->arch ();
+ gdb::array_view<const gdb_byte> expr (dlbaton->data, dlbaton->size);
+
+ return dwarf2_get_symbol_read_needs (expr,
+ dlbaton->per_cu,
+ dlbaton->per_objfile,
+ gdbarch_byte_order (arch),
+ dlbaton->per_cu->addr_size (),
+ dlbaton->per_cu->ref_addr_size ());
}
/* Return true if DATA points to the end of a piece. END is one past
loclist_generate_c_location
};
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* Unit test for the symbol needs check mechanism.
+
+ This mechanism is not expected to access the target. This
+ means that a conditional expressions that depends on a target
+ access needs to be evaluated in full.
+
+ This test is testing if the zero resulting branch is ignored. */
+
+static void
+symbol_needs_cond_zero ()
+{
+ const gdb_byte dwarf_expr[] {
+ 0x97, /* DW_OP_push_object_address */
+ 0x28, 0x5, 0x0, /* DW_OP_bra 5 */
+ 0x70, 0x0, /* DW_OP_breg0 0 */
+ 0x2f, 0x1, 0x0, /* DW_OP_skip 1 */
+ 0x30, /* DW_OP_lit0 */
+ 0x9f, /* DW_OP_stack_value */
+ };
+
+ symbol_needs_kind symbol_needs
+ = dwarf2_get_symbol_read_needs (dwarf_expr,
+ NULL /* per_objfile */,
+ NULL /* per_cu */,
+ BFD_ENDIAN_LITTLE,
+ 4 /* addr_size */,
+ 4 /* ref_addr_size */);
+
+ SELF_CHECK (symbol_needs == SYMBOL_NEEDS_FRAME);
+}
+
+/* Unit test for the symbol needs check mechanism.
+
+ This mechanism is not expected to access the target. This
+ means that a conditional expressions that depends on a target
+ access needs to be evaluated in full.
+
+ This test is testing if the nonzero resulting branch is ignored. */
+
+static void
+symbol_needs_cond_nonzero ()
+{
+ const gdb_byte dwarf_expr[] {
+ 0x97, /* DW_OP_push_object_address */
+ 0x30, /* DW_OP_lit0 */
+ 0x29, /* DW_OP_eq */
+ 0x28, 0x5, 0x0, /* DW_OP_bra 5 */
+ 0x70, 0x0, /* DW_OP_breg0 0 */
+ 0x2f, 0x1, 0x0, /* DW_OP_skip 1 */
+ 0x30, /* DW_OP_lit0 */
+ 0x9f, /* DW_OP_stack_value */
+ };
+
+ symbol_needs_kind symbol_needs
+ = dwarf2_get_symbol_read_needs (dwarf_expr,
+ NULL /* per_objfile */,
+ NULL /* per_cu */,
+ BFD_ENDIAN_LITTLE,
+ 4 /* addr_size */,
+ 4 /* ref_addr_size */);
+
+ SELF_CHECK (symbol_needs == SYMBOL_NEEDS_FRAME);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
void _initialize_dwarf2loc ();
void
_initialize_dwarf2loc ()
show_dwarf_always_disassemble,
&set_dwarf_cmdlist,
&show_dwarf_cmdlist);
+
+#if GDB_SELF_TEST
+ selftests::register_test ("symbol_needs_cond_zero",
+ selftests::symbol_needs_cond_zero);
+ selftests::register_test ("symbol_needs_cond_nonzero",
+ selftests::symbol_needs_cond_nonzero);
+#endif
}
--- /dev/null
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test the symbol needs check mechanism if it assumes that faking
+# reads from a target is a safe thing to do.
+#
+# In particular, the test uses a relative branch DWARF operation to
+# hide a register read. If the target reads are indeed faked, the
+# result returned will be wrong.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+ set regname x0
+} elseif { [is_aarch32_target]
+ || [istarget "s390*-*-*" ]
+ || [istarget "powerpc*-*-*"]
+ || [istarget "rs6000*-*-aix*"] } {
+ set regname r0
+} elseif { [is_x86_like_target] } {
+ set regname eax
+} elseif { [is_amd64_regs_target] } {
+ set regname rax
+} else {
+ verbose "Skipping ${gdb_test_file_name}."
+ return
+}
+
+standard_testfile symbol_needs_eval.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global dwarf_regnum regname
+
+ set exec_mask_var [gdb_target_symbol exec_mask]
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_name symbol_needs_eval.c}
+ {DW_AT_comp_dir /tmp}
+ } {
+ declare_labels int_type_label
+
+ # define int type
+ int_type_label: DW_TAG_base_type {
+ {DW_AT_name "int"}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_byte_size 4 DW_FORM_sdata}
+ }
+
+ # define artificial variable a
+ DW_TAG_variable {
+ {DW_AT_name a}
+ {DW_AT_type :$int_type_label}
+ {DW_AT_location {
+ DW_OP_addr $exec_mask_var
+ DW_OP_deref
+ DW_OP_bra 4 # conditional jump to DW_OP_bregx
+ DW_OP_lit0
+ DW_OP_skip 3 # jump to DW_OP_stack_value
+ DW_OP_bregx $dwarf_regnum 0
+ DW_OP_stack_value
+ } SPECIAL_expr}
+ {external 1 flag}
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+# The variable's location expression requires a frame,
+# so an error should be reported.
+gdb_test "print/d a" "No frame selected." "variable a can't be printed"
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_test_no_output "set var \$$regname = 2" "init reg to 2"
+
+gdb_test "print/d a" " = 2" "a == 2"
--- /dev/null
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test the symbol needs check mechanism if it assumes that faking
+# reads from a target is a safe thing to do.
+#
+# In particular, the test uses a relative branch DWARF operation to
+# potentially cause an infinite loop, if the target reads are indeed
+# faked.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+ set regname x0
+} elseif { [is_aarch32_target]
+ || [istarget "s390*-*-*" ]
+ || [istarget "powerpc*-*-*"]
+ || [istarget "rs6000*-*-aix*"] } {
+ set regname r0
+} elseif { [is_x86_like_target] } {
+ set regname eax
+} elseif { [is_amd64_regs_target] } {
+ set regname rax
+} else {
+ verbose "Skipping ${gdb_test_file_name}."
+ return
+}
+
+standard_testfile symbol_needs_eval.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global dwarf_regnum regname
+
+ set exec_mask_var [gdb_target_symbol exec_mask]
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_name symbol_needs_eval.c}
+ {DW_AT_comp_dir /tmp}
+ } {
+ declare_labels int_type_label
+
+ # define int type
+ int_type_label: DW_TAG_base_type {
+ {DW_AT_name "int"}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_byte_size 4 DW_FORM_sdata}
+ }
+
+ # add info for variable exec_mask
+ DW_TAG_variable {
+ {DW_AT_name exec_mask}
+ {DW_AT_type :$int_type_label}
+ {DW_AT_location {
+ DW_OP_addr $exec_mask_var
+ } SPECIAL_expr}
+ {external 1 flag}
+ }
+
+ # add info for subprogram main
+ DW_TAG_subprogram {
+ {MACRO_AT_func { main }}
+ {DW_AT_frame_base {
+ DW_OP_regx $dwarf_regnum
+ } SPECIAL_expr}
+ } {
+ # define artificial variable a
+ DW_TAG_variable {
+ {DW_AT_name a}
+ {DW_AT_type :$int_type_label}
+ {DW_AT_location {
+ DW_OP_lit1
+ DW_OP_addr $exec_mask_var
+ DW_OP_deref
+ DW_OP_skip 4 # jump to DW_OP_fbreg
+ DW_OP_drop
+ DW_OP_fbreg 0
+ DW_OP_dup
+ DW_OP_lit0
+ DW_OP_eq
+ DW_OP_bra -9 # conditional jump to DW_OP_drop
+ DW_OP_stack_value
+ } SPECIAL_expr}
+ {external 1 flag}
+ }
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_test_no_output "set var \$$regname = 2" "init reg to 2"
+gdb_test_no_output "set var exec_mask = 0" "init exec_mask to 0"
+
+gdb_test "print/d a" " = 2" "a == 2"