Adventures in Squeak: x64 sizeof PROCESSENTRY32

Over the past few months I have been developing my programming language, Squeak, which compiles Squeak files into first byte executable instructions for Windows systems.

Recently I have been adding support for x64 Windows in addition to x32 Windows. I ran into this frustrating behaviour while trying to calculate offsets for member variables in structs/pointer offsets and thought it might be helpful for others.

PROCESSENTRY32

The PROCESSENTRY32 struct is defined in tlhelp32.h. The PROCESSENTRY32 structure has been reproduced from PROCESSENTRY32 structure (tlhelp32.h) below.

typedef struct tagPROCESSENTRY32 {
  DWORD     dwSize;
  DWORD     cntUsage;
  DWORD     th32ProcessID;
  ULONG_PTR th32DefaultHeapID;
  DWORD     th32ModuleID;
  DWORD     cntThreads;
  DWORD     th32ParentProcessID;
  LONG      pcPriClassBase;
  DWORD     dwFlags;
  CHAR      szExeFile[MAX_PATH];
} PROCESSENTRY32;

In x86 Windows, the size of this structure is as expected, 296. This is broken down in the tables below.

Windows x86 (32 bit)

Information here comes from various sources, but the ‘guide’ can be found at the Windows documentation.

TypeWidth
DWORD0d04
ULONG_PTR0d04
LONG0d04
CHAR0d01
Widths of various types in x86 (32bit). Note that MAX_PATH is defined as 260.

This gives us the following layout:

MemberType (0dWidth)Offset
dwSizeDWORD (4)0d00
cntUsageDWORD (4)0d04
th32ProcessIDDWORD (4)0d08
th32DefaultHeapIDULONG_PTR (4)0d12
th32ModuleIDDWORD (4)0d16
cntThreadsDWORD (4)0d20
th32ParentProcessIDDWORD (4)0d24
pcPriClassBaseLONG (4)0d28
dwFlagsDWORD (4)0d32
szExeFile[MAX_PATH]CHAR (260)0d36
Member fields, their widths, and offsets.

This results in sizeof(PROCESSENTRY32) being 296 as expected.

For Windows x64, however, we do not experience the same behaviour, even when we accommodate for the width of ULONG_PTR.

Windows x64 (64 bit)

On Windows x64, we have similar sizes as with x86:

TypeWidth
DWORD0d04
ULONG_PTR0d08
LONG0d04
CHAR0d01
Widths of various types in x64(64bit). Note that MAX_PATH is defined as 260.

The additional 4 bytes in the span of the ULONG_PTR type would leave one to assume that the sizeof(PROCESSENTRY32) in x64 would be 300. This is not correct however. For struct elements 0d08 bytes wide, they seem to need to be aligned. This results in Windows, and the x86_64-w64-mingw32-gcc compiler inserting a wasted 0d04 bytes between th32ProcessID and th32DefaultHeapID and results in the sizeof(PROCESSENTRY32) being 304 bytes. At first I thought this was due to the constraint of pointer types requiring 16 byte alignment for x64; but the mingw32 compiler family seems to apply this for all 0d08 byte wide member fields for unpacked structs.

In the screenshot below shows the PROCESSENTRY32 struct on x64 in x64dbg, shows the structure. Here, a process with PID 308 and PPID 480 has a total of 0d08 bytes of padding between the th32ProcessID and th32DefaultHeapID fields.

This was a painful experience for me, and I wish I understood it better at the onset as I expended about 8 hours of my day trying to figure out what was happening within this structure when calling it within assembly for my compiler. For the time being, I have adjusted the offset calculator within Squeak as follows:

if ( arch == ARCHITECTURE_WIN_64 ) {
     if (SqType_Is_Pointer( sqObject->type) ){
        if ( sqStruct->size % 16 != 0 ){

            sqStruct->size += (16 - (sqStruct->size % 16));
        }
    }
} 

Where the SqType_Is_Pointer currently returns true for ‘pointer’ types. This may end up being required for any 0d08 byte wide field, who knows. If anyone has documentation that covers this, please let me know!


I’ve done a bit of further reading/research, turns out I could have found this information myself on Wikipedia. On Windows, 8 byte types will be 8 byte aligned. This has resulted in the following code:

if ( arch == ARCHITECTURE_WIN_64 ) {
  unsigned int alignment = SQUEAKTYPEWIDTH( sqObject-  >type, ARCHITECTURE_WIN_64);
  if ( sqStruct->size % alignment != 0 ){
    SqStruct->size += (alignment - (sqStruct->size % alignment));
  }
}

Time will tell if it actually works in all cases.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Secured By miniOrange