From b91b19840b0062b873ef6b7bdf38ddcdd8c105da Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 28 Jan 2021 08:51:31 -0300 Subject: [PATCH] dwarf_loader: Support DW_AT_data_bit_offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: "Daniel P. Berrangé" 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 --- dwarf_loader.c | 74 ++++++++++++++++++++++++++++++-------------------- dwarves.h | 2 ++ 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/dwarf_loader.c b/dwarf_loader.c index 8d27e4f..ac22c1b 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -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) { diff --git a/dwarves.h b/dwarves.h index 24405b7..98caf1a 100644 --- a/dwarves.h +++ b/dwarves.h @@ -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;