fprintf: Deal with zero sized arrays in the middle of a struct

Consider:

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

That is a roundabout way of using __attribute__(__aligned__(4)), but
should work nonetheless.

We were not putting the [0] in that zero sized array which ended up
making gcc complain with:

  $ gcc -g -c shm.c
  shm.c:199:29: error: flexible array member not at end of struct
    unsigned char              __pad1[];            /*    24     0 */
                               ^~~~~~
  $

Now this works, i.e. generates compilable source code out of the
type tags, be it from BTF or from DWARF, i.e. this is all from the
internal representation of such types, agnostic wrt the original type
format.

So, the full circle:

  $ pahole -C ipc64_perm /home/acme/git/build/v5.1-rc4+/ipc/shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

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

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $ pfunct --compile /home/acme/git/build/v5.1-rc4+/ipc/shm.o > shm.c
  $ gcc -g -c shm.c
  $ pahole -C ipc64_perm shm.o
  struct ipc64_perm {
  	__kernel_key_t             key;                  /*     0     4 */
  	__kernel_uid32_t           uid;                  /*     4     4 */
  	__kernel_gid32_t           gid;                  /*     8     4 */
  	__kernel_uid32_t           cuid;                 /*    12     4 */
  	__kernel_gid32_t           cgid;                 /*    16     4 */
  	__kernel_mode_t            mode;                 /*    20     4 */
  	unsigned char              __pad1[0];            /*    24     0 */
  	short unsigned int         seq;                  /*    24     2 */
  	short unsigned int         __pad2;               /*    26     2 */

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

  	__kernel_ulong_t           __unused1;            /*    32     8 */
  	__kernel_ulong_t           __unused2;            /*    40     8 */

  	/* size: 48, cachelines: 1, members: 11 */
  	/* sum members: 44, holes: 1, sum holes: 4 */
  	/* last cacheline: 48 bytes */
  };
  $

And for a chuckle, the original source code with a bit of history about
struct layout worries:

include/uapi/asm-generic/ipcbuf.h:

  /*
   * The generic ipc64_perm structure:
   * Note extra padding because this structure is passed back and forth
   * between kernel and user space.
   *
   * ipc64_perm was originally meant to be architecture specific, but
   * everyone just ended up making identical copies without specific
   * optimizations, so we may just as well all use the same one.
   *
   * Pad space is left for:
   * - 32-bit mode_t on architectures that only had 16 bit
   * - 32-bit seq
   * - 2 miscellaneous 32-bit values
   */

  struct ipc64_perm {
          __kernel_key_t          key;
          __kernel_uid32_t        uid;
          __kernel_gid32_t        gid;
          __kernel_uid32_t        cuid;
          __kernel_gid32_t        cgid;
          __kernel_mode_t         mode;
                                  /* pad if mode_t is u16: */
          unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
          unsigned short          seq;
          unsigned short          __pad2;
          __kernel_ulong_t        __unused1;
          __kernel_ulong_t        __unused2;
  };

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