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>
diff --git a/dwarves.c b/dwarves.c
index a9467f7..9542394 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -1346,6 +1346,34 @@
 	return type->natural_alignment;
 }
 
+/*
+ * Sometimes the only indication that a struct is __packed__ is for it to
+ * appear embedded in another and at an offset that is not natural for it,
+ * so, in !__packed__ parked struct, check for that and mark the types of
+ * members at unnatural alignments.
+ */
+void type__check_structs_at_unnatural_alignments(struct type *type, const struct cu *cu)
+{
+	struct class_member *member;
+
+	type__for_each_member(type, member) {
+		struct tag *member_type = tag__strip_typedefs_and_modifiers(&member->tag, cu);
+
+		if (!tag__is_struct(member_type))
+			continue;
+
+		size_t natural_alignment = tag__natural_alignment(member_type, cu);
+
+		/* Would this break the natural alignment */
+		if ((member->byte_offset % natural_alignment) != 0) {
+			struct class *cls = tag__class(member_type);
+
+			cls->is_packed = true;
+			cls->type.packed_attributes_inferred = true;
+		}
+       }
+}
+
 bool class__infer_packed_attributes(struct class *cls, const struct cu *cu)
 {
 	struct type *ctype = &cls->type;
@@ -1361,6 +1389,7 @@
 	class__find_holes(cls);
 
 	if (cls->padding != 0 || cls->nr_holes != 0) {
+		type__check_structs_at_unnatural_alignments(ctype, cu);
 		cls->is_packed = false;
 		goto out;
 	}
diff --git a/dwarves.h b/dwarves.h
index ed83e69..1c6e3a3 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1113,6 +1113,8 @@
 
 void union__infer_packed_attributes(struct type *type, const struct cu *cu);
 
+void type__check_structs_at_unnatural_alignments(struct type *type, const struct cu *cu);
+
 size_t class__fprintf(struct class *cls, const struct cu *cu, FILE *fp);
 
 void class__add_vtable_entry(struct class *cls, struct function *vtable_entry);
diff --git a/dwarves_emit.c b/dwarves_emit.c
index 2f40b33..bd9b316 100644
--- a/dwarves_emit.c
+++ b/dwarves_emit.c
@@ -299,6 +299,8 @@
 
 	type_emissions__add_definition(emissions, ctype);
 
+	type__check_structs_at_unnatural_alignments(ctype, cu);
+
 	type__for_each_member(ctype, pos)
 		if (tag__emit_definitions(&pos->tag, cu, emissions, fp))
 			fputc('\n', fp);