emit: Handle structs with DW_AT_alignment=1 meaning __packed__
...
In the following struct the ceph_entity_addr entries all appear marked with a
__attribute__((__aligned__(8)), which, for the first two members of this type,
'peer_addr' and 'peer_addr_for_me', don't cause the regenerated struct to
differ in layout from the original layout put in place by the compiler as per
the original source code.
But the third member of this type, 'actual_peer_addr' ends up in a different
offset, even in a different cacheline, here is how it looks like in the code generated
from the original source code, at offset 568.
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
And here is how it looks like when built from the regenerated source code, at
offset 568:
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
$ gcc -g -c ceph.c
$ pahole -C ceph_connection ceph.o | head -46
struct ceph_connection {
void * private; /* 0 8 */
const struct ceph_connection_operations * ops; /* 8 8 */
struct ceph_messenger * msgr; /* 16 8 */
atomic_t sock_state; /* 24 4 */
/* XXX 4 bytes hole, try to pack */
struct socket * sock; /* 32 8 */
struct ceph_entity_addr peer_addr __attribute__((__aligned__(8))); /* 40 136 */
/* --- cacheline 2 boundary (128 bytes) was 48 bytes ago --- */
struct ceph_entity_addr peer_addr_for_me __attribute__((__aligned__(8))); /* 176 136 */
/* --- cacheline 4 boundary (256 bytes) was 56 bytes ago --- */
long unsigned int flags; /* 312 8 */
/* --- cacheline 5 boundary (320 bytes) --- */
long unsigned int state; /* 320 8 */
const char * error_msg; /* 328 8 */
struct ceph_entity_name peer_name; /* 336 9 */
/* XXX 7 bytes hole, try to pack */
u64 peer_features; /* 352 8 */
u32 connect_seq; /* 360 4 */
u32 peer_global_seq; /* 364 4 */
struct ceph_auth_handshake * auth; /* 368 8 */
int auth_retry; /* 376 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 6 boundary (384 bytes) --- */
struct mutex mutex; /* 384 32 */
struct list_head out_queue; /* 416 16 */
struct list_head out_sent; /* 432 16 */
/* --- cacheline 7 boundary (448 bytes) --- */
u64 out_seq; /* 448 8 */
u64 in_seq; /* 456 8 */
u64 in_seq_acked; /* 464 8 */
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
/* XXX 7 bytes hole, try to pack */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(8))); /* 568 136 */
/* --- cacheline 11 boundary (704 bytes) --- */
$
That happens because 'struct ceph_entity_addr' has that __attribute__
((__aligned__(8)) in the regenerated source code, above, now look at how it
gets regenerated:
$ pahole -C ceph_entity_addr ceph.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(8))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(8)));
$
While when looking at the original DWARF:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(1)));
$
The confusion may further come from the fact that 'struct __kernel_sockaddr_storage' has,
in the regenerated source code, the __attribute__((__aligned__8)))
$ pahole -C __kernel_sockaddr_storage ceph.o
struct __kernel_sockaddr_storage {
__kernel_sa_family_t ss_family; /* 0 2 */
char __data[126]; /* 2 126 */
/* size: 128, cachelines: 2, members: 2 */
} __attribute__((__aligned__(8)));
$
Which is the same as in the original DWARF:
$ pahole -C __kernel_sockaddr_storage /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct __kernel_sockaddr_storage {
__kernel_sa_family_t ss_family; /* 0 2 */
char __data[126]; /* 2 126 */
/* size: 128, cachelines: 2, members: 2 */
} __attribute__((__aligned__(8)));
$
Looking at the original original source code for 'struct ceph_entity_addr'
helps here, as it reads:
include/linux/ceph/msgr.h, line 63:
/*
* entity_addr -- network address
*/
struct ceph_entity_addr {
__le32 type;
__le32 nonce; /* unique id for process (e.g. pid) */
struct sockaddr_storage in_addr;
} __attribute__ ((packed));
So the original code has no __attribute__((__aligned__(1))), so, lets look at
what the compiler generates for 'struct ceph_entity_addr':
$ readelf -wi /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep ceph_entity_addr -A7
<193a6> DW_AT_name : (indirect string, offset: 0x1586): ceph_entity_addr
<193aa> DW_AT_byte_size : 136
<193ab> DW_AT_alignment : 1
<193ac> DW_AT_decl_file : 296
<193ae> DW_AT_decl_line : 63
<193af> DW_AT_decl_column : 8
<193b0> DW_AT_sibling : <0x193e0>
<2><193b4>: Abbrev Number: 5 (DW_TAG_member)
$
So the natural alignment for 'struct ceph_entity_addr' ends up being the
natural alignment for 'struct __kernel_sockaddr_storage', which is 8, but
since 'struct ceph_entity_addr' was marked in the original source code as __packed__,
the compiler added the DW_AT_alignment: 1 to override that.
The heuristic in pahole, so far, took that __attribute__((__aligned__(1)))
literally:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(1)));
$
which ends up making the regenerated source code (with the __aligned__((1))),
generate a different layout, the __aligned__((8)) in one of its members
overrode that __aligned__((1)).
Take this into account and when faced with a structure which natural alignment
is not one and that has a DW_AT_alignment:1 to mean it really is __packed__.
Doing that makes the regenerated source code match the original structure
layouts, i.e. after the patch we get:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__packed__));
$
And that member in 'struct ceph_connection', in the original, continues to read:
$ pahole -C ceph_connection /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep -w actual_peer_addr -B4 -A6
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
$
While in the regenerated DWARF from the regenerated source code reads:
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
$ gcc -g -c ceph.c
$ pahole -C ceph_connection ceph.o | grep -w actual_peer_addr -B4 -A6
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
$
I.e. it now matches.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>