VxWorks 7 - Porting C Code from
32-Bit to 64-Bit
Copyright © 2019 Wind River Systems Inc.
ALL RIGHTS RESERVED
Description
How to make your code compatible with both VxWorks 32 bit and 64 bit platforms.
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
Document Version 0.1
Date: August, 2019
Wind River Systems, Inc.
www.windriver.com
Copyright
Unless otherwise agreed in writing, all copyright and intellectual property rights embodied in
this document are and shall remain the property of Wind River Systems, Inc. This document
is provided solely for the purposes of evaluating the work proposed and no other rights
whatsoever to use the information herein are granted. The contents of this document may not
be disclosed to any third party without the prior written consent of Wind River Systems, Inc.
Trademarks
Wind River, the Wind River logo, and VxWorks are registered trademarks of Wind River
Systems, Inc. Any third party trademarks referenced are the property of their respective
owners. For further information regarding Wind River trademarks, please see
http://windriver.com/company/terms/trademark.html.
2
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
Contents
ABOUT PORTING C CODE FROM 32‐BIT TO 64‐BIT ............................................................................................... 4
VXWORKS 32‐BIT AND 64‐BIT DATA MODELS ................................................................................................................... 4
WHY IS THE DIFFERENCE IN DATA MODELS SIGNIFICANT?...................................................................................................... 5
COMPILER IDENTIFICATION OF PORTING ISSUES ................................................................................................ 6
COMPILER ERRORS AND WARNINGS .................................................................................................................................. 7
32‐BIT/64‐BIT C CODE PORTABILITY .................................................................................................................... 7
CHECK INT AND LONG DECLARATIONS ................................................................................................................................ 7
CHECK ALL ASSIGNMENTS ............................................................................................................................................... 8
CHECK FOR DATA TYPE PROMOTION.................................................................................................................................. 8
CHECK USE OF POINTERS ................................................................................................................................................. 8
CHECK USE OF SIZEOF() ................................................................................................................................................... 9
CHECK USE OF TYPE CASTING ........................................................................................................................................... 9
CHECK FORMAT STRING CONVERSIONS .............................................................................................................................. 9
CHECK USE OF CONSTANTS .............................................................................................................................................. 9
Integer Constants ................................................................................................................................................. 9
0xFFFFFFFF Value ............................................................................................................................................... 10
ERROR Status ..................................................................................................................................................... 10
Bit Masks ............................................................................................................................................................ 10
Magic Numbers .................................................................................................................................................. 10
CHECK STRUCTURES AND UNIONS ................................................................................................................................... 10
USE FUNCTION PROTOTYPES .......................................................................................................................................... 11
USE PORTABLE DATA TYPES ........................................................................................................................................... 11
AS A LAST RESORT: USE CONDITIONAL COMPILATION MACRO ............................................................................................. 12
3
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
About Porting C Code from 32-Bit to 64-Bit
VxWorks for 64-bit hardware platforms is based on the LP64 data model, whereas VxWorks
for 32-bit platforms is based on the ILP32 data model. Code that was written for ILP32 must
be carefully examined and modified so as to run correctly with the LP64 data model
implementation of VxWorks. Most of the issues described in that chapter are common to
porting code from 32-to-64 bit platforms for many operating systems.
Adapting your C code to the LP64 data module is not sufficient for porting it to the 64-bit
configuration of VxWorks. To do so, you must also adapt your code to the current release of
VxWorks.
With some exceptions (which are largely related to the manipulation of 64-bit entities) code
that has been successfully ported to 64-bit VxWorks will successfully re-compile for the
ILP32 data model and execute on 32-bit VxWorks.
VxWorks 32-Bit and 64-Bit Data Models
Prior to VxWorks 6.8, the operating system was designed for use with 32-bit processors, and
adhered to the ILP32 data model. Beginning with VxWorks 6.9, the operating system can be
used with 32-bit and 64-bit processors, using the ILP32 and the LP64 data models,
respectively. The names of the data models refer to the size (32 or 64 bits) of specific data
types, where I stands for integer, L for long, and P for pointer. The following table shows the
size of data types (in bits) for each data model.
DATA TYPE ILP32 LP64
char 8 8
short 16 16
int 32 32
long 32 64
pointer 32 64
long long 64 64
float 32 32
double 64 64
long double IA: 96 others: 64 128
4
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
DATA TYPE ILP32 LP64
off_t VxWorks kernel: 32 64
VxWorks user: 64
size_t 32 64
ssize_t 32 64
ptrdiff_t 32 64
enum 32 32
The long long type comes from C99. In the LP64 data model it does not differ from the long
data type; however it does in the ILP32 data model.
The off_t, size_t, ssize_t, and ptrdiff_t types are POSIX types that represent a file size, an
object's unsigned size, a signed number of bytes read or written (-1 represents an error), and
the signed arithmetic difference between two pointers (respectively). They have been
introduced for the data size neutrality of some API (one does not have to know what size of
objects those types represent in order to use them).
For the ILP32 data model the off_t type is indicated in the table as having either a 32-bit size
or a 64-bit size. This is specific to VxWorks and indicates the size of off_t in the kernel (32
bit) environment and in the RTP environment (64 bits already).
The long double type size is not defined by the C99 standard other than being at least equal to
that of the double type. For the VxWorks compilers it is implemented as follows:
ARCHITECTURE SIZE OF LONG DOUBLE
ARM 32-bit 64-bit
ARM 64-bit 128-bit
Intel 32-bit 96-bit
Intel 64-bit 128-bit
PowerPC 32-bit 64-bit
PowerPC 64-bit 64-bit
Why is the Difference in Data Models Significant?
The main difficulty in porting C code that has been written for a 32-bit hardware platform to a
64- bit hardware platform, or in writing code that is portable between both platforms, has to
do with accounting for the ways in which differences in the sizes of data types can cause code
to malfunction.
5
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
In particular, the change in the size of the pointer type is one of the main causes for the
difficulty in porting code from ILP32 to LP64. One can no longer make the assumption that
an int type variable can hold a pointer (or any address for that matter).
CAUTION: For 64-bit VxWorks, due to the larger size of several data types in the LP64
data model, a task is likely to need a larger stack than what is sufficient for 32-bit VxWorks.
Typically an increase of 35% to 50% is enough to most applications, although there may be
cases in which the stack size must be doubled. Use the checkStack( ) shell command to
determine if your tasks' stacks are too small (that is, the high water mark is too close to the
size of the stack).
Compiler Identification of Porting Issues
The compiler can be used to identify code that may not be portable between 32-bit and 64-bit
VxWorks. The following compiler options are useful for identifying code that may not be
portable between 32-bit and 64-bit VxWorks, and they are enabled by default:
• -Wall
• -Wconversion
• -Wno-sign-conversion
Perform the following steps.
1. Compile your source file with the compiler for the 64-bit architecture. Be sure to use
the proper CPU name to select the proper toolchain. For example, CORE selects
ccpentium.
2. Look for the errors and warnings. For more information, see Compiler Errors and
Warnings on page 7.
3. Look for data size logic issues in your code.
4. Adapt your code following the guidelines provided in and check that it now compiles
properly with the compiler for the 64-bit architecture.
5. Compile your source file with the CPU type appropriate for the 32-bit architecture (for
example, PRESERVE nameliteral-ucPENTIUM4 instead of PRESERVE nameliteral-
ucCORE).
6. Check that there are no errors or warnings in the output. If there are, then go back to
step 4 and find another way of adapting your code.
6
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
Compiler Errors and Warnings
The compiler typically issues one of the following warnings to flag type cast inconsistencies,
when using only the -Wall option:
warning: cast to pointer from integer of different size
warning: cast from pointer to integer of different size
These are typically issued when int type variables are either assigned (with a cast) with the
value of pointer or are representing a pointer to a structure. For example:
tid = (int)taskIdCurrent; /* where 'tid' is an 'int'*/
((ISR_ID)arg)->isrTag = ...; /* where 'arg' is an 'int' */
By default no other warnings relevant to the LP64 programming environment are issued.
The assembler may produce the following error:
Error: suffix or operands invalid for 'pop'
This error is caused by assembly inlines, usually involving macros. This requires either an
update of the inline's assembly code or stopping use of those inlines until they are updated.
32-bit/64-Bit C Code Portability
Writing code that is portable between 32-bit and 64-bit hardware platforms means taking into
account the ways in which differences in the ILP32 and LP64 data models might cause code
to malfunction. The aim is to produce code that is data size neutral. This section provides
guidance both for writing portable code and for porting code from 32-bit to 64-bit hardware
platforms.
NOTE: The guidelines provided in this section are intended to be a summary of the key issues
involved in making C code portable between 32-bit and 64-bit hardware platforms. They do
not provide an exhaustive treatment of the topic (as well as code optimization, and so on).
Check int and long Declarations
While both the int and long types are both 32 bits in the ILP32 data model, the long type is 64
bits in the LP64 model. Code written for 32-bit hardware platforms may well use int and long
types indiscriminately; and int variables are often used as general purpose storage entities.
The int type cannot be used as a generic type in the LP64 programming environment as not
all data types or values fit in a 32-bit storage.
7
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
To ensure correct behavior on 64-bit platforms, and portability between 32-bit and 64-bit
platforms, make sure that all int and long assignments are correct for the intended use of the
variables and for the platform on which they will be used.
The long double type is a problematic type due to both the architectural and compiler-related
variation in its implementation. If this type is used in your code, consider carefully whether
your code really needs it. If so, determine what the compiler produces for the architecture in
question.
Check All Assignments
While integers and pointers are the same size on a 32-bit hardware platform, they are not the
same size on a 64-bit platform. Addresses cannot, therefore, be manipulated through int type
variables, function parameters, or function return values.
Data truncation problems can occur with assignments, initialization, returns and so on because
the long type and pointers are not the same size as int with the LP64 data model. Note that
these problems are not detected by the compiler (for example, while assigning a 64-bit
constant to a 32- bit integer triggers a warning, this does not happen when a 64-bit integer
variable is assigned to a 32-bit one).
Check for Data Type Promotion
Some of the most difficult issues with adapting code to the LP64 programming environment
are related to integer promotion rules and sign extension involved in logic expressions,
assignments, and arithmetic operations. For example, integer promotion rules cause silent
truncations when an integer type variable is assigned the value of a larger-size integer type
variable. The C standard should be consulted with regard to data type promotion.
Check Use of Pointers
Pointers and the int type are not the same size in the LP64 data model. The common practices
for the ILP32 programming environment of using int variables to store the values of pointers,
of casting pointers to int for pointer arithmetic, and so on, will cause pointer corruption.
In addition, because LP64 pointers are 64-bits, it is easy to produce a boundary overflow of an
array of pointers unless sizeof( ) is used to determine the NULL value at the end of an array
8
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
Check use of sizeof()
Because the int and long types are different sizes with the LP64 data model, the use of
sizeof( ) should be examined to make sure the operands are used correctly.
Check Use of Type Casting
While it may be tempting to get rid of compiler warnings by way of type casting, this may do
more harm than good because it silences the compiler when it would be indicating a genuine
problem.
Type casting can lead to value truncation or, worse, memory corruption when a pointer is
involved. For example, consider a function with a signature that has been modified to use a
size _t * instead of an int * parameter, and that is conventionally passed the address of
variables holding small values by way of this parameter. If those variables are not updated to
be of the size_t type but instead are left as int and type-cast to size_t * when their address is
passed to the function, then memory corruption ensues when the function attempts to read or
write 8 bytes at those addresses and only 4 bytes have been reserved by the caller code.
It is, therefore, much preferable to change the type of the variables involved in warnings
rather than use type casting. There are of course situations where type casting is required, but
they should carefully be identified as such.
Check Format String Conversions
String conversion formats for printf( ) and related functions must be used for the data type in
question. The %d format will not work with a long or pointer for the LP64 programming
environment. Use the %ld format for longs and %p for pointers.
Check Use of Constants
Check the use of all constants. Some of the issues you may encounter include the following:
Integer Constants
An un-suffixed integer constant (that is, without s qualifier suffix such as U or L) is
interpreted as the smallest type that can represent the value, starting with the int type. For
example, the integer constant 0xFFFFFFFF is an int, but the integer constant 0x100000000
is a long long for ILP32 or a long for LP64.
9
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
0xFFFFFFFF Value
In the LP64 data model the value 0xFFFFFFFF is not equivalent to -1, and is not even
negative. This may lead to test logic failing to work as intended. In the same vein, using
0xFFFFFFFF as a mask value may not work with LP64. Note that adding the L suffix to the
constant is not going to help in this case, as it will just tell the compiler that the constant is a
long instead of an int, which does not extend the constant, that is, does not make
0xFFFFFFFF interpreted as 0xFFFFFFFF.FFFFFFFF.
ERROR Status
The VxWorks ERROR return status must now be checked with equality or difference
operators only against the ERROR or OK constants (comparing ERROR against
0xFFFFFFFF does not work). The greater-than and less-than operators should not be used
with the ERROR constant. Furthermore, in order to avoid compilation warnings it may be
necessary to cast the ERROR macro with the type of the value against which it is being
compared if this value is not of the STATUS type, is not an int, or may not be equivalent to
an int on 64-bit VxWorks.
Bit Masks
All masks used in logical operations involving the long type should be carefully reviewed due
to the change in size of the long type between ILP32 and LP64.
Magic Numbers
Other constants to pay attention to include the following:
• 4 (the number of bytes in an integer), does not work for address arithmetic in LP64.
• 32 (the number of bits in an integer), does not work for all integer bit manipulation in
• LP64.
• 0x7fffffff (the maximum value of a 32-bit signed variable, often used as mask for
zeroing of the right-most bit in a 32-bit type).
• 0x80000000 (the minimum value of a 32-bit signed variable, often used as a mask for
allocation of the right-most bit in a 32-bit type).
Check Structures and Unions
The size and alignment of structures will be different in the ILP32 and LP64 programming
environments if the structures contain elements that are different sizes in the respective data
10
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
models. According to the C language, a structure is aligned on the same boundary as its most
strictly aligned member.
Structures should, therefore, be examined with consideration to changing the order of
members so that as many as possible fall on natural alignment. In general, move long and
pointer members to the beginning of the structure. While explicit padding may sometimes be
useful to counteract automatic padding introduced by the compiler, it should generally be
avoided in the context of portability because of data bloat.
Unions should be carefully examined to ensure that the size of the members maintains
equivalence with the LP64 data model. For example, the union of an int type member and a
long type member works seamlessly in the ILP32 programming environment but is much
more error- prone in the LP64 programming environment where one must be careful not to
store the 64-bit value in the 32-bit member of the union (which will result in truncation,
possibly silent if only standard warning level is used).
Use Function Prototypes
By default C compilers assume that a function returns a value of type int. If your code
includes a function that returns a long or a pointer (but for which there is no prototype or for
which you did not include the appropriate header file) then the compiler generates code that
truncates the return value to fit in 32-bit storage.
Create prototypes for your functions and use the prototypes of functions that are employed in
your code.
Use Portable Data Types
All APIs with parameters or return values that represent an offset in memory or in a file, or
the size of a region of memory or of a file, or the size of a memory buffer or a file should be
changed as follows:
• If the parameter or return value represents a size of a file, or the size of a region of a
file, or an offset in a file, replace int and unsigned int with off_t.
• If the parameter represents a region of memory or a buffer, replace int and unsigned int
with size_t.
• If the original return value represents a size in memory, but can be negative (usually
because it can return -1), then the replacement type should be ssize_t. If the return
value can only be positive then use size_t.
11
VxWorks 7 - Porting C Code from 32-Bit to 64-Bit
As a Last Resort: Use Conditional Compilation Macro
As a last resort, when code must implement logic that is not portable between data models,
the_WRS_CONFIG_LP64 macro can be used for dual definitions, as follows:
#ifdef _WRS_CONFIG_LP64
/* type definition for LP64 here */
#else /
/* type definition for ILP32 here */
...
#endif /* _WRS_CONFIG_LP64 */
Use this macro only when absolutely necessary. You should try to make code work with both
the ILP32 and LP64 programming environments. The macro must, however, be used when
the code must implement a logic that cannot apply to one or the other of the data models. The
typical case is a type definition involving size when the type name (but not the definition,
obviously) must be identical for both ILP32 and LP64. The same is true for macros defining
values that cannot be identical for both ILP32 and LP64.
The _WRS_CONFIG_LP64 macro is available when the vsbConfig.h header file is
included. The _WRS_CONFIG_LP64 make variable is also provided for use in makefiles.
12