Add support for nested composite locations

After allowing a location description to be placed on a DWARF stack,
in an effort to achieve a full composability of the DWARF expression,
it is necessary to enable forming of a nested composite location
descriptions.

To be able do this, a new operation DW_OP_LLVM_piece_end needs to be
introduced, along with some additional rules on the way how the
composite location description is formed using the existing DW_OP_piece
and DW_OP_bit_piece operations. These new rules are fully compatible
with the composite forming rules from the DWARF 5 standard.

More details on the new operation and added rules can be found here:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

gdb/ChangeLog:

	* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
	DW_OP_LLVM_piece_end operation support.
	* dwarf2/expr.c (class dwarf_value): Add copy constructor.
	(class dwarf_location): Add copy constructor.
	(class dwarf_undefined): Add copy constructor.
	(class dwarf_memory): Add copy constructor.
	(class dwarf_register): Add copy constructor.
	(class dwarf_implicit): Add copy constructor.
	(class dwarf_implicit_pointer): Add copy constructor.
	(class dwarf_composite): Add copy constructor.
	(read_from_location): Add composite completed check.
	(write_to_location): Add composite completed check.
	(read_value_contents_from_location): New function.
	(dwarf_entry_factory::copy_entry): New method.
	(rw_closure_value): Now calls read_value_contents_from_location
	function.
	(dwarf_expr_context::add_piece): Use new composite forming
	rules.
	(dwarf_expr_context::execute_stack_op): Add new
	DW_OP_LLVM_piece_end operation support.
	* dwarf2/loc.c (dwarf2_get_symbol_read_needs): Add new
	DW_OP_LLVM_piece_end operation support.

include/ChangeLog:

	* dwarf2.def (DW_OP_DUP): Add new DW_OP_LLVM_piece_end
          enumeration.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-llvm-piece-end.exp: New test.

Change-Id: Ib0b25e5de3f23df89d7d9e86aad56029c7d173df
This commit is contained in:
Zoran Zaric
2020-12-07 19:00:31 +00:00
committed by Simon Marchi
parent dab5aee6dc
commit 1127a49b32
5 changed files with 469 additions and 30 deletions

View File

@@ -365,6 +365,7 @@ compute_stack_depth_worker (int start, int *need_tempvar,
++stack_depth;
break;
case DW_OP_LLVM_piece_end:
case DW_OP_LLVM_offset_constu:
case DW_OP_nop:
break;

View File

@@ -308,6 +308,17 @@ public:
m_type = type;
}
dwarf_value (const dwarf_value &value)
{
struct type *type = value.m_type;
size_t type_len = TYPE_LENGTH (type);
m_contents.reset ((gdb_byte *) xzalloc (type_len));
memcpy (m_contents.get (), value.m_contents.get (), type_len);
m_type = type;
}
virtual ~dwarf_value () = default;
const gdb_byte* get_contents () const
@@ -352,6 +363,12 @@ public:
m_bit_suboffset = bit_suboffset % HOST_CHAR_BIT;
}
dwarf_location (const dwarf_location &location)
: m_offset (location.m_offset),
m_bit_suboffset (location.m_bit_suboffset),
m_initialised (location.m_initialised)
{}
virtual ~dwarf_location () = default;
LONGEST get_offset () const
@@ -404,6 +421,11 @@ public:
dwarf_undefined (LONGEST offset = 0, LONGEST bit_suboffset = 0)
: dwarf_location (offset, bit_suboffset)
{}
dwarf_undefined (const dwarf_undefined &undefined_entry)
: dwarf_location (undefined_entry)
{}
};
class dwarf_memory : public dwarf_location
@@ -415,6 +437,11 @@ public:
m_stack (stack)
{}
dwarf_memory (const dwarf_memory &memory_entry)
: dwarf_location (memory_entry),
m_stack (memory_entry.m_stack)
{}
bool in_stack () const
{
return m_stack;
@@ -440,6 +467,11 @@ public:
m_regnum (regnum)
{}
dwarf_register (const dwarf_register &register_entry)
: dwarf_location (register_entry),
m_regnum (register_entry.m_regnum)
{}
unsigned int get_regnum () const
{
return m_regnum;
@@ -468,6 +500,17 @@ public:
m_byte_order = byte_order;
}
dwarf_implicit (const dwarf_implicit &implicit_entry)
: dwarf_location (implicit_entry)
{
size_t size = implicit_entry.m_size;
m_contents.reset ((gdb_byte *) xzalloc (size));
memcpy (m_contents.get (), implicit_entry.m_contents.get (), size);
m_size = size;
m_byte_order = implicit_entry.m_byte_order;
}
const gdb_byte* get_contents () const
{
return m_contents.get ();
@@ -508,6 +551,14 @@ public:
m_addr_size (addr_size), m_die_offset (die_offset)
{}
dwarf_implicit_pointer (const dwarf_implicit_pointer &implicit_ptr_entry)
: dwarf_location (implicit_ptr_entry),
m_per_objfile (implicit_ptr_entry.m_per_objfile),
m_per_cu (implicit_ptr_entry.m_per_cu),
m_addr_size (implicit_ptr_entry.m_addr_size),
m_die_offset (implicit_ptr_entry.m_die_offset)
{}
dwarf2_per_objfile *get_per_objfile () const
{
return m_per_objfile;
@@ -551,6 +602,22 @@ public:
: dwarf_location (offset, bit_suboffset)
{}
dwarf_composite (const dwarf_composite &composite_entry)
: dwarf_location (composite_entry)
{
/* We do a shallow copy of the pieces because they are not
expected to be modified after they are already formed. */
for (unsigned int i = 0; i < composite_entry.m_pieces.size (); i++)
{
dwarf_location* location = composite_entry.m_pieces[i].m_location;
location->incref ();
m_pieces.emplace_back (location, composite_entry.m_pieces[i].m_size);
}
m_completed = composite_entry.m_completed;
}
/* A composite location gets detached from its factory object for
the purpose of lval_computed resolution, which means that it
needs to take care of garbage collecting its pieces. */
@@ -591,6 +658,16 @@ public:
return m_pieces.size ();
}
void set_completed (bool completed)
{
m_completed = completed;
};
bool is_completed () const
{
return m_completed;
};
private:
/* Composite piece that contains a piece location
description and it's size. */
@@ -608,6 +685,9 @@ private:
/* Vector of composite pieces. */
std::vector<struct piece> m_pieces;
/* True if location description is completed. */
bool m_completed = false;
};
/* Read contents from the location specified by the DWARF location
@@ -789,6 +869,9 @@ read_from_location (const dwarf_location *location, struct frame_info *frame,
unsigned int pieces_num = composite_entry->get_pieces_num ();
unsigned int i;
if (!composite_entry->is_completed ())
ill_formed_expression ();
total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
/* Skip pieces covered by the read offset. */
@@ -971,6 +1054,9 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
unsigned int pieces_num = composite_entry->get_pieces_num ();
unsigned int i;
if (!composite_entry->is_completed ())
ill_formed_expression ();
total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
/* Skip pieces covered by the write offset. */
@@ -1008,6 +1094,92 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
internal_error (__FILE__, __LINE__, _("invalid location type"));
}
/* Read value contents from the location specified by the DWARF
location description entry LOCATION.
The read operation is performed in the context of FRAME. BIT_SIZE
is the number of bits to read. The data read is copied to the
caller-managed buffer BUF. BITS_TO_SKIP is a bit offset into the
location and BUF_BIT_OFFSET is buffer BUF's bit offset.
LOCATION_BIT_LIMIT is a maximum number of bits that location can
hold, where value zero signifies that there is no such restriction.
Note that some location types can be read without a FRAME context. */
static void
read_value_contents_from_location (struct value * value,
const dwarf_location *location,
struct frame_info *frame,
LONGEST bits_to_skip,
int value_bit_offset, size_t bit_size,
size_t location_bit_limit)
{
/* Implicit pointers are handled later. */
if (dynamic_cast<const dwarf_implicit_pointer *> (location) != nullptr)
return;
auto composite_entry = dynamic_cast<const dwarf_composite *> (location);
if (composite_entry == nullptr)
{
int optimized, unavailable;
bool big_endian = type_byte_order (value_type (value)) == BFD_ENDIAN_BIG;
read_from_location (location, frame, bits_to_skip,
value_contents_raw (value),
value_bit_offset, bit_size, location_bit_limit,
big_endian, &optimized, &unavailable);
if (optimized)
mark_value_bits_optimized_out (value, value_bit_offset, bit_size);
if (unavailable)
mark_value_bits_unavailable (value, value_bit_offset, bit_size);
return;
}
if (!composite_entry->is_completed ())
ill_formed_expression ();
unsigned int pieces_num = composite_entry->get_pieces_num ();
unsigned int i;
LONGEST total_bits_to_skip = bits_to_skip
+ composite_entry->get_offset () * HOST_CHAR_BIT
+ composite_entry->get_bit_suboffset ();
/* Skip pieces covered by the read offset. */
for (i = 0; i < pieces_num; i++)
{
LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
if (total_bits_to_skip < piece_bit_size)
break;
total_bits_to_skip -= piece_bit_size;
}
for (; i < pieces_num; i++)
{
LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
const dwarf_location *piece = composite_entry->get_piece_at (i);
if (piece_bit_size > bit_size)
piece_bit_size = bit_size;
read_value_contents_from_location (value, piece, frame,
total_bits_to_skip,
value_bit_offset, piece_bit_size,
piece_bit_size);
if (bit_size == piece_bit_size)
break;
value_bit_offset += piece_bit_size;
bit_size -= piece_bit_size;
}
}
/* Convert a value entry to the matching struct value representation
of a given TYPE. OFFSET defines the offset into the value
contents.
@@ -1107,6 +1279,9 @@ public:
dwarf_composite *create_composite (LONGEST offset = 0,
LONGEST bit_suboffset = 0);
/* Create a deep copy of the DWARF ENTRY. */
dwarf_entry *copy_entry (dwarf_entry *entry);
/* Convert an entry to a location description entry. If the entry
is a location description entry a dynamic cast is applied.
@@ -1252,6 +1427,33 @@ dwarf_entry_factory::create_composite (LONGEST offset, LONGEST bit_suboffset)
return composite_entry;
}
dwarf_entry *
dwarf_entry_factory::copy_entry (dwarf_entry *entry)
{
dwarf_entry *entry_copy;
if (auto value = dynamic_cast<dwarf_value *> (entry))
entry_copy = new dwarf_value (*value);
else if (auto undefined = dynamic_cast<dwarf_undefined *> (entry))
entry_copy = new dwarf_undefined (*undefined);
else if (auto memory = dynamic_cast<dwarf_memory *> (entry))
entry_copy = new dwarf_memory (*memory);
else if (auto reg = dynamic_cast<dwarf_register *> (entry))
entry_copy = new dwarf_register (*reg);
else if (auto implicit = dynamic_cast<dwarf_implicit *> (entry))
entry_copy = new dwarf_implicit (*implicit);
else if (auto implicit_pointer
= dynamic_cast<dwarf_implicit_pointer *> (entry))
entry_copy = new dwarf_implicit_pointer (*implicit_pointer);
else if (auto composite = dynamic_cast<dwarf_composite *> (entry))
entry_copy = new dwarf_composite (*composite);
else
internal_error (__FILE__, __LINE__, _("invalid DWARF entry to copy."));
record_entry (entry_copy);
return entry_copy;
}
dwarf_location *
dwarf_entry_factory::entry_to_location (dwarf_entry *entry)
{
@@ -1460,30 +1662,19 @@ rw_closure_value (struct value *v, struct value *from)
const dwarf_location *location = composite_entry->get_piece_at (i);
ULONGEST bit_size = composite_entry->get_bit_size_at (i);
size_t this_bit_size = bit_size - bits_to_skip;
int optimized, unavailable;
if (this_bit_size > max_bit_offset - bit_offset)
this_bit_size = max_bit_offset - bit_offset;
if (from == NULL)
{
/* Implicit pointers are handled later. */
if (dynamic_cast<const dwarf_implicit_pointer *>
(location) == nullptr)
{
read_from_location (location, frame, bits_to_skip,
value_contents_raw (v), bit_offset,
this_bit_size, bit_size, big_endian,
&optimized, &unavailable);
if (optimized)
mark_value_bits_optimized_out (v, bit_offset, this_bit_size);
if (unavailable)
mark_value_bits_unavailable (v, bit_offset, this_bit_size);
}
read_value_contents_from_location (v, location, frame, bits_to_skip,
bit_offset, this_bit_size, bit_size);
}
else
{
int optimized, unavailable;
write_to_location (location, frame, bits_to_skip,
value_contents (from), bit_offset,
this_bit_size, bit_size, big_endian,
@@ -1892,10 +2083,34 @@ private:
/* Pop a top element of the stack and add as a composite piece.
If the fallowing top element of the stack is a composite
location description, the piece will be added to it. Otherwise
a new composite location description will be created and
the piece will be added to that composite. */
The action is based on the context:
- If the stack is empty, then an incomplete composite location
description (comprised of one undefined location description),
is pushed on the stack.
- Otherwise, if the top stack entry is an incomplete composite
location description, then it is updated to append a new piece
comprised of one undefined location description. The
incomplete composite location description is then left on the
stack.
- Otherwise, if the top stack entry is a location description or
can be converted to one, it is popped. Then:
- If the top stack entry (after popping) is a location
description comprised of one incomplete composite location
description, then it is updated to append a new piece
specified by the previously popped location description.
The incomplete composite location description is then left
on the stack.
- Otherwise, a new location description comprised of one
incomplete composite location description, with a new piece
specified by the previously popped location description, is
pushed on the stack.
- Otherwise, the DWARF expression is ill-formed */
dwarf_entry *add_piece (ULONGEST bit_size, ULONGEST bit_offset);
/* The engine for the expression evaluator. Using the context in this
@@ -2534,26 +2749,39 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
dwarf_location *piece_entry;
dwarf_composite *composite_entry;
if (!stack_empty_p ()
&& dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
if (stack_empty_p ())
piece_entry = entry_factory->create_undefined ();
else
{
piece_entry = entry_factory->entry_to_location (fetch (0));
if (auto old_composite_entry
= dynamic_cast<dwarf_composite *> (piece_entry))
{
if (!old_composite_entry->is_completed ())
piece_entry = entry_factory->create_undefined ();
}
else if (dynamic_cast<dwarf_undefined *> (piece_entry) != nullptr)
pop ();
}
if (dynamic_cast<dwarf_undefined *> (piece_entry) == nullptr)
{
piece_entry->add_bit_offset (bit_offset);
pop ();
}
else
piece_entry = entry_factory->create_undefined ();
piece_entry->add_bit_offset (bit_offset);
/* If stack is empty then it is a start of a new composite. In the
future this will check if the composite is finished or not. */
if (stack_empty_p ()
|| dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
composite_entry = entry_factory->create_composite ();
else
{
composite_entry = dynamic_cast<dwarf_composite *> (fetch (0));
pop ();
if (composite_entry->is_completed ())
composite_entry = entry_factory->create_composite ();
else
pop ();
}
composite_entry->add_piece (piece_entry, bit_size);
@@ -3156,7 +3384,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
break;
case DW_OP_dup:
result_entry = fetch (0);
result_entry = entry_factory->copy_entry (fetch (0));
break;
case DW_OP_drop:
@@ -3182,7 +3410,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
}
case DW_OP_over:
result_entry = fetch (1);
result_entry = entry_factory->copy_entry (fetch (1));
break;
case DW_OP_rot:
@@ -3742,6 +3970,23 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
result_entry = entry_factory->create_undefined ();
break;
case DW_OP_LLVM_piece_end:
{
dwarf_entry *entry = fetch (0);
dwarf_composite *composite_entry
= dynamic_cast<dwarf_composite *> (entry);
if (composite_entry == nullptr)
ill_formed_expression ();
if (composite_entry->is_completed ())
ill_formed_expression ();
composite_entry->set_completed (true);
goto no_push;
}
default:
error (_("Unhandled dwarf expression opcode 0x%x"), op);
}

View File

@@ -1832,6 +1832,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
case DW_OP_LLVM_offset:
case DW_OP_LLVM_bit_offset:
case DW_OP_LLVM_undefined:
case DW_OP_LLVM_piece_end:
break;
case DW_OP_form_tls_address:

View File

@@ -0,0 +1,191 @@
# 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 nested composition location description by using the new
# DW_OP_LLVM_piece_end operation.
#
# The test uses three nested levels of composite location descriptions
# to define a location of an array.
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 var-access.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 srcdir subdir srcfile
set buf_src [gdb_target_symbol buf]
set main_result [function_range main ${srcdir}/${subdir}/${srcfile}]
set main_start [lindex $main_result 0]
set main_length [lindex $main_result 1]
cu {} {
DW_TAG_compile_unit {
{DW_AT_name var-access.c}
{DW_AT_comp_dir /tmp}
} {
declare_labels array_type_label int_type_label char_type_label
# define char type
char_type_label: DW_TAG_base_type {
{DW_AT_name "char"}
{DW_AT_encoding @DW_ATE_signed}
{DW_AT_byte_size 1 DW_FORM_sdata}
}
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}
}
array_type_label: DW_TAG_array_type {
{DW_AT_type :$char_type_label}
} {
DW_TAG_subrange_type {
{DW_AT_type :$int_type_label}
{DW_AT_upper_bound 7 DW_FORM_udata}
}
}
DW_TAG_subprogram {
{DW_AT_name main}
{DW_AT_low_pc $main_start addr}
{DW_AT_high_pc $main_length data8}
} {
# Array spread in different pieces, of which some are
# undefined (1st and sixth bytes) and some are either
# in buf variable or REGNAME register.
#
# Location consists of three nested composite levels:
# - Third level consists of a composite location
# descriptions which hold a single simple location
# description each.
# - Second level consist of two more composite location
# descriptions that hold two of the third level
# composite location descriptions.
# - First level holds two of the second level composite
# location descriptions.
DW_TAG_variable {
{DW_AT_name var_array}
{DW_AT_type :$array_type_label}
{DW_AT_location {
# First level composite start
# Second level first composite start
# Third level first composite start
DW_OP_addr $buf_src
DW_OP_piece 0x2
DW_OP_LLVM_piece_end
# Third level first composite end
# Third level second composite start
DW_OP_LLVM_undefined
DW_OP_piece 0x1
DW_OP_LLVM_piece_end
# Third level second composite end
DW_OP_piece 0x1
DW_OP_swap
DW_OP_piece 0x2
DW_OP_LLVM_piece_end
# Second level first composite end
# Second level second composite start
# Third level third composite start
DW_OP_regx $dwarf_regnum
DW_OP_piece 0x4
DW_OP_LLVM_piece_end
# Third level third composite end
# Third level fourth composite start
DW_OP_LLVM_undefined
DW_OP_piece 0x1
DW_OP_LLVM_piece_end
# Third level fourth composite end
DW_OP_piece 0x1
DW_OP_swap
DW_OP_piece 0x4
DW_OP_LLVM_piece_end
# Second level second composite end
DW_OP_piece 0x5
DW_OP_swap
DW_OP_piece 0x3
DW_OP_LLVM_piece_end
# First level composite end
} SPECIAL_expr}
}
}
}
}
}
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 = 0x4030201" "init reg"
# Determine byte order.
set endian [get_endianness]
set optimized "<optimized out>"
switch $endian {
little {
set val "$optimized, 0x1, 0x2, 0x3, 0x4, $optimized, 0x0, 0x1"
}
big {
set val "$optimized, 0x4, 0x3, 0x2, 0x1, $optimized, 0x0, 0x1"
}
}
gdb_test "print/x var_array" " = \\{${val}\\}" "var_array print"

View File

@@ -709,6 +709,7 @@ DW_OP_DUP (DW_OP_LLVM_offset, 0xe3)
DW_OP_DUP (DW_OP_LLVM_offset_constu, 0xe4)
DW_OP_DUP (DW_OP_LLVM_bit_offset, 0xe5)
DW_OP (DW_OP_LLVM_undefined, 0xe7)
DW_OP_DUP (DW_OP_LLVM_piece_end, 0xea)
DW_END_OP
DW_FIRST_ATE (DW_ATE_void, 0x0)