Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andreas Arnez <arnez@linux.vnet.ibm.com>
To: Simon Marchi <simon.marchi@polymtl.ca>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH 5/9] Fix issues in write_pieced_value when targeting bit-fields
Date: Thu, 27 Apr 2017 17:54:00 -0000	[thread overview]
Message-ID: <m3inlpzqfy.fsf@oc1027705133.ibm.com> (raw)
In-Reply-To: <bc579290b5aa4bed9e1991454792c17a@polymtl.ca>

On Fri, Apr 14 2017, Simon Marchi wrote:

> On 2017-04-07 13:38, Andreas Arnez wrote:
>> There are multiple issues in write_pieced_value when dealing with a
>> bit-field as the target value:
>>
>> (1) The number of bits preceding the bit-field is calculated without
>>     considering the relative offset of the value's parent.
>
> If others are wondering when this can happen:
>
> struct s {
>     uint64_t foo;
>     struct {
> 	uint32_t bar;
> 	uint32_t bf : 10;
>     } baz;
> };
>
> If "val" is a struct value representing bf:
>
>  - value_offset(val) == 4 (sizeof bar)
>  - val->parent represents the whole baz structure
>  - value_offset(val->parent) == 8 (sizeof foo)
>
> If bf was a "standard", non-bitfield variable, its offset would be 12
> directly.

Right.  Now that you mention it, I realize that the test case doesn't
cover this yet.  So I'm enhancing it.

>
> There are multiple places that do "value_offset (parent) + value_offset
> (value)" when value is a bitfield.  Isn't it what we want most of the
> time?  If so, I wonder if eventually value_offset shouldn't instead return
> the computed offset, like:
>
> LONGEST
> value_offset (const struct value *value)
> {
>   if (value_bitsize (value))
>     return value->offset + value_offset (value_parent (value));
>   else
>     return value->offset;
> }

Maybe, but what would set_value_offset do then?

To be honest, I don't completely understand the definition of
value_offset, so I decided not to change anything there with this patch.
Maybe we should spend some time refactoring the value API, but I think
this is a separate task.

>
>> (2) On big-endian targets the source value's *most* significant bits are
>>     transferred to the target value, instead of its least significant
>>     bits.
>>
>> (3) The number of bytes containing a portion of the bit-field in a given
>>     piece is calculated with the wrong starting offset; thus the result
>>     may be off by one.
>>
>> (4) When checking whether the data can be transferred byte-wise, the
>>     transfer size is not verified to be byte-aligned.
>>
>> (5) When transferring the data via a buffer, the bit offset within the
>>     target value is not reduced to its sub-byte fraction before using it
>>     as a bit offset into the buffer.
>>
>> These issues are fixed.  For consistency, the affected logic that exists
>> in read_pieced_value as well is changed there in the same way.
>>
>> gdb/ChangeLog:
>>
>> 	* dwarf2loc.c (write_pieced_value): Fix various issues with
>> 	bit-field handling.
>> 	(read_pieced_value): Sync with changes in write_pieced_value.
>>
>> gdb/testsuite/ChangeLog:
>>
>> 	* gdb.dwarf2/var-access.exp: Add test for accessing bit-fields
>> 	whose containing structure is located in several DWARF pieces.
>> ---
>>  gdb/dwarf2loc.c                         | 54
>> +++++++++++++++------------------
>>  gdb/testsuite/gdb.dwarf2/var-access.exp | 50
>> +++++++++++++++++++++++++++++-
>>  2 files changed, 74 insertions(+), 30 deletions(-)
>>
>> diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
>> index 76a58a3..1f89a08 100644
>> --- a/gdb/dwarf2loc.c
>> +++ b/gdb/dwarf2loc.c
>> @@ -1775,7 +1775,8 @@ read_pieced_value (struct value *v)
>>    bits_to_skip = 8 * value_offset (v);
>>    if (value_bitsize (v))
>>      {
>> -      bits_to_skip += value_bitpos (v);
>> +      bits_to_skip += (8 * value_offset (value_parent (v))
>> +		       + value_bitpos (v));
>
> I guess this is related to (1).

Right.

>
>>        type_len = value_bitsize (v);
>>      }
>>    else
>> @@ -1796,18 +1797,11 @@ read_pieced_value (struct value *v)
>>  	  bits_to_skip -= this_size_bits;
>>  	  continue;
>>  	}
>> -      if (bits_to_skip > 0)
>> -	{
>> -	  dest_offset_bits = 0;
>> -	  source_offset_bits = bits_to_skip;
>> -	  this_size_bits -= bits_to_skip;
>> -	  bits_to_skip = 0;
>> -	}
>> -      else
>> -	{
>> -	  dest_offset_bits = offset;
>> -	  source_offset_bits = 0;
>> -	}
>> +      source_offset_bits = bits_to_skip;
>> +      this_size_bits -= bits_to_skip;
>> +      bits_to_skip = 0;
>> +      dest_offset_bits = offset;
>> +
>
> Is this snippet related to one of the problems you have described?  It
> seems to me like it's just simplifying the code, but it's not changing the
> behavior.  If that's the case, I'd suggest putting it in its own patch
> (along with its equivalent in write_pieced_value).

This is just to mirror the change in write_pieced_value.  See below for
the rationale of that.

>
>>        if (this_size_bits > type_len - offset)
>>  	this_size_bits = type_len - offset;
>>
>> @@ -1942,8 +1936,16 @@ write_pieced_value (struct value *to, struct
>> value *from)
>>    bits_to_skip = 8 * value_offset (to);
>>    if (value_bitsize (to))
>>      {
>> -      bits_to_skip += value_bitpos (to);
>> +      bits_to_skip += (8 * value_offset (value_parent (to))
>> +		       + value_bitpos (to));
>>        type_len = value_bitsize (to);
>> +      /* Use the least significant bits of FROM.  */
>> +      if (gdbarch_byte_order (get_type_arch (value_type (from)))
>> +	  == BFD_ENDIAN_BIG)
>> +	{
>> +	  offset = 8 * TYPE_LENGTH (value_type (from)) - type_len;
>> +	  type_len += offset;
>> +	}
>
> I guess this is related to (1) and (2).

Right.  Note that 'offset' may now be nonzero upon entering the loop.

>
>>      }
>>    else
>>      type_len = 8 * TYPE_LENGTH (value_type (to));
>> @@ -1962,25 +1964,19 @@ write_pieced_value (struct value *to, struct
>> value *from)
>>  	  bits_to_skip -= this_size_bits;
>>  	  continue;
>>  	}
>> -      if (bits_to_skip > 0)
>> -	{
>> -	  dest_offset_bits = bits_to_skip;
>> -	  source_offset_bits = 0;
>> -	  this_size_bits -= bits_to_skip;
>> -	  bits_to_skip = 0;
>> -	}
>> -      else
>> -	{
>> -	  dest_offset_bits = 0;
>> -	  source_offset_bits = offset;
>> -	}
>> +      dest_offset_bits = bits_to_skip;
>> +      this_size_bits -= bits_to_skip;
>> +      bits_to_skip = 0;
>> +      source_offset_bits = offset;
>> +

This is related to (2).  The old version assumed that 'offset' is still
zero when there are bits to skip, so a literal zero (instead of
'offset') could be copied into source_offset_bits.  This assumption
doesn't hold any longer, now that 'offset' may start with a nonzero
value.

>>        if (this_size_bits > type_len - offset)
>>  	this_size_bits = type_len - offset;
>>
>> -      this_size = (this_size_bits + source_offset_bits % 8 + 7) / 8;
>> +      this_size = (this_size_bits + dest_offset_bits % 8 + 7) / 8;
>
> I guess this is related to (3).

Yes.  Note that this looks like a classical copy & paste error.  The
line was probably copied from read_pieced_value, where
'source_offset_bits' is actually correct.

>
>>        source_offset = source_offset_bits / 8;
>>        dest_offset = dest_offset_bits / 8;
>> -      if (dest_offset_bits % 8 == 0 && source_offset_bits % 8 == 0)
>> +      if (dest_offset_bits % 8 == 0 && source_offset_bits % 8 == 0
>> +	  && this_size_bits % 8 == 0)
>
> ... and this to (4).

Correct.

>
>>  	{
>>  	  source_buffer = contents + source_offset;
>>  	  need_bitwise = 0;
>> @@ -2049,7 +2045,7 @@ write_pieced_value (struct value *to, struct value
>> *from)
>>  	      read_memory (p->v.mem.addr + dest_offset, buffer.data (), 1);
>>  	      read_memory (p->v.mem.addr + dest_offset + this_size - 1,
>>  			   &buffer[this_size - 1], 1);
>> -	      copy_bitwise (buffer.data (), dest_offset_bits,
>> +	      copy_bitwise (buffer.data (), dest_offset_bits % 8,
>
> ... and this to (5).

Also correct.

As you can see, the patch fixes 5 different, fairly independent bugs.
Thus it could have been split into 5 patches, but I found that a bit
extreme...

>
>>  			    contents, source_offset_bits,
>>  			    this_size_bits,
>>  			    bits_big_endian);
>> diff --git a/gdb/testsuite/gdb.dwarf2/var-access.exp
>> b/gdb/testsuite/gdb.dwarf2/var-access.exp
>> index 56a635a..c6abc87 100644
>> --- a/gdb/testsuite/gdb.dwarf2/var-access.exp
>> +++ b/gdb/testsuite/gdb.dwarf2/var-access.exp
>> @@ -62,7 +62,7 @@ Dwarf::assemble $asm_file {
>>  	} {
>>  	    declare_labels char_type_label
>>  	    declare_labels int_type_label short_type_label
>> -	    declare_labels array_a8_label struct_s_label
>> +	    declare_labels array_a8_label struct_s_label struct_t_label
>>
>>  	    char_type_label: base_type {
>>  		{name "char"}
>> @@ -111,6 +111,34 @@ Dwarf::assemble $asm_file {
>>  		}
>>  	    }
>>
>> +	    struct_t_label: structure_type {
>> +		{name "t"}
>> +		{byte_size 8 DW_FORM_sdata}
>> +	    } {
>> +		member {
>> +		    {name u}
>> +		    {type :$int_type_label}
>> +		}
>> +		member {
>> +		    {name x}
>> +		    {type :$int_type_label}
>> +		    {data_member_location 4 DW_FORM_udata}
>> +		    {bit_size 9 DW_FORM_udata}
>> +		}
>> +		member {
>> +		    {name y}
>> +		    {type :$int_type_label}
>> +		    {data_bit_offset 41 DW_FORM_udata}
>> +		    {bit_size 13 DW_FORM_udata}
>> +		}
>> +		member {
>> +		    {name z}
>> +		    {type :$int_type_label}
>> +		    {data_bit_offset 54 DW_FORM_udata}
>> +		    {bit_size 10 DW_FORM_udata}
>> +		}
>> +	    }
>> +
>>  	    DW_TAG_subprogram {
>>  		{name "main"}
>>  		{DW_AT_external 1 flag}
>> @@ -152,6 +180,18 @@ Dwarf::assemble $asm_file {
>>  			piece 1
>>  		    } SPECIAL_expr}
>>  		}
>> +		# Memory pieces for bitfield access.
>> +		DW_TAG_variable {
>> +		    {name "t1"}
>> +		    {type :$struct_t_label}
>> +		    {location {
>> +			piece 4
>> +			addr "$buf_var + 1"
>> +			piece 3
>> +			addr "$buf_var"
>> +			piece 1
>> +		    } SPECIAL_expr}
>> +		}
>>  	    }
>>  	}
>>      }
>> @@ -196,3 +236,11 @@ gdb_test_no_output "set var s2 = {191, 73, 231,
>> 123}" \
>>      "re-initialize s2"
>>  gdb_test "print/d s2"  " = \\{a = 191, b = 73, c = 231, d = 123\\}" \
>>      "verify re-initialized s2"
>> +
>> +# Unaligned bitfield access through byte-aligned pieces.
>> +gdb_test_no_output "set var t1.x = -7"
>> +gdb_test_no_output "set var t1.z = 340"
>> +gdb_test_no_output "set var t1.y = 1234"
>> +gdb_test "print t1" " = \\{u = <optimized out>, x = -7, y = 1234, z =
>> 340\\}" \
>> +    "verify t1"
>
> Maybe I'm missing something, but if there's the same bug in the write and
> read implementations, it's possible it would slip through, isn't it? For
> example, if the "set var" part messes up the bit ordering and the "print"
> part messes it up the same way, it would appear correct when it's not.
> Reading actual data from a progam won't work.
>
> In the other tests, you tested against known data already in memory, which
> made sense I think.

Yeah, OK, I can do that.

--
Andreas


  reply	other threads:[~2017-04-27 17:54 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-07 17:39 [PATCH 0/9] Various DWARF piece fixes Andreas Arnez
2017-04-07 17:39 ` [PATCH 1/9] Add test for modifiable DWARF locations Andreas Arnez
2017-04-13  4:00   ` Simon Marchi
2017-04-13 10:52     ` Andreas Arnez
2017-04-13  8:36   ` Yao Qi
2017-04-13 11:46     ` Andreas Arnez
2017-04-07 17:40 ` [PATCH 2/9] Fix size capping in write_pieced_value Andreas Arnez
2017-04-13  8:18   ` Yao Qi
2017-04-13 16:35     ` Andreas Arnez
2017-04-19  9:15       ` Yao Qi
2017-04-19 14:36         ` Andreas Arnez
2017-04-19 15:00           ` Yao Qi
2017-04-07 17:41 ` [PATCH 4/9] Remove addr_size field from struct piece_closure Andreas Arnez
2017-04-13  9:10   ` Yao Qi
2017-04-14  3:39     ` Simon Marchi
2017-04-18 17:25       ` Andreas Arnez
2017-04-18 18:49         ` Simon Marchi
2017-04-07 17:41 ` [PATCH 3/9] PR gdb/21226: Take DWARF stack value pieces from LSB end Andreas Arnez
2017-04-14  3:36   ` Simon Marchi
2017-04-18 16:32     ` Andreas Arnez
2017-04-18 16:43       ` Simon Marchi
2017-04-07 17:42 ` [PATCH 5/9] Fix issues in write_pieced_value when targeting bit-fields Andreas Arnez
2017-04-14  5:18   ` Simon Marchi
2017-04-27 17:54     ` Andreas Arnez [this message]
2017-05-03 13:59       ` Simon Marchi
2017-04-07 17:43 ` [PATCH 7/9] Improve logic for buffer allocation in read/write_pieced_value Andreas Arnez
2017-04-14 14:51   ` Simon Marchi
2017-04-07 17:43 ` [PATCH 6/9] Fix handling of DWARF register pieces on big-endian targets Andreas Arnez
2017-04-14 14:11   ` Simon Marchi
2017-04-19 18:03     ` Andreas Arnez
2017-04-07 17:44 ` [PATCH 8/9] Respect piece offset for DW_OP_bit_piece Andreas Arnez
2017-04-14 15:07   ` Simon Marchi
2017-04-07 17:45 ` [PATCH 9/9] Remove unnecessary copies of variables in read/write_pieced_value Andreas Arnez
2017-04-14 15:21   ` Simon Marchi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m3inlpzqfy.fsf@oc1027705133.ibm.com \
    --to=arnez@linux.vnet.ibm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@polymtl.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox