Converting Applications
Two basic issues arise for applications developers regarding conversion:
Data type consistency and the different data models
Interoperation between applications using different data models
Maintaining a single source with as few #ifdefs as possible is usually better than maintaining multiple source trees. This chapter provides guidelines for writing code that works correctly in both 32-bit and 64-bit environments. At best, the conversion of current code might require only a recompilation and relinking with the 64-bit libraries. However, for those cases where code changes are required, this chapter discusses the tools that help make conversion easier.
Data Model
As stated previously, the biggest difference between the 32-bit and 64-bit environments is the change in data-type models.
The C data-type model used for 32-bit applications is the ILP32 model, so named because ints, longs, and pointers are 32-bit. The LP64 data model is the C data-type model for 64-bit applications. This model was agreed upon by a consortium of companies across the industry. It is so named because longs and pointers grow to 64-bit quantities. The remaining C types int, short, and char are the same as in the ILP32 model.
The standard relationship between C integral types still holds true.
sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) |
Table 4-1 lists the basic C types, and their corresponding sizes in bits for both the ILP32 and LP64 data type models.
Table 4-1 Data Type Sizes in Bits
C data type | ILP32 | LP64 |
---|---|---|
char | 8 | unchanged |
short | 16 | unchanged |
int | 32 | unchanged |
long | 32 | 64 |
long long | 64 | unchanged |
pointer | 32 | 64 |
enum | 32 | unchanged |
float | 32 | unchanged |
double | 64 | unchanged |
long double | 128 | unchanged |
It is not unusual for current 32-bit applications to assume that ints, pointers, and longs are the same size. Because the size of longs and pointers change in the LP64 data model, you need to be aware that this change alone can cause many 32-bit to 64-bit conversion problems.
In addition, declarations and casts become very important in showing what is intended; how expressions are evaluated can be affected when the types change. The effects of standard C conversion rules are influenced by the change in data-type sizes. To adequately show what is intended, you might need to declare the types of constants. Casts might also be needed in expressions to make certain that the expression is evaluated the way you intended. This is particularly true in the case of sign extension, where explicit casting might be essential to show intent.
Other problems arise with built-in C operators, format strings, assembly language, and compatibility and interoperability.
The rest of this chapter advises you how to overcome these problems by:
Explaining the problems outlined above in more detail
Describing some of the derived types and include files that are useful to make code safe for both 32-bit and 64-bit
Describing the tools available for helping to make code 64-bit safe
Providing general rules for making code portable between the 32-bit and 64-bit environments
Implementing Single-Source Code
The sections that follow describe some of the resources available to application developers that help you write single-source code that supports both 32-bit and 64-bit compilation.
Derived Types
Using the system derived types helps make code 32-bit and 64-bit safe, since the derived types themselves are safe for both the ILP32 and LP64 data models. In general, using derived types to allow for change is good programming practice. Should the data model change in the future, or when porting to a different platform, only the system derived types need to change rather than the application.
The system include files <sys/types.h> and <inttypes.h> contain constants, macros, and derived types that are helpful in making applications 32-bit and 64-bit safe. While a detailed discussion of these is beyond the scope of this document, some are discussed in the sections that follow, as well as in Appendix A, Changes in Derived Types.
<sys/types.h>
An application source file that includes <sys/types.h> makes the definitions of the programming model symbols, _LP64 and _ILP32, available through inclusion of <sys/isa_defs.h>. This header also contains a number of basic derived types that should be used whenever appropriate. In particular, the following are of special interest:
clock_t | The type clock_t represents the system times in clock ticks. |
dev_t | The type dev_t is used for device numbers. |
off_t | The type off_t is used for file sizes and offsets. |
ptrdiff_t | The type ptrdiff_t is the signed integral type for the result of subtracting two pointers. |
size_t | The type size_t is for the size, in bytes, of objects in memory. |
ssize_t | The "signed size" type ssize_t is used by functions that return a count of bytes or an error indication. |
time_t | The type time_t is used for time in seconds. |
All of these types remain 32-bit quantities in the ILP32 compilation environment and grow to 64-bit quantities in the LP64 compilation environment.
The use of some of these types is explained in more detail later in this chapter under "Guidelines for Converting to LP64".
<inttypes.h>
The include file <inttypes.h> was added to the Solaris 2.6 release to provide constants, macros, and derived types that help programmers make their code compatible with explicitly sized data items, independent of the compilation environment. It contains mechanisms for manipulating 8-bit, 16-bit, 32-bit, and 64-bit objects. The file is part of an ANSI C proposal and tracks the ISO/JTC1/SC22/WG14 C committee's working draft for the revision of the current ISO C standard, ISO/IEC 9899:1990 Programming Language - C.
The basic features provided by <inttypes.h> are:
A set of fixed-width integer types
uintptr_t and other helpful types
Constant macros
Limits
Format string macros
These are discussed in more detail in the sections that follow.