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:
For the DW_OP_piece and DW_OP_bit_piece operations, in the DWARF 5
standard, it is stated that if the location description (of that piece)
is empty, then the piece is describing an undefined location
description.
The act of allowing any location description to be placed on a DWARF
stack means that now a new operations can be defined which could pop
more then one location description from a DWARF stack.
This means that the old rule is not really applicable any more and a
new operation that explicitly pushes an undefined location description
on the DWARF stack is needed.
This new rule however is fully backward compatibility as described
in the document found on:
Under the new definitions for the DW_OP_piece and DW_OP_bit_piece
operations.
gdb/ChangeLog:
* compile/compile-loc2c.c (compute_stack_depth_worker): Add
support for new DW_OP_LLVM_undefined operations.
* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Add
support for new DW_OP_LLVM_undefined operations.
* dwarf2/loc.c (dwarf2_get_symbol_read_needs): Add support
for new DW_OP_LLVM_undefined operations.
include/ChangeLog:
* dwarf2.def (DW_OP): New DW_OP_LLVM_undefined operations
enumeration.
Currently in DWARF, there are only two ways to specify an offset for a
location description.
For a memory location description, the location description can be
first converted to a DWARF value, after which an arithmetic operation
can be applied to it. This however, only works while there are no
address spaces involved, that are not mapped to a general address space
(CORE_ADDR). Another limitation is that there is no way to specify a
bit offset to that location description.
Second way of specifying an offset to a location description is more
universal and involves wrapping that location description in a
composite piece, where piece itself has a bit/byte offset defined. The
problem with this approach is that both DW_OP_piece and DW_OP_bit_piece
define an offset as a DWARF operation operand, which means that an
offset needs to be a constant value encoded into the DWARF expression.
By adding three new operations (DW_OP_LLVM_offset,
DW_OP_LLVM_offset_constu and DW_OP_LLVM_bit_offset) these restrictions
are now lifted.
Detailed descriptions of these new operations can be found here:
The same document also explores an idea of extending the
DW_OP_push_object_address operation to allow pushing any location
description on the DWARF stack. This together with the new bit/byte
offset operations, generalizes DWARF to work with bit fields and could
replace the odd passed-in buffer mechanics in a more elegant way.
There seem to be a difference in views on what the big endian machine
register byte ordering should be. On one hand, some would expect for
a register to behave in the same way as memory, but on another, there
seems to be an existing implementation for (IBM big endian based
machines) which seems to be viewing registers differently, depending
if the register location description is part of a composite piece or
not. More on this topic can be found here:
Unfortunately, the gdb current implementation favors the second option,
which feels like a target specific implementation.
Because of this, I've decided to not favor a specific implementation
in the added test for new DWARF operations (dw2-llvm-offset.exp), so
the test is restricted to only run on little endian platforms.
gdb/ChangeLog:
* ada-lang.c (coerce_unspec_val_to_type): Add source bit offset
argument to the value_contents_copy_raw call.
* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
DWARF operations support.
* dwarf2/expr.c (rw_closure_value): Add bit offset support.
(dwarf_expr_context::dwarf_entry_to_gdb_value): Add source bit
offset argument to the value_contents_copy_raw call.
(dwarf_expr_context::execute_stack_op): Add new DWARF
operations support.
* dwarf2/loc.c (dwarf2_get_symbol_read_needs): Add new DWARF
operations support.
* findvar.c (read_frame_register_value): Add source bit offset
argument to the value_contents_copy_raw call.
* valops.c (read_value_memory): Add bit offset support.
(value_assign): Add bit offset support.
(value_repeat): Add bit offset support.
(value_array): Add source bit offset argument to the
value_contents_copy_raw call.
(value_slice): Add source bit offset argument to the
value_contents_copy_raw call.
* value.c (value_contents_copy_raw): Add source bit offset
support.
(value_contents_copy): Add source bit offset argument to
value_contents_copy_raw call.
(value_primitive_field): Add source bit offset argument to the
value_contents_copy_raw call.
(value_from_component): Add source bit offset argument to the
value_contents_copy_raw call.
(value_fetch_lazy_memory): Add bit offset argument to the
read_value_memory call.
(value_fetch_lazy_register): Add source bit offset argument to
the value_contents_copy call.
* value.h (value_contents_copy): Add source bit offset
argument.
include/ChangeLog:
* dwarf2.def (DW_OP_DUP): New DWARF operations enumeration.
gdb/testsuite/ChangeLog:
* lib/dwarf.exp: Add support for new DW_OP_LLVM_offset_constu
DWARF operation.
* gdb.dwarf2/dw2-llvm-offset.exp: New test.
One of the main benefits of allowing location description to be on the
DWARF stack is that now CFI expression based register rules can be
defined using a location description operations. This allows a register
of one frame to be saved in any location, including any composite
location.
To fully support this feature, the execute_stack_op function in
dwarf2/frame.c needs to return a single struct value object instead of
just an address.
Function put_frame_register_bytes also needs to change to support any
location description.
This support is a one of the key features to truly support optimized
code.
gdb/ChangeLog:
* dwarf2/frame.c (execute_stack_op): Change to return a struct
value object.
(dwarf2_frame_cache): Change to call new execute_stack_op
definition.
(dwarf2_frame_prev_register): Change to call new execute_stack_op
definition.
* frame.c (put_frame_register_bytes): Add support for writing to
composite location description.
The dwarf_expr_require_composition function reports an error if the
last operation is not a leaf node of the DWARF expression. This was
previously used to prevent location description operations to be used
freely in the DWARF expression.
With the new approach, everything all operations are treated the same
and everything is composable, so there is no need for the previous
restriction in the expression evaluator.
gdb/ChangeLog:
* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Remove
the use of dwarf_expr_require_composition.
After enabling location description to be on a DWARF stack, it is now
needed to check the frame context information validity when creating a
register location description.
gdb/ChangeLog:
* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Add
check_frame_info call for DW_OP_reg operations.
read_addr_from_reg function is now only called from frame.c file, this
means that the function can safely be moved there.
gdb/ChangeLog:
* dwarf2/expr.c (read_addr_from_reg): Move function to frame.c.
* dwarf2/expr.h (read_addr_from_reg): Remove function.
* dwarf2/frame.c (read_addr_from_reg): Add function from
expr.c.
Class that describes a computed_lval closure needs to be update to fit
better with the new dwarf_entry set of classes. This also means that a
pieced_value_funcs interface with that closure, needs to be renamed and
updated accordingly.
Considering that a closure is designed to describe a computed location
description, it makes sense to rename piece_closure to a
computed_closure.
gdb/ChangeLog:
* dwarf2/expr.c (struct piece_closure): Change to
computed_closure class.
(allocate_piece_closure): Remove function.
(rw_pieced_value): Rename to rw_closure_value and change to use
computed_closure class.
(read_pieced_value): Rename to read_closure_value and change to
use computed_closure class.
(write_pieced_value): Rename to write_closure_value and change
to use computed_closure class.
(check_pieced_synthetic_pointer): Rename to
check_synthetic_pointer and change to use computed_closure
class.
(indirect_pieced_value): Rename to indirect_closure_value and
change to use computed_closure class.
(coerce_pieced_ref): Rename to coerce_closure_ref and change
to use computed_closure class.
(copy_pieced_value_closure): Rename to copy_value_closure and
change to use computed_closure class.
(free_pieced_value_closure): Rename to free_value_closure and
change to use computed_closure class.
(dwarf_expr_context::gdb_value_to_dwarf_entry): Change to use
computed_closure class.
(dwarf_expr_context::dwarf_entry_to_gdb_value): Change to use
computed_closure class.
After the switch to the new evaluator implementation, it is now
possible to completely remove the dwarf_expr_context class from the
expr.h interface and encapsulate it inside the expr.c file.
The new interface consists of a new function called dwarf2_eval_exp
that takes a DWARF expression stream, initial DWARF stack elements (in
a form of a vector of a struct value objects), evaluation context and
expected result type information. Function returns an evaluation result
in a form of a struct value object.
Currently, there is ever only one initial stack element provided to the
evaluator and that element is always a memory address, so having a
vector of struct value object might seems like an overkill.
In reality this new flexibility allows implementation of a new DWARF
attribute extensions that could provide any number of initial stack
elements to describe any location description or value.
gdb/ChangeLog:
* dwarf2/expr.c (dwarf2_eval_exp): New function.
(struct dwarf_expr_context): Move from expr.h.
(dwarf_expr_context::push_address): Remove function.
* dwarf2/expr.h (struct dwarf_expr_context): Move to expr.c.
* dwarf2/frame.c (execute_stack_op): Now calls dwarf2_eval_exp.
* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Now calls
dwarf2_eval_exp.
(dwarf2_locexpr_baton_eval): Now calls dwarf2_eval_exp.
To replace existing DWARF stack element (dwarf_stack_value) with
dwarf_entry class based objects, a support for conversion between
struct value and the new classes is needed. The reason for this is
that dwarf_entry based classes are not designed to be visible outside
the expr.c file. This makes the DWARF expression evaluator more self
contained. This can be beneficial if there is ever a need to have a
DWARF support in gdbserver.
Once the conversion support is added, the current DWARF stack element
can easily be swapped out.
gdb/ChangeLog:
* dwarf2/expr.c (dwarf_value_equal_op): New function.
(dwarf_value_less_op): New function.
(struct piece_closure): Change to use dwarf_entry based
classes.
(allocate_piece_closure): Change to use dwarf_entry based
classes.
(rw_pieced_value): Change to use dwarf_entry based classes.
(check_pieced_synthetic_pointer): Change to use dwarf_entry
based classes.
(check_synthetic_pointer_location): New function.
(indirect_pieced_value): Change to use dwarf_entry based
classes.
(indirect_from_location): New function.
(coerce_pieced_ref): Change to use dwarf_entry based classes.
(free_pieced_value_closure): Change to use dwarf_entry based
classes.
(dwarf_expr_context::~dwarf_expr_context): Instantiate
dwarf_entry_factory object.
(dwarf_expr_context::push): Change to use dwarf_entry based
classes.
(dwarf_expr_context::push_address): Change to use dwarf_entry
based classes.
(dwarf_expr_context::fetch): Change to use dwarf_entry based
classes.
(dwarf_expr_context::read_mem): Remove method.
(dwarf_expr_context::fetch_result): Change to use dwarf_entry
based classes.
(dwarf_expr_context::dwarf_entry_deref): New method.
(dwarf_expr_context::gdb_value_to_dwarf_entry): New method.
(dwarf_expr_context::dwarf_entry_to_gdb_value): New method.
(dwarf_expr_context::fetch_address): Change to use dwarf_entry
based classes.
(dwarf_expr_context::fetch_in_stack_memory): Change to use
dwarf_entry based classes.
(dwarf_expr_context::add_piece): Change to use dwarf_entry based
classes.
(dwarf_expr_context::execute_stack_op): Change to use dwarf_entry
based classes.
* dwarf2/expr.h (class dwarf_entry): New declaration.
(class dwarf_entry_factory): New declaration.
(enum dwarf_value_location): Remove enumeration.
(struct dwarf_expr_piece): Remove structure.
(struct dwarf_stack_value): Remove structure.
(struct dwarf_expr_context): Change to use dwarf_entry based
classes. Add dwarf_entry_factory object.