[PATCH 41/43] Add DWARF operations for byte and bit offset

Zoran Zaric via Gdb-patches
2021-03-01T14:48:15Z

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 call.
* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
DWARF operations support.
* dwarf2/expr.c (dwarf_register::to_gdb_value): Add bit offset
support.
(dwarf_register::to_gdb_value): Add bit offset support.
(dwarf_register::to_gdb_value): Add source bit
offset argument to the value_contents_copy call.
(dwarf_expr_context::execute_stack_op): Add new DWARF
operations support.
* findvar.c (read_frame_register_value): Add source bit offset
argument to the value_contents_copy 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 call.
(value_slice): Add source bit offset argument to the
value_contents_copy 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 call.
(value_from_component): Add source bit offset argument to the
value_contents_copy 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.
---
gdb/ada-lang.c | 2 +-
gdb/compile/compile-loc2c.c | 6 +
gdb/dwarf2/expr.c | 48 ++-
gdb/f-lang.c | 4 +-
gdb/findvar.c | 4 +-
gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp | 328 +++++++++++++++++++
gdb/testsuite/lib/dwarf.exp | 4 +
gdb/valops.c | 129 +++++---
gdb/value.c | 66 ++--
gdb/value.h | 2 +-
include/dwarf2.def | 4 +
11 files changed, 520 insertions(+), 77 deletions(-)
create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 419347f776d..197d6ba8dcb 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -611,7 +611,7 @@ coerce_unspec_val_to_type (struct value *val, struct type *type)
else
{
result = allocate_value (type);
- value_contents_copy (result, 0, val, 0, TYPE_LENGTH (type));
+ value_contents_copy (result, 0, val, 0, 0, TYPE_LENGTH (type));
}
set_value_component_location (result, val);
set_value_bitsize (result, value_bitsize (val));
diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index ef819799eb4..58558fa7d22 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -356,6 +356,12 @@ 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_offset_constu:
case DW_OP_nop:
break;

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 871f52acfd8..19e5f5e93b5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1079,6 +1079,7 @@ dwarf_memory::to_gdb_value (struct frame_info *frame, struct type *type,
address = value_as_address (value_from_pointer (ptr_type, address));
struct value *retval = value_at_lazy (subobj_type, address + subobj_offset);
set_value_stack (retval, m_stack);
+ set_value_bitpos (retval, m_bit_suboffset);
return retval;
}

@@ -1223,6 +1224,8 @@ dwarf_register::to_gdb_value (struct frame_info *frame, struct type *type,
else
set_value_offset (retval, retval_offset + m_offset);

+ set_value_bitpos (retval, m_bit_suboffset);
+
/* Get the data. */
read_frame_register_value (retval, frame);

@@ -1234,7 +1237,7 @@ dwarf_register::to_gdb_value (struct frame_info *frame, struct type *type,
return a generic optimized out value instead, so that we show
<optimized out> instead of <not saved>. */
struct value *temp = allocate_value (subobj_type);
- value_contents_copy (temp, 0, retval, 0, TYPE_LENGTH (subobj_type));
+ value_contents_copy (temp, 0, retval, 0, 0, TYPE_LENGTH (subobj_type));
retval = temp;
}

@@ -3792,6 +3795,49 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
this->addr_info->addr);
break;

+ case DW_OP_LLVM_offset:
+ {
+ auto value = fetch (0)->to_value (address_type);
+ pop ();
+
+ dwarf_require_integral (value->get_type ());
+
+ auto location = fetch (0)->to_location (this->gdbarch);
+ pop ();
+
+ location->add_bit_offset (value->to_long () * HOST_CHAR_BIT);
+ result_entry = location;
+ }
+ break;
+
+ case DW_OP_LLVM_offset_constu:
+ {
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
+ result = uoffset;
+
+ auto location = fetch (0)->to_location (this->gdbarch);
+ pop ();
+
+ location->add_bit_offset (result * HOST_CHAR_BIT);
+ result_entry = location;
+ }
+ break;
+
+ case DW_OP_LLVM_bit_offset:
+ {
+ auto value = fetch (0)->to_value (address_type);
+ pop ();
+
+ dwarf_require_integral (value->get_type ());
+
+ auto location = fetch (0)->to_location (this->gdbarch);
+ pop ();
+
+ location->add_bit_offset (value->to_long ());
+ result_entry = location;
+ }
+ break;
+
default:
error (_("Unhandled dwarf expression opcode 0x%x"), op);
}
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 01de51837f6..161398e0c5b 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -171,7 +171,7 @@ fortran_bounds_all_dims (bool lbound_p,
gdb_assert (dst_offset + TYPE_LENGTH (value_type (v))
<= TYPE_LENGTH (value_type (result)));
gdb_assert (TYPE_LENGTH (value_type (v)) == elm_len);
- value_contents_copy (result, dst_offset, v, 0, elm_len);
+ value_contents_copy (result, dst_offset, v, 0, 0, elm_len);

/* Peel another dimension of the array. */
array_type = TYPE_TARGET_TYPE (array_type);
@@ -299,7 +299,7 @@ class fortran_array_repacker_base_impl
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));
}
diff --git a/gdb/findvar.c b/gdb/findvar.c
index fcd97191c14..b7e8ded4fb6 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -869,6 +869,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)));

@@ -892,7 +893,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;
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp
new file mode 100644
index 00000000000..afbd123a16f
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp
@@ -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"
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index a913cd48d67..cdb1c025ddc 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1045,6 +1045,10 @@ namespace eval Dwarf {
_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"
diff --git a/gdb/valops.c b/gdb/valops.c
index fec821ad932..9e96af191a3 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1031,20 +1031,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)
@@ -1061,6 +1072,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.
@@ -1132,7 +1147,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))
{
@@ -1158,10 +1173,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
{
@@ -1176,10 +1206,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
@@ -1187,36 +1213,47 @@ 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);
- size_t changed_len;
- gdb_byte buffer[sizeof (LONGEST)];
- int optim, unavail;
+ int changed_len;
+ bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;

- changed_len = (value_bitpos (toval)
- + value_bitsize (toval)
- + HOST_CHAR_BIT - 1)
- / HOST_CHAR_BIT;
+ if (bitsize)
+ {
+ offset += value_offset (value_parent (toval));

- if (changed_len > sizeof (LONGEST))
- error (_("Can't handle bitfields which "
- "don't fit in a %d bit word."),
- (int) sizeof (LONGEST) * HOST_CHAR_BIT);
+ 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;

if (!get_frame_register_bytes (frame, value_reg, offset,
- {buffer, changed_len},
- &optim, &unavail))
+ buffer, &optim, &unavail))
{
if (optim)
throw_error (OPTIMIZED_OUT_ERROR,
@@ -1226,11 +1263,10 @@ 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,
- {buffer, changed_len});
+ put_frame_register_bytes (frame, value_reg, offset, buffer);
}
else
{
@@ -1250,8 +1286,7 @@ value_assign (struct value *toval, struct value *fromval)
= gdb::make_array_view (value_contents (fromval),
TYPE_LENGTH (type));
put_frame_register_bytes (frame, value_reg,
- value_offset (toval),
- contents);
+ offset, contents);
}
}

@@ -1352,21 +1387,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;
}
@@ -1715,7 +1751,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;
}
@@ -1725,7 +1761,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;
}

@@ -3992,7 +4029,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));
}

diff --git a/gdb/value.c b/gdb/value.c
index 52f55f0bb97..437e4df3e01 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1306,9 +1306,10 @@ value_ranges_copy_adjusted (struct value *dst, int dst_bit_offset,

static 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);
+ bit_length = length * unit_size * HOST_CHAR_BIT;
+
+ 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_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;
+ 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_bit_offset,
- src, src_bit_offset,
+ 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
@@ -3050,7 +3065,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;
@@ -3085,7 +3100,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));
@@ -3679,7 +3694,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);
@@ -3862,9 +3877,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. */
@@ -3877,10 +3892,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);
@@ -3923,6 +3934,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))
@@ -3931,9 +3947,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)
{
diff --git a/gdb/value.h b/gdb/value.h
index cbb60e2f073..240041b4406 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -740,7 +740,7 @@ 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 struct value *allocate_repeat_value (struct type *type, int count);

diff --git a/include/dwarf2.def b/include/dwarf2.def
index 1ae6e1df298..5e622695d60 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -704,6 +704,10 @@ 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_END_OP

DW_FIRST_ATE (DW_ATE_void, 0x0)
--
2.17.1