/* See expr.h. */
+value *
+dwarf_expr_context::deref (CORE_ADDR addr, int size, struct type *type)
+{
+ gdb_byte *buf = (gdb_byte *) alloca (size);
+ this->read_mem (buf, addr, size);
+
+ if (type == nullptr)
+ type = this->address_type ();
+
+ /* If the size of the object read from memory is different
+ from the type length, we need to zero-extend it. */
+ if (type->length () != size)
+ {
+ gdbarch *arch = this->m_per_objfile->objfile->arch ();
+ bfd_endian byte_order = gdbarch_byte_order (arch);
+ ULONGEST datum
+ = extract_unsigned_integer (buf, size, byte_order);
+
+ buf = (gdb_byte *) alloca (type->length ());
+ store_unsigned_integer (buf, type->length (),
+ byte_order, datum);
+ }
+
+ return value_from_contents_and_address (type, buf, addr);
+}
+
+/* See expr.h. */
+
void
dwarf_expr_context::push_dwarf_reg_entry_value (call_site_parameter_kind kind,
call_site_parameter_u kind_u,
return 1;
}
+/* Return true if, for an expr evaluated in the context of FRAME, we can
+ assume that DW_OP_entry_value (expr) == expr.
+
+ We can assume this right after executing a call, when stopped at the
+ start of the called function, in other words, when:
+ - FRAME is the innermost frame, and
+ - FRAME->pc is the first insn in a function. */
+
+static bool
+trivial_entry_value (frame_info_ptr frame)
+{
+ bool innermost_frame = frame_relative_level (frame) == 0;
+
+ /* Get pc corresponding to frame. Use get_frame_address_in_block to make
+ sure we get a pc in the correct function in the case of tail calls. */
+ CORE_ADDR pc = get_frame_address_in_block (frame);
+ bool at_first_insn = find_function_type (pc) != nullptr;
+
+ return innermost_frame && at_first_insn;
+}
+
/* The engine for the expression evaluator. Using the context in this
object, evaluate the expression between OP_PTR and OP_END. */
case DW_OP_GNU_deref_type:
{
int addr_size = (op == DW_OP_deref ? this->m_addr_size : *op_ptr++);
- gdb_byte *buf = (gdb_byte *) alloca (addr_size);
CORE_ADDR addr = fetch_address (0);
struct type *type;
else
type = address_type;
- this->read_mem (buf, addr, addr_size);
-
- /* If the size of the object read from memory is different
- from the type length, we need to zero-extend it. */
- if (type->length () != addr_size)
- {
- ULONGEST datum =
- extract_unsigned_integer (buf, addr_size, byte_order);
-
- buf = (gdb_byte *) alloca (type->length ());
- store_unsigned_integer (buf, type->length (),
- byte_order, datum);
- }
-
- result_val = value_from_contents_and_address (type, buf, addr);
+ result_val = this->deref (addr, addr_size, type);
break;
}
if (kind_u.dwarf_reg != -1)
{
op_ptr += len;
+
+ if (trivial_entry_value (this->m_frame))
+ {
+ /* We can assume that DW_OP_entry_value (expr) == expr.
+ Handle as DW_OP_regx. */
+ result_val
+ = value_from_ulongest (address_type, kind_u.dwarf_reg);
+ this->m_location = DWARF_VALUE_REGISTER;
+ break;
+ }
+
this->push_dwarf_reg_entry_value (CALL_SITE_PARAMETER_DWARF_REG,
kind_u,
-1 /* deref_size */);
if (deref_size == -1)
deref_size = this->m_addr_size;
op_ptr += len;
+
+ if (trivial_entry_value (this->m_frame))
+ {
+ /* We can assume that DW_OP_entry_value (expr) == expr.
+ Handle as DW_OP_bregx;DW_OP_deref_size. */
+ CORE_ADDR addr
+ = read_addr_from_reg (this->m_frame, kind_u.dwarf_reg);
+ result_val = this->deref (addr, deref_size);
+ break;
+ }
+
this->push_dwarf_reg_entry_value (CALL_SITE_PARAMETER_DWARF_REG,
kind_u, deref_size);
goto no_push;
but with the address being 0. In this situation, we arrange for
memory reads to come from the passed-in buffer. */
void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
+
+ /* Deref ADDR with size SIZE and return a value of type TYPE.
+ If TYPE == nullptr, defaults to this->address_type (). */
+ value *deref (CORE_ADDR addr, int size, struct type *type = nullptr);
};
/* Return the value of register number REG (a DWARF register number),
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2025 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/>. */
+
+int var = 2;
+
+static
+void bar (int *p)
+{
+ asm ("bar_label: .globl bar_label");
+}
+
+int
+main()
+{
+ asm ("main_label: .globl main_label");
+ bar (&var);
+ return 0;
+}
--- /dev/null
+# Copyright 2025 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/>.
+
+# Check that we can get DW_OP_entry_value at function entry.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile .c -debug.S
+
+set test "get dwarf regnum for first argument register"
+if { [is_x86_64_m64_target] } {
+ set dwarf_regnum 5
+} elseif { [istarget riscv64*] } {
+ set dwarf_regnum 10
+} else {
+ set reason "architecture-specific setting missing"
+ unsupported "$test ($reason)"
+ return
+}
+pass $test
+
+# Set up the DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global srcdir subdir srcfile
+
+ get_func_info main
+ get_func_info bar
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_name $srcfile}
+ } {
+ declare_labels integer
+
+ integer: DW_TAG_base_type {
+ {DW_AT_byte_size 8 DW_FORM_sdata}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_name integer}
+ }
+
+ DW_TAG_subprogram {
+ { DW_AT_name main }
+ { DW_AT_low_pc $main_start DW_FORM_addr }
+ { DW_AT_high_pc $main_end DW_FORM_addr }
+ } {
+ DW_TAG_variable {
+ { DW_AT_name argc }
+ { DW_AT_type :$integer }
+ { DW_AT_location {
+ DW_OP_entry_value {
+ DW_OP_regx $::dwarf_regnum
+ }
+ } SPECIAL_expr }
+ }
+ }
+
+ DW_TAG_subprogram {
+ { DW_AT_name bar }
+ { DW_AT_low_pc $bar_start DW_FORM_addr }
+ { DW_AT_high_pc $bar_end DW_FORM_addr }
+ } {
+ DW_TAG_variable {
+ { DW_AT_name foo }
+ { DW_AT_type :$integer }
+ { DW_AT_location {
+ DW_OP_entry_value {
+ DW_OP_bregx $::dwarf_regnum 0
+ DW_OP_deref_size 4
+ }
+ DW_OP_stack_value
+ } SPECIAL_expr }
+ }
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+if { ![runto *main] } {
+ return
+}
+
+with_test_prefix "at main+0" {
+ gdb_test "p argc" " = 1"
+
+ gdb_test "stepi"
+}
+
+with_test_prefix "at main+1" {
+ gdb_test "p argc" " = <optimized out>"
+}
+
+gdb_breakpoint "*bar"
+gdb_continue_to_breakpoint "bar"
+
+with_test_prefix "at bar+0" {
+ gdb_test "p foo" " = 2"
+
+ gdb_test "stepi"
+}
+
+with_test_prefix "at bar+1" {
+ gdb_test "p foo" " = <optimized out>"
+}
# used, as indicated in the header of the section where the location
# description is found.
#
- # (FIXME should use 'info complete' here.)
# Each list's first element is the opcode, either short or long
# forms are accepted.
# FIXME argument handling
proc _location { body dwarf_version addr_size offset_size } {
variable _constants
+ set collected_lines ""
foreach line [split $body \n] {
# Ignore blank lines, and allow embedded comments.
- if {[lindex $line 0] == "" || [regexp -- {^[ \t]*#} $line]} {
+ if { [regexp -- {^[ \t]*$} $line] || [regexp -- {^[ \t]*#} $line] } {
+ continue
+ }
+ if { $collected_lines != "" } {
+ set line "$collected_lines\n$line"
+ set collected_lines ""
+ }
+ if { ! [info complete $line] } {
+ set collected_lines $line
continue
}
set opcode [_map_name [lindex $line 0] _OP]
_op .2byte $argvec(label)
}
+ DW_OP_entry_value {
+ _get_args $line $opcode body
+ set l1 [new_label "expr_start"]
+ set l2 [new_label "expr_end"]
+ _op .uleb128 "$l2 - $l1" "expression"
+ define_label $l1
+ _location $argvec(body) $dwarf_version $addr_size \
+ $offset_size
+ define_label $l2
+ }
+
DW_OP_implicit_value {
set l1 [new_label "value_start"]
set l2 [new_label "value_end"]