core: Use unnatural alignment of struct embedded in another to infer __packed__

Since we don't have something like DW_AT_alignment for
__attribute__((__packed__)), we need to use whatever hints that are
there in the alignments to figure out if a naturally packed struct has
the __attribute__((packed)) in the original sources, because that is
needed to waiver its natural alignment requisites.

For instance,

  /* Used at: btrfs.c */
  /* <1e7b> /home/acme/git/pahole/btrfs.c:199 */
  struct btrfs_block_group_cache {
          struct btrfs_key   key;                          /*     0    17 */
          struct btrfs_block_group_item item;              /*    17    24 */

          /* XXX 7 bytes hole, try to pack */

          struct btrfs_fs_info *     fs_info;              /*    48     8 */
          struct inode *             inode;                /*    56     8 */

In the original source code, btrfs_block_group_item is marked
__packed__, and being so, even seemingly unnecessarily, makes it, when
embedded in another struct, like the above, forfeit its natural
alingment, that would be 8 bytes, and instead appear right at the 17th
byte offset...

  struct btrfs_block_group_item {
          __le64                     used;                 /*     0     8 */
          __le64                     chunk_objectid;       /*     8     8 */
          __le64                     flags;                /*    16     8 */

          /* size: 24, cachelines: 1, members: 3 */
          /* last cacheline: 24 bytes */
  } __attribute__((__packed__));

So we need to, seeing its use at a unnatural offset, go backwards to the
btrfs_block_group_item pahole internal data structure, 'struct type' and
mark is_packed field as 'true', despite it not looking like a packed
struct.

Same thing with:

  struct ieee80211_mcs_info {
          u8                         rx_mask[10];          /*     0    10 */
          __le16                     rx_highest;           /*    10     2 */
          u8                         tx_params;            /*    12     1 */
          u8                         reserved[3];          /*    13     3 */

          /* size: 16, cachelines: 1, members: 4 */
          /* last cacheline: 16 bytes */
  };

That is naturally aligned and as 16 bytes, a power of two, then when it appears at the end of:

  $ pahole -IC ieee80211_sta_ht_cap vht.o
  /* Used at: vht.c */
  /* <31ea> /home/acme/git/pahole/vht.c:1769 */
  struct ieee80211_sta_ht_cap {
  	u16                        cap;                  /*     0     2 */
  	bool                       ht_supported;         /*     2     1 */
  	u8                         ampdu_factor;         /*     3     1 */
  	u8                         ampdu_density;        /*     4     1 */

  	/* XXX 1 byte hole, try to pack */

  	struct ieee80211_mcs_info mcs;                   /*     6    16 */

  	/* size: 22, cachelines: 1, members: 5 */
  	/* sum members: 21, holes: 1, sum holes: 1 */
  	/* last cacheline: 22 bytes */
  };
  $

We get that one byte hole if ieee80211_mcs_info isn't marked __packed__, as soon as we mark it:

  $ pahole -IC ieee80211_sta_ht_cap vht.o
  /* Used at: vht.c */
  /* <31ea> /home/acme/git/pahole/vht.c:1769 */
  struct ieee80211_sta_ht_cap {
  	u16                        cap;                  /*     0     2 */
  	bool                       ht_supported;         /*     2     1 */
  	u8                         ampdu_factor;         /*     3     1 */
  	u8                         ampdu_density;        /*     4     1 */
  	struct ieee80211_mcs_info mcs;                   /*     5    16 */

  	/* size: 22, cachelines: 1, members: 5 */
  	/* padding: 1 */
  	/* last cacheline: 22 bytes */
  };
  [acme@quaco pahole]$

It works, so __packed__ in this case just says: trow away the natural
alignment, make it 1 in whatever container structs.

So, before emitting the types for some struct, we go back looking at
each of its members and checking for such unnatural offsets, marking the
types as __packed__. Now:

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/net/mac80211/vht.o | grep "^struct ieee80211_mcs_info" -A8
  struct ieee80211_mcs_info {
  	u8                         rx_mask[10];          /*     0    10 */
  	__le16                     rx_highest;           /*    10     2 */
  	u8                         tx_params;            /*    12     1 */
  	u8                         reserved[3];          /*    13     3 */

  	/* size: 16, cachelines: 1, members: 4 */
  	/* last cacheline: 16 bytes */
  } __attribute__((__packed__));
  $

  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/btrfs/free-space-tree.o | grep "^struct btrfs_block_group_item" -A7
  struct btrfs_block_group_item {
  	__le64                     used;                 /*     0     8 */
  	__le64                     chunk_objectid;       /*     8     8 */
  	__le64                     flags;                /*    16     8 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* last cacheline: 24 bytes */
  } __attribute__((__packed__));
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
3 files changed