dwarf_loader: Support DW_AT_data_bit_offset

This appeared in DWARF4 but is supported only in gcc's -gdwarf-5,
support it in a way that makes the output be the same for both cases:

  $ gcc -gdwarf-4 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	long int                   a:1;                  /*     0: 0  8 */
  	long int                   b:1;                  /*     0: 1  8 */
  	long int                   c:1;                  /*     0: 2  8 */

  	/* XXX 29 bits hole, try to pack */
  	/* Bitfield combined with next fields */

  	int                        after_bitfield;       /*     4     4 */

  	/* size: 8, cachelines: 1, members: 4 */
  	/* sum members: 4 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* last cacheline: 8 bytes */
  };
  $ gcc -gdwarf-5 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	long int                   a:1;                  /*     0: 0  8 */
  	long int                   b:1;                  /*     0: 1  8 */
  	long int                   c:1;                  /*     0: 2  8 */

  	/* XXX 29 bits hole, try to pack */
  	/* Bitfield combined with next fields */

  	int                        after_bitfield;       /*     4     4 */

  	/* size: 8, cachelines: 1, members: 4 */
  	/* sum members: 4 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* last cacheline: 8 bytes */
  };
  $

Now with an integer before the bitfield:

  $ cat examples/dwarf5/bf.c
  struct pea {
  	int before_bitfield;
  	long a:1, b:1, c:1;
  	int after_bitfield;
  } p;
  $ gcc -gdwarf-4 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	int                        before_bitfield;      /*     0     4 */

  	/* Bitfield combined with previous fields */

  	long int                   a:1;                  /*     0:32  8 */
  	long int                   b:1;                  /*     0:33  8 */
  	long int                   c:1;                  /*     0:34  8 */

  	/* XXX 29 bits hole, try to pack */

  	int                        after_bitfield;       /*     8     4 */

  	/* size: 16, cachelines: 1, members: 5 */
  	/* sum members: 8 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* padding: 4 */
  	/* last cacheline: 16 bytes */
  };
  $ gcc -gdwarf-5 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	int                        before_bitfield;      /*     0     4 */

  	/* Bitfield combined with previous fields */

  	long int                   a:1;                  /*     0:32  8 */
  	long int                   b:1;                  /*     0:33  8 */
  	long int                   c:1;                  /*     0:34  8 */

  	/* XXX 29 bits hole, try to pack */

  	int                        after_bitfield;       /*     8     4 */

  	/* size: 16, cachelines: 1, members: 5 */
  	/* sum members: 8 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* padding: 4 */
  	/* last cacheline: 16 bytes */
  };
  $

And an array of long integers at the start, before the combination of an
integer with a long integer bitfield:

  $ cat examples/dwarf5/bf.c
  struct pea {
  	long array[3];
  	int before_bitfield;
  	long a:1, b:1, c:1;
  	int after_bitfield;
  } p;
  $ gcc -gdwarf-4 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	long int                   array[3];             /*     0    24 */
  	int                        before_bitfield;      /*    24     4 */

  	/* Bitfield combined with previous fields */

  	long int                   a:1;                  /*    24:32  8 */
  	long int                   b:1;                  /*    24:33  8 */
  	long int                   c:1;                  /*    24:34  8 */

  	/* XXX 29 bits hole, try to pack */

  	int                        after_bitfield;       /*    32     4 */

  	/* size: 40, cachelines: 1, members: 6 */
  	/* sum members: 32 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* padding: 4 */
  	/* last cacheline: 40 bytes */
  };
  $ gcc -gdwarf-5 -c examples/dwarf5/bf.c
  $ pahole bf.o
  struct pea {
  	long int                   array[3];             /*     0    24 */
  	int                        before_bitfield;      /*    24     4 */

  	/* Bitfield combined with previous fields */

  	long int                   a:1;                  /*    24:32  8 */
  	long int                   b:1;                  /*    24:33  8 */
  	long int                   c:1;                  /*    24:34  8 */

  	/* XXX 29 bits hole, try to pack */

  	int                        after_bitfield;       /*    32     4 */

  	/* size: 40, cachelines: 1, members: 6 */
  	/* sum members: 32 */
  	/* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */
  	/* padding: 4 */
  	/* last cacheline: 40 bytes */
  };
  $

Reported-by: Mark Wielaard <mark@klomp.org>
Tested-by: "Daniel P. Berrangé" <berrange@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1919965
Link: https://lore.kernel.org/dwarves/20210128121122.GA775562@kernel.org/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2021-01-28 08:51:31 -03:00
parent c692e8ac5c
commit b91b19840b
2 changed files with 47 additions and 29 deletions

View File

@ -776,27 +776,36 @@ static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu,
Dwarf_Attribute attr;
if (dwarf_attr(die, DW_AT_data_member_location, &attr) != NULL) {
member->byte_offset = __attr_offset(&attr);
member->has_bit_offset = dwarf_attr(die, DW_AT_data_bit_offset, &attr) != NULL;
if (member->has_bit_offset) {
member->bit_offset = __attr_offset(&attr);
// byte_offset and bitfield_offset will be recalculated later, when
// we discover the size of this bitfield base type.
} else {
member->is_static = !in_union;
if (dwarf_attr(die, DW_AT_data_member_location, &attr) != NULL) {
member->byte_offset = __attr_offset(&attr);
} else {
member->is_static = !in_union;
}
/*
* Bit offset calculated here is valid only for byte-aligned
* fields. For bitfields on little-endian archs we need to
* adjust them taking into account byte size of the field,
* which might not be yet known. So we'll re-calculate bit
* offset later, in class_member__cache_byte_size.
*/
member->bit_offset = member->byte_offset * 8;
member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset);
}
/*
* Bit offset calculated here is valid only for byte-aligned
* fields. For bitfields on little-endian archs we need to
* adjust them taking into account byte size of the field,
* which might not be yet known. So we'll re-calculate bit
* offset later, in class_member__cache_byte_size.
*/
member->bit_offset = member->byte_offset * 8;
/*
* If DW_AT_byte_size is not present, byte size will be
* determined later in class_member__cache_byte_size using
* base integer/enum type
*/
member->byte_size = attr_numeric(die, DW_AT_byte_size);
member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset);
member->bitfield_size = attr_numeric(die, DW_AT_bit_size);
member->bit_hole = 0;
member->bitfield_end = 0;
@ -2275,24 +2284,31 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu,
return 0;
}
/*
* For little-endian architectures, DWARF data emitted by gcc/clang
* specifies bitfield offset as an offset from the highest-order bit
* of an underlying integral type (e.g., int) to a highest-order bit
* of a bitfield. E.g., for bitfield taking first 5 bits of int-backed
* bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit
* size), which is very counter-intuitive and isn't a natural
* extension of byte offset, which on little-endian points to
* lowest-order byte. So here we re-adjust bitfield offset to be an
* offset from lowest-order bit of underlying integral type to
* a lowest-order bit of a bitfield. This makes bitfield offset
* a natural extension of byte offset for bitfields and is uniform
* with how big-endian bit offsets work.
*/
if (cu->little_endian) {
member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size;
if (!member->has_bit_offset) {
/*
* For little-endian architectures, DWARF data emitted by gcc/clang
* specifies bitfield offset as an offset from the highest-order bit
* of an underlying integral type (e.g., int) to a highest-order bit
* of a bitfield. E.g., for bitfield taking first 5 bits of int-backed
* bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit
* size), which is very counter-intuitive and isn't a natural
* extension of byte offset, which on little-endian points to
* lowest-order byte. So here we re-adjust bitfield offset to be an
* offset from lowest-order bit of underlying integral type to
* a lowest-order bit of a bitfield. This makes bitfield offset
* a natural extension of byte offset for bitfields and is uniform
* with how big-endian bit offsets work.
*/
if (cu->little_endian)
member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size;
member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
} else {
// DWARF5 has DW_AT_data_bit_offset, offset in bits from the
// start of the container type (struct, class, etc).
member->byte_offset = member->bit_offset / 8;
member->bitfield_offset = member->bit_offset - member->byte_offset * 8;
}
member->bit_offset = member->byte_offset * 8 + member->bitfield_offset;
/* make sure bitfield offset is non-negative */
if (member->bitfield_offset < 0) {

View File

@ -899,6 +899,7 @@ static inline int function__inlined(const struct function *func)
* @accessibility - DW_ACCESS_{public,protected,private}
* @virtuality - DW_VIRTUALITY_{none,virtual,pure_virtual}
* @hole - If there is a hole before the next one (or the end of the struct)
* @has_bit_offset: Don't recalcule this, it came from the debug info (DWARF5's DW_AT_data_bit_offset)
*/
struct class_member {
struct tag tag;
@ -915,6 +916,7 @@ struct class_member {
uint32_t alignment;
uint8_t visited:1;
uint8_t is_static:1;
uint8_t has_bit_offset:1;
uint8_t accessibility:2;
uint8_t virtuality:2;
uint16_t hole;