Menu

#275 pack-struct option is broken

open-accepted
nobody
None
5
2014-08-26
2012-02-29
Anonymous
No

$ i686-w64-mingw32-gcc --version
i686-w64-mingw32-gcc (GCC) 4.7.0 20120224 (Fedora MinGW 4.7.0-0.5.20120224.fc16_cross)

I am attaching test case that you can easily see the problem.

$ gcc -m32 test-ps.c
$ ./a.out
size: 12
$ gcc -fpack-struct -m32 test-ps.c
size: 6
$ i686-w64-mingw32-gcc test-ps.c
$ wine ./a-1.exe
size: 12
$ i686-w64-mingw32-gcc -fpack-struct test-ps.c
$ wine ./a.exe
size: 9

Discussion

  • Kai Tietz

    Kai Tietz - 2012-02-29

    The gcc 4.7 gcc for mingw has by default -mms-bitfields activated. Which means struct-layout has changed and alignment in struct.
    You can force a specific layout by using attribute ms_struct or by attribute gcc_struct.

    The difference between those two layouts is in bitfield packing and in alignment of those field members.

    For example:

    struct {
    unsigned long long c : 1;
    unsigned int a : 1;
    unsigned int b : 1;
    }

    will be layout differently.

    For gcc-layout all bits geting merged together within one bitfield. gcc struct makes no difference in actual type-sizes for bitfields.
    For ms-layout c will end in a 8-byte field, and just a and b getting merged together as they have same type-size.

    This is to be expected behavior. You might want to try here -mno-ms-bitfields.

     
  • Kai Tietz

    Kai Tietz - 2012-02-29
    • status: open --> pending-invalid
     
  • Kai Tietz

    Kai Tietz - 2012-02-29
    • status: pending-invalid --> pending-accepted
     
  • Kai Tietz

    Kai Tietz - 2012-02-29

    Ah, I missed that default-alignment is treated now for ms_struct union/struct-s too.
    You can solve the issue also by additing explicit #pragma pack(1) of __attribute__ ((pack(1))).

    The cause why it gets aligned to 9 is that. Default alignment of fields is 4 on 32-bit.
    So first char a[1]; is placed in 4-bytes, long b also, and the last element has 4 too.
    By enabling struct-pack, All fields in structure getting merged.
    So char a[1]; has one byte size, long b has 4 byte size, plus char c[1] has 1. But by structure-alignment of 4, structure gets expanded in the last field to 4.

    I see that here alignment is one too big ... hmm, maybe an issue indeed.

    Work-a-round use explicit #pragma pack(1), if you don't want structure alignment at end.

     
  • Anonymous

    Anonymous - 2012-02-29

    Adding -mno-ms-bitfields does fix the issue that I am having. I was expecting 6 and not 8 (or 9). I did not know about the defaults change. Should I report the 9 issue upstream (gcc)?

     
  • Anonymous

    Anonymous - 2012-02-29
    • status: pending-accepted --> open-accepted
     
  • Anonymous

    Anonymous - 2012-02-29

    I was too quick to say it is fixed. There seem to be other byte alignment issues. I will continue investigating.

     
  • Kai Tietz

    Kai Tietz - 2012-03-01

    The general issue is that the __attribute__ ((__packed)) applies only to last field of struct. This is the cause for the size of 9. Trick is here '#pragma pack(1)'. By it you get expected sizes. The issue is that this field-alignment gets applied even for struct/union's marked to be packed (or via -fpack-struct option).

     
  • Ozkan Sezer

    Ozkan Sezer - 2012-03-02

    The remark of "__attribute__ ((__packed)) applies only to last field of struct" is curious: I never experienced that before in any gcc versions, and if that is a gcc-4.7 thing it is a devastatingly incompatible change. For the record, I just compiled Michael's testcase with gcc45 for win32 and it prints 6 when ran under wine. Same when compiled for x86-linux. Is your remark accurate Kai?

     
  • Anonymous

    Anonymous - 2012-03-02

    My last comment was a false alarm. Adding "-mno-ms-bitfields" solves all my issues. I use software that heavily relies on bitfields and packed structs (yes, it is very old).

    I agree with sezero, even if I am an outside nobody. This is a drastic change in defaults.

    Perhaps this "bug" needs to be changed to an "enhancement" to change the defaults back to pre-4.7 values to prevent further problems with other projects.

     
  • Michael Zeilfelder

    I run into this as well and I don't think the change is correct. I get now _different_ results from compiling with Microsoft Compiler and packing when using -m-ms-bitfields while I get _identical_ results when compiling with -mno-ms-bitfields. And when __attribute__ ((__packed)) applies only to last field of struct then m[no]-ms-bitfields shouldn't really make a difference in my case:

    #include <cstdio>

    #if defined(_MSC_VER)
    #pragma pack( push, packing )
    #pragma pack( 1 )
    #endif

    struct Header
    {
    unsigned char IdLength;
    unsigned char ColorMapType;
    unsigned char ImageType;
    unsigned char FirstEntryIndex[2];
    unsigned short ColorMapLength;
    unsigned char ColorMapEntrySize;
    unsigned char XOrigin[2];
    unsigned char YOrigin[2];
    unsigned short ImageWidth;
    }
    #if defined(__GNUC__)
    __attribute__((packed))
    #endif
    ;

    #if defined(_MSC_VER)
    #pragma pack( pop, packing )
    #endif

    int main ()
    {
    printf("sizeof(Header): %d\n", sizeof(Header));
    return 0;
    }

    But my results for size are:
    With VS 2010 (32 bit as well as 64-bit): 14
    Compiled with gcc 4.7 and -mno-ms-bitfields: 14
    gcc 4.7 and new default (or with -mms-bitfields explicitly): 16

    So gcc 4.7 is now incompatible with MS compiler as well as with gcc 4.6 as far as I can see.

     

Log in to post a comment.