64 bit Porting
Finally I got time to translate “Porting applicazioni a 64 bit” to English. Based on my expertise I’d like to post this article as I think this is a real tricky topic.
With 64 bit processors (and then the 64 bit OS) is needed to port applications to 64 bit to take advantages of it. 64 bit processors have internal registers, ALU, etc. capable to manage 64 bit wide data and natively 64 bit micro instructions. Please, be aware that I’m talking about true 64 bit machine (for instance, Intel Itanium) and not 32 bit processors able to deal with 64 bit applications due to some improvements (new micro instructions set EM64T and AMD64). Main advantages:
- large process space. 32 bit processors can address up to ~4GB address space (this limitation has been overcome with the EM64T and AMD64 technology or by pure 64 bit processors).
- Files dimension (>4GB using standard API): 32 bit processors can manipulate up to 2GB of file.
- Mathematical operation are faster on 64 bit machine.
I didn’t talk about speed this topic require another post.
You can run your 32 bit application on a 64 bit machine but you’ll lose performance. So basically what you have to do is to “recompile” your application on a 64 bit machine. Once compiled you’ll see that it’ll be bigger that the same 32 bit application. Recompile your application could be tricky if your code is “not ready“. And in this post I’ll show you the issue to take care to gain this “64 bit readiness”.
There are seven point to pay attention:
- Variables declaration.
- Type definitions
- Functions parameters
we’ll take a look at some of those but before going through them, let’s have a look at the “data model”. The data model represent the size that the primitive variables have. For instance the LP64 says that Long and pointers have dimension 64 bit (see table below). Just note that from that table float and double are excluded because standard. The data model is really important, it gives the info about the data representation in memory.
Just for your info:
32 bit Un*x: ILP32
32 bit Windows: ILP32
32 bit OS X: ILP32
64 bit *nix: LP64
64 bit Windows: LLP64
64 bit OSX: LP64
if you make assumption that your software will be executed forever on 32 bit platform you’re safe indeed all the OSs uses the same data model ( 🙁 but really do you want to make this silly assumption!?!?).
- Variable Declaration:
- using properly the “L” and “U” suffix for integer constants.
- using unsigned integer carefully to avoid sign extension.
- using integer and long instead of char to increase performance (alignment overhead in the latest case).
- using for the char pointer the unsigned suffix avoiding the sign extension.
- summing two signed integer returns a signed integer.
- summing an int with a long returns a long (the same with the other types)
- summing an unsigned with a signed returns an usigned.
In 64 bit environments
integers and pointers have different byte wide, that’s one of the main point to take care during the porting. So some recommendations:
- doesn’t assign long int variable type to the int one.
- doesn’t assign pointer to int and vice versa.
- doesn’t mix-up unsigned and signed in the assignment (see expression section).
The endianess is very important when you’re dealing with different architectures. It’s referred to the way data are organized in memory. They can be organized in “little endian” or “big endian”. Little Endian: least significant byte is stored in the smallest address (e.g.: x86 architecture). Big Endian: most significant byte is stored in the smallest byte (Motorola processors, ARM processors after version 3). The network format is Big-Endian, there’re some helper method that can convert data in network order (big endian) or host order (little endian): hton16 (host to network and return a int 16), hton32, ntoh16, ntoh32. In 64 bit environment there are not the same helper method but we have some other “macro” like: bswap_16, bswap_32, bswap_64.
- Type Definitions:
It’s not convenient to use directly in your code native types (int, unsigned int, short, etc.) but to provide a wrapper layer that will facilitate the porting to a new architecture/OS mapping basic type with others more meaningfull: UINT16, INT16, INT8, etc … replacing in the header file the associations between basic tapes and new types would allow a smoother porting.+
- gcc options:
when porting (or just writing your code) on 64 bit systems just try to compile your code with this gcc parameters:
- -arch x86_64: 64 bit (x86) gcc options.
- -Wconversion: this flag cause some warning when same conversions occur.
- -Wshorten: similar to the previous but specific for 64 bit. With this flag on the compiler will cause warnings if a data is truncated from 64 bit to 32. This flag is really important!
and lately, even though not specific for 64 bit, good to have:
for more info just take a look at the man page.
- Major 64-bit changes
- Compiling 64-Bit Code
- Unix-Why Lp64