fprintf: Notice explicit bitfield alignment modifications

I.e. when we find that the last member has a bit_hole, i.e. it is part
of a bitfield, and the current field has a bitfield_size, i.e. it _also_
is part of a bitfield, the only explanation is that they were
artificially put in different base types, i.e. like in these fields
in the linux kernel 'struct task_struct', here reconstructed by pahole:

  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | grep :0 -B9 -A12
          unsigned int               personality;          /*  1128     4 */
          unsigned int               sched_reset_on_fork:1; /*  1132: 0  4 */
          unsigned int               sched_contributes_to_load:1; /*  1132: 1  4 */
          unsigned int               sched_migrated:1;     /*  1132: 2  4 */
          unsigned int               sched_remote_wakeup:1; /*  1132: 3  4 */

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

          /* Force alignment to the next boundary: */
          unsigned int               :0;

          unsigned int               in_execve:1;          /*  1136: 0  4 */
          unsigned int               in_iowait:1;          /*  1136: 1  4 */
          unsigned int               restore_sigmask:1;    /*  1136: 2  4 */
          unsigned int               in_user_fault:1;      /*  1136: 3  4 */
          unsigned int               no_cgroup_migration:1; /*  1136: 4  4 */
          unsigned int               use_memdelay:1;       /*  1136: 5  4 */

          /* XXX 26 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          long unsigned int          atomic_flags;         /*  1144     8 */
  $

This matches the original definition in the original kernel sources, and
further more, the following sequence proves that with this and DW_AT_alignment,
we can go full circle, i.e.:

1. from an object file reconstruct the source code for all the types that
   appears in function signatures, if pointers, them they will be fully defined,
   not just forward declared:

  $ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o | egrep -w 'sched_change_group|task_struct {' -B10 -A5

          /* --- cacheline 3 boundary (192 bytes) --- */
          struct fpu                 fpu __attribute__((__aligned__(64))); /*   192  4160 */

          /* size: 4352, cachelines: 68, members: 21 */
          /* sum members: 4316, holes: 2, sum holes: 32 */
          /* sum bitfield members: 2 bits, bit holes: 1, sum bit holes: 30 bits */
          /* forced alignments: 1, forced holes: 1, sum forced holes: 28 */
  };

  struct task_struct {
          struct thread_info         thread_info;          /*     0    16 */

          /* XXX last struct has 4 bytes of padding */

          volatile long int          state;                /*    16     8 */
  --
          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };

  void sched_change_group(struct task_struct * tsk, int type)
  {
  }
  $

2. Build the regenerated skeleton function + its types:

  $ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o > sched_change_group.c
  $ gcc -g -c sched_change_group.c
  $ file sched_change_group.o
  sched_change_group.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
  $

3. Now lets see if the original 'struct task_struct' printed by pahole, matches
   the the output printed by pahole for the DWARF info generated for the regenerated
   'struct task_struct' source code in sched_change_group.c:

  $ pahole -C task_struct sched_change_group.o | tail

          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };
  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | tail

          /* --- cacheline 104 boundary (6656 bytes) --- */
          struct thread_struct       thread __attribute__((__aligned__(64))); /*  6656  4352 */

          /* size: 11008, cachelines: 172, members: 207 */
          /* sum members: 10902, holes: 16, sum holes: 98 */
          /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
          /* paddings: 3, sum paddings: 14 */
          /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
  };
  $

  Furthermore:

  $ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o > /tmp/original
  $ pahole -C task_struct sched_change_group.o > /tmp/regenerated
  $ diff -u /tmp/original /tmp/regenerated
  $

So one of the most complex data structures in the Linux kernel seems to be under control,
and it uses zero sized unnamed bitfields and __attribute__((aligned(N))), a DWARF5 goodie,
time to go tag v1.13!

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2019-04-09 15:44:42 -03:00
parent 75f32a24c7
commit a104eb1ea1
1 changed files with 13 additions and 0 deletions

View File

@ -1361,6 +1361,19 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu,
* all over the place.
*/
last_size != 0) {
if (last->bit_hole != 0 && pos->bitfield_size) {
type = cu__type(cu, pos->tag.type);
if (type == NULL) {
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
continue;
}
printed += fprintf(fp, "\n%.*s/* Force alignment to the next boundary: */\n", cconf.indent, tabs);
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
printed += type__fprintf(type, cu, "", &cconf, fp);
printed += fprintf(fp, ":0;\n");
}
if (pos->byte_offset < last->byte_offset ||
(pos->byte_offset == last->byte_offset &&
last->bitfield_size == 0 &&