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
next prev parent 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