dwarves_fprintf: Handle negative bit_offsets in packed structs with bitfields

Andrii reported that structs with bitfields and with __attribute__((packed)) were
not producing correct output, that is because pahole and friends didn't
knew about something Yonghong discovered: DW_AT_bit_offset may be
negative when a bitfield straddles a 4 byte boundary, fix it so that we
produce a saner output:

  $ cat examples/yonghong/packed_bitfield.c
  struct packed {
          char x1: 1;
          char x2: 3;
          char x3: 3;
          int y1: 7;
          int y2: 20;
  } __attribute__((packed));
  struct packed g;
  $

  cc -g -c examples/yonghong/packed_bitfield.c

  $ readelf -wi packed_bitfield.o | grep bit_offset
    <37>   DW_AT_bit_offset  : 7
    <46>   DW_AT_bit_offset  : 4
    <55>   DW_AT_bit_offset  : 1
    <64>   DW_AT_bit_offset  : 18
    <73>   DW_AT_bit_offset  : -2
  $

Before:

  $ pahole packed_bitfield.o
  struct packed {
          char                       x1:1;                 /*     0: 7  1 */
          char                       x2:3;                 /*     0: 4  1 */
          char                       x3:3;                 /*     0: 1  1 */
          int                        y1:7;                 /*     0:18  4 */
          int                        y2:20;                /*     0:4294967294  4 */

          /* size: 5, cachelines: 1, members: 5 */
          /* padding: 1 */
          /* bit_padding: 254 bits */
          /* last cacheline: 5 bytes */

          /* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
  };
  $

Now:

  $ pahole packed_bitfield.o
  struct packed {
          char                       x1:1;                 /*     0: 7  1 */
          char                       x2:3;                 /*     0: 4  1 */
          char                       x3:3;                 /*     0: 1  1 */
          int                        y1:7;                 /*     0:18  4 */
          int                        y2:20;                /*     4:30  4 */

          /* size: 5, cachelines: 1, members: 5 */
          /* padding: 1 */
          /* bit_padding: 254 bits */
          /* last cacheline: 5 bytes */

          /* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
  };
  $

And for two big endian archs, one 32-bit and the other 64-bit:

  $ file ~acme/git/tmp/packed_bitfield.powerpc.o
  /home/acme/git/tmp/packed_bitfield.powerpc.o: ELF 32-bit MSB relocatable, PowerPC or cisco 4500, version 1 (SYSV), with debug_info, not stripped
  $ pahole ~acme/git/tmp/packed_bitfield.powerpc.o
  struct packed {
          char                       x1:1;                 /*     0: 0  1 */
          char                       x2:3;                 /*     0: 1  1 */
          char                       x3:3;                 /*     0: 4  1 */
          int                        y1:7;                 /*     0: 7  4 */
          int                        y2:20;                /*     0:14  4 */

          /* size: 5, cachelines: 1, members: 5 */
          /* padding: 1 */
          /* bit_padding: 254 bits */
          /* last cacheline: 5 bytes */

          /* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
  };
  $
  $ file ~acme/git/tmp/packed_bitfield.sparc64.o
  /home/acme/git/tmp/packed_bitfield.sparc64.o: ELF 64-bit MSB relocatable, SPARC V9, relaxed memory ordering, version 1 (SYSV), with debug_info, not stripped
  $
  $ pahole ~acme/git/tmp/packed_bitfield.sparc64.o
  struct packed {
          char                       x1:1;                 /*     0: 0  1 */
          char                       x2:3;                 /*     0: 1  1 */
          char                       x3:3;                 /*     0: 4  1 */
          int                        y1:7;                 /*     0: 7  4 */
          int                        y2:20;                /*     0:14  4 */

          /* size: 5, cachelines: 1, members: 5 */
          /* padding: 1 */
          /* bit_padding: 254 bits */
          /* last cacheline: 5 bytes */

          /* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
  };

Now to fix the holes calculations.

Reported-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2019-01-15 13:52:29 -03:00
parent b0cf845e02
commit 1182664d6a
1 changed files with 9 additions and 1 deletions

View File

@ -753,6 +753,9 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb
sconf.base_offset = offset;
}
if (member->bitfield_offset < 0)
offset += member->byte_size;
if (!conf->suppress_comments)
printed_cacheline = class__fprintf_cacheline_boundary(conf, offset, fp);
@ -807,9 +810,14 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb
offset);
if (member->bitfield_size != 0) {
unsigned int bitfield_offset = member->bitfield_offset;
if (member->bitfield_offset < 0)
bitfield_offset = member->byte_size * 8 + member->bitfield_offset;
printed += fprintf(fp, sconf.hex_fmt ?
":%#2x" : ":%2u",
member->bitfield_offset);
bitfield_offset);
size_spacing -= 3;
}