diff options
author | Tom de Vries <tdevries@suse.de> | 2025-04-09 12:02:18 +0200 |
---|---|---|
committer | Tom de Vries <tdevries@suse.de> | 2025-04-09 12:02:18 +0200 |
commit | 1bafda2c4595f0f936a5845caf9667b70b198091 (patch) | |
tree | bfa39a306a83e53607626c26e150d1f238a9716a /gdb/dwarf2 | |
parent | 981fe5fd80faf511aa265e841a380c9b46be30e6 (diff) | |
download | binutils-1bafda2c4595f0f936a5845caf9667b70b198091.zip binutils-1bafda2c4595f0f936a5845caf9667b70b198091.tar.gz binutils-1bafda2c4595f0f936a5845caf9667b70b198091.tar.bz2 |
[gdb/symtab] Handle DW_OP_entry_value at function entry
On riscv64-linux, with test-case gdb.base/vla-optimized-out.exp I ran into:
...
(gdb) p sizeof (a)^M
$2 = <optimized out>^M
(gdb) FAIL: $exp: o1: printed size of optimized out vla
...
The variable a has type 0xbf:
...
<1><bf>: Abbrev Number: 12 (DW_TAG_array_type)
<c0> DW_AT_type : <0xe3>
<c4> DW_AT_sibling : <0xdc>
<2><c8>: Abbrev Number: 13 (DW_TAG_subrange_type)
<c9> DW_AT_type : <0xdc>
<cd> DW_AT_upper_bound : 13 byte block:
a3 1 5a 23 1 8 20 24 8 20 26 31 1c
(DW_OP_entry_value: (DW_OP_reg10 (a0));
DW_OP_plus_uconst: 1; DW_OP_const1u: 32;
DW_OP_shl; DW_OP_const1u: 32; DW_OP_shra;
DW_OP_lit1; DW_OP_minus)
...
which has an upper bound using a DW_OP_entry_value, and since the
corresponding call site contains no information to resolve the value of a0 at
function entry:
...
<2><6b>: Abbrev Number: 6 (DW_TAG_call_site)
<6c> DW_AT_call_return_pc: 0x638
<74> DW_AT_call_origin : <0x85>
...
evaluting the dwarf expression fails, and we get <optimized out>.
My first thought was to try breaking at *f1 instead of f1 to see if that would
help, but actually the breakpoint resolved to the same address.
In other words, the inferior is stopped at function entry.
Fix this by resolving DW_OP_entry_value when stopped at function entry by
simply evaluating the expression.
This handles these two cases (x86_64, using reg rdi):
- DW_OP_entry_value: (DW_OP_regx: 5 (rdi))
- DW_OP_entry_value: (DW_OP_bregx: 5 (rdi) 0; DW_OP_deref_size: 4)
Tested on x86_64-linux.
Tested gdb.base/vla-optimized-out.exp on riscv64-linux.
Tested an earlier version of gdb.dwarf2/dw2-entry-value-2.exp on
riscv64-linux, but atm I'm running into trouble on that machine (cfarm92) so
I haven't tested the current version there.
Diffstat (limited to 'gdb/dwarf2')
-rw-r--r-- | gdb/dwarf2/expr.c | 88 | ||||
-rw-r--r-- | gdb/dwarf2/expr.h | 4 |
2 files changed, 76 insertions, 16 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), |