0% found this document useful (0 votes)
12 views

Adding 64 Bit Pointer Support To 32 Bit Runtime Library

Uploaded by

panopticonvoid
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

Adding 64 Bit Pointer Support To 32 Bit Runtime Library

Uploaded by

panopticonvoid
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Duane A.

Smith

Adding 64-bit Pointer


Support to a 32-bit
Run-time Library

A key component of delivering 64-bit addressing The OpenVMS Alpha operating system, version 7.0,
on the OpenVMS Alpha operating system, ver- has extended the address space accessible to applica-
sion 7.0, is an enhanced C run-time library that tions beyond the traditional 32-bit address space. This
new address space is referred to as 64-bit virtual mem-
allows application programmers to allocate and
ory and requires a 64-bit pointer for memory access.1
utilize 64-bit virtual memory from their C pro- The operating system has an additional set of new
grams. This C run-time library includes modified memory allocation routines that allows programs to
programming interfaces and additional new allocate and release 64-bit memory. In OpenVMS
interfaces yet ensures upward compatibility Alpha version 7.0, this set of routines is the only mech-
for existing applications. The same run-time anism available to acquire 64-bit memory.
For application programs to take advantage of these
library supports applications that use only
new OpenVMS programming interfaces, high-level
32-bit addresses, only 64-bit addresses, or programming languages such as C had to support
a combination of both. Source code changes 64-bit pointers. Both the C compiler and the C run-
are not required to utilize 64-bit addresses, time library required changes to provide this support.
although recompilation is necessary. The new The compiler needed to understand both 32-bit and
techniques used to analyze and modify the 64-bit pointers, and the run-time library needed to
accept and return such pointers.
interfaces are not specific to the C run-time
The compiler has a new qualifier called /pointer_size,
library and can serve as a guide for engineers which sets the default pointer size for the compilation
who are enhancing their programming inter- to either 32 bits or 64 bits. Also added to the compiler
faces to support 64-bit pointers. are pragmas (directives) that can be used within the
source code to change the active pointer size. An
application program is not required to compile each
module using the same /pointer_size qualifier; some
modules may use 32-bit pointers while others use
64-bit pointers. Benson, Noel, and Peterson describe
these compiler enhancements.2 The DEC C User’s
Guide for OpenVMS Systems documents the qualifier
and the pragmas.3
The C run-time library added 64-bit pointer sup-
port either by modifying the existing interface to a
function or by adding a second interface to the same
function. Public header files define the C run-time
library interfaces. These header files contain the pub-
licly accessible function prototypes and structure defi-
nitions. The library documentation and header files
are shipped with the C compiler; the C run-time
library ships with the operating system.
This paper discusses all phases of the enhancements
to the C run-time library, from project concepts
through the analysis, the design, and finally the imple-
mentation. The DEC C Runtime Library Reference
Manual for OpenVMS Systems contains user documen-
tation regarding the changes.4

Digital Technical Journal Vol. 8 No. 2 1996 83


Starting the Project 3. Duplicated to have a 64-bit-specific interface
4. Restricted from using 64-bit pointers
We devoted the initial two months of the project to
understanding the overall OpenVMS presentation of One last point to come from the meetings was
64-bit addresses and deciding how to present 64-bit that many of the C run-time library interfaces are
enhancements to customers. Representatives from implemented by calling other OpenVMS images. For
OpenVMS engineering, the compiler team, the run- example, the Curses Screen Management interfaces
time library team, and the OpenVMS Calling Standard make calls to the OpenVMS Screen Management
team met weekly with the goal of converging on the (SMG) facility. It is important that the C run-time
deliverables for OpenVMS Alpha version 7.0. library defines the interfaces to support 64-bit
The project team was committed to preserving both addresses without looking at the implementation of
source code compatibility and the upward compati- the function. Consistency and completeness of the
bility aspects of shareable images on the OpenVMS interface are more important than the complexity
operating system. Early discussions with application of the implementation. In the SMG example, if the
developers reinforced our belief that the OpenVMS C run-time library needs to make a copy of a string
system must allow applications to use 32-bit and prior to passing the string to the SMG facility, this
64-bit pointers within the same application. The team is what will be implemented.
also agreed that for a mixed-pointer application to
work effectively, a single run-time library would need Analyzing the Interfaces
to support both 32-bit and 64-bit pointers; however,
there was no known precedent for designing such The process of analyzing the interfaces began by creat-
a library. ing a document that listed all the header files and the
One implication of the decision to design a run- definitions in these files. A total of 50 header files that
time library that supported 32-bit and 64-bit pointers contained approximately 50 structures and 500 proto-
was that the library could never return an unsolicited types needed to be analyzed. Each structure or pro-
64-bit pointer. Returning a 64-bit pointer to an totype had to be examined to see if a change in pointer
application that was expecting a 32-bit pointer would size would affect the interface. Keep in mind that
result in the loss of one half of an address. Although we analyzed only the interfaces; we did not examine
typically this error would cause a hardware exception, the underlying implementation changes that would
the resulting address could be a valid address. Storing be required.
to or reading from such an address could result in
incorrect behavior that would be difficult to detect. Analyzing the Structures
The OpenVMS Calling Standard specifies that argu- It is necessary to distinguish between a structure,
ments passed to a function be 64-bit values.5 If a which may contain pointers, and a pointer to the struc-
32-bit address is used, it is always sign extended to ture itself. For example, the div_t structure contains
form a 64-bit address that can be used by the Alpha two integer fields. Although the size of the pointer
hardware. The C run-time library team exploited this to div_t does not affect the contents of the structure,
fact when creating the 64-bit interface to the library. the entire structure may be allocated in 32-bit or 64-bit
The team also agreed that using 64-bit pointers virtual memory. Functions that accept a pointer to such
should be as simple as possible; the simplest mode a structure may need to be modified to accommodate
would allow the application to compile using the the 64-bit case. The div_t structure is
qualifier /pointer_size=64 without making source typedef struct {
code changes. The design of 64-bit support must int quot, rem;
appear as a logical extension to the C programming } div_t;
environment in use today. Furthermore, applications
Many structures used in the C run-time library
written to conform strictly to the ANSI standard must
interfaces are allocated by the run-time library in
be able to use 64-bit pointers while remaining confor-
response to a function call. For example, a call to the
mant. For example, allocating 64-bit virtual memory
fopen function returns the following pointer to
would be an extension to the standard C memory man-
the FILE structure:
agement functions malloc, calloc, realloc, and free.
This paper shows that each of the C run-time library FILE *fopen(const char *filename,
interfaces examined falls into one of the following const char *mode);
four categories (listed in order of added complexity
The C run-time library always allocates FILE struc-
to library users):
tures in 32-bit virtual memory and returns a 32-bit
1. Not affected by the size of a pointer pointer to the calling program. This important con-
2. Enhanced to accept both pointer sizes cept can dramatically impact the use of 64-bit pointers

84 Digital Technical Journal Vol. 8 No. 2 1996


in structures. If a FILE pointer is always a 32-bit implementation would be modified to accept only
pointer, structures that contain only FILE pointers are 64-bit addresses. An implementation that supports
not affected by the choice of pointer size. We use this parameters of either pointer size is referred to as being
information when we look at the layout of structures 64-bit friendly. The function strlen is an example of
and examine function prototypes that accept pointers a 64-bit-friendly function.
to structures.
size_t strlen(const char *string);
In this paper, structures that are always allocated in
32-bit virtual memory are referred to as structures The string parameter is the only part of the strlen func-
bound to low memory. After determining which tion that the pointer size affects. To support 64-bit
structures are bound to low memory, we examine the addressing, the strlen function had to be modified to
layout of each structure to decide if the structure accept a 64-bit pointer.
is affected by pointer size. We keep in mind that
pointer size does not affect a structure that is bound Parameters Bound to Low Memory In structures bound
to low memory. to low memory, the addresses that the programs pass
For upward compatibility, the analysis must always are always 32-bit addresses. One explanation is that
consider existing software that depends on the layout the structures are managed by the run-time library,
of the structure. In the case of public header file analy- and the only method of creating, destroying, or
sis, such dependence will probably always be present. obtaining the addresses of these structures is by calling
An application may have executable code that, for a library routine. Given that a single library services
example, fetches 4 bytes beginning at byte 12 of the both 32-bit and 64-bit calling programs, the library
structure and dereferences those 4 bytes as the address does not change the structures based on command
of a string. qualifiers, nor does it allocate the structures in 64-bit
For these public structures, the analysis must weigh virtual memory. For user convenience, the C run-time
the impact of forcing these structures to be 32-bit library implemented these pointers as 32-bit return
pointers. If the decision is made to allocate two differ- values but 64-bit-friendly parameters.
ent structure types, each function that either returns The reason for this design became apparent while
or is passed such a structure must have a pointer-size- testing the 64-bit interfaces to the library. Consider
specific implementation. The case analysis and further the following code fragment, which exists in many
details appear in the section Pointer to Pointer-size- applications:
sensitive Structures.
FILE *fp;
char buffer[100];
Analyzing the Function Prototypes fp = fopen(“the_file”, “r”);
Analyzing functions only requires looking at the func- fread(array, sizeof(buffer), 1, fp);
tion prototypes. To determine the effect of pointer
size on a function, we look at each parameter and The C run-time library always allocates a FILE
return value that involves a pointer. This section structure in 32-bit virtual memory. When the previous
describes the types of situations that are affected by code fragment is compiled using /pointer_size=64, fp
pointer size, from the simplest type to the most com- is declared as a 64-bit pointer to a FILE structure,
plex. Note that when a program passes an array of any because using this qualifier specifies the default pointer
type to a function, the array is passed as a pointer and size to be used. When the fopen function returns the
must be considered. 32-bit pointer, the return value is sign extended into
the 64-bit FILE pointer. If the fourth parameter of the
Making 64-bit-friendly Parameters As mentioned in fread function had been declared as a 32-bit FILE
the section Starting the Project, the OpenVMS Calling pointer, the compiler would report an error when the
Standard specifies that a 32-bit address is sign 64-bit FILE pointer fp was passed as an argument.
extended to a 64-bit address when passed as an This example explains why the C run-time library
argument to a function. This implies that existing declares structures bound to low memory as 32-bit
programs that pass addresses as parameters are already return values but 64-bit parameters.
sign extending those 32-bit addresses to be passed as
64-bit quantities. Each 32-bit address can, therefore, Parameters Restricted to Low Memory Structures
be expressed as a 64-bit address in which the top restricted to low memory are similar to those bound to
32 bits are zero. low memory except that the user allocates the struc-
This sign-extending scheme allows the run-time tures and can allocate the structures in high memory.
library to have a single implementation that can be The C run-time library cannot support the allocation
used by both 32-bit and 64-bit calling programs. This of such structures in 64-bit virtual memory.

Digital Technical Journal Vol. 8 No. 2 1996 85


An example of a parameter being restricted to a the function has no way of determining how many
low memory address is the buffer being passed as the bytes to write. Writing 4 bytes may truncate a pointer;
parameter to the function setbuf. The parameter writing 8 bytes may overwrite 4 bytes of user data that
defines this buffer to be used for I/O operations. The follows the pointer. The strtod function, therefore, has
user expects to see this buffer change as I/O opera- two implementations. The first expects endptr to be
tions are performed on the file. If the run-time library the address of a 32-bit pointer, and the second expects
made a copy of this buffer, the changes would appear endptr to be the address of a 64-bit pointer.
in the copy and not in the original buffer that the user
supplied. When the C run-time library begins to use Pointer to Pointer-size-sensitive Structures Many func-
the 64-bit OpenVMS Record Management Services tions receive the address of a structure. If the analysis
(RMS) interface, this low-memory restriction will be reveals that the layout of this structure is dependent
removed. upon pointer size, the functions that receive or return
In most cases, the run-time library is able to hide this structure must have pointer-size-specific entry points.
the fact that the 32-bit RMS interface is not able to Note that the layout of the structure is separate
interpret a 64-bit virtual memory address. Consider from whether the structure is allocated in low memory
the filename parameter to the fopen function. If the or in high memory. The 32-bit-specific entry point is
parameter is a 64-bit virtual memory address, the run- needed to understand the layout of the structure, but
time library copies this parameter to 32-bit virtual the parameter should allow this structure to be allo-
memory and passes the address of the copy to RMS. cated in high memory.
Neither the user nor RMS is aware that this copy has Functions that receive the address of an array of
been made. The library may copy the data if and only if addresses are treated in the same way, assuming that
such a copy operation does not change functionality or the addresses in the array are neither bound nor
significantly degrade performance. restricted to low memory. The function being called
needs to know if the array contains 32-bit addresses or
Size-independent Structure Pointers Many functions 64-bit addresses. Unlike the address of the array, the
receive the address of a structure whose layout is not individual members of the array are not sign extended
affected by pointer size. The simplest address in this to 64-bit values.
category is that of an array of integers. This array may Separate implementations are necessary only to
be in either 32-bit or 64-bit virtual memory, but only determine the layout of what is being pointed to. The
one interface is needed to determine the layout of the 32-bit interface handles pointers to structures contain-
structure. If the structure layout is independent of ing 32-bit addresses, and the 64-bit interface handles
pointer size, then pointer-size-specific entry points are pointers to structures containing 64-bit addresses.
not required for this parameter. The developer would
still make the parameter 64-bit friendly so that the user Functions That Return Pointers Many functions return
would have the freedom to make the allocation that is pointers as the value of the function. These pointers are
best for the application. either pointer-size specific or they are not affected by
the pointer size. Similar to its specifications for 64-bit-
Pointer to Pointer Parameters It is common practice friendly parameters, the OpenVMS Calling Standard
for a function to be passed a pointer to a pointer. If the indicates that return values on the OpenVMS Alpha
pointer being pointed to is not bound or restricted to operating system are always sign extended to 64-bit
a 32-bit address, then two implementations of the values and returned in register zero (R0).
function are necessary. To make an address parameter 64-bit friendly, a
To understand why some functions require two function allows a 64-bit address to be passed, thus
implementations, consider the following strtod enabling both 32-bit and 64-bit calling programs to
function: use a single interface. Conversely, if a function returns
a 64-bit address to a 32-bit calling program, the
double strtod(const char *string,
char **endptr); address is safely returned in R0 but is truncated when
moved from R0 into the user’s data area. A 64-bit-
The strtod function converts a string to a floating- friendly address return value is always 32 bits. When
point double-precision number. The second parame- moved from R0 into the calling program’s variable,
ter to this function, endptr, is a pointer to a memory it is sign extended when the calling program is using
location into which the address of the first unrecog- 64-bit addresses.
nized character is to be placed. The caller of this func- If the return value of a function can be a 64-bit
tion has allocated either 4 or 8 bytes to store this address, this function must have pointer-size-specific
address. Without pointer-size-specific entry points, entry points. If the function returns the address of a

86 Digital Technical Journal Vol. 8 No. 2 1996


structure that is bound to low memory, such as a FILE The C run-time library header files conditionally
or WINDOW pointer, the return value does not force compile based on the value of this predefined macro.
separate entry points. A zero value indicates to the header files that the com-
Certain functions, such as malloc, allocate memory puting environment is purely 32-bit. The pointer-size-
on behalf of the calling program and return the address specific function prototypes are not defined. The user
of that memory as the value of the function. These must use the /pointer_size qualifier to access 64-bit
functions have two implementations: the 32-bit inter- functionality. The choice of 32 or 64 determines the
face always allocates 32-bit virtual memory, and the default pointer size.
64-bit interface always allocates 64-bit virtual memory. The header files define two distinct types of declara-
Many string and memory functions have return val- tions: those that have a single implementation and
ues that are relative to a parameter passed to the same those that have pointer-size-specific implementations.
routine. These addresses may be returned as high The addresses passed or returned from functions that
memory addresses if and only if the parameter is a have a single implementation are either bound to low
high memory address. memory, restricted to low memory, or widened to
The following is the function prototype for strcat, accept a 64-bit pointer.
which is found in the header file <string.h>: Those functions that have pointer-size-specific
entry points have three function prototypes defined.
char *strcat(char *s1, const char *s2);
Using malloc as an example, prototypes are created for
The strcat function appends the string pointed to by the functions malloc, _malloc32, and _malloc64. The
s2 to the string pointed to by s1. The return value is latter two prototypes are the pointer-size-specific pro-
the address of the latest string s1. totypes and are defined only when the /pointer_size
In this case, the size of the pointer in the return qualifier is used. The malloc prototype defaults to call-
value is always the same as the size of the pointer ing _malloc32 when the default pointer size is 32 bits.
passed as the first parameter. The C programming lan- The malloc prototype defaults to calling _malloc64
guage has no way to reflect this. Since the function when the default pointer size is 64 bits. Applica-
may return a 64-bit pointer, the strcat function must tion programmers who mix pointer types use the
have two entry points. /pointer_size qualifier to establish the default pointer
As discussed earlier, the pointer size used for para- size but can then use the _malloc32 and _malloc64
meter s2 is not related to the returned pointer size. explicitly to achieve nondefault behavior.
The C run-time library made this s2 argument 64-bit In addition to being enhanced to support 64-bit
friendly by declaring it a 64-bit pointer. This declara- pointers, the C compiler has the added capability of
tion allows the application programmer to concate- detecting incorrect mixed-pointer usage. It is the
nate a string in high memory to one in low memory function prototype found in the header files that tells
without altering the source code. The following strcat the compiler exactly what pointer size is permitted or
function statement shows this declaration: expected in a call. Proper use of the header files helps
prevent pointer truncation.
char *strcat(char *s1, __char_ptr64 s2); The actual functions called in the C run-time library
are either decc$malloc or decc$_malloc64, depending
The data type __char_ ptr64 is a 64-bit character
on the pointer size. The C run-time library does not
pointer whose definition and use will be explained
contain an entry point called decc$_malloc32. This
later in this paper.
naming scheme was selected so that applications that
link on older systems always get the 32-bit interface.
High-level Design
The C compiler has always looked at a table within
the C run-time library shareable image for assistance in
The /pointer_size qualifier is available in those
name prefixing. Using this table, the compiler knows
versions of the C compiler that support 64-bit point-
to change calls to the malloc function into calls to the
ers. The compiler has a predefined macro named
decc$malloc function and not to change calls to xyz,
__INITIAL_POINTER_SIZE whose value is based on
which is not a C run-time library function, into calls to
the use of the /pointer_size qualifier. The macro
decc$xyz.
accepts the following values:
The C run-time library and the C compiler have
■ 0, which indicates that the /pointer_size qualifier is added new information to the table that tells the com-
not used or is not available piler which functions have pointer-size-specific entry
■ 32, which indicates that the /pointer_size qualifier points. When the compiler sees a call to the function
is used and has a value of 32 _xyz32, it looks it up in the name table. If the name of
the function is found, the compiler then looks at
■ 64, which indicates that the /pointer_size qualifier
is used and has a value of 64

Digital Technical Journal Vol. 8 No. 2 1996 87


whether the function is the 32-bit-specific entry point. prototype and was immediately moved to the section
If it is, the compiler forms the prefixed name by “Functions that support 64-bit pointers.”
adding “decc$” to the beginning of the name but Organizing <header.h> in this way gave us an accu-
also removes the “_” and the “32.” Consequently, the rate reading of how many more functions needed
function name _malloc32 becomes decc$malloc, but 64-bit support. If any of the sections became empty,
the function name _xyz32 does not change. we did not remove the section. This approach worked
well because while some engineers were doing 64-bit
Implementation work, others were adding new functions. Any new
functions added to a header file after the 64-bit work
To illustrate changes that needed to be made to the was done would be placed in the section “Functions
header files, we invented a single header file called that need 64-bit support.” Prior to shipping the
<header.h>. This file, which is shown in Figure 1, illus- header files, we removed the empty sections.
trates the classes of problems faced by a developer who
is adding support for 64-bit pointers. The functions Preparing the Source Code
defined in this header file are actual C run-time library After several false starts, we settled on a design for
functions. modifying the source code for 64-bit support. The
expected starting design was to modify the source
Preparing the Header File code by adding pointer_size pragmas and compile the
The first pass through <header.h> resulted in a num- source modules using the /pointer_size qualifier.
ber of changes in terms of formatting, commenting, Some modules would use /pointer_size=32; others
and 64-bit support. Realizing that many modifications would use /pointer_size=64. The major drawback to
would be made to the header files, we considered this approach was that looking at a variable declared as
readability a major goal for this release of these files. a pointer requires an understanding of the context in
The initial header files assumed a uniform pointer which that variable appears. No longer would “char *”
size of 32 bits for the OpenVMS operating system. be simply a character pointer. It could be a 32-bit or a
During the first pass through <header.h>, we added 64-bit character pointer, and the implementer needed
pointer-size pragmas to ensure that the file saved the to know which one.
user’s pointer size, set the pointer size to 32 bits, and The design on which we decided overcomes the
then restored the user’s pointer size at the end of the readability problem. By default, source files are not
header. compiled with the /pointer_size qualifier. This means
Next we formatted <header.h> to show the various that no pointer-size manipulation occurs when includ-
categories that the structures and functions fall into. ing the header files. The readability of the source code
The categories and the result of the first pass through is improved in that the implementers can see which
<header.h> can be seen in Figure 2. For example, pointers are 32-bit pointers and which are 64-bit
the function rand had no pointers in the function pointers.

#ifndef __HEADER_LOADED
#define __HEADER_LOADED 1

#ifndef __SIZE_T
# define __SIZE_T 1
typedef unsigned int size_t;
#endif

int execv(const char *, char *[]);


void free(void *);
void *malloc(size_t);
int rand(void);
char *strcat(char *, const char *);
char *strerror(int);
size_t strlen(const char *);

#endif /* __HEADER_LOADED */

Figure 1
Original Header File <header.h>

88 Digital Technical Journal Vol. 8 No. 2 1996


#ifndef __HEADER_LOADED
#define __HEADER_LOADED 1

/*
** Ensure that we begin with 32-bit pointers.
*/
#if __INITIAL_POINTER_SIZE
# if (__VMS_VER < 70000000)
# error "Pointer size added in OpenVMS V7.0 for Alpha“
# endif
# pragma __pointer_size __save
# pragma __pointer_size 32
#endif

/*
** STRUCTURES NOT AFFECTED BY POINTERS
*/
#ifndef __SIZE_T
# define __SIZE_T 1
typedef unsigned int size_t;
#endif

/*
** FUNCTIONS THAT NEED 64-BIT SUPPORT
*/
int execv(const char *, char *[]);
void free(void *);
void *malloc(size_t);
char *strcat(char *, const char *);
char *strerror(int);
size_t strlen(const char *);

/*
** Create 32-bit header file typedefs.
*/

/*
** Create 64-bit header file typedefs.
*/

/*
** FUNCTIONS RESTRICTED FROM 64 BITS
*/

/*
** Change default to 64-bit pointers.
*/
#if __INITIAL_POINTER_SIZE
# pragma __pointer_size 64
#endif

/*
** FUNCTIONS THAT SUPPORT 64-BIT POINTERS
*/
int rand(void);

/*
** Restore the user’s pointer context.
*/
#if __INITIAL_POINTER_SIZE
# pragma __pointer_size __restore
#endif

#endif /* __HEADER_LOADED */

Figure 2
First Pass through <header.h>

Digital Technical Journal Vol. 8 No. 2 1996 89


We created a C run-time library private header 32-bit character pointer, whereas 64-bit pointers use
file called <wide_types.src>. This header file has the typedefs whose names begin with “__wide.” The
appropriate pragmas to define 64-bit pointer types used name of the new typedef is __wide_char_ptr, which is
within the C run-time library, as shown in Figure 3. read as a 64-bit character pointer.
This header file also contains the definitions of macros The C run-time library design also requires that the
used in the implementations of the functions. Figure 4 implementation of a function include all header files
shows the macros declared in <wide_types.src>. that define the function. This ensures that the imple-
Once a module includes the file <wide_types.src>, mentation matches the header files as they are modi-
the compilation of that module changes to add the fied to support 64-bit pointers. For functions defined
qualifier /pointer_size=32. This change improves the in multiple header files, this ensures that header files
readability of the code because “char *” is read as a do not contradict each other.

/*
** This include file defines all 32-bit and 64-bit data types used in
** the implementation of 64-bit addresses in the C run-time library.
**
** Those modules that are compiled with a 64-bit-capable compiler
** are required to enable pointer size with /POINTER_SIZE=32.
*/
#ifdef __INITIAL_POINTER_SIZE
# if (__INITIAL_POINTER_SIZE != 32)
# error “This module must be compiled /pointer_size=32”
# endif
#endif

/*
** All interfaces that require 64-bit pointers must use one of
** the following definitions. When this header file is used on
** platforms not supporting 64-bit pointers, these definitions
** will define 32-bit pointers.
*/
#ifdef __INITIAL_POINTER_SIZE
# pragma __pointer_size __save
# pragma __pointer_size 64
#endif

typedef char *__wide_char_ptr;


typedef const char *__wide_const_char_ptr;

typedef int *__wide_int_ptr;


typedef const int *__wide_const_int_ptr;

typedef char **__wide_char_ptr_ptr;


typedef const char **__wide_const_char_ptr_ptr;

typedef void *__wide_void_ptr;


typedef const void *__wide_const_void_ptr;

#include <curses.h>
typedef WINDOW *__wide_WINDOW_ptr;

#include <string.h>
typedef size_t *__wide_size_t_ptr;

/*
** Restore pointer size.
*/
#ifdef __INITIAL_POINTER_SIZE
# pragma __pointer_size __restore
#endif

Figure 3
Typedefs from <wide_types.src>

90 Digital Technical Journal Vol. 8 No. 2 1996


/*
** Define macros that are used to determine pointer size and
** macros that will copy from high memory onto the stack.
*/
#ifdef __INITIAL_POINTER_SIZE

# include <builtins.h>

# define C$$IS_SHORT_ADDR(addr) \
((((__int64)(addr)<<32)>>32) == (unsigned __int64)addr)

# define C$$SHORT_ADDR_OF_STRING(addr) \
(C$$IS_SHORT_ADDR(addr) ? (char *) (addr) \
:(char *) strcpy(__ALLOCA(strlen(addr) + 1), (addr)))

# define C$$SHORT_ADDR_OF_STRUCT(addr) \
(C$$IS_SHORT_ADDR(addr) ? (void *) (addr) \
:(void *) memcpy(__ALLOCA(sizeof(* addr)), (addr), sizeof(*addr)))

# define C$$SHORT_ADDR_OF_MEMORY(addr, len) \


(C$$IS_SHORT_ADDR(addr) ? (void *) (addr) \
:(void *) memcpy(__ALLOCA(len), (addr), len))

#else

# define C$$IS_SHORT_ADDR(addr) (1)


# define C$$SHORT_ADDR_OF_STRING(addr) (addr)
# define C$$SHORT_ADDR_OF_STRUCT(addr) (addr)
# define C$$SHORT_ADDR_OF_MEMORY(addr, len) (addr)

#endif

Figure 4
Macros from <wide_types.src>

Implementing the strerror Return Pointer The private header file typedefs are always declared
The function strerror always returns a 32-bit pointer. starting with two underscores and ending in either
The memory is allocated by the C run-time library for “_ptr32” or “_ptr64.” These typedefs are created only
both 32-bit and 64-bit calling programs. As shown when the header file needs to be in a particular
in Figure 5, we moved the function strerror into the pointer-size mode while referring to a pointer of the
section “Functions that support 64-bit pointers” of other size. The return value of strerror is modified to
<header.h> to show that there are no restrictions on use the typedef __char_ptr32.
the use of this function. Including the header file, which declares strerror,
The “Create 32-bit header file typedefs” section of allows the compiler to verify that the arguments,
<header.h> is in the 32-bit pointer section, where the return values, and pointer sizes are correct.
bound-to-low-memory data structures are declared.
The function returns a pointer to a character string. Widening the strlen Argument
We, therefore, added typedefs for __char_ptr32 and The function strlen accepts a constant character
__const_char_ptr32 while in a 32-bit pointer context. pointer and returns an unsigned integer (size_t).
These declarations are protected with the definition of Implementing full 64-bit support in strlen means
__CHAR _ PTR32 to allow multiple header files to use changing the parameter to a 64-bit constant character
the same naming convention. Declarations of the pointer. If an application passes a 32-bit pointer to
const form of the typedef are always made in the same the strlen function, the compiler-generated code sign
conditional code since they usually are needed and extends the pointer. The required header file mod-
using the same condition removes the need for a dif- ification is to simply move strlen from the sec-
ferent protecting name. tion “Functions that need 64-bit support” to the
The strerror function could have been implemented section “Functions that support 64-bit pointers.”
in <header.h> by placing the function in the 32-bit sec- The steps necessary for the source code to support
tion, but that would have implied that the 32-bit 64-bit addressing are as follows:
pointer was a restriction that could be removed later.
1. Ensure that the module includes header files that
The pointer is not a restriction, and the strerror func-
declare strlen.
tion fully supports 64-bit pointers.

Digital Technical Journal Vol. 8 No. 2 1996 91


#ifndef __HEADER_LOADED
#define __HEADER_LOADED 1

/*
** Ensure that we begin with 32-bit pointers.
*/
#if __INITIAL_POINTER_SIZE
# if (__VMS_VER < 70000000)
# error ”Pointer size added in OpenVMS V7.0 for Alpha“
# endif
# pragma __pointer_size __save
# pragma __pointer_size 32
#endif

/*
** STRUCTURES NOT AFFECTED BY POINTERS
*/
#ifndef __SIZE_T
# define __SIZE_T 1
typedef unsigned int size_t;
#endif

/*
** FUNCTIONS THAT NEED 64-BIT SUPPORT
*/

/*
** Create 32-bit header file typedefs.
*/
#ifndef __CHAR_PTR32
# define __CHAR_PTR32 1
typedef char *__char_ptr32;
typedef const char *__const_char_ptr32;
#endif

/*
** Create 64-bit header file typedefs.
*/
#ifndef __CHAR_PTR64
# define __CHAR_PTR64 1
# pragma __pointer_size 64
typedef char *__char_ptr64;
typedef const char *__const_char_ptr64;
# pragma __pointer_size 32
#endif

/*
** FUNCTIONS RESTRICTED FROM 64 BITS
*/
int execv(__const_char_ptr64, char *[]);

/*
** Change default to 64-bit pointers.
*/
#if __INITIAL_POINTER_SIZE
# pragma __pointer_size 64
#endif

/*
** The following functions have interfaces of XXX, _XXX32,
** and _XXX64.
**
** The function strcat has two interfaces because the return
** argument is a pointer that is relative to the first arguments.
**
** The malloc function returns either a 32-bit or a 64-bit
** memory address.
*/
#if __INITIAL_POINTER_SIZE == 32
# pragma __pointer_size 32
#endif

Figure 5
Final Form of <header.h>

92 Digital Technical Journal Vol. 8 No. 2 1996


void *malloc(size_t __size);
char *strcat(char *__s1, __const_char_ptr64 __s2);

#if __INITIAL_POINTER_SIZE == 32
# pragma __pointer_size 64
#endif

#if __INITIAL_POINTER_SIZE && __VMS_VER >= 70000000


# pragma __pointer_size 32
void *_malloc32(size_t);
char *_strcat32(char *__s1, __const_char_ptr64 __s2);
# pragma __pointer_size 64
void *_malloc64(size_t);
char *_strcat64(char *__s1, const char *__s2);

#endif

/*
** FUNCTIONS THAT SUPPORT 64-BIT POINTERS
*/
void free(void *__ptr);
int rand(void);
size_t strlen(const char *__s);

__char_ptr32 strerror(int __errnum);

/*
** Restore the user’s pointer context.
*/
#if __INITIAL_POINTER_SIZE
# pragma __pointer_size __restore

#endif

#endif /* __HEADER_LOADED */

Figure 5
Continued

2. Add the following line of code to the top of the Initially, the execv function was to have had two
module: #include <wide_types.src>. implementations. The parameters passed to the execv
3. Change the declaration of the function to accept function are used as the parameters to the main func-
a __wide_const_char_ptr parameter instead of the tion of the child process being started. Because no
previous const char * parameter. assumptions could be made about that child process
(in terms of support for 64-bit pointers), these para-
4. Visually follow this argument through the code,
meters are restricted to low memory addresses.
looking for assignment statements. This particular
To illustrate that the argv passing was a restriction,
function would be a simple loop. If local variables
we place that prototype into the section “Functions
store this pointer, they must also be declared as
restricted from 64 bits” of <header.h>. The first argu-
__wide_const_char_ptr.
ment, the name of the file, did not need to have this
5. Compile the source code using the directive restriction. The section “Create 64-bit header file
/warn=enable=maylosedata to have the compiler typedefs” was enhanced to add the definition of
help detect pointer truncation. __const_char_ptr64, which allows the prototypes to
6. Add a new test to the test system to exercise 64-bit define a 64-bit pointer to constant characters while in
pointers. either 32-bit or 64-bit context.

Restricting execv from High Memory Returning a Relative Pointer in strcat


Examination of the execv function prototype showed The strcat function returns a pointer relative to its first
that this function receives two arguments. The first argument. We looked at this function and determined
argument is a pointer to the name of the file to start. that it required two entry points. In addition, we
The second argument represents the argv array that is widened the second parameter, which is the address of
to be passed to the child process. This array of pointers the string to concatenate to the second, to allow the
to null terminated strings ends with a NULL pointer. application to concatenate a 64-bit string to a 32-bit
string without source code changes.

Digital Technical Journal Vol. 8 No. 2 1996 93


Figure 5 shows the changes made to support func- int free(__wide_void_ptr ptr) {
if (!(C$$IS_SHORT_ADDR(ptr)))
tions that have pointer-size-specific entry points. The return(c$$_free64(ptr));
prototypes of functions XXX, _XXX32, and _XXX64 else return(c$$_free32((void *) ptr);
begin in 64-bit pointer-size mode. Since the unmodi- }
fied function name (strcat, XXX) is to be in the pointer
size specified by the /pointer_size qualifier, the Concluding Remarks
pointer size is changed from 64 bits to 32 bits if and
only if the user has specified /pointer_size=32. At this The project took approximately seven person-months
point, we are not certain of the pointer size in effect. to complete. The work involved two months to deter-
We know only that the size is the same as the size of mine what we wanted to do, one month to figure out
the qualifier. The second argument to strcat uses the how we were going to do it, and four person-months
__const_char_ptr64 typedef in case we are in 32-bit to modify, document, and test the software.
pointer mode. Notice the declaration of _strcat64 During the initial two months, the technical leaders
does not use this typedef because we are guaranteed met on a weekly basis and discussed the overall
to be in 64-bit pointer context. Figure 6 shows the approach to adding 64-bit pointers to the OpenVMS
implementation of both the 32-bit and the 64-bit environment. Since I was the technical lead for the C
strcat functions. run-time library project, this initial phase occupied
between 25 and 50 percent of my time.
The 64-bit malloc Function The one month of detailed analysis and design con-
The implementation of multiple entry points was dis- sumed more than 90 percent of my time and resulted
cussed and demonstrated in the strcat implementation. in a detailed document of approximately 100 pages.
Although multiple entry points are typically added to The document covered each of the 50 header files and
avoid truncating pointers, functions such as memory 500 function interfaces. The functions were grouped
allocation routines have newly defined behavior. by type, based on the amount of work required to
The functions decc$malloc and decc$_malloc64 support 64-bit pointers.
use new support provided by the OpenVMS Alpha The first month of implementation occupied nearly
operating system for allocating, extending, and freeing all of my time, as I made several false starts. Once I
64-bit virtual memory. The C run-time library utilizes worked out the final implementation technique, I
this new functionality through the LIBRTL entry completed at least two of each type of work. As coding
points. The LIBRTL group added new entry points for deadlines approached, I taught two other engineers on
each of the existing memory management functions. my team how to add 64-bit pointer support, pointing
The LIBRTL includes an additional second entry out those functions already completed for reference.
point for the free function. Since our implementation They came up to speed within one week. Together, we
of the free function simply widens the pointer, we end completed the work during the final month of the
up with a single, C run-time library function that must project.
choose which LIBRTL function to call.

#include <string.h>
#include <wide_types.src>

/*
** STRCAT/_STRCAT64
**
** The ‘strcat’ function concatenates ‘s2’, including the
** terminating null character, to the end of ‘s1’.
*/

__wide_char_ptr _strcat64(__wide_char_ptr s1, __wide_const_char_ptr s2)


{
(void) _memcpy64((s1 + strlen(s1)), s2, (strlen(s2) + 1));
return(s1);
}

char *_strcat32(char *s1, __wide_const_char_ptr s2) {


(void) memcpy((s1 + strlen(s1)), s2, (strlen(s2) + 1));
return(s1);

Figure 6
Implementation of 32-bit and 64-bit strcat Functions

94 Digital Technical Journal Vol. 8 No. 2 1996


Acknowledgments

The author would like to acknowledge the others who


contributed to the success of the C run-time library
project. The engineers who helped with various
aspects of the analysis, design, and implementation
were Sandra Whitman, Brian McCarthy, Greg Tarsa,
Marc Noel, Boris Gubenko, and Ken Cowan. Our
writer, John Paolillo, worked countless hours docu-
menting the changes we made to the library.

References

1. M. Harvey and L. Szubowicz, “Extending OpenVMS


for 64-bit Addressable Virtual Memory,” Digital
Technical Journal, vol. 8, no. 2 (1996, this issue):
57–71.
2. T. Benson, K. Noel, and R. Peterson, “The OpenVMS
Mixed Pointer Size Environment,” Digital Technical
Journal, vol. 8, no. 2 (1996, this issue): 72–82.
3. DEC C User’s Guide for OpenVMS Systems (Maynard,
Mass.: Digital Equipment Corporation, Order No.
AA-PUNZE-TK, 1995).

4. DEC C Runtime Library Reference Manual for


OpenVMS Systems (Maynard, Mass.: Digital Equipment
Corporation, Order No. AA-PUNEE-TK, 1995).
5. OpenVMS Calling Standard (Maynard, Mass.: Digital
Equipment Corporation, Order No. AA-QSBBA-TE,
1995).

Biography

Duane A. Smith
As a consulting software engineer, Duane Smith is currently
architect and project leader of the C run-time library for
the OpenVMS VAX and Alpha platforms. He joined Digital
in 1981 and has worked on a variety of projects, including
the A-to-Z Database Manager and the Language-Sensitive
Editor. Duane received his B.S. in engineering from the
University of Connecticut in 1981 and his M.S. in soft-
ware engineering from Wang Institute of Graduate Studies
in 1987. He pursued his master’s degree through Digital’s
Graduate Engineering Education Program (GEEP). Duane
holds one U.S. patent issued for the DECwindows Structured
Visual Navigation (SVN) widget.

Digital Technical Journal Vol. 8 No. 2 1996 95

You might also like