Imported C Structure is not mapped correctly lazarus / 64-FPC development ---------------------------------------------------------------------- domibay_hugo · 2021-01-04 15:30 I'm developing an Application that interacts with an External Shared Library over a C Language Structure: Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #define MAX_RESULT 128 typedef struct FooResult {  uint32_t iversion;  int8_t iresultcount;  int8_t ierrorcode;  int64_t arrresult[MAX_RESULT];} FooResult;  So I created this Pascal Structure: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i const  MAX_RESULT = 128; type  PResultArray = ^TResultArray;  TResultArray = array[0..(MAX_RESULT - 1)] of Int64;   PFooResult = ^TFooResult;  TFooResult = packed record    iversion: cuint32;    iresultcount: cint8;    ierrorcode: cint8;    arrresult: TResultArray;  end;  The issue that I'm observing is that the Array TFooResult.arrresult cannot be accessed correctly. But when reading the Array TResultArray FreePascal shows the values correctly. The Library Code is: Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #include #include #include    #define MAX_RESULT 128  typedef struct FooResult {  uint32_t iversion;  int8_t iresultcount;  int8_t ierrorcode;  int64_t arrresult[MAX_RESULT];} FooResult;  /* fooutils.h */ extern FooResult* foo_get_result();extern void foo_build_result(FooResult*);extern const int64_t* foo_get_result_array(const FooResult*);extern void foo_result_debug(const FooResult*);extern void foo_result_free(FooResult*);    FooResult* foo_get_result(){        FooResult* pfooresult = (FooResult*)malloc(sizeof(FooResult));        int irs;          pfooresult->iversion = 0;        pfooresult->iresultcount = -1;        pfooresult->ierrorcode = 0;       for(irs = 0; irs < MAX_RESULT; irs++)        {                pfooresult->arrresult[irs] = -1;        }          return pfooresult;} void foo_build_result(FooResult* pfooresult){        if(pfooresult != NULL)        {                pfooresult->iversion += 1;                pfooresult->ierrorcode = 0;                 if(pfooresult->iresultcount <= 127)            {                if(pfooresult->iresultcount != -1)                {                      pfooresult->arrresult[pfooresult->iresultcount] = pfooresult->iversion;                         pfooresult->iresultcount += 1;                } else {                        pfooresult->arrresult[0] = pfooresult->iversion;                   pfooresult->iresultcount = 1;                }  //if frs.iresultcount != -1              } else {                  pfooresult->ierrorcode = 4;              }  //if(pfooresult->iresultcount <= 127)        }  //if(pfooresult != Null)} const int64_t* foo_get_result_array(const FooResult* pfooresult){        if(pfooresult != NULL)        {              return pfooresult->arrresult;        }        else        {              return NULL;        }} void foo_result_debug(const FooResult* pfooresult){        if(pfooresult != NULL)        {              int irs;                  printf("FooResult* Debug: '*FooResult { iversion: %d, iresultcount: %d, ierrorcode: %d"                              , pfooresult->iversion, pfooresult->iresultcount, pfooresult->ierrorcode);                printf(", arrresult: [");               for(irs = 0; irs < MAX_RESULT; irs++)                {                      if(irs > 0) { printf(", "); }                       printf("%d", (int)pfooresult->arrresult[irs]);                }               printf("] }'\n");        }        else  //FooResult is not set        {                printf("FooResult Debug: 'NULL'\n");      }       //if(pfooresult != Null) } void foo_result_free(FooResult* pfooresult){        if(pfooresult != NULL)        {                free(pfooresult);        } }  the C Language Host Application is this: Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #include #include   #define MAX_RESULT 128  typedef struct FooResult {  uint32_t iversion;  int8_t iresultcount;  int8_t ierrorcode;  int64_t arrresult[MAX_RESULT];} FooResult;  /* foo- utils.h */ extern FooResult* foo_get_result();extern void foo_build_result(const FooResult*);extern const int64_t* foo_get_result_array(const FooResult*);extern void foo_result_debug(const FooResult*);extern void foo_result_free(FooResult*);    int main (){        FooResult* pfoors;        const uint64_t* parrrs;    int irs, igtcnt;     printf("FooResult: building ...\n");     pfoors = foo_get_result();   if(pfoors != NULL)    {        printf("FooResult: built.\n");       printf("FooResult - Debug 0:\n");         foo_result_debug(pfoors);       printf("FooResult - Content:\n");         printf("FooResult - Code: [%d]\n", pfoors->ierrorcode);        printf("FooResult: Version '%d'\n", pfoors->iversion);        printf("FooResult: (Count: '%d / %d'):\n", pfoors->iresultcount, MAX_RESULT);         for(irs = 0; irs < pfoors->iresultcount; irs++)        {          printf("FooResult: Result[%d]: '%d'\n", irs, pfoors->arrresult[irs]);        }       printf("FooResult: 3 Results filling ...\n");       printf("FooResult: Result 1 filling ...\n");       foo_build_result(pfoors);       printf("FooResult - Debug 1:\n");       foo_result_debug(pfoors);     printf("FooResult: Result 2 filling ...\n");       foo_build_result(pfoors);       printf("FooResult - Debug 2:\n");     foo_result_debug(pfoors);       printf("FooResult: Result 3 filling ...\n");       foo_build_result(pfoors);       printf("FooResult - Debug 3:\n");       foo_result_debug(pfoors);       printf("FooResult: 3 Results filled.\n");       printf("FooResult - Code: [%d]\n", pfoors->ierrorcode);      printf("FooResult: Version '%d'\n", pfoors->iversion);      printf("FooResult: (Count: '%d / %d'):\n", pfoors->iresultcount, MAX_RESULT);       for(irs = 0; irs < pfoors->iresultcount; irs++)      {          printf("FooResult: Result[%d]: '%d'\n", irs, pfoors->arrresult[irs]);      }       igtcnt = pfoors->iresultcount;       printf("FooResult: Array getting ...\n");       parrrs = foo_get_result_array(pfoors);       if(parrrs != NULL)      {        for(irs = 0; irs < igtcnt; irs++)        {              printf("FooResult: Array[%d]: '%d'\n", irs, *(parrrs + irs));        }       }      else  //if(parrrs != NULL)          printf("FooResult: Array get failed!\n");     }    else  //if(pfoors != NULL)      printf("FooResult: Build failed!\n");     printf("FooResult: deleting ...\n");     foo_result_free(pfoors);     printf("FooResult: deleted.\n");         printf("FooResult: App done.\n");   return 0;}  The C Language Host Application can interact with the Library perfectly and reading the field without any issue: $ ./foo_result FooResult: building ... FooResult: built. FooResult - Debug 0: FooResult* Debug: '*FooResult { iversion: 0, iresultcount: -1, ierrorcode: 0, arrresult: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult - Content: FooResult - Code: [ 0 ] FooResult: Version '0' FooResult: (Count: '-1 / 128'): FooResult: 3 Results filling ... FooResult: Result 1 filling ... FooResult - Debug 1: FooResult* Debug: '*FooResult { iversion: 1, iresultcount: 1, ierrorcode: 0, arrresult: [1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: Result 2 filling ... FooResult - Debug 2: FooResult* Debug: '*FooResult { iversion: 2, iresultcount: 2, ierrorcode: 0, arrresult: [1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: Result 3 filling ... FooResult - Debug 3: FooResult* Debug: '*FooResult { iversion: 3, iresultcount: 3, ierrorcode: 0, arrresult: [1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: 3 Results filled. FooResult - Code: [ 0 ] FooResult: Version '3' FooResult: (Count: '3 / 128'): FooResult: Result[0]: '1' FooResult: Result[1]: '2' FooResult: Result[2]: '3' FooResult: Array getting ... FooResult: Array[0]: '1' FooResult: Array[1]: '2' FooResult: Array[2]: '3' FooResult: deleting ... FooResult: deleted. FooResult: App done. Now the corresponding FreePascal Host Application reads only Rubbish. Which seems to me to be an Issue of Memory Outlay. The FreePascal Application is this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program demofooresult;  uses  SysUtils, ctypes; const  External_library='libfoo_utils.so'; {Setup as you need} {$IFDEF FPC}{$PACKRECORDS C}{$ENDIF}  const  MAX_RESULT = 128;  type  PFooStruct = Pointer;   PResultArray = ^TResultArray;  TResultArray = array[0..(MAX_RESULT - 1)] of Int64;   PFooResult = ^TFooResult;  TFooResult = packed record    iversion: cuint32;    iresultcount: cint8;    ierrorcode: cint8;    arrresult: TResultArray;  end;  function foo_get_result(): PFooResult; cdecl; external External_library name 'foo_get_result';procedure foo_build_result(pfoo: PFooResult); cdecl; external External_library name 'foo_build_result';function foo_get_result_array(pfoo: PFooResult): PResultArray; cdecl; external External_library name 'foo_get_result_array';procedure foo_result_debug(pfoo: PFooResult); cdecl; external External_library name 'foo_result_debug';procedure foo_result_free(pfoo: PFooResult); cdecl; external External_library name 'foo_result_free';  var  pfoors: PFooResult;  parrrs: PResultArray;  irs, igtcnt: Integer;begin  WriteLn('FooResult: building ...');   pfoors := foo_get_result;   if pfoors <> Nil then  begin    WriteLn('FooResult: built.');     WriteLn('FooResult - Debug 0:');     foo_result_debug(pfoors);     WriteLn('FooResult - Content:');    WriteLn('FooResult - Code: [', pfoors^.ierrorcode, ']');    WriteLn('FooResult: Version '#39, pfoors^.iversion, #39);    WriteLn('FooResult: (Count: '#39, pfoors^.iresultcount, ' / ', MAX_RESULT, #39'):');     for irs := 0 to pfoors^.iresultcount - 1 do      WriteLn('FooResult: Result[', irs, ']: '#39, pfoors^.arrresult[irs], #39'):');     WriteLn('FooResult: 3 Results filling ...');     WriteLn('FooResult: Result 1 filling ...');     foo_build_result(pfoors);     WriteLn('FooResult - Debug 1:');     foo_result_debug(pfoors);     WriteLn('FooResult: Result 2 filling ...');     foo_build_result(pfoors);     WriteLn('FooResult - Debug 2:');     foo_result_debug(pfoors);     WriteLn('FooResult: Result 3 filling ...');     foo_build_result(pfoors);     WriteLn('FooResult - Debug 3:');     foo_result_debug(pfoors);     WriteLn('FooResult: 3 Results filled.');     WriteLn('FooResult - Code: [', pfoors^.ierrorcode, ']');    WriteLn('FooResult: Version '#39, pfoors^.iversion, #39);    WriteLn('FooResult: (Count: '#39, pfoors^.iresultcount, ' / ', MAX_RESULT, #39'):');     for irs := 0 to pfoors^.iresultcount - 1 do      WriteLn('FooResult: Result[', irs, ']: '#39, pfoors^.arrresult[irs], #39);     igtcnt := pfoors^.iresultcount;     WriteLn('FooResult: Array getting ...');   parrrs := foo_get_result_array(pfoors);     if parrrs <> Nil then    begin      for irs := 0 to igtcnt - 1 do        WriteLn('FooResult: Array[', irs, ']: '#39, parrrs^[irs], #39);     end    else  //if parrrs <> Nil then      WriteLn('FooResult: Array get failed!');   end  else  //if pfoors <> Nil then    WriteLn('FooResult: Build failed!');   WriteLn('FooResult: deleting ...');   foo_result_free(pfoors);   WriteLn('FooResult: deleted.');   WriteLn('App: done.');end.  which prints this Output: $ ./demofooresult FooResult: building ... FooResult: built. FooResult - Debug 0: FooResult* Debug: '*FooResult { iversion: 0, iresultcount: -1, ierrorcode: 0, arrresult: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult - Content: FooResult - Code: [ 0 ] FooResult: Version '0' FooResult: (Count: '-1 / 128'): FooResult: 3 Results filling ... FooResult: Result 1 filling ... FooResult - Debug 1: FooResult* Debug: '*FooResult { iversion: 1, iresultcount: 1, ierrorcode: 0, arrresult: [1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: Result 2 filling ... FooResult - Debug 2: FooResult* Debug: '*FooResult { iversion: 2, iresultcount: 2, ierrorcode: 0, arrresult: [1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: Result 3 filling ... FooResult - Debug 3: FooResult* Debug: '*FooResult { iversion: 3, iresultcount: 3, ierrorcode: 0, arrresult: [1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] }' FooResult: 3 Results filled. FooResult - Code: [ 0 ] FooResult: Version '3' FooResult: (Count: '3 / 128'): FooResult: Result[0]: '65536' FooResult: Result[1]: '131072' FooResult: Result[2]: '196608' FooResult: Array getting ... FooResult: Array[0]: '1' FooResult: Array[1]: '2' FooResult: Array[2]: '3' FooResult: deleting ... FooResult: deleted. App: done. Heap dump by heaptrc unit of /plath/to/demofooresult 6 memory blocks allocated : 719/728 6 memory blocks freed     : 719/728 0 unfreed memory blocks : 0 True heap size : 327680 True free heap : 327680 the direct access to TFooResult.arrresult prints only Rubbish although the Array is as Static Array defined: FooResult - Code: [ 0 ] FooResult: Version '3' FooResult: (Count: '3 / 128'): FooResult: Result[0]: '65536' FooResult: Result[1]: '131072' FooResult: Result[2]: '196608' ---------------------------------------------------------------------- marcov · 2021-01-04 15:34 You declare your Pascal structure as packed. You don't declare your C struct as packed. Solution: declare your C struct as packed by whatever means your C compiler allows. (or remove the packed and add {$packrecords C} above the record in Pascal, which is the most likely packing default for a C compiler). ---------------------------------------------------------------------- domibay_hugo · 2021-01-04 15:48 Thank you very much for this Tip! the access to the Array TFooResult.arrresult works now correctly. Although I had the understanding that the packed record definition is required to match with C Language Library Definitions. I also found that the unit ctypes includes the .inc file /rtl/linux/ptypes.inc which again defines the directives: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$I ctypes.inc}{$packrecords c} ---------------------------------------------------------------------- PascalDragon · 2021-01-05 08:04 Quote from: domibay_hugo on January 04, 2021, 04:48:11 pmAlthough I had the understanding that the packed record definition is required to match with C Language Library Definitions. No. packed ensures that there is no padding between fields of a record. That is not the case by default in C. Quote from: domibay_hugo on January 04, 2021, 04:48:11 pmI also found that the unit ctypes includes the .inc file /rtl/linux/ptypes.inc which again defines the directives: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$I ctypes.inc}{$packrecords c}  That only applies to the records declared inside the ctypes unit (and only if they aren't declared as packed). ---------------------------------------------------------------------- marcov · 2021-01-05 08:46 Quote from: domibay_hugo on January 04, 2021, 04:48:11 pm Although I had the understanding that the packed record definition is required to match with C Language Library Definitions. I was not aware that the C language standard said something about packing. Usually standards are very restrictive to specify too much about it. ---------------------------------------------------------------------- domibay_hugo · 2021-01-12 15:30 Quote from: PascalDragon on January 05, 2021, 09:04:14 amQuote from: domibay_hugo on January 04, 2021, 04:48:11 pmAlthough I had the understanding that the packed record definition is required to match with C Language Library Definitions. No. packed ensures that there is no padding between fields of a record. That is not the case by default in C. Quote from: domibay_hugo on January 04, 2021, 04:48:11 pmI also found that the unit ctypes includes the .inc file /rtl/linux/ptypes.inc which again defines the directives: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$I ctypes.inc}{$packrecords c}  That only applies to the records declared inside the ctypes unit (and only if they aren't declared as packed). So, does that mean that Pascal Structures natively map with C Language Structures without the need of the Directive {$packrecords c} or the Keyword packed ? ---------------------------------------------------------------------- PascalDragon · 2021-01-13 08:01 Quote from: domibay_hugo on January 12, 2021, 04:30:10 pmSo, does that mean that Pascal Structures natively map with C Language Structures without the need of the Directive {$packrecords c} or the Keyword packed ? Not necessarily. That's why the {$packrecords c} directive exists. Also packed is only a right choice if the C code enforces 1-Byte alignment as well. In the end you always need to check what the C code does as it might use a custom alignment (this is for example the case for some structs/records on Windows where the headers enforce a different alignment than the default one and thus the Windows unit changes the {$packrecords xxx} accordingly).