[PATCH/RFC] Re: DWARF5 DW_AT_data_bit_offset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em Thu, Jan 21, 2021 at 12:35:16PM +0100, Mark Wielaard escreveu:
> On Fri, Oct 02, 2020 at 06:18:19PM -0300, Arnaldo Carvalho de Melo wrote:
> > Em Fri, Oct 02, 2020 at 06:11:06PM +0200, Mark Wielaard escreveu:
> > > Seems pahole with a recent version of elfutils libdw already handles
> > > most DWARF5 encodings. One thing it doesn't handle yet is
> > > DW_AT_data_bit_offset (this is actually a DWARF4 thing, but gcc only
> > > emits it for -gdwarf-5).

<SNIP>

> > > $ gcc -gdwarf-5 -c bf.c
> > > $ ./pahole ./bf.o 
> > > DW_AT_<0xd>=0x21
> > > DW_AT_<0xd>=0x21
> > > DW_AT_<0xd>=0x21
> > > struct pea {
> > > 	int                        type;                 /*     0     4 */
> > > 	static long int                   a;             /*     0     0 */
> > > 	static long int                   b;             /*     0     0 */
> > > 	static long int                   c;             /*     0     0 */
> > > 
> > > 	/* size: 8, cachelines: 1, members: 1, static members: 3 */
> > > 	/* padding: 4 */
> > > 	/* last cacheline: 8 bytes */
> > > };

> > > Note that GCC11 might default to DWARF5.

> > Thanks for the detailed report, I'm releasing v1.18 right now, will look
> > into that for v1.19.

> Note that GCC11 indeed just switched to producing DWARF5 by default.
> It is not released yet, but already in stage4 and Fedora will start
> doing a mass-rebuild with it soon to shake out the last remaining bugs.

So, 1.20 will have the fix below, please check if what is in:

https://git.kernel.org/pub/scm/devel/pahole/pahole.git/log/?h=DW_AT_data_bit_offset

Fixes it for you, the cset with all the tests performed is this one:

https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?h=DW_AT_data_bit_offset&id=a77f039bb49bc97badf938049245f013fe3de4aa

Now looking at https://bugzilla.redhat.com/show_bug.cgi?id=1919965 ...

For convenience:

commit a77f039bb49bc97badf938049245f013fe3de4aa
Author: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Date:   Thu Jan 28 08:51:31 2021 -0300

    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@xxxxxxxxx>
    Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 8d27e4fc236b9575..ac22c1b0883ddee6 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 24405b79ac71e686..98caf1abc54d58fa 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;



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux