diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/dwarf2/expr.c | 88 | ||||
-rw-r--r-- | gdb/dwarf2/expr.h | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.c | 32 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp | 125 | ||||
-rw-r--r-- | gdb/testsuite/lib/dwarf.exp | 23 |
5 files changed, 254 insertions, 18 deletions
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c index 46799d4..346bf7f 100644 --- a/gdb/dwarf2/expr.c +++ b/gdb/dwarf2/expr.c @@ -880,6 +880,34 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr, /* 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, @@ -1507,6 +1535,27 @@ dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf, 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. */ @@ -1916,7 +1965,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr, 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; @@ -1931,21 +1979,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr, 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; } @@ -2274,6 +2308,17 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr, 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 */); @@ -2288,6 +2333,17 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr, 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; diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h index ab92d9a..0129fb9 100644 --- a/gdb/dwarf2/expr.h +++ b/gdb/dwarf2/expr.h @@ -256,6 +256,10 @@ private: 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), diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.c new file mode 100644 index 0000000..57025e0 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.c @@ -0,0 +1,32 @@ +/* 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; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp new file mode 100644 index 0000000..55ecf9c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp @@ -0,0 +1,125 @@ +# 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>" +} diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index f765bca..46b39a1 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -1244,7 +1244,6 @@ namespace eval Dwarf { # 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 @@ -1252,9 +1251,18 @@ namespace eval Dwarf { 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] @@ -1340,6 +1348,17 @@ namespace eval Dwarf { _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"] |