New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

incorrect size calculation of structs #129

Closed
cocreature opened this Issue Mar 18, 2015 · 3 comments

Comments

Projects
None yet
2 participants
@cocreature

cocreature commented Mar 18, 2015

I have the struct

struct wlc_interface {
  struct {
    bool (*created)(wlc_handle output);
    void (*destroyed)(wlc_handle output);

      /** Output got or lost focus. */
    void (*focus)(wlc_handle output, bool focus);

      /** Output resolution changed. */
    void (*resolution)(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to);
    int x;
  } output;

  struct {
    int y;
  } view;
};

The function pointers all have a size of 8 bytes while the int is 32 bytes. That makes 36 bytes for the output struct. c2hs calculates a complete size of 40 bytes using {#sizeof wlc_interface #} and an offset of 36 bytes for the y. However that is incorrect as the structs seem to be 8 byte aligned (that is also what {#alignof wlc_interface #} shows). So 4 byte padding are inserted and the view struct and so also y start at 40 byte with the total size being 48 byte (again due to alignment as far as I can tell).

@ian-ross

This comment has been minimized.

Member

ian-ross commented Mar 18, 2015

I'm not sure it's possible to correctly determine the size of a structure like this using what's available in the Haskell FFI (and in particular what you can get from Storable methods). On a 64-bit system with 32-bit int, the size and alignment of pointers is 8 bytes, while the size and alignment of int is 4:

GHCi, version 7.8.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
λ > import Foreign.Storable
λ > import Foreign.C.Types
λ > sizeOf (undefined :: CInt)
4
λ > alignment (undefined :: CInt)
4

If I write this in C:

#include <stdio.h>

struct Tst1 {
  struct {
    int *a;
    int b;
  } A;

  struct {
    int c;
  } B;
};

struct Tst2 {
  int *a;
  int b;
  int c;
};

int main(int argc, char *argv[])
{
  printf("%u\n", sizeof(struct Tst1));
  printf("%u\n", sizeof(struct Tst2));
}

then on a 64-bit system using GCC, I get that sizeof(Tst1) == 24 and sizeof(Tst2) == 16, i.e. the compiler has inserted 4 bytes of padding in the case where you have a nested structure, but not in the case where you have a single structure with the same members in the same order without any nesting. (With CLang and on a 64-bit Windows system using the Microsoft compiler, I get the same, while using the 32-bit Microsoft compiler, I get 12 and 12, i.e. no extra padding.)

As far as I know, there is no way from Haskell to determine when the C compiler inserts this type of padding -- all you have are sizeOf and alignment from Foreign.Storable. In order to support this kind of thing, we would need to completely change the way that C2HS handles size and alignment calculations. Instead of using what's available from Foreign.Storable, we would need to programmatically write, compile and run lots of little C programs to query the C compiler for the exact sizes of structures and offsets of their members. Something like this for the example above:

  struct Tst1 tst1;
  void *base1 = (void *)(&tst1);
  void *aptr1 = (void *)(&(tst1.A.a));
  void *bptr1 = (void *)(&(tst1.A.b));
  void *cptr1 = (void *)(&(tst1.B.c));
  printf("\n%u %u %u\n", aptr1-base1, bptr1-base1, cptr1-base1);

  struct Tst2 tst2;
  void *base2 = (void *)(&tst2);
  void *aptr2 = (void *)(&(tst2.a));
  void *bptr2 = (void *)(&(tst2.b));
  void *cptr2 = (void *)(&(tst2.c));
  printf("%u %u %u\n", aptr2-base2, bptr2-base2, cptr2-base2);

That's just not the way that C2HS works though, and it would require quite a big rewrite of all the structure handling code to do this. For the moment, C2HS can only calculate information about structures that can be determined using information available from Foreign.Storable.

@cocreature

This comment has been minimized.

cocreature commented Mar 18, 2015

Allright, that's what I expected.

@ian-ross

This comment has been minimized.

Member

ian-ross commented Mar 20, 2015

I'm going to close this, but I'll keep this idea in mind. There have been a couple of things recently where it's become clear that querying the C compiler by writing these kinds of little programs might be a better approach to finding out information that C2HS needs, so I might switch over to this approach at some point. That would make structure sizing and offset calculations much easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment