Compare commits

...

30 Commits

Author SHA1 Message Date
Zoran Zaric
1127a49b32 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
2020-12-08 11:16:21 -05:00
Zoran Zaric
dab5aee6dc Add support for DW_OP_LLVM_undefined operation
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:

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

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.

gdb/testsuite/ChangeLog:

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

Change-Id: I2064c8fa3c7bc6488a226082b807e802d6d943ab
2020-12-08 11:16:21 -05:00
Zoran Zaric
a85f75281a Add DWARF operations for byte and bit offset
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:

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

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:

https://sourceware.org/legacy-ml/gdb-patches/2017-04/msg00177.html

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.

Change-Id: I2a6928c2a647debe50c59756be2197d60b6bc2df
2020-12-08 11:16:21 -05:00
Zoran Zaric
a6491910ec Add support for any location description in CFI
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.

Change-Id: I0c23ba56310174a2f8e539be72a11ac554efcaca
2020-12-08 11:16:21 -05:00
Zoran Zaric
07089eff99 Remove DWARF expression composition check
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.

Change-Id: Ibcfd02dfec0cf7354cf2494a13ae1a5125c1233a
2020-12-08 11:16:21 -05:00
Zoran Zaric
b96b71276e Add frame info check to DW_OP_reg operations
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.

Change-Id: I3e4a4d1eaba10b3b34be3c3bdbd1010a46ba4cd0
2020-12-08 11:16:21 -05:00
Zoran Zaric
0a014570ff Move read_addr_from_reg function to frame.c
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.

Change-Id: I15c798d3e001f56cfd78a1e96dd0d6034c844b91
2020-12-08 11:16:20 -05:00
Zoran Zaric
f7bc3a13f2 Rename and update the piece_closure structure
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.

Change-Id: Ice1df6e01c852bb8a94ba74e683d66fe57838036
2020-12-08 11:16:20 -05:00
Zoran Zaric
c12b925f78 Remove dwarf_expr_context from expr.h interface
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.

Change-Id: I5b2cce5424546d48fd00fb95d53681e41478cd09
2020-12-08 11:16:20 -05:00
Zoran Zaric
bedecfcbea Change DWARF stack to use new dwarf_entry classes
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.

Change-Id: I828c9b087c8ab3f57d9b208b360bcf5688810586
2020-12-08 11:16:20 -05:00
Zoran Zaric
7200429a72 Add dwarf_entry factory class to expr.c
This patch introduces a new class in charge of creating, tracking and
destroying dwarf_entry class based objects.

gdb/ChangeLog:

	* dwarf2/expr.c (ill_formed_expression): New function.
	(value_to_gdb_value): New function.
	(class dwarf_entry_factory): New class.
	(dwarf_entry_factory::~dwarf_entry_factory): New method.
	(dwarf_entry_factory::record_entry): New method.
	(dwarf_entry_factory::create_value): New method.
	(dwarf_entry_factory::create_undefined): New method.
	(dwarf_entry_factory::create_memory): New method.
	(dwarf_entry_factory::create_register): New method.
	(dwarf_entry_factory::create_implicit): New method.
	(dwarf_entry_factory::create_implicit_pointer): New method.
	(dwarf_entry_factory::create_composite): New method.
	(dwarf_entry_factory::entry_to_location): New method.
	(dwarf_entry_factory::entry_to_value): New method.
	(dwarf_entry_factory::value_binary_op): New method.
	(dwarf_entry_factory::value_negation_op): New method.
	(dwarf_entry_factory::value_complement_op): New method.
	(dwarf_entry_factory::value_cast_op): New method.

Change-Id: I679d2a80d9d9a64d6d19fefe6c18afc7a4357fe9
2020-12-08 11:16:20 -05:00
Zoran Zaric
4a11d48735 Add new location description access interface
After adding interface for register and memory location access, a new
top level interface for accessing any location, described by the
dwarf_location class based objects, can now be defined.

Also, the address_type method is now needed to be outside of the
dwarf_stack_value class to allow creation of the DWARF generic type
independently of that class.

	* dwarf2/expr.c (read_from_location): New function.
	(write_to_location): New function.
	(address_type): New function.
	* dwarf2/expr.h (address_type): Exposed function.

Change-Id: I634ad56441cc95a1cc693a1950a4d81aaadb9fd8
2020-12-08 11:16:20 -05:00
Zoran Zaric
fda7ca2701 Add new classes that model DWARF stack element
The rest of the patch series addresses the issues described in a
"Motivation" section of the AMD's DWARF standard extensions that
can be found at:

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

The document describes a couple of main issues found when using the
current DWARF 5 version to describe optimized code for SIMD and SIMT
architectures.

Without going much into details described in the document, the main
point is that DWARF version 5 does not allow a proper support for
address spaces and it does not allow a location description to be part
of a DWARF expression, unless it is a leaf node of that expression.

Both issues can be solved in a clean way by introducing a new set of
classes that describe all entry types which can be placed on a DWARF
stack, while keeping a full backward compatibility with the previous
standard version. These entry types can now be either a typed value
or any location description.

Currently, the result of an expression evaluation is kept in a separate
data structure, while with the new approach, it will be always found as
a top element of the DWARF stack. The reason why this approach is
backward compatible is because in version 5, a location description
is only allowed to be a leaf node of the expression or a composite
piece.

Question here is, why do we need a new set of classes and why not just
use the struct value instead?

As it stands, there are couple of issues with using the struct value to
describe a DWARF stack element:

 - It is not designed to represent a DWARF location description
specifically, instead it behaves more like unified debug information
format that represents an actual target resource. One example of this
is accessing data of a struct value register location description,
where if the amount of data accessed is larger then the register,
results in accessing more then one register. In DWARF this is not a
valid behavior and locations that span more then one register should be
described as a composite location description.

- There is a tight coupling between struct value and it's type
information, regardless if the data represented is describing a value
(not_lval) or a location description. While the type information
dictates how the data is accessed for a struct value case, in DWARF,
location description doesn't have a type so data access is not bound by
it.

- DWARF values only support much simpler base types, while struct value
can be linked to any type. Admittedly, new classes are still using the
same struct value infrastructure for a value based operations at the
moment, but that is planned to change in the near future.

- struct value register location description requires a frame id
information which makes them unsuitable for CFA expression evaluation.

So, there seems to be a lack of separation of concerns in the design
of a struct value infrastructure, while the new classes are handling
one specific purpose and are completely encapsulated in the expr.c.

Additional benefit of this design is that it makes a step in a
right direction for being able to evaluate DWARF expressions on a
gdbserver side in the near future, which sounds like a desirable thing.

It is also worth mentioning that this new location description
representation is based on a bit granularity (the bit_suboffset class
member) even though the DWARF standard has a very limited support for
it (mostly used for DW_OP_bit_piece operation).

By allowing any location description to define a bit sub-offset of the
location, we are able to give more options for supporting of new
concepts (like the existing packed arrays in Ada language).

In this patch, a new set of classes that describe a DWARF stack element
are added. The new classes are:

- Value - describes a numerical value with a DWARF base type.
- Location description - describes a DWARF location description.
  - Undefined location - describes a location that is not defined.
  - Memory location - describes a location in memory.
  - Register location - describes a register location in a frame
    context.
  - Implicit location - describes a location that implicitly holds a
    value that it describes.
  - Implicit pointer - describes a concept of an implicit pointer to
    a source variable.
  - Composite location - describes a location that is composed from
    pieces of other location descriptions.

For now, these classes are just defined, and they are planned to be
used by the following patches.

gdb/ChangeLog:

	* dwarf2/expr.c (class dwarf_entry): New class.
	(class dwarf_value): New class.
	(class dwarf_location): New class.
	(class dwarf_undefined): New class.
	(class dwarf_memory): New class.
	(class dwarf_register): New class.
	(class dwarf_implicit): New class.
	(class dwarf_implicit_pointer): New class.
	(class dwarf_composite): New class.
	* value.c (pack_unsigned_long): Expose function.
	* value.h (pack_unsigned_long): Expose function.

Change-Id: If19996cd9778d4dd12e9c141af788f0fdbf16227
2020-12-08 11:16:20 -05:00
Zoran Zaric
0ead7f247c Add new memory access interface to expr.c
DWARF expression evaluator is currently using a few different
interfaces for memory access: write_memory_with_notification,
read_value_memory, read_memory.

They all seem incosistent, while some of them even need a struct
value typed argument to be present.

This patch is simplifying that interface by replacing it with two new
low level functions: read_from_memory and write_to_memory.

The advantage of this new interface is that it behaves in the same way
as the register access interface from the previous patch. Both of these
have the same error returning policy, which will be usefull for the
following patches.

	* dwarf2/expr.c (xfer_from_memory):  New function.
	(read_from_memory): New function.
	(write_to_memory): New function.
	(rw_pieced_value): Now calls the read_from_memory and
	write_to_memory functions.

Change-Id: Id1f4c9b8127da83dece543e68757ffb1de2afedd
2020-12-08 11:16:19 -05:00
Zoran Zaric
b9b1a8273b Add new register access interface to expr.c
DWARF expression evaluator is currently using get_frame_register_bytes
and put_frame_register_bytes interface for register access.

The problem with evaluator using this interface is that it allows a
bleed out register access. This means that if the caller specifies a
larger amount of data then the size of a specified register, the
operation will continue accessing the neighboring registers until a
full amount of data has been reached.

DWARF specification does not define this behavior, so a new simplified
register access interface is needed instead.

	* dwarf2/expr.c (read_from_register): New function.
	(write_to_register): New function.
	(rw_pieced_value): Now calls the read_from_register and
	write_to_register functions.

Change-Id: I885b6a02353bc3fcf0dd5c600b5f1a73a7f9e340
2020-12-08 11:16:19 -05:00
Zoran Zaric
0dc32c82c3 Add as_lval argument to expression evaluator
There are cases where the result of the expression evaluation is
expected to be in a form of a value and not location description.

One place that has this requirement is dwarf_entry_parameter_to_value
function, but more are expected in the future. Until now, this
requirement was fulfilled by extending the evaluated expression with
a DW_OP_stack_value operation at the end.

New implementation, introduces a new evaluation argument instead.

	* dwarf2/expr.c (dwarf_expr_context::fetch_result): Add as_lval
	argument.
	(dwarf_expr_context::eval_exp): Add as_lval argument.
	* dwarf2/expr.h (struct dwarf_expr_context): Add as_lval
	argument to fetch_result and eval_exp methods.
	* dwarf2/frame.c (execute_stack_op): Add as_lval argument.
	* dwarf2/loc.c (dwarf_entry_parameter_to_value): Remove
	DWARF expression extension.
	(dwarf2_evaluate_loc_desc_full): Add as_lval argument support.
	(dwarf2_evaluate_loc_desc): Add as_lval argument support.
	(dwarf2_locexpr_baton_eval): Add as_lval argument support.

Change-Id: I04000d8a506030c747a82cd14d1729a90339ebaf
2020-12-08 11:16:19 -05:00
Zoran Zaric
e6bada58eb Simplify dwarf_expr_context class interface
Idea of this patch is to get a clean and simple public interface for
the dwarf_expr_context class, looking like:

- constructor,
- destructor,
- push_address method and
- eval_exp method.

Where constructor should only ever require a target architecture
information. This information is held in per object file
(dwarf2_per_objfile) structure, so it makes sense to keep that
structure as a constructor argument. It also makes sense to get the
address size from that structure, but unfortunately that interface
doesn't exist at the moment, so the dwarf_expr_context class user
needs to provide that information.

The push_address method is used to push a CORE_ADDR as a value on
top of the DWARF stack before the evaluation. This method can be
later changed to push any struct value object on the stack.

The eval_exp method is the method that evaluates a DWARF expression
and provides the evaluation result, in a form of a single struct
value object that describes a location. To do this, the method requires
a context of the evaluation, as well as expected result type
information. If the type information is not provided, the DWARF generic
type will be used instead.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context): Add
	address size argument.
	(dwarf_expr_context::read_mem): Change to use property_addr_info
	structure.
	(dwarf_expr_context::eval_exp): New function.
	(dwarf_expr_context::execute_stack_op): Change to use
	property_addr_info structure.
	* dwarf2/expr.h (struct dwarf_expr_context): New eval_exp
	declaration. Change eval and fetch_result method to private.
	* dwarf2/frame.c (execute_stack_op): Change to call eval_exp
	method.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to call
	eval_exp method.
	(dwarf2_locexpr_baton_eval): Change to call eval_exp method.

Change-Id: I8abd0dbf93e74a8a5adce143bfdea83c802233c3
2020-12-08 11:16:19 -05:00
Zoran Zaric
5e8cb1a0bb Make DWARF evaluator return a single struct value
The patch is addressing the issue of class users writing and reading
the internal data of the dwarf_expr_context class.

At this point, all conditions are met for the DWARF evaluator to return
an evaluation result in a form of a single struct value object.

gdb/ChangeLog:

	* dwarf2/expr.c (pieced_value_funcs): Chenge to static
	function.
	(allocate_piece_closure): Change to static function.
	(dwarf_expr_context::fetch_result): New function.
	* dwarf2/expr.h (struct piece_closure): Remove declaration.
	(struct dwarf_expr_context): fetch_result new declaration.
	fetch, fetch_address and fetch_in_stack_memory members move
	to private.
	(allocate_piece_closure): Remove.
	* dwarf2/frame.c (execute_stack_op): Change to use
	fetch_result.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to use
	fetch_result.
	(dwarf2_locexpr_baton_eval): Change to use fetch_result.

Change-Id: I03829eb03de7b3dc38e06757d1fa02fc299e25a5
2020-12-08 11:16:19 -05:00
Zoran Zaric
17ac2b0a7e Move piece_closure and its support to expr.c
Following 5 patches series is trying to clean up the interface of the
DWARF expression evaluator class (dwarf_expr_context).

After merging all expression evaluators into one class, the next
logical step is to make a clean user interface for that class. To do
that, we first need to address the issue of class users writing and
reading the internal data of the class directly.

Fixing the case of writing is simple, it makes sense for an evaluator
instance to be per architecture basis. Currently, the best separation
seems to be per object file, so having that data (dwarf2_per_objfile)
as a constructor argument makes sense. It also makes sense to get the
address size from that object file, but unfortunately that interface
does not exist at the moment.

Luckily, address size information is already available to the users
through other means. As a result, the address size also needs to be a
class constructor argument, at least until a better interface for
acquiring that information from an object file is implemented.

The rest of the user written data comes down to a context of an
evaluated expression (compilation unit context, frame context and
passed in buffer context) and a source type information that a result
of evaluating expression is representing. So, it makes sense for all of
these to be arguments of an evaluation method.

To address the problem of reading the dwarf_expr_context class
internal data, we first need to understand why it is implemented that
way?

This is actualy a question of which existing class can be used to
represent both values and a location descriptions and why it is not
used currently?

The answer is in a struct value class/structure, but the problem is
that before the evaluators were merged, only one evaluator had an
infrastructure to resolve composite and implicit pointer location
descriptions.

After the merge, we are now able to use the struct value to represent
any result of the expression evaluation. It also makes sense to move
all infrastructure for those location descriptions to the expr.c file
considering that that is the only place using that infrastructure.

What we are left with in the end is a clean public interface of the
dwarf_expr_context class containing:

- constructor,
- destructor,
- push_address method and
- eval_exp method.

The idea with this particular patch is to move piece_closure structure
and the interface that handles it (lval_funcs) to expr.c file.

While implicit pointer location descriptions are still not useful in
the CFI context (of the AMD's DWARF standard extensions), the composite
location descriptions are certainly necessary to describe a results of
specific compiler optimizations.

Considering that a piece_closure structure is used to represent both,
there was no benefit in splitting them.

gdb/ChangeLog:

	* dwarf2/expr.c (struct piece_closure): Add from loc.c.
	(allocate_piece_closure): Add from loc.c.
	(bits_to_bytes): Add from loc.c.
	(rw_pieced_value): Add from loc.c.
	(read_pieced_value): Add from loc.c.
	(write_pieced_value): Add from loc.c.
	(check_pieced_synthetic_pointer): Add from loc.c.
	(indirect_pieced_value): Add from loc.c.
	(coerce_pieced_ref): Add from loc.c.
	(copy_pieced_value_closure): Add from loc.c.
	(free_pieced_value_closure): Add from loc.c.
	(sect_variable_value): Add from loc.c.
	* dwarf2/loc.c (sect_variable_value): Move to expr.c.
	(struct piece_closure): Move to expr.c.
	(allocate_piece_closure): Move to expr.c.
	(bits_to_bytes): Move to expr.c.
	(rw_pieced_value): Move to expr.c.
	(read_pieced_value): Move to expr.c.
	(write_pieced_value): Move to expr.c.
	(check_pieced_synthetic_pointer): Move to expr.c.
	(indirect_pieced_value): Move to expr.c.
	(coerce_pieced_ref): Move to expr.c.
	(copy_pieced_value_closure): Move to expr.c.
	(free_pieced_value_closure): Move to expr.c.
	* dwarf2/loc.h (invalid_synthetic_pointer): Expose function.

Change-Id: Ibe6b53db07dc0b6a80138870fa3187cdcce84597
2020-12-08 11:16:19 -05:00
Zoran Zaric
467ee74161 Merge evaluate_for_locexpr_baton evaluator
The evaluate_for_locexpr_baton is the last derived class from the
dwarf_expr_context class. It's purpose is to support the passed in
buffer functionality.

Although, it is not really necessary to merge this class with it's
base class, doing that simplifies new expression evaluator design.

Considering that this functionality is going around the DWARF standard,
it is also reasonable to expect that with a new evaluator design and
extending the push object address functionality to accept any location
description, there will be no need to support passed in buffers.

Alternatively, it would also makes sense to abstract the interaction
between the evaluator and a given resource in the near future. The
passed in buffer would then be a specialization of that abstraction.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::read_mem): Merge with
	evaluate_for_locexpr_baton implementation.
	* dwarf2/loc.c (class evaluate_for_locexpr_baton): Remove
	class.
	(evaluate_for_locexpr_baton::read_mem): Move to
	dwarf_expr_context.
	(dwarf2_locexpr_baton_eval): Instantiate dwarf_expr_context
	instead of evaluate_for_locexpr_baton class.

Change-Id: I7925e07fa93245ea542af81a9f9f0bfbdac7f309
2020-12-08 11:16:19 -05:00
Zoran Zaric
754848f25e Remove empty frame and full evaluators
There are no virtual methods that require different specialization in
dwarf_expr_context class. This means that derived classes
dwarf_expr_executor and dwarf_evaluate_loc_desc are not needed any
more.

As a result of this, the  evaluate_for_locexpr_baton class base class
is now the dwarf_expr_context class.

There might be a need for a better class hierarchy when we know more
about the direction of the future DWARF versions and gdb extensions,
but that is out of the scope of this patch series.

gdb/ChangeLog:

	* dwarf2/frame.c (class dwarf_expr_executor): Remove class.
	(execute_stack_op): Instantiate dwarf_expr_context instead of
	dwarf_evaluate_loc_desc class.
	* dwarf2/loc.c (class dwarf_evaluate_loc_desc): Remove class.
	(dwarf2_evaluate_loc_desc_full): Instantiate dwarf_expr_context
	instead of dwarf_evaluate_loc_desc class.
	(struct evaluate_for_locexpr_baton): Derive from
	dwarf_expr_context.

Change-Id: Iadf2c52002f79a927f0a8327842163bfecfd2db6
2020-12-08 11:16:19 -05:00
Zoran Zaric
c5ce968925 Inline get_reg_value method of dwarf_expr_context
The get_reg_value method is a small function that is only called once,
so it can be inlined to simplify the dwarf_expr_context class.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_reg_value): Remove
	method.
	(dwarf_expr_context::execute_stack_op): Inline get_reg_value
	method.
	* dwarf2/expr.h (dwarf_expr_context::get_reg_value): Remove
	method.

Change-Id: I7bee8587d30983355d371f0769068f712cfbce1c
2020-12-08 11:16:18 -05:00
Zoran Zaric
c729602de0 Move push_dwarf_reg_entry_value to expr.c
Following the idea of merging the evaluators, the
push_dwarf_reg_entry_value method can be moved from
dwarf_expr_executor and dwarf_evaluate_loc_desc classes
to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c
        (dwarf_expr_context::push_dwarf_reg_entry_value): Move from
	dwarf_evaluate_loc_desc.
	* dwarf2/frame.c
	(dwarf_expr_executor::push_dwarf_reg_entry_value): Remove
	method.
	* dwarf2/loc.c (dwarf_expr_reg_to_entry_parameter): Expose
	function.
	(dwarf_evaluate_loc_desc::push_dwarf_reg_entry_value): Move to
	dwarf_expr_context.
	* dwarf2/loc.h (dwarf_expr_reg_to_entry_parameter): Expose
	function.

Change-Id: I770c2eebaf27089384a26ea2f852f44ca2e2d8f9
2020-12-08 11:16:18 -05:00
Zoran Zaric
abcb8a73df Move read_mem to dwarf_expr_context
Following the idea of merging the evaluators, the read_mem method can
be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc classes
to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::read_mem): Move from
	dwarf_evaluate_loc_desc.
	* dwarf2/frame.c (dwarf_expr_executor::read_mem): Remove
	method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::read_mem): Move to
	dwarf_expr_context.

Change-Id: Ied28c2c0fc808fb5e41f9816df8ac731fa4ac2fa
2020-12-08 11:16:18 -05:00
Zoran Zaric
de00629fea Move get_object_address to dwarf_expr_context
Following the idea of merging the evaluators, the get_object_address
and can be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc
classes to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_object_address): Move
	from dwarf_evaluate_loc_desc.
	(class dwarf_expr_context): Add object address member to
	dwarf_expr_context.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_executor::get_object_address):
	Remove method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_object_address):
	move to dwarf_expr_context.
	(class dwarf_evaluate_loc_desc): Move object address member to
	dwarf_expr_context.

Change-Id: I67ee9e004af1f0d513d8832f10948c7cf22dd422
2020-12-08 11:16:18 -05:00
Zoran Zaric
0440f23844 Move dwarf_call to dwarf_expr_context
Following the idea of merging the evaluators, the dwarf_call and
get_frame_pc method can be moved from dwarf_expr_executor and
dwarf_evaluate_loc_desc classes to their base class dwarf_expr_context.
Once this is done, the get_frame_pc can be replace with lambda
function.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::dwarf_call): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_frame_pc): Replace with lambda.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_executor::dwarf_call): Remove
	method.
	(dwarf_expr_executor::get_frame_pc): Remove method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_frame_pc): Remove
	method.
	(dwarf_evaluate_loc_desc::dwarf_call): Move to
	dwarf_expr_context.
	(per_cu_dwarf_call): Inline function.

Change-Id: Ib0b6048e9b264e54cfdbd8dd76f91cd3f76af673
2020-12-08 11:16:18 -05:00
Zoran Zaric
3b3ac5a5fe Move compilation unit info to dwarf_expr_context
This patch moves the compilation unit context information and support
from dwarf_expr_executor and dwarf_evaluate_loc_desc to
dwarf_expr_context evaluator. The idea is to report an error when a
given operation requires a compilation unit information to be resolved,
which is not available.

gdb/ChangeLog:

	* dwarf2/expr.c (ensure_have_per_cu): New function.
	(dwarf_expr_context::dwarf_expr_context): Add compilation unit
	context information.
	(dwarf_expr_context::get_base_type): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_addr_index): Remove method.
	(dwarf_expr_context::dwarf_variable_value): Remove method.
	(dwarf_expr_context::execute_stack_op): Call compilation unit
	context info check. Inline get_addr_index and
	dwarf_variable_value methods.
	* dwarf2/expr.h (struct dwarf_expr_context): Add compilation
	context info. Remove get_addr_index and dwarf_variable_value.
	* dwarf2/frame.c (dwarf_expr_executor::get_addr_index): Remove
	method.
	(dwarf_expr_executor::dwarf_variable_value): Remove method.
	* dwarf2/loc.c (sect_variable_value): Expose function.
	(dwarf_evaluate_loc_desc::get_addr_index): Remove method.
	(dwarf_evaluate_loc_desc::dwarf_variable_value): Remove method.
	(class dwarf_evaluate_loc_desc): Move compilation unit context
	information to dwarf_expr_context class.
	* dwarf2/loc.h (sect_variable_value): Expose function.

Change-Id: Ib159fbbdffb25fa442cd6174d90e9522e8ccc9d1
2020-12-08 11:16:18 -05:00
Zoran Zaric
528991c044 Remove get_frame_cfa from dwarf_expr_context
Following the idea of merging the evaluators, the get_frame_cfa method
can be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc
classes to their base class dwarf_expr_context. Once this is done,
it becomes apparent that the method is only called once and it can be
inlined.

It is also necessary to check if the frame context information was
provided before the DW_OP_call_frame_cfa operation is executed.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_frame_cfa): Remove
	method.
	(dwarf_expr_context::execute_stack_op): Call frame context info
	check for DW_OP_call_frame_cfa. Remove use of get_frame_cfa.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_cfa): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_context::get_frame_cfa): Remove
	method.
	* dwarf2/loc.c (dwarf_expr_context::get_frame_cfa): Remove
	method.

Change-Id: I126a97a51bbb4c97f4ee669c96d054426bd79070
2020-12-08 11:16:18 -05:00
Zoran Zaric
da7e10b674 Move frame context info to dwarf_expr_context
Following 15 patches in this patch series is cleaning up the design of
the DWARF expression evaluator (dwarf_expr_context) to make future
extensions of that evaluator easier and cleaner to implement.

There are three subclasses of the dwarf_expr_context class
(dwarf_expr_executor, dwarf_evaluate_loc_desc and
evaluate_for_locexpr_baton). Here is a short description of each class:

- dwarf_expr_executor is evaluating a DWARF expression in a context
  of a Call Frame Information. The overridden methods of this subclass
  report an error if a specific DWARF operation, represented by that
  method, is not allowed in a CFI context. The source code of this
  subclass lacks the support for composite as well as implicit pointer
  location description.

- dwarf_evaluate_loc_desc can evaluate any expression with no
  restrictions. All of the methods that this subclass overrides are
  actually doing what they are intended to do. This subclass contains
  a full support for all location description types.

- evaluate_for_locexpr_baton subclass is a specialization of the
  dwarf_evaluate_loc_desc subclass and it's function is to add
  support for passed in buffers. This seems to be a way to go around
  the fact that DWARF standard lacks a bit offset support for memory
  location descriptions as well as using any location description for
  the push object address functionality.

It all comes down to this question: what is a function of a DWARF
expression evaluator?

Is it to evaluate the expression in a given context or to check the
correctness of that expression in that context?

Currently, the only reason why there is a dwarf_expr_executor subclass
is to report an invalid DWARF expression in a context of a CFI, but is
that what the evaluator is supposed to do considering that the evaluator
is not tied to a given DWARF version?

There are more and more vendor and GNU extensions that are not part of
the DWARF standard, so is it that impossible to expect that some of the
extensions could actually lift the previously imposed restrictions of
the CFI context? Not to mention that every new DWARF version is lifting
some restrictions anyway.

The thing that makes more sense for an evaluator to do, is to take the
context of an evaluation and checks the requirements of every operation
evaluated against that context. With this approach, the evaluator would
report an error only if parts of the context, necessary for the
evaluation, are missing.

If this approach is taken, then the unification of the
dwarf_evaluate_loc_desc, dwarf_expr_executor and dwarf_expr_context
is the next logical step. This makes a design of the DWARF expression
evaluator cleaner and allows more flexibility when supporting future
vendor and GNU extensions.

Additional benefit here is that now all evaluators have access to all
location description types, which means that a vendor extended CFI
rules could support composite location description as well. This also
means that a new evaluator interface can be changed to return a single
struct value (that describes the result of the evaluation) instead of
a caller poking around the dwarf_expr_context internal data for answers
(like it is done currently).

This patch starts the merging proccess by moving the frame context
information and support from dwarf_expr_executor and
dwarf_evaluate_loc_desc to dwarf_expr_context evaluator. The idea
is to report an error when a given operation requires a frame
information to be resolved, if that information is not present.

gdb/ChangeLog:

	* dwarf2/expr.c (ensure_have_frame): New function.
	(read_addr_from_reg): Add from frame.c.
	(dwarf_expr_context::dwarf_expr_context): Add frame info to
	dwarf_expr_context.
	(dwarf_expr_context::read_addr_from_reg): Remove.
	(dwarf_expr_context::get_reg_value): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_frame_base): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::execute_stack_op): Call frame context info
	check. Remove use of read_addr_from_reg method.
	* dwarf2/expr.h (struct dwarf_expr_context): Add frame info
	member, read_addr_from_reg, get_reg_value and get_frame_base
	declaration.
	(read_addr_from_reg): Move to expr.c.
	* dwarf2/frame.c (read_addr_from_reg): Move to
	dwarf_expr_context.
	(dwarf_expr_executor::read_addr_from_reg): Remove.
	(dwarf_expr_executor::get_frame_base): Remove.
	(dwarf_expr_executor::get_reg_value): Remove.
	(execute_stack_op): Use read_addr_from_reg function instead of
	read_addr_from_reg method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_frame_base): Move
	to dwarf_expr_context.
	(dwarf_evaluate_loc_desc::get_reg_value): Move to
	dwarf_expr_context.
	(dwarf_evaluate_loc_desc::read_addr_from_reg): Remove.
	(dwarf2_locexpr_baton_eval):Use read_addr_from_reg function
	instead of read_addr_from_reg method.

Change-Id: Ic0eb7266933e33330f3feff9140a1d0630c1a589
2020-12-08 11:16:17 -05:00
Zoran Zaric
a358e4f261 Replace the symbol needs evaluator with a parser
This patch addresses a design problem with the symbol_needs_eval_context
class. It exposes the problem by introducing two new testsuite test
cases.

To explain the issue, I first need to explain the dwarf_expr_context
class that the symbol_needs_eval_context class derives from.

The intention behind the dwarf_expr_context class is to commonize the
DWARF expression evaluation mechanism for different evaluation
contexts. Currently in gdb, the evaluation context can contain some or
all of the following information: architecture, object file, frame and
compilation unit.

Depending on the information needed to evaluate a given expression,
there are currently three distinct DWARF expression evaluators:

 - Frame: designed to evaluate an expression in the context of a call
   frame information (dwarf_expr_executor class). This evaluator doesn't
   need a compilation unit information.

 - Location description: designed to evaluate an expression in the
   context of a source level information (dwarf_evaluate_loc_desc
   class). This evaluator expects all information needed for the
   evaluation of the given expression to be present.

 - Symbol needs: designed to answer a question about the parts of the
   context information required to evaluate a DWARF expression behind a
   given symbol (symbol_needs_eval_context class). This evaluator
   doesn't need a frame information.

The functional difference between the symbol needs evaluator and the
others is that this evaluator is not meant to interact with the actual
target. Instead, it is supposed to check which parts of the context
information are needed for the given DWARF expression to be evaluated by
the location description evaluator.

The idea is to take advantage of the existing dwarf_expr_context
evaluation mechanism and to fake all required interactions with the
actual target, by returning back dummy values. The evaluation result is
returned as one of three possible values, based on operations found in a
given expression:

- SYMBOL_NEEDS_NONE,
- SYMBOL_NEEDS_REGISTERS and
- SYMBOL_NEEDS_FRAME.

The problem here is that faking results of target interactions can yield
an incorrect evaluation result.

For example, if we have a conditional DWARF expression, where the
condition depends on a value read from an actual target, and the true
branch of the condition requires a frame information to be evaluated,
while the false branch doesn't, fake target reads could conclude that a
frame information is not needed, where in fact it is. This wrong
information would then cause the expression to be actually evaluated (by
the location description evaluator) with a missing frame information.
This would then crash the debugger.

The gdb.dwarf2/symbol_needs_eval_fail.exp test introduces this
scenario, with the following DWARF expression:

                   DW_OP_addr $some_variable
                   DW_OP_deref
                   DW_OP_bra 4  # conditional jump to DW_OP_bregx
                   DW_OP_lit0
                   DW_OP_skip 3  # jump to DW_OP_stack_value
                   DW_OP_bregx $dwarf_regnum 0
                   DW_OP_stack_value

This expression describes a case where some variable dictates the
location of another variable. Depending on a value of some_variable, the
variable whose location is described by this expression is either read
from a register or it is defined as a constant value 0. In both cases,
the value will be returned as an implicit location description on the
DWARF stack.

Currently, when the symbol needs evaluator fakes a memory read from the
address behind the some_variable variable, the constant value 0 is used
as the value of the variable A, and the check returns the
SYMBOL_NEEDS_NONE result.

This is clearly a wrong result and it causes the debugger to crash.

The scenario might sound strange to some people, but it comes from a
SIMD/SIMT architecture where $some_variable is an execution mask.  In
any case, it is a valid DWARF expression, and GDB shouldn't crash while
evaluating it. Also, a similar example could be made based on a
condition of the frame base value, where if that value is concluded to
be 0, the variable location could be defaulted to a TLS based memory
address.

The gdb.dwarf2/symbol_needs_eval_timeout.exp test introduces a second
scenario. This scenario is a bit more abstract due to the DWARF
assembler lacking the CFI support, but it exposes a different
manifestation of the same problem. Like in the previous scenario, the
DWARF expression used in the test is valid:

                       DW_OP_lit1
                       DW_OP_addr $some_variable
                       DW_OP_deref
                       DW_OP_skip 4  # jump to DW_OP_fbreg
                       DW_OP_drop
                       DW_OP_fbreg 0
                       DW_OP_dup
                       DW_OP_lit0
                       DW_OP_eq
                       DW_OP_bra -9  # conditional jump to DW_OP_drop
                       DW_OP_stack_value

Similarly to the previous scenario, the location of a variable A is an
implicit location description with a constant value that depends on a
value held by a global variable. The difference from the previous case
is that DWARF expression contains a loop instead of just one branch. The
end condition of that loop depends on the expectation that a frame base
value is never zero. Currently, the act of faking the target reads will
cause the symbol needs evaluator to get stuck in an infinite loop.

Somebody could argue that we could change the fake reads to return
something else, but that would only hide the real problem.

The general impression seems to be that the desired design is to have
one class that deals with parsing of the DWARF expression, while there
are virtual methods that deal with specifics of some operations.

Using an evaluator mechanism here doesn't seem to be correct, because
the act of evaluation relies on accessing the data from the actual
target with the possibility of skipping the evaluation of some parts of
the expression.

To better explain the proposed solution for the issue, I first need to
explain a couple more details behind the current design:

There are multiple places in gdb that handle DWARF expression parsing
for different purposes. Some are in charge of converting the expression
to some other internal representation (decode_location_expression,
disassemble_dwarf_expression and dwarf2_compile_expr_to_ax), some are
analysing the expression for specific information
(compute_stack_depth_worker) and some are in charge of evaluating the
expression in a given context (dwarf_expr_context::execute_stack_op
and decode_locdesc).

The problem is that all those functions have a similar (large) switch
statement that handles each DWARF expression operation. The result of
this is a code duplication and harder maintenance.

As a step into the right direction to solve this problem (at least for
the purpose of a DWARF expression evaluation) the expression parsing was
commonized inside of an evaluator base class (dwarf_expr_context). This
makes sense for all derived classes, except for the symbol needs
evaluator (symbol_needs_eval_context) class.

As described previously the problem with this evaluator is that if the
evaluator is not allowed to access the actual target, it is not really
evaluating.

Instead, the desired function of a symbol needs evaluator seems to fall
more into expression analysis category. This means that a more natural
fit for this evaluator is to be a symbol needs analysis, similar to the
existing compute_stack_depth_worker analysis.

Another problem is that using a heavyweight mechanism of an evaluator
to do an expression analysis seems to be an unneeded overhead. It also
requires a more complicated design of the parent class to support fake
target reads.

The reality is that the whole symbol_needs_eval_context class can be
replaced with a lightweight recursive analysis function, that will give
more correct result without compromising the design of the
dwarf_expr_context class.

The downside of this approach is adding of one more similar switch
statement, but at least this way the new symbol needs analysis will be
a lightweight mechnism and it will provide a correct result for any
given DWARF expression.

A more desired long term design would be to have one class that deals
with parsing of the DWARF expression, while there would be a virtual
methods that deal with specifics of some DWARF operations. Then that
class would be used as a base for all DWARF expression parsing mentioned
at the beginning.

This however, requires a far bigger changes that are out of the scope
of this patch series.

To support the new implementation, I have also added two new self tests,
found in a file dwarf2/loc.c (named symbol_needs_cond_nonzero and
symbol_needs_cond_zero), which expose a similar problem to the one
described in the first testsuite test case. The difference is that the
global some_variable is replaced with a use of a
DW_OP_push_object_address operation. The idea is for the symbol needs
evaluator to return the SYMBOL_NEEDS_FRAME value, regardless of the
evaluation of that operation.

The new analysis requires the DWARF location description for the
argc argument of the niam function to change in the assembly file
gdb.python/amd64-py-framefilter-invalidarg.S. Originally, expression
ended with a 0 value byte, which was never reached by the symbol needs
evaluator, because it was detecting a stack underflow when evaluating
the operation before. The new approach does not simulate a DWARF
stack anymore, so the 0 value byte needs to be removed because it
makes the DWARF expression invalid.

gdb/ChangeLog:

        * dwarf2/loc.c (class symbol_needs_eval_context): Remove.
        (dwarf2_get_symbol_read_needs): New function.
        (dwarf2_loc_desc_get_symbol_read_needs): Remove.
        (locexpr_get_symbol_read_needs): Use
        dwarf2_get_symbol_read_needs.
        (symbol_needs_cond_zero): New function.
        (symbol_needs_cond_nonzero): New function.
        (_initialize_dwarf2loc): Register selftests.

gdb/testsuite/ChangeLog:

        * gdb.python/amd64-py-framefilter-invalidarg.S : Update argc
          DWARF location expression.
        * lib/dwarf.exp (_location): Handle DW_OP_fbreg.
        * gdb.dwarf2/symbol_needs_eval.c: New file.
        * gdb.dwarf2/symbol_needs_eval_fail.exp: New file.
        * gdb.dwarf2/symbol_needs_eval_timeout.exp: New file.

Change-Id: I6f18c7cd4c5a7733e0e4372002ec2d2ef44c5601
2020-12-08 11:16:17 -05:00
23 changed files with 4759 additions and 2032 deletions

View File

@@ -606,7 +606,7 @@ coerce_unspec_val_to_type (struct value *val, struct type *type)
else
{
result = allocate_value (type);
value_contents_copy_raw (result, 0, val, 0, TYPE_LENGTH (type));
value_contents_copy_raw (result, 0, val, 0, 0, TYPE_LENGTH (type));
}
set_value_component_location (result, val);
set_value_bitsize (result, value_bitsize (val));

View File

@@ -356,6 +356,17 @@ compute_stack_depth_worker (int start, int *need_tempvar,
(*info)[offset].label = 1;
break;
case DW_OP_LLVM_offset:
case DW_OP_LLVM_bit_offset:
--stack_depth;
break;
case DW_OP_LLVM_undefined:
++stack_depth;
break;
case DW_OP_LLVM_piece_end:
case DW_OP_LLVM_offset_constu:
case DW_OP_nop:
break;

File diff suppressed because it is too large Load Diff

View File

@@ -27,252 +27,56 @@
struct dwarf2_per_objfile;
/* The location of a value. */
enum dwarf_value_location
{
/* The piece is in memory.
The value on the dwarf stack is its address. */
DWARF_VALUE_MEMORY,
/* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
FRAME context. The PER_OBJFILE contains a pointer to the PER_BFD
information. ADDR_SIZE defines a size of the DWARF generic type.
INIT_VALUES vector contains values that are expected to be pushed
on a DWARF expression stack before the evaluation. AS_LVAL defines
if the returned struct value is expected to be a value or a location
description. Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
expected struct value representation of the evaluation result. The
ADDR_INFO property can be specified to override the range of memory
addresses with the passed in buffer. */
struct value *dwarf2_eval_exp (const gdb_byte *addr, size_t len, bool as_lval,
struct dwarf2_per_objfile *per_objfile,
struct dwarf2_per_cu_data *per_cu,
struct frame_info *frame, int addr_size,
std::vector<struct value *> *init_values,
const struct property_addr_info *addr_info,
struct type *type = nullptr,
struct type *subobj_type = nullptr,
LONGEST subobj_offset = 0);
/* The piece is in a register.
The value on the dwarf stack is the register number. */
DWARF_VALUE_REGISTER,
/* The piece is on the dwarf stack. */
DWARF_VALUE_STACK,
/* The piece is a literal. */
DWARF_VALUE_LITERAL,
/* The piece was optimized out. */
DWARF_VALUE_OPTIMIZED_OUT,
/* The piece is an implicit pointer. */
DWARF_VALUE_IMPLICIT_POINTER
};
/* A piece of an object, as recorded by DW_OP_piece or DW_OP_bit_piece. */
struct dwarf_expr_piece
{
enum dwarf_value_location location;
union
{
struct
{
/* This piece's address, for DWARF_VALUE_MEMORY pieces. */
CORE_ADDR addr;
/* Non-zero if the piece is known to be in memory and on
the program's stack. */
bool in_stack_memory;
} mem;
/* The piece's register number, for DWARF_VALUE_REGISTER pieces. */
int regno;
/* The piece's literal value, for DWARF_VALUE_STACK pieces. */
struct value *value;
struct
{
/* A pointer to the data making up this piece,
for DWARF_VALUE_LITERAL pieces. */
const gdb_byte *data;
/* The length of the available data. */
ULONGEST length;
} literal;
/* Used for DWARF_VALUE_IMPLICIT_POINTER. */
struct
{
/* The referent DIE from DW_OP_implicit_pointer. */
sect_offset die_sect_off;
/* The byte offset into the resulting data. */
LONGEST offset;
} ptr;
} v;
/* The length of the piece, in bits. */
ULONGEST size;
/* The piece offset, in bits. */
ULONGEST offset;
};
/* The dwarf expression stack. */
struct dwarf_stack_value
{
dwarf_stack_value (struct value *value_, int in_stack_memory_)
: value (value_), in_stack_memory (in_stack_memory_)
{}
struct value *value;
/* True if the piece is in memory and is known to be on the program's stack.
It is always ok to set this to zero. This is used, for example, to
optimize memory access from the target. It can vastly speed up backtraces
on long latency connections when "set stack-cache on". */
bool in_stack_memory;
};
/* The expression evaluator works with a dwarf_expr_context, describing
its current state and its callbacks. */
struct dwarf_expr_context
{
dwarf_expr_context (dwarf2_per_objfile *per_objfile);
virtual ~dwarf_expr_context () = default;
void push_address (CORE_ADDR value, bool in_stack_memory);
void eval (const gdb_byte *addr, size_t len);
struct value *fetch (int n);
CORE_ADDR fetch_address (int n);
bool fetch_in_stack_memory (int n);
/* The stack of values. */
std::vector<dwarf_stack_value> stack;
/* Target architecture to use for address operations. */
struct gdbarch *gdbarch;
/* Target address size in bytes. */
int addr_size;
/* DW_FORM_ref_addr size in bytes. If -1 DWARF is executed from a frame
context and operations depending on DW_FORM_ref_addr are not allowed. */
int ref_addr_size;
/* The current depth of dwarf expression recursion, via DW_OP_call*,
DW_OP_fbreg, DW_OP_push_object_address, etc., and the maximum
depth we'll tolerate before raising an error. */
int recursion_depth, max_recursion_depth;
/* Location of the value. */
enum dwarf_value_location location;
/* For DWARF_VALUE_LITERAL, the current literal value's length and
data. For DWARF_VALUE_IMPLICIT_POINTER, LEN is the offset of the
target DIE of sect_offset kind. */
ULONGEST len;
const gdb_byte *data;
/* Initialization status of variable: Non-zero if variable has been
initialized; zero otherwise. */
int initialized;
/* A vector of pieces.
Each time DW_OP_piece is executed, we add a new element to the
end of this array, recording the current top of the stack, the
current location, and the size given as the operand to
DW_OP_piece. We then pop the top value from the stack, reset the
location, and resume evaluation.
The Dwarf spec doesn't say whether DW_OP_piece pops the top value
from the stack. We do, ensuring that clients of this interface
expecting to see a value left on the top of the stack (say, code
evaluating frame base expressions or CFA's specified with
DW_CFA_def_cfa_expression) will get an error if the expression
actually marks all the values it computes as pieces.
If an expression never uses DW_OP_piece, num_pieces will be zero.
(It would be nice to present these cases as expressions yielding
a single piece, so that callers need not distinguish between the
no-DW_OP_piece and one-DW_OP_piece cases. But expressions with
no DW_OP_piece operations have no value to place in a piece's
'size' field; the size comes from the surrounding data. So the
two cases need to be handled separately.) */
std::vector<dwarf_expr_piece> pieces;
/* We evaluate the expression in the context of this objfile. */
dwarf2_per_objfile *per_objfile;
/* Return the value of register number REGNUM (a DWARF register number),
read as an address. */
virtual CORE_ADDR read_addr_from_reg (int regnum) = 0;
/* Return a value of type TYPE, stored in register number REGNUM
of the frame associated to the given BATON.
REGNUM is a DWARF register number. */
virtual struct value *get_reg_value (struct type *type, int regnum) = 0;
/* Read LENGTH bytes at ADDR into BUF. */
virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
/* Return the location expression for the frame base attribute, in
START and LENGTH. The result must be live until the current
expression evaluation is complete. */
virtual void get_frame_base (const gdb_byte **start, size_t *length) = 0;
/* Return the CFA for the frame. */
virtual CORE_ADDR get_frame_cfa () = 0;
/* Return the PC for the frame. */
virtual CORE_ADDR get_frame_pc ()
{
error (_("%s is invalid in this context"), "DW_OP_implicit_pointer");
}
/* Return the thread-local storage address for
DW_OP_GNU_push_tls_address or DW_OP_form_tls_address. */
virtual CORE_ADDR get_tls_address (CORE_ADDR offset) = 0;
/* Execute DW_AT_location expression for the DWARF expression
subroutine in the DIE at DIE_CU_OFF in the CU. Do not touch
STACK while it being passed to and returned from the called DWARF
subroutine. */
virtual void dwarf_call (cu_offset die_cu_off) = 0;
/* Execute "variable value" operation on the DIE at SECT_OFF. */
virtual struct value *dwarf_variable_value (sect_offset sect_off) = 0;
/* Return the base type given by the indicated DIE at DIE_CU_OFF.
This can throw an exception if the DIE is invalid or does not
represent a base type. SIZE is non-zero if this function should
verify that the resulting type has the correct size. */
virtual struct type *get_base_type (cu_offset die_cu_off, int size)
{
/* Anything will do. */
return builtin_type (this->gdbarch)->builtin_int;
}
/* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
parameter matching KIND and KIND_U at the caller of specified BATON.
If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
DW_AT_call_value. */
virtual void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
union call_site_parameter_u kind_u,
int deref_size) = 0;
/* Return the address indexed by DW_OP_addrx or DW_OP_GNU_addr_index.
This can throw an exception if the index is out of range. */
virtual CORE_ADDR get_addr_index (unsigned int index) = 0;
/* Return the `object address' for DW_OP_push_object_address. */
virtual CORE_ADDR get_object_address () = 0;
private:
struct type *address_type () const;
void push (struct value *value, bool in_stack_memory);
bool stack_empty_p () const;
void add_piece (ULONGEST size, ULONGEST offset);
void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
void pop ();
};
/* Return the address type used of the GDBARCH architecture and
ADDR_SIZE is expected size of the type. */
struct type *address_type (struct gdbarch *gdbarch, int addr_size);
/* Check that the current operator is either at the end of an
expression, or that it is followed by a composition operator or by
DW_OP_GNU_uninit (which should terminate the expression). */
void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
const char *);
/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_reg* return the
DWARF register number. Otherwise return -1. */
int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end);
/* If <BUF..BUF_END] contains DW_FORM_block* with just DW_OP_breg*(0) and
DW_OP_deref* return the DWARF register number. Otherwise return -1.
DEREF_SIZE_RETURN contains -1 for DW_OP_deref; otherwise it contains the
size from DW_OP_deref_size. */
int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf,
const gdb_byte *buf_end,
CORE_ADDR *deref_size_return);
/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_fbreg(X) fill
in FB_OFFSET_RETURN with the X offset and return 1. Otherwise return 0. */
int dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
CORE_ADDR *fb_offset_return);
/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_bregSP(X) fill
in SP_OFFSET_RETURN with the X offset and return 1. Otherwise return 0.
The matched SP register number depends on GDBARCH. */
int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
const gdb_byte *buf_end,
CORE_ADDR *sp_offset_return);
@@ -312,14 +116,17 @@ gdb_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
return buf + bytes_read;
}
/* Helper to read a uleb128 value or throw an error. */
extern const gdb_byte *safe_read_uleb128 (const gdb_byte *buf,
const gdb_byte *buf_end,
uint64_t *r);
/* Helper to read a sleb128 value or throw an error. */
extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
const gdb_byte *buf_end,
int64_t *r);
/* Helper to skip a leb128 value or throw an error. */
extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
const gdb_byte *buf_end);

View File

@@ -191,17 +191,17 @@ dwarf2_frame_state::dwarf2_frame_state (CORE_ADDR pc_, struct dwarf2_cie *cie)
retaddr_column (cie->return_address_register)
{
}
/* Helper functions for execute_stack_op. */
/* Return the value of register number REG (a DWARF register number),
read as an address in a given FRAME. */
static CORE_ADDR
read_addr_from_reg (struct frame_info *this_frame, int reg)
read_addr_from_reg (struct frame_info *frame, int reg)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct gdbarch *gdbarch = get_frame_arch (frame);
int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
return address_from_register (regnum, this_frame);
return address_from_register (regnum, frame);
}
/* Execute the required actions for both the DW_CFA_restore and
@@ -235,116 +235,35 @@ register %s (#%d) at %s"),
}
}
class dwarf_expr_executor : public dwarf_expr_context
{
public:
dwarf_expr_executor (dwarf2_per_objfile *per_objfile)
: dwarf_expr_context (per_objfile)
{}
struct frame_info *this_frame;
CORE_ADDR read_addr_from_reg (int reg) override
{
return ::read_addr_from_reg (this_frame, reg);
}
struct value *get_reg_value (struct type *type, int reg) override
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
return value_from_register (type, regnum, this_frame);
}
void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
{
read_memory (addr, buf, len);
}
void get_frame_base (const gdb_byte **start, size_t *length) override
{
invalid ("DW_OP_fbreg");
}
void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
union call_site_parameter_u kind_u,
int deref_size) override
{
invalid ("DW_OP_entry_value");
}
CORE_ADDR get_object_address () override
{
invalid ("DW_OP_push_object_address");
}
CORE_ADDR get_frame_cfa () override
{
invalid ("DW_OP_call_frame_cfa");
}
CORE_ADDR get_tls_address (CORE_ADDR offset) override
{
invalid ("DW_OP_form_tls_address");
}
void dwarf_call (cu_offset die_offset) override
{
invalid ("DW_OP_call*");
}
struct value *dwarf_variable_value (sect_offset sect_off) override
{
invalid ("DW_OP_GNU_variable_value");
}
CORE_ADDR get_addr_index (unsigned int index) override
{
invalid ("DW_OP_addrx or DW_OP_GNU_addr_index");
}
private:
void invalid (const char *op) ATTRIBUTE_NORETURN
{
error (_("%s is invalid in this context"), op);
}
};
static CORE_ADDR
static value *
execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
struct frame_info *this_frame, CORE_ADDR initial,
int initial_in_stack_memory, dwarf2_per_objfile *per_objfile)
int initial_in_stack_memory, dwarf2_per_objfile *per_objfile,
struct type* type = nullptr, bool as_lval = true)
{
CORE_ADDR result;
dwarf_expr_executor ctx (per_objfile);
scoped_value_mark free_values;
struct type *init_type = address_type (per_objfile->objfile->arch (),
addr_size);
ctx.this_frame = this_frame;
ctx.gdbarch = get_frame_arch (this_frame);
ctx.addr_size = addr_size;
ctx.ref_addr_size = -1;
struct value *init_value = value_at_lazy (init_type, initial);
std::vector<struct value *> init_values;
ctx.push_address (initial, initial_in_stack_memory);
ctx.eval (exp, len);
set_value_stack (init_value, initial_in_stack_memory);
init_values.push_back (init_value);
if (ctx.location == DWARF_VALUE_MEMORY)
result = ctx.fetch_address (0);
else if (ctx.location == DWARF_VALUE_REGISTER)
result = ctx.read_addr_from_reg (value_as_long (ctx.fetch (0)));
else
{
/* This is actually invalid DWARF, but if we ever do run across
it somehow, we might as well support it. So, instead, report
it as unimplemented. */
error (_("\
Not implemented: computing unwound register using explicit value operator"));
}
struct value *result_val
= dwarf2_eval_exp (exp, len, true, per_objfile, nullptr,
this_frame, addr_size, &init_values, nullptr);
return result;
/* We need to clean up all the values that are not needed any more.
The problem with a value_ref_ptr class is that it disconnects the
RETVAL from the value garbage collection, so we need to make
a copy of that value on the stack to keep everything consistent.
The value_ref_ptr will clean up after itself at the end of this block. */
value_ref_ptr value_holder = value_ref_ptr::new_reference (result_val);
free_values.free_to_mark ();
return value_copy(result_val);
}
@@ -1075,10 +994,14 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
break;
case CFA_EXP:
cache->cfa =
execute_stack_op (fs.regs.cfa_exp, fs.regs.cfa_exp_len,
cache->addr_size, this_frame, 0, 0,
cache->per_objfile);
{
struct value *value
= execute_stack_op (fs.regs.cfa_exp, fs.regs.cfa_exp_len,
cache->addr_size, this_frame, 0, 0,
cache->per_objfile);
cache->cfa = value_address (value);
}
break;
default:
@@ -1276,24 +1199,22 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
return frame_unwind_got_register (this_frame, regnum, realnum);
case DWARF2_FRAME_REG_SAVED_EXP:
addr = execute_stack_op (cache->reg[regnum].loc.exp.start,
return execute_stack_op (cache->reg[regnum].loc.exp.start,
cache->reg[regnum].loc.exp.len,
cache->addr_size,
this_frame, cache->cfa, 1,
cache->per_objfile);
return frame_unwind_got_memory (this_frame, regnum, addr);
cache->addr_size, this_frame,
cache->cfa, 1, cache->per_objfile,
register_type (gdbarch, regnum));
case DWARF2_FRAME_REG_SAVED_VAL_OFFSET:
addr = cache->cfa + cache->reg[regnum].loc.offset;
return frame_unwind_got_constant (this_frame, regnum, addr);
case DWARF2_FRAME_REG_SAVED_VAL_EXP:
addr = execute_stack_op (cache->reg[regnum].loc.exp.start,
return execute_stack_op (cache->reg[regnum].loc.exp.start,
cache->reg[regnum].loc.exp.len,
cache->addr_size,
this_frame, cache->cfa, 1,
cache->per_objfile);
return frame_unwind_got_constant (this_frame, regnum, addr);
cache->addr_size, this_frame,
cache->cfa, 1, cache->per_objfile,
register_type (gdbarch, regnum), false);
case DWARF2_FRAME_REG_UNSPECIFIED:
/* GCC, in its infinite wisdom decided to not provide unwind

File diff suppressed because it is too large Load Diff

View File

@@ -53,16 +53,32 @@ extern void func_get_frame_base_dwarf_block (struct symbol *framefunc,
const gdb_byte **start,
size_t *length);
/* Fetch call_site_parameter from caller matching KIND and KIND_U.
FRAME is for callee.
Function always returns non-NULL, it throws NO_ENTRY_VALUE_ERROR
otherwise. */
struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
(struct frame_info *frame,
enum call_site_parameter_kind kind,
union call_site_parameter_u kind_u,
dwarf2_per_cu_data **per_cu_return,
dwarf2_per_objfile **per_objfile_return);
/* Evaluate a location description, starting at DATA and with length
SIZE, to find the current location of variable of TYPE in the context
of FRAME. */
of FRAME. AS_LVAL defines if the resulting struct value is expected to
be a value or a location description. */
struct value *dwarf2_evaluate_loc_desc (struct type *type,
struct frame_info *frame,
const gdb_byte *data,
size_t size,
dwarf2_per_cu_data *per_cu,
dwarf2_per_objfile *per_objfile);
dwarf2_per_objfile *per_objfile,
bool as_lval = true);
/* A chain of addresses that might be needed to resolve a dynamic
property. */
@@ -264,4 +280,18 @@ extern int dwarf_reg_to_regnum (struct gdbarch *arch, int dwarf_reg);
extern int dwarf_reg_to_regnum_or_error (struct gdbarch *arch,
ULONGEST dwarf_reg);
/* Helper function which throws an error if a synthetic pointer is
invalid. */
extern void invalid_synthetic_pointer (void);
/* Fetch the value pointed to by a synthetic pointer. */
extern struct value *indirect_synthetic_pointer
(sect_offset die, LONGEST byte_offset,
dwarf2_per_cu_data *per_cu,
dwarf2_per_objfile *per_objfile,
struct frame_info *frame,
struct type *type, bool resolve_abstract_p = false);
#endif /* dwarf2loc.h */

View File

@@ -196,7 +196,7 @@ protected:
available offset. */
void copy_element_to_dest (struct value *elt)
{
value_contents_copy (m_dest, m_dest_offset, elt, 0,
value_contents_copy (m_dest, m_dest_offset, elt, 0, 0,
TYPE_LENGTH (value_type (elt)));
m_dest_offset += TYPE_LENGTH (value_type (elt));
}

View File

@@ -871,6 +871,7 @@ read_frame_register_value (struct value *value, struct frame_info *frame)
struct gdbarch *gdbarch = get_frame_arch (frame);
LONGEST offset = 0;
LONGEST reg_offset = value_offset (value);
LONGEST bit_offset = value_bitpos (value);
int regnum = VALUE_REGNUM (value);
int len = type_length_units (check_typedef (value_type (value)));
@@ -894,7 +895,8 @@ read_frame_register_value (struct value *value, struct frame_info *frame)
if (reg_len > len)
reg_len = len;
value_contents_copy (value, offset, regval, reg_offset, reg_len);
value_contents_copy (value, offset, regval, reg_offset,
bit_offset, reg_len);
offset += reg_len;
len -= reg_len;

View File

@@ -1567,25 +1567,49 @@ put_frame_register_bytes (struct frame_info *frame, int regnum,
{
int curr_len = register_size (gdbarch, regnum) - offset;
struct value *value = frame_unwind_register_value (frame->next,
regnum);
if (curr_len > len)
curr_len = len;
if (curr_len == register_size (gdbarch, regnum))
/* Compute value is a special new case. The problem is that
the computed callback mechanism only supports a struct
value arguments, so we need to make one. */
if (value != NULL && VALUE_LVAL (value) == lval_computed)
{
struct value *from_value;
const struct lval_funcs *funcs = value_computed_funcs (value);
struct type * reg_type = register_type (gdbarch, regnum);
if (funcs->write == NULL)
error (_("Attempt to assign to an unmodifiable value."));
from_value = allocate_value (reg_type);
memcpy (value_contents_raw (from_value), myaddr,
TYPE_LENGTH (reg_type));
set_value_offset (value, offset);
funcs->write (value, from_value);
release_value (from_value);
}
else if (curr_len == register_size (gdbarch, regnum))
{
put_frame_register (frame, regnum, myaddr);
}
else
{
struct value *value = frame_unwind_register_value (frame->next,
regnum);
gdb_assert (value != NULL);
memcpy ((char *) value_contents_writeable (value) + offset, myaddr,
curr_len);
memcpy ((char *) value_contents_writeable (value) + offset,
myaddr, curr_len);
put_frame_register (frame, regnum, value_contents_raw (value));
release_value (value);
}
if (value != NULL)
release_value (value);
myaddr += curr_len;
len -= curr_len;
offset = 0;

View File

@@ -0,0 +1,328 @@
# 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 DWARF operation that allow adding byte and bit offset to any
# location description.
#
# In particular, the test uses memory and register location
# descriptions (both as standalone and parts of the composite
# location), and applies different byte and bit offsets to them.
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_var [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 char_type_label int_type_label
declare_labels array_size_4_type_label array_size_8_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}
}
# define int type.
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}
}
# define 4 byte size array type.
array_size_4_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 3 DW_FORM_udata}
}
}
# define 8 byte size array type.
array_size_8_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}
} {
# define original buf variable.
DW_TAG_variable {
{DW_AT_name buf}
{DW_AT_type :$array_size_4_type_label}
{DW_AT_location {
DW_OP_addr $buf_var
} SPECIAL_expr}
}
# defined a variable located in
# a third byte of the buf variable.
DW_TAG_variable {
{DW_AT_name buf_byte_3}
{DW_AT_type :$char_type_label}
{DW_AT_location {
DW_OP_addr $buf_var
DW_OP_LLVM_offset_constu 2
} SPECIAL_expr}
{external 1 flag}
}
# defined a variable located in a second byte
# of the buf variable with a bit offset of one.
DW_TAG_variable {
{DW_AT_name buf_byte_2_bit_1}
{DW_AT_type :$char_type_label}
{DW_AT_location {
DW_OP_addr $buf_var
DW_OP_lit9
DW_OP_LLVM_bit_offset
} SPECIAL_expr}
{external 1 flag}
}
# defined a variable located in a
# third byte of the REGNAME register.
DW_TAG_variable {
{DW_AT_name reg_byte_3}
{DW_AT_type :$char_type_label}
{DW_AT_location {
DW_OP_regx $dwarf_regnum
DW_OP_lit2
DW_OP_LLVM_offset
} SPECIAL_expr}
{external 1 flag}
}
# defined a variable located in a second byte of
# the REGNAME register with a bit offset of one.
DW_TAG_variable {
{DW_AT_name reg_byte_2_bit_1}
{DW_AT_type :$char_type_label}
{DW_AT_location {
DW_OP_regx $dwarf_regnum
DW_OP_lit1
DW_OP_LLVM_offset
DW_OP_lit1
DW_OP_LLVM_bit_offset
} SPECIAL_expr}
{external 1 flag}
}
# Define an array variable spread in different
# pieces of buf variable and REGNAME register.
DW_TAG_variable {
{DW_AT_name mix_array}
{DW_AT_type :$array_size_8_type_label}
{DW_AT_location {
# a byte piece located in a
# fourth byte of the buf variable.
DW_OP_addr $buf_var
DW_OP_LLVM_offset_constu 3
DW_OP_piece 0x1
# a byte piece located in a
# third byte of the buf variable.
DW_OP_addr $buf_var
DW_OP_lit2
DW_OP_LLVM_offset
DW_OP_piece 0x1
# a byte piece located in a second byte of
# the buf variable with a bit offset of one.
DW_OP_addr $buf_var
DW_OP_lit1
DW_OP_LLVM_offset
DW_OP_lit1
DW_OP_LLVM_bit_offset
DW_OP_piece 0x1
# a four bit piece located in a first byte
# of the buf variable with a bit offset of one.
DW_OP_addr $buf_var
DW_OP_LLVM_offset_constu 0
DW_OP_bit_piece 0x4 0x1
# a four bit piece located in a first byte of
# the buf variable with a bit offset of eight.
DW_OP_addr $buf_var
DW_OP_lit1
DW_OP_LLVM_bit_offset
DW_OP_LLVM_offset_constu 0
DW_OP_bit_piece 0x4 0x7
# a byte piece located in a fourth
# byte of the REGNAME register.
DW_OP_regx $dwarf_regnum
DW_OP_LLVM_offset_constu 3
DW_OP_piece 0x1
# a byte piece located in a third
# byte of the REGNAME register.
DW_OP_regx $dwarf_regnum
DW_OP_lit2
DW_OP_LLVM_offset
DW_OP_piece 0x1
# a byte piece located in a second byte of the
# REGNAME register with a bit offset of one.
DW_OP_regx $dwarf_regnum
DW_OP_lit1
DW_OP_LLVM_offset
DW_OP_lit1
DW_OP_LLVM_bit_offset
DW_OP_piece 0x1
# a four bit piece located in a first byte of
# the REGNAME register with a bit offset of one.
DW_OP_regx $dwarf_regnum
DW_OP_LLVM_offset_constu 0
DW_OP_bit_piece 0x4 0x1
# a four bit piece located in a first byte of the
# REGNAME register with a bit offset of eight.
DW_OP_regx $dwarf_regnum
DW_OP_lit1
DW_OP_LLVM_bit_offset
DW_OP_LLVM_offset_constu 0
DW_OP_bit_piece 0x4 0x7
} SPECIAL_expr}
{external 1 flag}
}
}
}
}
}
if { [prepare_for_testing ${testfile}.exp ${testfile} \
[list $srcfile $asm_file] {nodebug}] } {
return -1
}
if ![runto_main] {
return -1
}
# Determine byte order.
set endian [get_endianness]
if { $endian != "little" } then {
verbose "Skipping ${gdb_test_file_name}."
return
}
gdb_test_no_output "set var \$$regname = 0x04030201" "init reg"
gdb_test_no_output "set var *\(\(unsigned int *\) buf\) = 0x04030201" \
"init buf"
gdb_test "print/x buf_byte_3" " = 0x3" "buf_byte_3 == 0x3"
gdb_test "print/x buf_byte_2_bit_1" " = 0x81" \
"print buf_byte_2_bit_1"
gdb_test "print/x reg_byte_3" " = 0x3" "reg_byte_3 == 0x3"
gdb_test "print/x reg_byte_2_bit_1" " = 0x81" \
"print reg_byte_2_bit_1"
gdb_test_no_output "set var buf_byte_3 = 0x4" "init buf_byte_3 to 0x4"
gdb_test "print/x buf_byte_3" " = 0x4" "buf_byte_3 == 0x4"
gdb_test_no_output "set var buf_byte_2_bit_1 = 0x4" \
"init buf_byte_2_bit_1 to 0x4"
gdb_test "print/x buf_byte_2_bit_1" " = 0x4" "buf_byte_2_bit_1 == 0x4"
gdb_test "print/x buf" " = \\{0x1, 0x8, 0x4, 0x4\\}" "buf print"
gdb_test_no_output "set var reg_byte_3 = 0x4" "init reg_byte_3 to 0x4"
gdb_test "print/x reg_byte_3" " = 0x4" "reg_byte_3 == 0x4"
gdb_test_no_output "set var reg_byte_2_bit_1 = 0x4" \
"init reg_byte_2_bit_1 to 0x4"
gdb_test "print/x reg_byte_2_bit_1" " = 0x4" "reg_byte_2_bit_1 == 0x4"
gdb_test "print/x \$$regname" " = 0x4040801" "\$$regname print"
gdb_test_no_output "set var \$$regname = 0x04030201" "reset reg"
gdb_test_no_output "set var *\(\(unsigned int *\) buf\) = 0x04030201" \
"reset buf"
gdb_test "print/x mix_array" \
" = \\{0x4, 0x3, 0x81, 0x20, 0x4, 0x3, 0x81, 0x20\\}" \
"mix_array print"
gdb_test_no_output "set var mix_array\[1\] = 0x4" \
"set mix_array second byte"
gdb_test_no_output "set var mix_array\[2\] = 0x4" \
"set mix_array third byte"
gdb_test_no_output "set var mix_array\[5\] = 0x4" \
"set mix_array fifth byte"
gdb_test_no_output "set var mix_array\[6\] = 0x4" \
"set mix_array sixth byte"
gdb_test "print/x mix_array" \
" = \\{0x4, 0x4, 0x4, 0x80, 0x4, 0x4, 0x4, 0x80\\}" \
"mix_array second print"
gdb_test "print/x buf" " = \\{0x1, 0x8, 0x4, 0x4\\}" "buf second print"
gdb_test "print/x \$$regname" " = 0x4040801" "\$$regname second print"

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

@@ -0,0 +1,144 @@
# 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 new DW_OP_LLVM_undefined operation.
#
# The test uses a composite location description, where some pieces
# of that location description are undefined location description.
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 int_type_label char_type_label array_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 5 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 in a
# REGNAME register.
DW_TAG_variable {
{DW_AT_name var_array}
{DW_AT_type :$array_type_label}
{DW_AT_location {
DW_OP_LLVM_undefined
DW_OP_piece 0x1
DW_OP_regx $dwarf_regnum
DW_OP_piece 0x4
DW_OP_LLVM_undefined
DW_OP_piece 0x1
} SPECIAL_expr}
}
DW_TAG_variable {
{DW_AT_name var_int}
{DW_AT_type :$int_type_label}
{DW_AT_location {
DW_OP_LLVM_undefined
} 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 = 0x04030201" "init reg"
# Determine byte order.
set endian [get_endianness]
switch $endian {
little {set val "<optimized out>, 0x1, 0x2, 0x3, 0x4, <optimized out>"}
big {set val "<optimized out>, 0x4, 0x3, 0x2, 0x1, <optimized out>"}
}
gdb_test "print/x var_array" " = \\{${val}\\}" "var_array print"
gdb_test "print/x var_int" " = <optimized out>" "var_int print"

View File

@@ -0,0 +1,25 @@
/* This testcase is part of GDB, the GNU debugger.
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/>. */
int exec_mask = 1;
int
main (void)
{
asm volatile ("main_label: .globl main_label");
return 0;
}

View File

@@ -0,0 +1,108 @@
# 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 symbol needs check mechanism if it assumes that faking
# reads from a target is a safe thing to do.
#
# In particular, the test uses a relative branch DWARF operation to
# hide a register read. If the target reads are indeed faked, the
# result returned will be wrong.
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 symbol_needs_eval.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
set exec_mask_var [gdb_target_symbol exec_mask]
cu {} {
DW_TAG_compile_unit {
{DW_AT_name symbol_needs_eval.c}
{DW_AT_comp_dir /tmp}
} {
declare_labels int_type_label
# define int type
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}
}
# define artificial variable a
DW_TAG_variable {
{DW_AT_name a}
{DW_AT_type :$int_type_label}
{DW_AT_location {
DW_OP_addr $exec_mask_var
DW_OP_deref
DW_OP_bra 4 # conditional jump to DW_OP_bregx
DW_OP_lit0
DW_OP_skip 3 # jump to DW_OP_stack_value
DW_OP_bregx $dwarf_regnum 0
DW_OP_stack_value
} SPECIAL_expr}
{external 1 flag}
}
}
}
}
if { [prepare_for_testing ${testfile}.exp ${testfile} \
[list $srcfile $asm_file] {nodebug}] } {
return -1
}
# The variable's location expression requires a frame,
# so an error should be reported.
gdb_test "print/d a" "No frame selected." "variable a can't be printed"
if ![runto_main] {
return -1
}
gdb_test_no_output "set var \$$regname = 2" "init reg to 2"
gdb_test "print/d a" " = 2" "a == 2"

View File

@@ -0,0 +1,127 @@
# 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 symbol needs check mechanism if it assumes that faking
# reads from a target is a safe thing to do.
#
# In particular, the test uses a relative branch DWARF operation to
# potentially cause an infinite loop, if the target reads are indeed
# faked.
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 symbol_needs_eval.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
set exec_mask_var [gdb_target_symbol exec_mask]
cu {} {
DW_TAG_compile_unit {
{DW_AT_name symbol_needs_eval.c}
{DW_AT_comp_dir /tmp}
} {
declare_labels int_type_label
# define int type
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}
}
# add info for variable exec_mask
DW_TAG_variable {
{DW_AT_name exec_mask}
{DW_AT_type :$int_type_label}
{DW_AT_location {
DW_OP_addr $exec_mask_var
} SPECIAL_expr}
{external 1 flag}
}
# add info for subprogram main
DW_TAG_subprogram {
{MACRO_AT_func { main }}
{DW_AT_frame_base {
DW_OP_regx $dwarf_regnum
} SPECIAL_expr}
} {
# define artificial variable a
DW_TAG_variable {
{DW_AT_name a}
{DW_AT_type :$int_type_label}
{DW_AT_location {
DW_OP_lit1
DW_OP_addr $exec_mask_var
DW_OP_deref
DW_OP_skip 4 # jump to DW_OP_fbreg
DW_OP_drop
DW_OP_fbreg 0
DW_OP_dup
DW_OP_lit0
DW_OP_eq
DW_OP_bra -9 # conditional jump to DW_OP_drop
DW_OP_stack_value
} SPECIAL_expr}
{external 1 flag}
}
}
}
}
}
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 = 2" "init reg to 2"
gdb_test_no_output "set var exec_mask = 0" "init exec_mask to 0"
gdb_test "print/d a" " = 2" "a == 2"

View File

@@ -102,7 +102,6 @@ die4e:
.uleb128 1f - 2f # DW_AT_location
2:
.byte 0x13 # DW_OP_drop
.quad 0
1:
#endif
die5c:

View File

@@ -63,4 +63,4 @@ set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
gdb_test_no_output "source ${remote_python_file}" "load python file"
gdb_test "bt" "niam \\(argc=<error reading variable: dwarf expression stack underflow>, argv=0x\[0-9a-f\]+\\) at py-framefilter-invalidarg.c:\[0-9\]+" "bt full with filters"
gdb_test "bt" "niam \\(argc=<error reading variable: dwarf expression stack underflow>, argv=0x\[0-9a-f\]+\\) at py-framefilter-invalidarg.c:\[0-9\]+" "bt full with filters"

View File

@@ -1018,6 +1018,14 @@ namespace eval Dwarf {
_op .sleb128 [lindex $line 2]
}
DW_OP_fbreg {
_op .sleb128 [lindex $line 1]
}
DW_OP_LLVM_offset_constu {
_op .uleb128 [lindex $line 1]
}
default {
if {[llength $line] > 1} {
error "Unimplemented: operands in location for $opcode"

View File

@@ -1018,20 +1018,31 @@ read_value_memory (struct value *val, LONGEST bit_offset,
ULONGEST xfered_total = 0;
struct gdbarch *arch = get_value_arch (val);
int unit_size = gdbarch_addressable_memory_unit_size (arch);
bool big_endian = type_byte_order (value_type (val)) == BFD_ENDIAN_BIG;
enum target_object object;
size_t extended_length
= length + (bit_offset + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
gdb_byte *buffer_ptr = buffer;
gdb::byte_vector temp_buffer;
if (bit_offset)
{
temp_buffer.resize (extended_length);
buffer_ptr = temp_buffer.data ();
}
object = stack ? TARGET_OBJECT_STACK_MEMORY : TARGET_OBJECT_MEMORY;
while (xfered_total < length)
while (xfered_total < extended_length)
{
enum target_xfer_status status;
ULONGEST xfered_partial;
status = target_xfer_partial (current_top_target (),
object, NULL,
buffer + xfered_total * unit_size, NULL,
buffer_ptr + xfered_total * unit_size, NULL,
memaddr + xfered_total,
length - xfered_total,
extended_length - xfered_total,
&xfered_partial);
if (status == TARGET_XFER_OK)
@@ -1048,6 +1059,10 @@ read_value_memory (struct value *val, LONGEST bit_offset,
xfered_total += xfered_partial;
QUIT;
}
if (bit_offset)
copy_bitwise (buffer, 0, temp_buffer.data (),
bit_offset, length * HOST_CHAR_BIT, big_endian);
}
/* Store the contents of FROMVAL into the location of TOVAL.
@@ -1119,7 +1134,7 @@ value_assign (struct value *toval, struct value *fromval)
const gdb_byte *dest_buffer;
CORE_ADDR changed_addr;
int changed_len;
gdb_byte buffer[sizeof (LONGEST)];
gdb::byte_vector buffer;
if (value_bitsize (toval))
{
@@ -1145,10 +1160,25 @@ value_assign (struct value *toval, struct value *fromval)
"don't fit in a %d bit word."),
(int) sizeof (LONGEST) * HOST_CHAR_BIT);
read_memory (changed_addr, buffer, changed_len);
modify_field (type, buffer, value_as_long (fromval),
buffer.resize (changed_len);
read_memory (changed_addr, buffer.data (), changed_len);
modify_field (type, buffer.data (), value_as_long (fromval),
value_bitpos (toval), value_bitsize (toval));
dest_buffer = buffer;
dest_buffer = buffer.data ();
}
else if (value_bitpos (toval))
{
int bitpos = value_bitpos (toval);
bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
changed_addr = value_address (toval);
changed_len = TYPE_LENGTH (type)
+ (bitpos + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
buffer.resize (changed_len);
read_memory (changed_addr, buffer.data (), changed_len);
copy_bitwise (buffer.data (), bitpos, value_contents (fromval),
0, TYPE_LENGTH (type) * HOST_CHAR_BIT, big_endian);
dest_buffer = buffer.data();
}
else
{
@@ -1163,10 +1193,6 @@ value_assign (struct value *toval, struct value *fromval)
case lval_register:
{
struct frame_info *frame;
struct gdbarch *gdbarch;
int value_reg;
/* Figure out which frame this is in currently.
We use VALUE_FRAME_ID for obtaining the value's frame id instead of
@@ -1174,35 +1200,45 @@ value_assign (struct value *toval, struct value *fromval)
put_frame_register_bytes() below. That function will (eventually)
perform the necessary unwind operation by first obtaining the next
frame. */
frame = frame_find_by_id (VALUE_FRAME_ID (toval));
value_reg = VALUE_REGNUM (toval);
struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval));
if (!frame)
error (_("Value being assigned to is no longer active."));
gdbarch = get_frame_arch (frame);
struct gdbarch *gdbarch = get_frame_arch (frame);
int value_reg = VALUE_REGNUM (toval);
LONGEST bitpos = value_bitpos (toval);
LONGEST bitsize = value_bitsize (toval);
LONGEST offset = value_offset (toval);
if (value_bitsize (toval))
if (bitpos || bitsize)
{
struct value *parent = value_parent (toval);
LONGEST offset = value_offset (parent) + value_offset (toval);
int changed_len;
gdb_byte buffer[sizeof (LONGEST)];
bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
if (bitsize)
{
changed_len = (bitpos + bitsize + HOST_CHAR_BIT - 1)
/ HOST_CHAR_BIT;
if (changed_len > (int) sizeof (LONGEST))
error (_("Can't handle bitfields which "
"don't fit in a %d bit word."),
(int) sizeof (LONGEST) * HOST_CHAR_BIT);
}
else
{
changed_len = TYPE_LENGTH (type)
+ (bitpos + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
bitsize = TYPE_LENGTH (type) * HOST_CHAR_BIT;
}
gdb::byte_vector buffer (changed_len);
int optim, unavail;
changed_len = (value_bitpos (toval)
+ value_bitsize (toval)
+ HOST_CHAR_BIT - 1)
/ HOST_CHAR_BIT;
if (changed_len > (int) sizeof (LONGEST))
error (_("Can't handle bitfields which "
"don't fit in a %d bit word."),
(int) sizeof (LONGEST) * HOST_CHAR_BIT);
if (!get_frame_register_bytes (frame, value_reg, offset,
changed_len, buffer,
changed_len, buffer.data (),
&optim, &unavail))
{
if (optim)
@@ -1213,11 +1249,11 @@ value_assign (struct value *toval, struct value *fromval)
_("value is not available"));
}
modify_field (type, buffer, value_as_long (fromval),
value_bitpos (toval), value_bitsize (toval));
copy_bitwise (buffer.data (), bitpos, value_contents (fromval),
0, bitsize, big_endian);
put_frame_register_bytes (frame, value_reg, offset,
changed_len, buffer);
changed_len, buffer.data ());
}
else
{
@@ -1234,8 +1270,7 @@ value_assign (struct value *toval, struct value *fromval)
else
{
put_frame_register_bytes (frame, value_reg,
value_offset (toval),
TYPE_LENGTH (type),
offset, TYPE_LENGTH (type),
value_contents (fromval));
}
}
@@ -1337,21 +1372,22 @@ value_assign (struct value *toval, struct value *fromval)
struct value *
value_repeat (struct value *arg1, int count)
{
struct value *val;
if (VALUE_LVAL (arg1) != lval_memory)
error (_("Only values in memory can be extended with '@'."));
if (count < 1)
error (_("Invalid number %d of repetitions."), count);
val = allocate_repeat_value (value_enclosing_type (arg1), count);
struct value *val
= allocate_repeat_value (value_enclosing_type (arg1), count);
VALUE_LVAL (val) = lval_memory;
set_value_address (val, value_address (arg1));
set_value_bitpos (val, value_bitpos (arg1));
struct type *enclosing_type = value_enclosing_type (val);
read_value_memory (val, 0, value_stack (val), value_address (val),
value_contents_all_raw (val),
type_length_units (value_enclosing_type (val)));
read_value_memory (val, value_bitpos (val), value_stack (val),
value_address (val), value_contents_all_raw (val),
type_length_units (enclosing_type));
return val;
}
@@ -1700,7 +1736,7 @@ value_array (int lowbound, int highbound, struct value **elemvec)
{
val = allocate_value (arraytype);
for (idx = 0; idx < nelem; idx++)
value_contents_copy (val, idx * typelength, elemvec[idx], 0,
value_contents_copy (val, idx * typelength, elemvec[idx], 0, 0,
typelength);
return val;
}
@@ -1710,7 +1746,8 @@ value_array (int lowbound, int highbound, struct value **elemvec)
val = allocate_value (arraytype);
for (idx = 0; idx < nelem; idx++)
value_contents_copy (val, idx * typelength, elemvec[idx], 0, typelength);
value_contents_copy (val, idx * typelength, elemvec[idx], 0, 0,
typelength);
return val;
}
@@ -3977,7 +4014,7 @@ value_slice (struct value *array, int lowbound, int length)
else
{
slice = allocate_value (slice_type);
value_contents_copy (slice, 0, array, offset,
value_contents_copy (slice, 0, array, offset, 0,
type_length_units (slice_type));
}

View File

@@ -1306,9 +1306,10 @@ value_ranges_copy_adjusted (struct value *dst, int dst_bit_offset,
void
value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
struct value *src, LONGEST src_offset, LONGEST length)
struct value *src, LONGEST src_offset,
LONGEST src_bit_offset, LONGEST length)
{
LONGEST src_bit_offset, dst_bit_offset, bit_length;
LONGEST src_total_bit_offset, dst_total_bit_offset, bit_length;
struct gdbarch *arch = get_value_arch (src);
int unit_size = gdbarch_addressable_memory_unit_size (arch);
@@ -1327,17 +1328,29 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
TARGET_CHAR_BIT * length));
/* Copy the data. */
memcpy (value_contents_all_raw (dst) + dst_offset * unit_size,
value_contents_all_raw (src) + src_offset * unit_size,
length * unit_size);
/* Copy the meta-data, adjusted. */
src_bit_offset = src_offset * unit_size * HOST_CHAR_BIT;
dst_bit_offset = dst_offset * unit_size * HOST_CHAR_BIT;
bit_length = length * unit_size * HOST_CHAR_BIT;
value_ranges_copy_adjusted (dst, dst_bit_offset,
src, src_bit_offset,
if (src_bit_offset)
{
bool big_endian = type_byte_order (value_type (dst)) == BFD_ENDIAN_BIG;
copy_bitwise (value_contents_all_raw (dst) + dst_offset * unit_size, 0,
value_contents_all_raw (src) + src_offset * unit_size,
src_bit_offset, bit_length, big_endian);
}
else
memcpy (value_contents_all_raw (dst) + dst_offset * unit_size,
value_contents_all_raw (src) + src_offset * unit_size,
length * unit_size);
/* Copy the meta-data, adjusted. */
src_total_bit_offset = src_offset * unit_size * HOST_CHAR_BIT
+ src_bit_offset;
dst_total_bit_offset = dst_offset * unit_size * HOST_CHAR_BIT;
value_ranges_copy_adjusted (dst, dst_total_bit_offset,
src, src_total_bit_offset,
bit_length);
}
@@ -1353,12 +1366,14 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
void
value_contents_copy (struct value *dst, LONGEST dst_offset,
struct value *src, LONGEST src_offset, LONGEST length)
struct value *src, LONGEST src_offset,
LONGEST src_bit_offset, LONGEST length)
{
if (src->lazy)
value_fetch_lazy (src);
value_contents_copy_raw (dst, dst_offset, src, src_offset, length);
value_contents_copy_raw (dst, dst_offset, src, src_offset,
src_bit_offset, length);
}
int
@@ -3015,7 +3030,7 @@ value_primitive_field (struct value *arg1, LONGEST offset,
else
{
v = allocate_value (value_enclosing_type (arg1));
value_contents_copy_raw (v, 0, arg1, 0,
value_contents_copy_raw (v, 0, arg1, 0, 0,
TYPE_LENGTH (value_enclosing_type (arg1)));
}
v->type = type;
@@ -3050,7 +3065,7 @@ value_primitive_field (struct value *arg1, LONGEST offset,
v = allocate_value (type);
value_contents_copy_raw (v, value_embedded_offset (v),
arg1, value_embedded_offset (arg1) + offset,
type_length_units (type));
0, type_length_units (type));
}
v->offset = (value_offset (arg1) + offset
+ value_embedded_offset (arg1));
@@ -3401,7 +3416,7 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
/* Pack NUM into BUF using a target format of TYPE. */
static void
void
pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
{
LONGEST len;
@@ -3644,7 +3659,7 @@ value_from_component (struct value *whole, struct type *type, LONGEST offset)
v = allocate_value (type);
value_contents_copy (v, value_embedded_offset (v),
whole, value_embedded_offset (whole) + offset,
type_length_units (type));
0, type_length_units (type));
}
v->offset = value_offset (whole) + offset + value_embedded_offset (whole);
set_value_component_location (v, whole);
@@ -3827,9 +3842,9 @@ value_fetch_lazy_memory (struct value *val)
struct type *type = check_typedef (value_enclosing_type (val));
if (TYPE_LENGTH (type))
read_value_memory (val, 0, value_stack (val),
addr, value_contents_all_raw (val),
type_length_units (type));
read_value_memory (val, value_bitpos (val), value_stack (val),
addr, value_contents_all_raw (val),
type_length_units (type));
}
/* Helper for value_fetch_lazy when the value is in a register. */
@@ -3842,10 +3857,6 @@ value_fetch_lazy_register (struct value *val)
struct type *type = check_typedef (value_type (val));
struct value *new_val = val, *mark = value_mark ();
/* Offsets are not supported here; lazy register values must
refer to the entire register. */
gdb_assert (value_offset (val) == 0);
while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
{
struct frame_id next_frame_id = VALUE_NEXT_FRAME_ID (new_val);
@@ -3888,6 +3899,11 @@ value_fetch_lazy_register (struct value *val)
_("infinite loop while fetching a register"));
}
/* Check if NEW_VALUE is big enough to cover
the expected VAL type with an offset. */
gdb_assert ((TYPE_LENGTH (type) + value_offset (val))
<= TYPE_LENGTH (value_type (new_val)));
/* If it's still lazy (for instance, a saved register on the
stack), fetch it. */
if (value_lazy (new_val))
@@ -3896,9 +3912,9 @@ value_fetch_lazy_register (struct value *val)
/* Copy the contents and the unavailability/optimized-out
meta-data from NEW_VAL to VAL. */
set_value_lazy (val, 0);
value_contents_copy (val, value_embedded_offset (val),
new_val, value_embedded_offset (new_val),
type_length_units (type));
value_contents_copy (val, value_embedded_offset (val), new_val,
value_embedded_offset (new_val) + value_offset (val),
value_bitpos (val), type_length_units (type));
if (frame_debug)
{

View File

@@ -677,6 +677,8 @@ extern struct value *value_field_bitfield (struct type *type, int fieldno,
const struct value *val);
extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
extern void pack_unsigned_long (gdb_byte *buf, struct type *type,
ULONGEST num);
extern struct value *value_from_longest (struct type *type, LONGEST num);
extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
@@ -737,10 +739,10 @@ extern struct value *allocate_value (struct type *type);
extern struct value *allocate_value_lazy (struct type *type);
extern void value_contents_copy (struct value *dst, LONGEST dst_offset,
struct value *src, LONGEST src_offset,
LONGEST length);
LONGEST src_bit_offset, LONGEST length);
extern void value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
struct value *src, LONGEST src_offset,
LONGEST length);
LONGEST src_bit_offset, LONGEST length);
extern struct value *allocate_repeat_value (struct type *type, int count);

View File

@@ -704,6 +704,12 @@ DW_OP (DW_OP_PGI_omp_thread_num, 0xf8)
to 0 except explicitly documented for one action. Please refer AArch64 DWARF
ABI documentation for details. */
DW_OP (DW_OP_AARCH64_operation, 0xea)
/* LLVM extensions for heterogeneous targets */
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)