|
|
HP C
|
Previous | Contents | Index |
OpenVMS 64-bit virtual addressing support makes the 64-bit virtual address space defined by the Alpha and Itanium architectures available to the OpenVMS operating system and its users. It also allows per-process virtual addressing for accessing dynamically mapped data beyond traditional 32-bit limits.
The HP C compiler supports 64-bit pointers on all hardware platforms where the OpenVMS operating system supports 64-bit pointers; that is, on the Alpha and Itanium processors.
This support is provided through command-line qualifiers and pragma preprocessor directives that control the size of the C pointer because:
Single objects larger than 2 gigabytes are not fully supported, even with 64-bit virtual addressing in effect. |
No changes are required for existing 32-bit applications that do not need to exploit 64-bit addressing.
The following qualifiers, pragmas, and predefined macro control pointer size:
The /POINTER_SIZE qualifier lets you specify a value of 64 or 32 (or LONG or SHORT) as the default pointer size within the compilation unit. You can compile one set of modules using 32-bit pointers and another set using 64-bit pointers. Take care when these two separate groups of modules call each other.
The default is /NOPOINTER_SIZE, which:
This default represents no change over previous versions of HP C.
Specifying /POINTER_SIZE with a keyword value (32, 64, SHORT, or LONG) has the following effects:
Use of the /POINTER_SIZE qualifier also influences the processing of HP C RTL header files:
See the HP C Run-Time Library Reference Manual for OpenVMS Systems for more information on the impact of 64-bit pointer support on HP C RTL functions.
See Section 1.3.4 for more information about /POINTER_SIZE.
The __INITIAL_POINTER_SIZE preprocessor macro is useful for header-file authors to determine:
Header-file code can then be conditionalized using the following preprocessor directives:
#if defined (__INITIAL_POINTER_SIZE) /* Compiler supports 64-bit pointers */ #if __INITIAL_POINTER_SIZE > 0 /* Application uses 64-bit pointers */ #if __INITIAL_POINTER_SIZE == 32 /* Application uses some 64-bit pointers, but default RTL routines are 32-bit.*/ #if __INITIAL_POINTER_SIZE == 64 /* Application uses 64-bit pointers and default RTL routines are 64-bit. */ |
Use the /CHECK=POINTER_SIZE qualifier to generate code that checks 64-bit pointer values at runtime to make sure they can fit in a 32-bit pointer. If such a value cannot be represented by a 32-bit pointer, the run-time code signals a range error (SS$_RANGEERR).
Be aware that the compiler generates the same kinds of warning messages for pointer-size mismatches whether or not this qualifier is specified. The run-time checks can detect problems that cannot be detected at compile time, and can help determine whether or not certain warnings are safe to suppress.
See Section 1.3.4 for more information about /CHECK=POINTER_SIZE, including defaults and an example.
The #pragma pointer_size and #pragma required_pointer_size preprocessor directives can be used to change the pointer size currently in effect within a compilation unit. You can default pointers to 32-bits and then declare specific pointers within the module as 64-bits. In this case, you also need to specifically call the _malloc64 form of malloc to obtain memory from the 64-bit memory area.
These pragmas have the following format:
#pragma pointer_size keyword #pragma required_pointer_size keyword |
The keyword is one of the following:
{ short |32} | 32-bit pointer |
{ long |64} | 64-bit pointer |
save | Saves the current pointer size |
restore | Restores the current pointer size to its last saved state |
The #pragma pointer_size and #pragma required_pointer_size directives work essentially the same way, except that #pragma required_pointer_size always takes effect regardless of command-line qualifiers, while #pragma pointer_size is only in effect when the /POINTER_SIZE command-line qualifier is used.
The #pragma pointer_size behavior allows a program to be built using 64-bit features as purely as a 32-bit program, just by changing the command-line qualifier.
The #pragma required_pointer_size is intended for use in header files where interfaces to system data structures must use a specific pointer size regardless of how the program is compiled.
See Sections 5.4.19 and 5.4.20 for more information on the pointer-size pragmas.
The pointer-size qualifiers and pragmas affect only a limited number of constructs in the C language itself. At places where the syntax creates a pointer type, the pointer-size context determines the size of that type. Pointer-size context is defined by the most recent pragma (or command-line qualifier) affecting pointer size.
Here are examples of places in the syntax where a pointer type is created:
int **p; // Declaration ip = (int **)i; // Cast |
void foo(int ia[10][20]) {} // Means the following: void foo(int (*ia)[20]) {} |
void foo (int func()): // Means the following: void foo (int (*)() func); |
typedef int a_type[10]; void foo (a_type ia); // Means the following: void foo (int *ia); |
Note that a typedef binds the meaning of pointer syntax while a macro does not. Even though both constructs can contain a * used in a declaration, the * in the macro definition is not affected by any pointer-size controls until the point at which the macro is expanded. For example:
#pragma pointer_size 64 typedef int * j_ptr; // * is 64-bit #define J_PTR int * // * is not analyzed #pragma pointer_size 32 j_ptr j; // j is a 64-bit pointer. J_PTR J; // J is a 32-bit pointer. |
The following special cases are not affected by pointer-size context:
#pragma pointer_size 64 main(int argc, char **argv) { ASSERT(sizeof(argv[0]) == 4); } |
#pragma pointer_size 64 ASSERT(sizeof("x" + 0) == 4); |
sizeof(&foo) == 32 sizeof(&s ->next) == sizeof(s) |
#pragma pointer_size 64 typedef int * ip64; #pragma pointer_size 32 typedef int * ip32; ip64 a,b; ip32 c; a = (ip32)b; // No high-order bits are lost c = (ip32)b; // High-order bits are lost |
An application can use both 32-bit and 64-bit addresses. The following semantics apply when mixing pointers:
The following general header-file considerations should be kept in mind:
fprintf(FILE *, const char *, ...); |
Be aware that pointer-size controls are not unique in the way they affect header files; other features that affect data layout have similar impact. For example, most header files should be compiled with 32-bit pointers regardless of pointer-size context. Also, most system header files (on OpenVMS Alpha and I64 systems) must be compiled with member_alignment regardless of user pragmas or qualifiers.
To address this issue more generally, the pragma environment directive can be used to save context and set header defaults at the beginning of each header file, and then to restore context at the end. See Section 5.4.4 for a description of pragma environment .
For header files that have not yet been upgraded to use #pragma environment, the /POINTER_SIZE=64 qualifier can be difficult to use effectively. For such header files that are not 64-bit aware, the compiler automatically applies user-defined prologue and epilogue files before and after the text of the included header file. See Section 1.7.4 for more information on prologue/epilogue files.
HP C automatically processes user-supplied prologue and epilogue header files. This feature is an aid to using header files that are not 64-bit aware within an application that is built to exploit 64-bit addressing.
HP C header files typically contain a section at the top that:
A section at the end of the header file then restores these pragmas to their previously-saved state.
Mixed pointer sizes introduce another kind of state that typically needs to be saved, set, and restored in header files that define fixed 32-bit interfaces to libraries and data structures.
The #pragma environment preprocessor directive allows headers to control all compiler states (message suppression, extern_model , member_alignment , and pointer_size ) with one directive.
However, for header files that have not yet been upgraded to use #pragma environment , the /POINTER_SIZE=64 qualifier can be difficult to use effectively. In this case, the automatic mechanism to include prologue/epilogue files allows you to protect all of the header files within a single directory (or modules within a single text library). You do this by copying two short files into each directory or library that needs it, without having to edit each header file or library module separately.
In time, you should modify header files to either exploit 64-bit addressing (like the HP C RTL), or to protect themselves with #pragma environment . Prologue/epilogue processing can ease this transition.
Prologue/epilogue file are processed in the following way:
__DECC_INCLUDE_PROLOGUE.H __DECC_INCLUDE_EPILOGUE.H |
The prologue/epilogue files are otherwise treated as if they had been included explicitly: #line directives are generated for them if /PREPROCESS_ONLY output is produced, and they appear as dependencies if /MMS_DEPENDENCY output is produced.
To take advantage of prologue/epilogue processing for included header files, you need to create two files, __DECC_INCLUDE_PROLOGUE.H and __DECC_INCLUDE_EPILOGUE.H , in the same directory as the included file.
Suggested content for a prologue file is:
__DECC_INCLUDE_PROLOGUE.H: #ifdef __PRAGMA_ENVIRONMENT #pragma environment save #pragma environment header_defaults #else #error "__DECC_INCLUDE_PROLOGUE.H: This compiler does not support pragma environment" #endif |
Suggested content for an epilogue file is:
__DECC_INCLUDE_EPILOGUE.H: #ifdef __PRAGMA_ENVIRONMENT #pragma __environment restore #else #error "__DECC_INCLUDE_EPILOGUE.H: This compiler does not support pragma environment" #endif |
Consider the following suggestions to avoid problems related to pointer size:
The following examples illustrate the use and misuse of 64-bit pointers.
Example 1-2 Watch Out for Pointers to Pointers (**) |
---|
/* CC/NAME=AS_IS/POINTER_SIZE=64 */ #include <stdio.h> #pragma pointer_size 64 char *C[2] = {"AB", "CD"}; /* sizeof(C) = 16 */ char **CPTRPTR = C; char **CPTR; #pragma pointer_size 32 char *c[2] = {"ab", "cd"}; /* sizeof(C) = 8 */ char **cptrptr = c; char **cptr; int main (void) { CPTR = cptr; /* No problem. */ cptr = CPTR; /* %CC-W-MAYLOSEDATA */ CPTRPTR = cptrptr; /* %CC-W-PTRMISMATCH */ cptrptr = CPTRPTR; /* MAYLOSEDATA & PTRMISMATCH */ puts(cptrptr[0]); /* ab */ puts(cptrptr[1]); /* cd */ puts(CPTRPTR[0]); /* Bad address passed. */ puts(CPTRPTR[1]); /* Fetch off end of c. */ } |
Compiling Example 1-2 produces:
$ cc example1/name=as_is/pointer_size cptr = CPTR; /* %CC-W-MAYLOSEDATA */ ....^ %CC-W-MAYLOSEDATA, In this statement, "CPTR" has a larger data size than "short pointer to char". Assignment may result in data loss.) CPTRPTR = cptrptr; /* %CC-W-PTRMISMATCH */ ....^ %CC-W-PTRMISMATCH, In this statement, the referenced type of the pointer value "cptrptr" is "short pointer to char", which is not compatible with "long pointer to char". cptrptr = CPTRPTR; /* MAYLOSEDATA & PTRMISMATCH */ ....^ %CC-W-MAYLOSEDATA, In this statement, "CPTRPTR" has a larger data size than "short pointer to short pointer to char". Assignment may result in data loss.) cptrptr = CPTRPTR; /* MAYLOSEDATA & PTRMISMATCH */ ....^ %CC-W-PTRMISMATCH, In this statement, the referenced type of the pointer value "CPTRPTR" is "long pointer to char", which is not compatible with "short pointer to char". |
Example 1-3 Trivial 64-Bit Exploitation |
---|
#include <stdio.h> #include <stdlib.h> __int64 limit, count; size_t bytes; char *cp, *prevcp; int main(int argc, char **argv) { sscanf(argv[1], "%d", &bytes); sscanf(argv[2], "%Ld", &limit); printf("bytes %d, limit %Ld, tot %Ld\n", bytes, limit, bytes * limit); for (count=0; count < limit; count++) { if (!(cp = malloc(bytes))) { printf("Max %Ld bytes.\n", bytes * (count + 1)); break; } else if (!prevcp) printf("First addr %Lx.\n", cp); } prevcp = cp; printf("Last addr %Lx.\n", prevcp); } |
Compiling, linking, and running Example 1-3 produces:
$ cc example2 $ link example2 $ example2:==$sys$login:[.john]example2 ! << set up a symbol $ example2 65536 1234567890123456 bytes 65536, limit 1234567890123456, tot 7121664952292605952 First addr 610b0. First addr 730b0. First addr 850b0. First addr 970b0. First addr a90b0. . . . First addr f1c30b0. First addr f1d50b0. First addr f1e70b0. First addr f1f90b0. First addr f20b0b0. Max 225378304 bytes. Last addr 0. $ $ cc/pointer_size=64 example2 $ link example2 $ example2 65536 1234567890123456 bytes 65536, limit 1234567890123456, tot 7121664952292605952 First addr 1c0010010. Max 42532864 bytes. Last addr 1c2d8e010. |
Example 1-4 Preceding Example No Longer Trivial |
---|
#include <stdio.h> #include <stdlib.h> __int64 limit, count; size_t bytes; char *cp, *prevcp; static void do_args(char **args) { sscanf(argv[1], "%d", &bytes); sscanf(argv[2], "%Ld", &limit); printf("bytes %d, limit %Ld, tot %Ld\n", bytes, limit, bytes * limit); } int main(int argc, char **argv) { do_args(argv); for (count=0; count < limit; count++) { if (!(cp = malloc(bytes))) { printf("Max %Ld bytes.\n", bytes * (count + 1)); break; } else if (!prevcp) { printf("First addr %Lx.\n", cp); } prevcp = cp; printf("Last addr %Lx.\n", prevcp); } |
Compiling Example 1-4 produces:
$ cc/pointer_size=64 example3 do_args(argv); ....^ %CC-W-PTRMISMATCH, In this statement, the referenced type of the pointer value "argv" is "short pointer to char", which is not compatible with "long pointer to char". |
Previous | Next | Contents | Index |
|