Issue #25 resolved

Wrong sizeof() calculation for struct on x64 platform

Leonard Ritter
created an issue

The following struct

{{{ typedef struct SDL_TextEditingEvent { Uint32 type; /< ::SDL_TEXTEDITING */ Uint32 timestamp; Uint32 windowID; /< The window with keyboard focus, if any / char text[32]; /< The editing text */ int start; /< The start cursor of selected editing text / int length; /*< The length of selected editing text / } SDL_TextEditingEvent; }}}

is calculated to be a size of 52, when it is actually 56 bytes big, probably due to x64 alignment rules, see http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86. My guess is that the struct itself is padded to match 8 byte alignment.

Comments (6)

  1. Armin Rigo

    Closed after discussion on IRC: this struct is really 52 bytes when compiled on its own but 56 bytes when compiled as part of the header. Maybe there is some obscure packing directive, although we couldn't find it. Closed anyway because it's again a case in which you should not use verify() with too precise information.

  2. Leonard Ritter reporter

    I sat down with a collague of mine who happens to know some gcc kung fu and we puzzled it out (using gcc's excellent -S -save-temps flags and looking at the assembly code of the module, esp. the tables).

    I apparently failed to give you a crucial piece of information which at the time I misread or misunderstood, I'm sorry for that. The original reports description is a red herring. The issue is much more interesting :)

    See following code to understand the issue:

    #include <stdio.h>
    
    typedef struct {
            char text[17];
    } A;
    
    typedef struct {
            void *glorious_void_star;
    } B;
    
    typedef union {
            A a;
            B b;
    } U;
    
    int main(int argc, char **argv) {
            printf("sizeof(A) = %zu\n", sizeof(A)); // should be 17
            printf("sizeof(B) = %zu\n", sizeof(B)); // should be 8 on x64
            printf("sizeof(U) = %zu\n", sizeof(U)); // take a guess and breathe deeply.
            return 0;
    }
    

    It appears sizeof(SDL_TextEditingEvent) is indeed 52, but the union SDL_Event that contains it has size 56, even though SDL_TextEditingEvent is the largest struct in this union. This is because unions and structs have to inherit the alignment rules of their members. Other structs included in this union carry pointer references which force 8 byte alignment, unfortunately forcing SDL_Event to pad its total size of 52 to 56, even though that serves no purpose whatsoever.

    The rule that describes this behavior is mentioned here: http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html, esp. following: Note that the alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question. (Reading this again I'm not sure this is the rule that actually applies. But the example from above remains valid. sizeof(U) is 24.)

    Therefore I suggest to fix cffi's sizeof() implementation for unions, so that it takes this rule into account.

  3. Armin Rigo

    Thanks! Indeed, the size of unions is bogus in case the item with the largest alignment is not the one of the largest size. This rule from C actually makes a lot of sense (e.g. if you have an array of unions, every item in the array needs to be aligned as much as any union member requires).

    Fixed in both PyPy and CPython.

  4. Log in to comment