• Arnaldo Carvalho de Melo's avatar
    core: Use unnatural alignment of struct embedded in another to infer __packed__ · c8fc6f5a
    Arnaldo Carvalho de Melo authored
    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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
    c8fc6f5a