Library tutorials & articles

Typical errors of porting C++ code on the 64-bit platform

Storing of pointer addresses

A large number of errors during the migration on 64-bit systems are related to the change of a pointer size in relation to the size of usual integers. In the environment with the data ILP32 usual integers and pointers have the same size. Unfortunately the 32-bit code is based on this supposition everywhere. Pointers are often casted to int, unsigned int and other types improper to fulfill address calculations.

You should understand exactly that one should use only memsize types for integer pointers form. Preference should be given to uintptr_t type for it shows intentions more clearly and makes the code more portable saving it from changes in future

Let’s look at two small examples.

1) char *p;
   p = (char *) ((int)p & PAGEOFFSET);

 

2) DWORD tmp = (DWORD)malloc(ArraySize); 
   ...
   int *ptr = (int *)tmp;

The both examples do not take into account that the pointer size may differ from 32-bits. They use the explicit type conversion which truncates high bits in the pointer and this is surely a mistake on the 64-bit system. Here are the corrected variants which use integer memsize type intptr_t and DWORD_PTR to store pointer addresses:

1) char *p;
   p = (char *) ((intptr_t)p & PAGEOFFSET);

 

2) DWORD_PTR tmp = (DWORD_PTR)malloc(ArraySize); 
   ...
   int *ptr = (int *)tmp;

The danger of the two examples studied is that the fail in the program may be found much time later. The program may work absolutely correctly with a small data size on the 64-bit system while the truncated addresses lie in first 4 Gb of memory. And then on launching the program for large production aims there will be the memory allocation out of first 4 Gb. The code given in the examples will cause an undefined behavior of the program on the object out of first 4 Gb while processing the pointer.

The following code won’t hide and will show up at the first execution.

void GetBufferAddr(void **retPtr) {
  ...
  // Access violation on 64-bit system
  *retPtr = p;
}

unsigned bufAddress;
GetBufferAddr((void **)&bufAddress); 

The correction is also in the choice of the type capable to store the pointer.

uintptr_t bufAddress;
GetBufferAddr((void **)&bufAddress); //OK

There are situations when storing of the pointer address into a 32-bit type is just necessary. Mostly such situations appear when it is necessary to work with old API functions. For such cases one should resort to special functions LongToIntPtr, PtrToUlong etc.

In the end I’d like to mention that it will be a bad style to store the pointer address into types which are always equal 64-bits. One will have to correct the code shown further again when 128-bit systems will appear.

PVOID p;
// Bad style. The 128-bit time will come.
__int64 n = __int64(p);
p = PVOID(n);

Comments

  1. 01 Jan 1999 at 00:00

Leave a comment

Sign in or Join us (it's free).

Andrey Karpov
AddThis

Related podcasts

  • Interview with Shawn Burke on Microsoft's .NET Source Code Release

    Scott and Carl talk with Shawn Burke on the culmination of his many-year-old plan to get parts of the source of the .NET Framework released. With Visual Studio 2008, a simple process will allow developers to STEP INTO the .NET Framework Source from the IDE. This'll be a great debugging and learni...

Want to stay in touch with what's going on? Follow us on twitter!