Method and apparatus for providing runtime checking features in a compiled programming development environment

A method and apparatus for performing runtime checking during program execution in a compiled environment using the full ANSI-C programming language. The present invention detects a number of errors during runtime that cannot be found by a compiler at the precise moment that a respective C language restriction is violated. The present invention also provides the user with a direct indication of the problem, thus saving debugging time. The runtime checking features of the present invention further detects when a user is using library functions improperly. When C source code is compiled, the present invention allocates special data structures for every pointer, array and structure object in the program. An association is made between each of these objects, and its special data structure in the compiler symbol table. At runtime, these data structures contain status information about their associated objects. The present invention also inserts special machine language instructions for C expressions during compilation that either modify values in the special data structures or call internal runtime checking functions according to the present invention that use the information in the respective data structures to determine whether an expression is illegal and report errors if necessary. The runtime checking features of the present invention include a method for specifying precise restrictions on the arguments that may be passed to library functions. These restrictions are used to determine whether arguments to library functions conform to their respective restrictions and reports any violations to the user at runtime, indicating which argument caused the error and which restriction was violated.

RESERVATION OF COPYRIGHT 
A portion of the disclosure of this patent document contains material to 
which a claim of copyright protection is made. The copyright owner has no 
objection to the facsimile reproduction by anyone of the patent document 
or the patent disclosure as it appears in the Patent and Trademark Office 
patent file or records, but reserves all other rights whatsoever. 
MICROFICHE APPENDIX 
The present disclosure includes a microfiche appendix comprising 1 
microfiche and 103 frames. The microfiche appendix comprises appendices 
A-F, which are source code listings of one embodiment of the present 
invention. Appendix A is a source code listing of compiler code which 
creates data structures and inserts calls to runtime checking functions. 
Appendix B is a source code listing of various runtime checking functions. 
Appendix C is a source code listing of various dynamic memory runtime 
checking functions. Appendix D is a source code listing of various 
external library runtime checking functions. Appendices E and F are header 
files for appendices A-D. 
FIELD OF THE INVENTION 
The present invention relates to programing development and debugging 
tools, and more particularly to a method and apparatus for performing 
runtime checking of a compiled program to detect errors during program 
run-time that cannot otherwise be found by a compiler. 
DESCRIPTION OF THE RELATED ART 
Computer programmers are being called upon to develop increasingly larger 
and more complex software applications. In order to aid programmers in 
developing these applications, various software development environments 
have been created which include a number of useful tools to aid 
programmers. Programming development environments are available for many 
different programming languages and various examples include "Visual 
Basic", "QuickBASIC", "ThinkC", "Visual C++", "Borland C++", "Symantec 
C++", etc. The above trademarks, as well as other marks used in the 
present application, are trademarks of their respective companies. 
A typical C programming development environment includes a source code 
editor, a 32 bit ANSI-C compatible compiler, a linker, a debugger, and 
standard ANSI-C libraries. The source code editor acts much like a word 
processor in allowing the software developer to enter source code which 
appears as text on the screen. The compiler operates to compile a source 
code listing into an object file, which is a set of executable machine 
language instructions that can then be executed on a computer. The linker 
is provided to link multiple object files and external libraries into a 
program. The debugger is used to aid in debugging the source code listing 
during execution. 
In text-based programming environments such as C, Pascal, BASIC, etc. there 
are typically a number of restrictions on what constitutes a legal or 
valid program. For example, the C programming language includes a number 
of rules of "defined behavior" based on the ANSI-C standard. If a source 
code listing developed by a programmer does not conform to this "defined 
behavior," then either the program will not compile or the program will 
not execute properly when compiled. A certain number of programming 
language violations can be discovered by the compiler at compile time. 
However, many of the restrictions cannot be enforced by a compiler, and 
thus a compiled and executable program will typically include a number of 
errors or violations, referred to as bugs. In most programming 
environments, finding these bugs, referred to as debugging, is extremely 
difficult and time consuming, even when a debugging tool is available. 
Some bugs are subtle, i.e. , they do not cause immediate problems in the 
program but cause significant problems much later during execution when 
finding the problem is more difficult. As a result the vast majority of 
software products released today include numerous undocumented bugs. 
Undiscovered bugs in a computer program cost software developers much time 
and money in addition to the time and money spent debugging the program. 
For example, a software developer is typically required to maintain a team 
of software application engineers to field user complaints, issue software 
patches to fix particularly troublesome bugs, and devote much research and 
development time in searching for and fixing bugs in future product 
releases. For the individual programmer, debugging is a very time 
consuming process, and individual programmers will typically spend much of 
their time and effort in finding and fixing bugs in their programs. 
Therefore, a method and apparatus is greatly desired which provides a 
runtime checking capability wherein many types of errors in a program 
which cannot be found at compile time can be detected at the precise time 
during execution when a restriction is violated. It would also be greatly 
desirable for the runtime checker to provide the user with a direct 
indication of the problem and the line of code where it occurred, thus 
saving a tremendous amount of debugging time. 
Various program development environments have incorporated limited runtime 
checking features. For example, a version of runtime checking was 
implemented in "LabWindows" for DOS version 1.2, which was released by 
National Instruments in October of 1989. LabWindows for DOS was a 
programming environment which supported a subset of C and BASIC that did 
not include structures and also did not include pointer data types, except 
as a way of declaring scalars passed by reference. For example, an integer 
passed by reference could be referred to as .sup.* i, but pointer 
arithmetic was not allowed and thus operations such as i++ and i[5] were 
illegal. Also, since pointers were not supported, dynamic memory 
allocation was not supported. 
LabWindows for DOS also implemented language subsets through an 
interpreter. In other words, LabWindows for DOS would not compile a source 
code listing into executable machine language instructions, but rather 
interpreted source code instructions one at a time. The user source code 
was translated into a parse tree and at runtime the parse tree was 
traversed by the execution subsystem. In order to execute an operation 
node in the parse tree, a function specific to the operation type 
specified by the node was called. LabWindows for DOS further included 
built-in libraries which were statically linked into the environment. 
Object module libraries could be loaded either at runtime or during the 
execution of a menu command. These object modules or library files could 
be written or developed by the user, as long as they were compiled by 
certain defined compilers and obeyed a small set of restrictions. 
The runtime checking implemented in LabWindows for DOS version 1.2 was 
designed to accomplish the following objectives. The first objective was 
to prevent users from accessing beyond the end of strings or arrays. Since 
LabWindows for DOS only implemented a subset of C and BASIC that did not 
include pointer data types, the runtime checking was limited to checking 
strings and arrays previously declared by a user, or subarrays and 
substrings passed to a function. This is a far easier task than it would 
be to implement runtime checking in an environment that supports the full 
ANSI-C language, which includes pointers, structures, casting and dynamic 
memory allocation. Further since the LabWindows for DOS environment was 
interpreted rather than compiled, the compiler issues associated with 
implementing runtime checking in a compiled program were not addressed. 
Performing runtime checking in an interpreted environment is much simpler 
than in a compiled environment. For example, in the interpreter used in 
LabWindows for DOS, much of the information that was needed for the 
runtime checking was required to be maintained by the interpreter for 
other reasons. Also, the fact that the code necessary to perform runtime 
checking was required to be executed before certain operations were 
executed was not much of an inconvenience because these operations were 
executed via C code in any case. In contrast, in a typical compiled-code 
system, very little of the information available at compile time is 
retained at runtime. To perform runtime checking, large amounts of data 
must be maintained that would otherwise not be maintained. However, it is 
also important that the size of the data maintained be kept to an absolute 
minimum because users are typically expected to create very large 
applications containing many modules in compiled code systems. 
Although runtime checking is easier to implement in an interpreted 
environment than in a compiled environment, in general a programming 
development environment which includes a compiler is preferable to an 
interpreted environment because compiled code executes faster than 
interpreted code. Although a runtime checking capability will considerably 
slow down compiled code execution, the compiled code will still typically 
execute much faster than interpreted code that also includes runtime 
checking capabilities. In addition to greater speed of execution, 
compilers are considered to be more sophisticated than interpreters, and 
thus an environment which includes a compiler is more desirable than one 
which merely includes an interpreter. 
The runtime checking capability provided in LabWindows for DOS was also 
designed to prevent users from passing argument values to library 
functions that could cause the functions to access beyond the end of 
strings or arrays. This was principally for situations where the passing 
of such argument values would not be detectable by the library alone 
without aid from the interpreter and its symbol tables. Runtime checking 
was further designed to prevent users from passing argument values to 
library functions that were inconsistent with each other or inconsistent 
with argument values passed to the library in a previous function call. 
This was also designed to prevent situations where such inconsistencies 
would not be detectable by the library alone without aid from the 
interpreter and its symbol tables. 
The latter two runtime checking operations described above which checked 
arguments to library functions were required to be handcoded. In other 
words, for each of these functions for which a runtime check was 
necessary, a companion function was required to be written which received 
information from the interpreter. This companion function required 
knowledge of the interpreter's data structures, and consequently only 
developers of LabWindows for DOS were able to create these companion 
functions. In addition, the companion function was called only if it was 
installed in the symbol table for the library when its related library 
function was installed. There was no automated way of accomplishing this 
except by handcoding the function installation, which again could only be 
done by developers of LabWindows for DOS. Therefore, although LabWindows 
for DOS could load object module libraries written by anyone, there was no 
automated mechanism available to allow users to implement runtime checking 
for their library functions. Only LabWindows developers could implement 
runtime checking, and runtime checking was performed only for libraries 
developed by National Instruments. Further, although LabWindows for DOS 
provided some functions that could be called in the object module library 
to check array and string sizes, this was not a complete solution. If the 
object module library function was called internally as well as by the end 
user program, these special runtime checking functions could not be used. 
Another product that includes limited runtime checking capabilities is 
QuickBASIC from Microsoft Corporation. QuickBASIC contains an editor, 
compiler, and debugger in one program and includes a runtime checking 
feature that ensures that the user does not access beyond the end of an 
array. The QuickBASIC language stores each array with size information, 
and thus programs compiled with the stand alone QuickBASIC compiler 
automatically perform these runtime checks. However, QuickBASIC does not 
include a pointer data type and thus does not require runtime checking for 
pointer data types. Further, QuickBASIC does not include embedded 
libraries and thus does not include runtime checking for embedded 
libraries. QuickBASIC also does not have any mechanism for specifying 
runtime checks for compiled code, except that code compiled with the 
QuickBASIC compiler automatically performs array access checks as 
described above. Furthermore, if compiled C modules are used, a language 
interface routine must be written that can use the (undocumented) size 
information in the array to perform its own checks. Therefore, although 
QuickBASIC provides some limited runtime checking capabilities, i.e., 
automatic runtime checking for array accesses, a runtime checking 
capability is greatly desired which contends with all aspects of runtime 
checking. A runtime checking capability is further desired which has a 
general mechanism for supporting runtime checks at the point where 
externally compiled code is called into a program. 
A UNIX product called CodeCenter from Centerline Corporation includes a C 
interpreter that performs limited runtime checks. However, CodeCenter only 
performs runtime checking in an interpreted environment and does not 
include a compiler. As noted above, it is far easier to perform runtime 
checking in an interpreted environment than it is in a compiled 
environment. CodeCenter also does not include a general mechanism for 
supporting runtime checks at the point where compiled code is called into 
a program. Code Center may also include an embedded ANSI-C library that 
performs runtime checking, but there is no general mechanism provided for 
other library developers to use. 
A UNIX product called Purify developed by Pure Software, Inc. takes a very 
different approach to runtime checking. Purify is a "back-end" add on 
product that is not a compiler or a programming environment like the 
products described above. Rather, Purify modifies a user's object files to 
record information after every memory access and makes checks before every 
memory access. This product makes only limited checks at the language 
level. For instance, Purify imposes "invalid zones" around array 
boundaries which allow it to catch the overriding of arrays by a few 
bytes, but not by a large number of bytes. Thus, Purify does not perform 
runtime checks that are supported at the compiler lever and thus does not 
make complete checks at the language level. For more information on the 
operation of Purify, please see U.S. Pat. No. 5,193,180 titled "System for 
Modifying Relocatable Object Code Files to Monitor Accesses to Dynamically 
Allocated Memory" and assigned to Pure Sonware. 
One drawback to Purify is that information provided to the user when an 
error occurs is limited since Purify has little source code information. 
It would be highly desirable for a runtime checking program to provide a 
user with the precise location (including the exact C expression) that 
caused an error. 
Also, Purify catches only fatal errors and does not attempt to catch the 
following non-fatal errors: a) Illegal pointer arithmetic; b) Illegal 
pointer comparison; c) Illegal pointer subtraction; d) Casting an object 
into a larger type; and e) Assignment of invalid pointer expressions. It 
would be highly desirable for a runtime checking program to detect the 
above non-fatal errors. 
Further, Purify does not strictly enforce certain ANSI-C restrictions 
because it does not have language information. In particular, Purify may 
not catch an illegal reference of a pointer to freed memory. If the block 
of memory to which a freed pointer refers is reallocated by another call 
to malloc .oval-hollow., references to the freed pointer may not flag an 
error since it now points to an allocated block of memory. Purify may also 
not catch references beyond array boundaries of locals since it does not 
check for boundaries of local arrays. Also, Purify may not catch 
references of more than 8 bytes beyond array boundaries. Purify cannot 
catch accesses that extend more than the number of bytes in the "buffer 
zone" at array boundaries. 
In addition, Purify requires modification of externally compiled object and 
libraries. Finally, Purify is closely tied to the hardware and system for 
which it was designed. Complete knowledge of the system functions, object 
code format, and the type of CPU is required. It would be highly desirable 
for a runtime checking program to be hardware/system independent. 
Therefore, an improved method and apparatus for performing runtime checking 
in a programming development environment is desired. An improved runtime 
checking capability is desired which can perform runtime checking on 
compiled programs which support full ANSI-C, including pointers, 
structures, casting and dynamic memory allocation. Performing runtime 
checking in this context is considerably more difficult than in an 
environment that supports only a subset of a text-based language that does 
not contain pointer data types, etc. Runtime checking in a full ANSI-C 
environment must contend with pointers that can be assigned arbitrary 
values, be incremented or decremented, and point to structures that 
contain pointers to other structures. A runtime checking capability is 
also desired which can be incorporated into a programming development 
environment including a compiler, as opposed to merely an interpreter. As 
discussed above, performing runtime checking in a compiled environment is 
far more difficult than in an environment which merely includes an 
interpreter. A runtime checking capability is further desired which can 
provide an automated mechanism for users to create and install runtime 
checking functions for their object module library functions. A capability 
is also desired which performs runtime checks on the arguments to library 
functions, their consistency with other parameters, and their consistency 
with previous information passed to the library. 
SUMMARY OF THE INVENTION 
The present invention comprises a method and apparatus for performing 
runtime checking operations during execution of a compiled program in an 
environment which supports the full ANSI-C programming language. The 
present invention detects a number of errors during runtime that cannot be 
found by a compiler, including errors in various types of pointer 
manipulation and dynamic memory allocation. The runtime checking features 
of the present invention further detect when a user is using library 
functions improperly. The above errors are detected at the precise moment 
that a respective C language restriction is violated, and the user is 
provided with a direct indication of the problem and the location in the 
source code file where the violation occurred, thus saving debugging time. 
The runtime checking features of the present invention are implemented in 
an integrated compiler/execution environment which allows direct access to 
compiler data structures at runtime. The present invention is also 
preferably implemented in a single process system, and thus there is no 
overhead involved with context switches and interprocess communication. 
When a respective source code file is compiled, the present invention 
allocates special data structures for every pointer and aggregate data 
item, i.e., arrays and structures, in the program. At runtime, these data 
structures contain status information about their associated objects. 
An association is made between each of these objects and its special data 
structure in the compiler symbol table. For an executable file not 
compiled by the compiler of the present invention that includes 
definitions of pointers or aggregate data items used in other executable 
files compiled by the compiler of the present invention, a linker operates 
to assign an ignore value to those expressions where the pointers and 
aggregate data items are used to disable runtime checking for these 
objects. This is because these objects will not include the associated 
data structures necessary for runtime checking. 
Also during compilation, the present invention inserts special machine 
language instructions and function calls into the executable code for C 
expressions that manipulate pointers, arrays, or structures. The 
instructions are used to modify values in the special data structures at 
runtime. The function calls call internal runtime checking functions 
according to the present invention that use the information in the 
respective data structures to determine whether an expression is illegal 
or invalid and report errors if necessary. These runtime checking 
functions utilize coordinates where the respective C expression being 
checked is located so that the user can be informed of the precise 
location in the source code file where the bug is located. 
The special data structures that are created by the present invention 
collectively contain information about every pointer, array and structure 
in the user's program. These data structures are as follows. A data 
structure referred to as Block Info is associated with every aggregate 
data item, including arrays and structures, but not unions, and contains 
information about the beginning and size of the object and whether the 
object is a structure or an array. A data structure referred as Pointer 
Info is associated with every pointer object, including pointers in 
structures and arrays, and includes information about the object to which 
the pointer refers. A data structure referred to as Dynamic Block Info is 
associated with every block of memory allocated by the malloc function and 
is created at runtime. This data structure includes information regarding 
the beginning of the memory block, the size of the block, whether the 
object contained in the memory block is a structure or an array and the 
type that was first used to reference the dynamic memory. A fourth data 
structure is used to store current runtime information, i.e., information 
about the current runtime state as it relates to runtime checking. The 
information in this data structure facilitates use of protocols for 
passing and retrieving runtime checking information for function 
arguments, and for returning and receiving runtime checking information 
from a called function. 
During runtime or program execution, runtime checking operations are 
provided for almost all pointer references, including pointer 
dereferences, pointer arithmetic operations, pointer and structure 
assignments, pointer comparisons and subtractions, pointer casting, and 
passing and returning pointers and structures to and from functions. These 
runtime checking functions check for illegal or invalid operations and 
report the error to the user. 
Dynamic memory operations are handled differently than other operations 
because, unlike other objects, the type and size of objects stored in 
dynamic memory are not known at compile time. In other words, the memory 
allocation routines in C include no information about the type of the 
object that will be stored in memory. The present invention dynamically 
allocates a Dynamic Block Info data structure for each dynamically 
allocated object. When an object is deallocated, the present invention 
sets the size field of the Dynamic Block Info data structure to zero to 
indicate that the object is free. The Dynamic Block Info data structure is 
preferably never deallocated itself until the program terminates. In order 
to better track dynamic memory objects, the present invention monitors 
cast expressions from pointers into dynamic memory. The first time a 
pointer into dynamic memory is cast into a non-void type, the present 
invention records this type in the Dynamic Block Info data structure and 
allocates any additional informational objects needed for providing 
runtime checking. 
The present invention further includes runtime checking operations for 
library functions. The present invention allows a user to specify 
restrictions for the arguments to individual functions in a library. A 
library compiler referred to as libprot uses these user-specified 
restrictions to generate C source code that includes calls to internal 
functions which verify that the arguments to the functions satisfy the 
restrictions. The generated source code is compiled and linked in to the 
respective source code being compiled. When the user's compiled code calls 
a library function, the present invention links in the specially generated 
function which performs checks on the arguments at runtime before calling 
the actual library function. In this manner, invalid arguments are 
detected at runtime prior to the library function being called. 
Therefore, a method and apparatus for performing runtime checking in a 
compiled programming development environment is disclosed. The present 
invention implements runtime checking in an environment which supports the 
full ANSI-C programming language, including pointer manipulation and 
dynamic memory allocation. Further, the present invention supports runtime 
checking of arguments passed to external libraries.

DETAILED DESCRIPTION OF THE PREFERRED EMBODIMENT 
Referring now to FIG. 1, a diagram illustrating a programming development 
environment including runtime checking capabilities according to the 
present invention is shown. In the preferred embodiment, the programming 
development environment preferably includes a C source code editor 42, a 
32 bit ANSI-C compatible compiler 44, a linker 46, a debugger 48, standard 
ANSI-C libraries 50, various instrumentation libraries 52, a user 
interface library and editor 54, various miscellaneous libraries 56, and 
runtime checking functions 60 according to the present invention. The 
compiler 44 creates data structures and inserts various instructions and 
function calls into the compiled code according to the present invention. 
The data structures are preferably created for certain data items or 
objects in the user's code, preferably for each pointer and aggregate data 
item. In the present disclosure, the term aggregate data item includes 
arrays and structures. The instructions inserted into the code are 
generally used during runtime to update these data structures as the 
respective data items change during program execution. The function calls 
comprise calls to runtime checking functions 60 which perform various 
runtime checking operations of the present invention. The runtime checking 
functions of the present invention alert the user to errors in his/her 
code as well as the precise line where the error occurred. In the present 
disclosure, the various types of errors which may occur in a program are 
collectively referred to as invalid operations. 
In the preferred embodiment, the present invention is incorporated into a 
programming development environment that is adapted for instrumentation 
programming. In this embodiment, the instrumentation libraries include 
various instrumentation specific tools and libraries for data acquisition 
analysis and presentation, including libraries for GPIB (general purpose 
interface bus), RS-232, VXI bus, data acquisition and analysis, as well as 
various instruments drivers for controlling devices from various 
instrument vendors. However, it is noted that the runtime checking 
features of the present invention can be adapted for any type of 
development environment, including either a general purpose programming 
development environment or environments that are specifically adapted and 
suited for other applications. It is also noted that the runtime checking 
features of the present invention may be adapted to any type of 
programming language, including C, C++, Pascal, BASIC, Ada, etc. However, 
in the preferred embodiment, the runtime checking features of the present 
invention are adapted to the ANSI-C programming language. 
Referring now to FIG. 1A, a block diagram illustrating operation of the 
compiler 44 and linker 46 of the present invention in transforming one or 
more source code files into linked object code files or executable ties is 
shown. As shown, one or more source code files 102 and 104 are compiled by 
the compiler of the present invention and thus produce unlinked object 
code 112 and 114 as shown. In addition, a source code file 106 may be 
compiled by an external compiler 107, i.e., a compiler not according to 
the present invention, which generates unlinked external object code file 
116. The various unlinked object code generated by the compiler of the 
present invention 112 and 114 as well as the unlinked external object code 
116 are then provided to the linker 46 according to the present invention. 
The linker 46 operates to link the respective object code files into 
linked object code 122, 124 and 126 as shown. 
In many cases it is possible for one source code file 102 to have 
expressions which include objects, i.e., pointers or aggregate data items, 
wherein the definition of these objects appear in a separate source code 
file 104. In the instance where the source code file which includes a 
respective object in an expression and the source code file which includes 
the definition of this object are both compiled by the compiler of the 
present invention, the linker 46 operates to associate the data structure 
created by the compiler of the present invention and the use of that 
object in the respective source code file. In the instance where the 
source code file which includes a respective object in an expression is 
compiled by the compiler of the present invention, and the source code 
file which includes the definition of this object is not compiled by the 
compiler of the present invention, the linker 46 operates to return an 
IGNORE value for each instance where that object is used. 
Referring now to FIG. 1B, a function referred to as PtrGetResolvedAddress 
comprised in the linker 46 is called in step 130. This function receives 
an object or identifier, preferably either a pointer or aggregate data 
item, in step 132 from a source code file compiled using the compiler of 
the present invention. In step 134 the function determines if the 
definition of the respective identifier was compiled by the compiler of 
the present invention. If not, then in step 136 the IGNORE value is 
returned for this identifier, and thus during execution no runtime 
checking of this object or identifier occurs. In this manner, runtime 
checking is disabled for identifiers or objects that were defined in 
externally compiled code. This is necessary because no data structure will 
have been created for this object, and thus no runtime checking can occur 
for this object. 
If the definition of the respective identifier was compiled using the 
compiler of the present invention, then in step 140 the linker finds the 
address of the Pointer Info data structure for the identifier from the 
compiler symbol table. In step 142, the linker returns this address to the 
respective function. Therefore, the function PtrGetResolvedAddress 
operates on each global identifier or object compiled by the compiler of 
the present invention to link or associate respective objects with their 
respective data structure. For objects that are defined in a source code 
file that is not compiled by the compiler of the present invention, 
runtime checking is disabled for this object. 
Referring now to FIGS. 2A-C, a flowchart diagram illustrating a portion of 
the operation of the compiler in compiling a source code program listing 
is shown. When the compiler of the present invention encounters various 
declarations, instructions, etc. in the source code listing, the compiler 
creates various data structures, inserts instructions, or inserts calls to 
runtime checking functions. These data structures, instructions, and 
function calls are then used during runtime of the compiled source code to 
implement the execution checking features of the present invention. A 
source code listing of one embodiment of the compiler is included as 
Appendix A in the file up.sub.-- comp.c. 
In the preferred embodiment of the invention, for each function call 
inserted into the code the compiler passes the coordinates of the 
respective expression being checked. These coordinates take the form of 
PtrLeftCoord and PtrRightCoord, which are encoded with information about 
the line number and column number of the expression. The variable 
PtrLeftCoord is encoded with the line number and column number of the 
beginning of the expression and the variable PtrRightCoord is encoded with 
the line number and column number of the end of the expression. The 
compiler generates these coordinates as it scans through the source code 
file during compilation. If an error occurs these coordinates are used to 
inform the user of the precise location where the error occurred. 
Declarations of Structures, Arrays, and Pointers--Data Structures 
As shown in FIG. 2A, when the compiler encounters a declaration of a 
structure, an array, or a pointer in step 202, the compiler creates a 
special data structure associated with the particular object being 
declared according to the present invention in step 204. The respective 
data structure for each object is maintained and updated during runtime 
and is used to facilitate the runtime checking features of the present 
invention. The various data structures that are created by the compiler 
and used according to the present invention are described below. 
1. Block Info Data Structure 
Referring now to FIG. 3, a data structure referred to as Block Info is an 
8-byte object that is created by the compiler for respective aggregate 
data items, these preferably being arrays and structures, but not unions. 
It is noted that runtime checking is not provided for unions or their 
members in the preferred embodiment. Also, arrays of arrays are treated as 
single multidimensional arrays. As a result, no Block Info is generated 
for arrays within arrays. The Block Info data structure includes the 
following fields: 
Start [32 bits]: points to beginning of the object 
Size [31 bits]: size of the object (in bytes) 
SFlag [1 bit]: indicates if the data item is a structure or an array 
2. Pointer Info Data Structure 
Referring now to FIG. 4, a data structure referred to as Pointer Info is a 
12-byte object that is associated with every pointer object, including 
pointers in structures and arrays. A Pointer Info data structure is 
created for each pointer object during compile time. The Pointer Info data 
structure includes the following fields: 
Info [32 bits]: contains information about the object to which the pointer 
refers. The type of information contained in this field depends upon the 
type of the pointer. Possible information includes: 
1) address of the respective Pointer Info data structure if the pointer 
object is a pointer to a pointer; or 
2) address of the respective Block Info data structure if the pointer 
object is a pointer to a structure; or 
3) one of the following special values: 
CHAR, CHAR1, UCHAR, UCHAR1, . . . 
These indicate that the pointer points to a single scalar object of the 
type specified. A scalar data type is herein defined as any data type 
other than a structure, pointer, array or void. Examples include numeric 
data types, character data types, etc. There is a distinct special value 
for each byte of each type so that it can be determined whether the 
pointer points to the first byte of an `int` object or whether the pointer 
points to the fourth byte of a `double` object, and so on. It is noted 
that according to the ANSI-C standard, pointers may legally point just 
beyond the end of an object. 
NULL 
Indicates that the pointer was assigned the NULL value. It is noted that 
not all pointers that have the NULL value have pointer information with 
this value. Other possible values are IGNORE and UNINIT. 
IGNORE 
Indicates that runtime checking should not be performed for the pointer. 
This is the result of certain pointer casts and return values of library 
functions. Pointers are also ignored when the value in the Pointer Info 
data structure does not correspond to the actual pointer value. This 
usually results from modification of pointers in object code that has not 
been compiled by the compiler 44 of the present invention. 
UNINIT 
Indicates that the pointer was never initialized. All global pointers are 
automatically initialized to NULL before execution, but local pointers 
must be set explicitly by the user program before they can be used. 
INVALID 
Indicates that the pointer has a value that resulted from an invalid 
pointer operation. 
FUNCTION 
Indicates that the pointer points to a function. It is noted that some 
normal pointer operations are illegal for pointers to functions. 
STRUCT 
Indicates that the pointer points into a structure of unknown type. This 
results from casting a pointer to a structure into a pointer to a scalar 
(e.g. a.sub.-- char.sub.-- ptr=(char *)&a.sub.-- struct). 
DYNAMIC 
DYNAMIC CLEAR 
These two special values indicate that the (void) pointer points into an 
area of dynamic memory that was just allocated. The second value indicates 
that the memory was cleared after it was allocated. 
SizePtr [32 bits]: points to the respective Block Info data structure if 
the pointer points to an element of an array or structure; otherwise this 
field is empty. 
Value [32 bits]: contains the value of the pointer. 
Dynamic Memory Allocation--Data Structure 
Referring now to FIG. 5, a data structure referred to as Dynamic Block Info 
is a 12-byte object that is associated with respective blocks of memory 
allocated by the malloc library function, etc. Unlike the prior two data 
structures which are created at compile time, a Dynamic Block Info data 
structure is created at runtime for each block of memory allocated by a 
malloc function call. This data structure must be created at runtime 
because the malloc function call is a call to a function in an external 
library. The Dynamic Block Info data structure includes the following 
fields: 
Start [32 bits]: points to the beginning of the memory block 
Size [31 bits]: size of the block (in bytes) 
SFlag [1 bit]: indicates if the memory allocated is a structure or an array 
Type [32 bits]: points to the compiler data structure that describes the 
type that was first used to reference the dynamic memory. 
The code that creates a Dynamic Block Info data structure is found at page 
3 of Appendix C in the file up.sub.-- run2.c. 
Current Runtime Information--Data Structure 
Referring now to FIG. 6, a data structure referred to as Current Runtime 
Information is a single structure that contains information about the 
current runtime state as it relates to runtime checking operations. This 
data structure is provided in the programming environment and is 
initialized with information prior to execution of a compiled program. 
Certain protocols must be followed for passing and retrieving runtime 
checking information for function arguments and for returning and 
receiving runtime checking information from a function because of the 
possibility that the called function or the calling function has not been 
compiled by the compiler of the present invention. The information in this 
structure facilitates these protocols. The fields comprised in the Current 
Runtime Information data structure are as follows. 
Argument Info: an array comprising the arguments to the current function. 
This structure contains Pointer Info and type information for each 
argument. The information in the Argument Info field is used in the 
prologue of compiled functions to copy the information into local storage. 
This information is also used by the library checking functions to check 
the validity of arguments before calling the library function. 
Called function Pointer: points to the address of the last known called 
function. The information in this field is used to determine whether the 
argument information in the Argument Info field corresponds to the current 
function. 
Return Value Info: a Pointer Info data structure that contains information 
for the return value of a function. 
Returning function Pointer: points to the address of the function that is 
returning a value. The information in this field is used to determine to 
which function the value in the Return Value Info field belongs. 
Pointer Reference 
1. Compiler Operation 
Referring again to FIG. 2A, when the compiler encounters a reference to a 
pointer in step 206, the compiler inserts a call to a function referred to 
as .sub.-- PtrRValue in step 208. This function is used to retrieve the 
Pointer Info data structure for the respective pointer being referenced. 
This function also includes a comparison of the value of the pointer with 
the value in its Pointer Info data structure. The code which inserts this 
function call is found at page 18 of Appendix A in the file up.sub.-- 
comp.c. 
2. Runtime Checking Operation 
Referring now to FIG. 7, whenever a pointer is referenced in an expression 
in step 700, the function .sub.-- PtrRValue is called and receives as an 
argument an address which points to the Pointer Info data structure for 
the pointer referenced. In step 702 the .sub.-- PtrRValue function first 
checks to see whether the value passed in is actually a valid address to a 
Pointer Info data structure, or rather is a special value of a predefined 
type. The argument passed into the .sub.-- PtrRValue function, which is 
intended to be an address of a Pointer Info data structure, can be a 
special value in certain cases where there is a pointer dereference 
expression involving a pointer to a pointer. In such an instance, the Info 
field of the pointer, which would normally hold the address of a Pointer 
Info data structure in the case of a pointer to a pointer, might receive a 
special value if the pointer being pointed to was invalid, uninitialized, 
etc. If the value passed in is a special value, then the function returns 
INVALID, and the function completes. 
If the value passed in is an address to a Pointer Info data structure, then 
this pointer contains a valid Pointer Info data structure, and the value 
of the pointer and the value stored in the Pointer Info object are 
compared in step 704. If the values are determined to correspond in step 
706, then the Info field of the Pointer Info data structure is returned in 
step 712. If these values do not correspond in step 706, then the IGNORE 
special value is returned in step 708. Thus, if the values do not 
correspond, then no further checking for the pointer is performed within 
the expression. In this instance it is assumed that an external event has 
occurred which has changed the value of the pointer unbeknownst to the 
programming environment of the present invention. One example of this is 
external object code that has not been compiled by the compiler of the 
present invention. 
Therefore, this function is inserted by the compiler whenever a pointer is 
referenced in the C program to obtain the Pointer Info for this pointer. 
This function is performed for all pointer references described below. The 
function .sub.-- PtrRValue is found at page 20 of Appendix B in the file 
up.sub.-- run1.c. 
Pointer Arithmetic Expressions 
1. Compiler Operation 
Referring again to FIG. 2A, when the compiler encounters a pointer 
arithmetic expression in step 210, such as a[i]; *(a+i); ptr+3; ptr-4; the 
following operations occur. The compiler inserts a function call to the 
function .sub.-- PtrChkArith in step 212 passing the following arguments: 
the type of the pointer expression, the actual pointer involved in the 
pointer expression, the integer value that is either added or subtracted 
to the pointer, the Info field of the Pointer Info data structure for the 
pointer, the address of the block information for the object pointed to, 
if any, and the coordinate information about the location of the 
expression in the source file. The code for this operation is found in the 
function PtrArith in Appendix A in the file up.sub.-- comp.c, pages 25 and 
26. 
2. Runtime Checking Operation 
Referring now to FIGS. 8A-8C, during runtime when a pointer arithmetic 
expression is encountered, the function .sub.-- PtrChkArith is called in 
step 600 and performs the following checks. This function uses the 
information provided in the Pointer Info data structure associated with 
the pointer used in the arithmetic expression. 
If the Info field of the Pointer Info data structure contains the IGNORE 
special value in step 602, then no checks are performed and the function 
returns the Info field in step 604. 
If the Info field contains one of the following special values: UNINIT, 
INVALID, or FUNCTION in step 608, then a non-fatal error is reported in 
step 610. The Info field passed into this function is also returned in 
step 612. 
If the Info field contains the NULL special value or the value of the 
pointer is NULL in step 616, then a non-fatal error is reported in step 
618 and the function returns an INVALID special value in step 620. 
If the arithmetic operation is determined to be an array indexing 
expression (indicated by the `type` parameter) in step 624, then the 
subscript in the expression is compared to the size of the array in step 
626. If the subscript is negative or it is larger than the array size in 
step 628, then an error is reported in step 630. 
If the arithmetic operation is determined not to be an array indexing 
expression in step 624, then in step 632 (FIG. 8B) the function determines 
if the pointer in the expression includes a Block Info data structure. As 
mentioned above, the pointer will include a Block Info data structure if 
the pointer points to an element in either an array or a structure. If the 
pointer includes a Block Info data structure, then the function determines 
if the Block Info data structure refers to a freed object in step 654, 
i.e., a deallocated block of memory. If so, then in step 662 an error is 
reported. If the Block Info data structure does not refer to a freed 
dynamic object in step 654, then the result of the pointer arithmetic is 
compared to the bounds of the array or structure given by the Block Info 
data structure in step 656. If the result is determined to not be within 
the array or structure in step 658, then an error is reported in step 660. 
If the object in the expression does not include a Block Info data 
structure in step 632, then the function determines if the addend is zero 
in step 634. If so, the function returns the Info field of the Pointer 
Info data structure in step 650. If the addend is determined to be a 
non-zero number in step 634, then in step 636 the function determines if 
the Info field of the Pointer Info data structure contains one of the 
special values for scalar objects (CHAR, CHAR1, etc.). If so, then the 
function determines if the result of the pointer arithmetic points two 
bytes beyond or one byte before the scalar object in step 638. If so, an 
error is reported in step 644 and the function returns an INVALID value in 
step 646. If the result of the pointer arithmetic does not point two bytes 
beyond or one byte before the scalar object in step 638, then in step 640 
the function returns the special scalar value. 
If the Info field of the Pointer Info data structure does not contain one 
of the special values for scalar objects in step 636, then the function 
advances to step 668 (FIG. 8C). In step 668 the function determines if the 
pointer involved in the arithmetic expression involves either a pointer to 
a pointer or a pointer to a structure. If the pointer is determined to 
point to either a structure or another pointer in step 668 (FIG. 6C), then 
the location of the Block Info or Pointer Info data structure for the 
referenced object is calculated and returned in step 674. If the pointer 
is determined to not point to either a structure or a pointer in step 668, 
then the Info field of the Pointer Info data structure is returned in step 
670. 
If a pointer arithmetic expression includes two or more embedded arithmetic 
operations, i.e., various subexpressions within a larger arithmetic 
expression, each subexpressions will have a function call to the function 
.sub.-- PtrChkArith. The function call resulting from the subexpression 
will return pointer information to the larger expression in which the 
respective subexpression is embedded for that larger expression's function 
call to the function .sub.-- PtrChkArith. The function .sub.-- PtrChkArith 
is at pages 26, 27, and 28 of Appendix B in the file up.sub.-- run1.c. 
Pointer Dereference 
1. Compiler Operation 
Referring again to FIG. 2A, when the compiler encounters a pointer 
dereference in step 214, i.e., when an access to the object to which a 
pointer points is encountered, the following operations occur. The 
compiler inserts a function call in step 216 to a function called .sub.-- 
PtrChkDeref in the code being compiled prior to the pointer dereference. 
This function call passes the arguments of the current function where the 
pointer dereference is located, the type of the pointer expression, the 
actual pointer itself, the Pointer Info data structure corresponding to 
the pointer in the expression, and the Block Info data structure 
corresponding to the pointer expression, if the pointer points to an array 
or structure. In addition, the two coordinates of the pointer expression, 
i.e., the location in the source code where the pointer expression exists, 
is passed along to the function. If an error occurs during runtime, the 
coordinate information is used to indicate to the user where the runtime 
error occurred. The code which inserts this function call prior to a 
pointer dereference is found at page 20 of appendix A in the file 
up.sub.-- comp.c. 
2. Runtime Checking Operation 
Referring now to FIGS. 9A-C, during runtime when a pointer dereference is 
encountered, the function .sub.-- PtrChkDeref performs the following 
checks. 
If the Info field of the Pointer Info data structure contains the IGNORE 
special value in step 802, then the function advances to step 804. In step 
804 the function determines if the value of the pointer is NULL. If the 
value of the pointer is NULL in step 804, then a Fatal error is reported 
in step 806. IF the value of the pointer is not NULL in step 804, then no 
checks are performed. 
IF the Info field contains one of the Following special values: NULL, 
UNINIT, INVALID in step 810 then a Fatal error is reported in step 812. 
Referring now to FIG. 9B, in step 816 the function determines if a Block 
Info data structure was passed into the function. If so, then in step 826 
the function determines if the Block Info data structure refers to a Freed 
object, i.e., a deallocated block of dynamic memory. IF so, then an error 
is reported in step 836. IF the Block Info data structure is determined to 
not point to a free object in step 826, then in step 828 the result of the 
pointer dereference is compared to the bounds of the array or structure 
given by the Block Info data structure. In step 830 the function 
determines if the result is within the array or structure. If not, then in 
step 832 an error is reported. Otherwise operation completes. 
IF there is determined to be no Block Info data structure passed into the 
function in step 816, then in step 818 the function determines if the Info 
field of the Pointer Into data structure contains a special value For a 
scalar object, i.e., the Info field contains one of the special values for 
scalar objects (CHAR, CHAR1, etc.). If so, then in step 820 the function 
determines if the result of the pointer arithmetic would point one byte 
beyond or one byte before the scalar object. If so, then an error is 
reported in step 822, and operation completes. If the Info field does not 
contain a special value for a scalar object in step 818, then the function 
advances to step 840 in FIG. 9C. IF the result of the pointer arithmetic 
in step 820 does not point one byte beyond or one byte before the scalar 
object, then the function also advances to step 840 in FIG. 9C. 
Referring now to FIG. 9C, in step 840 the function determines if the 
pointer expression type is a function pointer. If the pointer expression 
type is determined to be a function pointer in step 840, then in step 852 
the function determines what the Pointer Info data structure indicates. If 
the Pointer Info indicates that the pointer is not a function pointer in 
step 852, then an error is reported in step 856. If the Pointer Info 
indicates that the pointer is a function pointer in step 852, then 
operation completes. If the pointer expression type is determined not to 
be a function pointer in step 840, then in step 842 the function 
determines what the Pointer Info data structure indicates. If the Pointer 
Info indicates that the pointer is a function pointer in step 842, then an 
error is reported in step 846. Otherwise operation completes. 
Therefore, the runtime checking that occurs on a Pointer Dereference is 
very similar to the runtime checking that occurs for a pointer arithmetic 
expression. The compiler's type is compared with the actual type of the 
object that the pointer references, and some cases are reported as errors 
such as dereferencing a data pointer as a function or vice versa. 
Dereferencing invalid pointer values is a fatal error because it may cause 
a memory fault or other serious problem. Pointer dereference errors 
include dereference of uninitialized pointers, null pointers, 
out-of-bounds pointer expressions, freed pointers, and invalid pointer 
expressions. The function .sub.-- PtrChkDeref is found at page 31 of 
Appendix B in the file up.sub.-- run1.c. 
Structure Member Reference 
1. Compiler Operation 
A structure member reference includes the C expression struct.member as 
well as the pointer dereference arrow member to structures. Referring now 
to FIG. 2A, when the compiler encounters a structure member reference in 
step 218, the compiler inserts a function call to the function .sub.-- 
PtrStructMem passing the address of the structure, the pointer information 
for the structure, the offset information for the structure member and the 
coordinates of the expression in step 220. The code which inserts this 
function call is found at page 23 of appendix A in the file up.sub.-- 
comp.c. 
2. Runtime Checking Operations 
The function .sub.-- PtrStructMem performs no runtime checking operations 
itself, but is used to return the pointer information for a structure 
member. Referring now to FIG. 10, when the member of a structure is 
referenced during execution in step 950, the function .sub.-- PtrStructMem 
determines if the Info field contains the IGNORE special value in step 
952. If so, the function returns the IGNORE special value in step 966. If 
the Info field is determined not to contain the IGNORE special value in 
step 952, then the function determines if the Info field is a special 
value in step 954. If so, the Info field is returned in step 962. If not, 
then the value in the Info field is incremented by the offset that 
corresponds to the structure member in step 956 and the value is returned 
in step 958. This function is found at page 25 of Appendix B in the file 
up.sub.-- run1.c. 
Pointer Cast Expression 
A cast expression forces an expression to be of a specific type when a 
pointer cast is encountered during execution, it is necessary for the 
present invention to keep track of the new data types to which the 
respective variable is being cast so that this information can be provided 
to other runtime checking functions. If pointer casts were not monitored 
in this way, then a pointer cast of an object from one data type to 
another could confuse later runtime checking operations involving this 
object. If the pointer cast involves a dynamically created memory object, 
Block Info and Pointer Info data structures regarding this new object are 
created at runtime. 
1. Compiler Operation 
Referring again to FIG. 2A, when a cast expression is encountered by the 
compiler in step 222, the following operations occur. If the cast is from 
a void pointer type to any other type of pointer, then the compiler 
inserts a call to the function .sub.-- PtrCast in step 224. If the cast 
expression is casting to a structure pointer, or a pointer to a pointer, 
or a pointer to an array, then the compiler does not insert a function 
call. If the cast expression is casting from a pointer to a structure to 
any other type of scalar pointer, then a call is inserted to the function 
.sub.-- PtrCastStructBlk in step 224. Finally, if the cast expression is 
from either a pointer to a structure, or a pointer to a pointer, or a 
pointer to an array cast to a pointer to a scalar object, then the 
compiler inserts a call to the function .sub.-- PtrCastToScalar in step 
224. This code is found at pages 24 and 25 of Appendix A in the file 
up.sub.-- comp.c. 
2. Runtime Checking Operation 
The three functions .sub.-- PtrCast, .sub.-- PtrCastToScalar, and .sub.-- 
PtrCastStructBlk monitor the new data types to which a respective pointer 
variable is cast and also create new Block Info and Pointer Info data 
structures if the pointer cast involves a dynamically created memory 
object. These functions are found at pages 24 and 25 of the file up.sub.-- 
run1.c. 
The table illustrated in FIG. 11 describes the actions performed when 
casting from one type of pointer to another. The table shows the actions 
performed for a cast expression: (Y.sup.*) e, where expression e has type 
(X.sup.*), and X and Y are arbitrary C types. The Pointer Info and Block 
Info headings in the table describe the Pointer Info and Block Info data 
structures of the resulting expression. The table classifies various C 
types into the following three categories: 
(1) Struct/Pointer/Array; 
(2) Void; and 
(3) Scalar (includes everything but above) 
The scalar classification includes all data types other than 
struct/pointer/array and void, including the integer numeric data types, 
characters and unions. 
As shown in the table, when a pointer to a scalar is cast to a pointer to a 
scalar, a pointer to a scalar is cast to a void pointer, or a void pointer 
is cast to a pointer to a scalar, the Info field and SizePtr field for the 
resulting expression are simply copied from the e expression. However, 
when either a pointer to scalar or a void pointer is cast to either of a 
pointer to a pointer, pointer to a structure or pointer to an array, 
IGNORE is placed into the Pointer Info data structure and EMPTY is placed 
into the Block Info data structure. This is because, although this 
operation is allowed in C, this operation is very unusual and would 
confuse the runtime checking operation of the present invention. 
Pointer casting errors are generated by these functions when a pointer 
expression is cast to type (AnyType.sup.*) and there is not enough space 
for an object of type AnyType at the location given by the expression. In 
the preferred embodiment, this occurs only when casting a dynamically 
allocated object for the first time, for example (double.sup.*) 
malloc(1)). 
These functions are found at pages 24 and 25 of Appendix B in the file 
up.sub.-- run1.c. 
Address of Object Expression 
1. Compiler Operation 
Referring now to FIG. 2B, when the compiler encounters a C expression 
involved with taking the address of an object, i.e. , &obj, in step 226, 
the following operations occur. In step 228, the compiler creates a 
Pointer Info data structure corresponding to the elements in the 
expression. The value stored in the Info field is one of the special 
values corresponding to the type of the object as described above, and the 
value of the SizePtr field is set to zero, indicating that it is not an 
array or struct object. An expression which takes the address of an object 
is essentially equivalent to a pointer declaration and thus, as in a 
pointer declaration, the compiler creates a Pointer Info data structure. 
It is also noted that no function calls to runtime checking functions are 
inserted. The code which generates these data structures is found at pages 
20 and 21 of appendix A in the file up.sub.-- comp.c. 
Pointer and Structure Assignment 
1. Compiler Operation 
Referring now to FIG. 2B, when an assignment of either a pointer or a 
structure is encountered, the compiler performs the following operations. 
For a pointer assignment in step 230, the compiler inserts a call to the 
function .sub.-- PtrCheckAssign in step 232 passing the following 
arguments: the fight side of the assignment expression, the pointer 
information for the left hand side of the expression and the information 
in the Pointer Info data structure for the right hand side of the 
expression. In addition, coordinate information is passed to the function 
.sub.-- PtrCheckAssign which indicates the line and column numbers of the 
expression that is being checked. If an error occurs during runtime, the 
function .sub.-- PtrCheckAssign uses the coordinate information to 
indicate to the user where the runtime error occurred. For a structure 
assignment in step 234, if the right side of the structure assignment is 
determined to have no pointer information in step 236, then the compiler 
inserts a function call to the function .sub.-- PtrlgnoreStruct in step 
238 which indicates that the left side of the structure assignment should 
also not have pointer information. If the right side of the assignment is 
determined to have pointer information in step 236, then a call to the 
function .sub.-- PtrAssignStruct is inserted which assigns the pointer 
information from the right side of the expression to the left side. The 
code which inserts these function calls is found at pages 21 and 22 of 
appendix A in the file up.sub.-- comp.c. 
2. Runtime Checking Operation 
Referring now to FIGS. 12A and 12B, the runtime function .sub.-- 
PtrChkAssign first checks to see if the left side of the expression 
contains Pointer Info in step 302. If so, then in step 304 the Pointer 
Info from the right side of the expression is copied to the leer side of 
the expression, and the function then advances to step 306. If the left 
side of the expression does not contain Pointer Info, i.e., the address to 
what should be the Pointer Info is a special value, then the function 
advances directly to step 306. In step 306 the function checks the Pointer 
Info for the right side of the expression. If the pointer information for 
the right side of the expression is determined to be either IGNORE or NULL 
in step 308, then no operations are performed. If the Info field for the 
right side is UNINIT, i.e., the pointer on the fight side is determined to 
be uninitialized in step 3 12, then an assignment of uninitialized value 
error is reported in step 314. If pointer on the right side is determined 
to be INVALID in step 318, then an assignment of invalid value error is 
reported in step 320. 
Referring now to FIG. 12B, in step 324 the function determines if the right 
side pointer value is a non-zero number. If the right side pointer value 
is zero, then operation completes. If the value is non-zero, then in step 
326 the function determines if the object on the right side of the 
assignment includes block information. If not, then operation completes. 
If so, then the function checks to see if the object is a freed object in 
step 330. If the object is determined to be a freed object in step 330, 
the function reports an assignment of a freed dynamic object error in step 
340. If the object is determined not to be a freed object in step 330, 
then the function checks to see if the pointer value of the right hand 
side points to within the object described by the block information in 
step 332. If so, then operation completes. If not, the function reports 
either an assignment of an over array bounds or under array bounds error 
in step 334. This function is located at page 30 of Appendix B in the file 
up.sub.-- run1.c. 
Thus, pointer assignment errors are generated when pointer variables are 
assigned an invalid pointer value, and the warnings provided by the 
present invention can help determine when a particular pointer becomes 
invalid. The Pointer Info for the right side of the assignment is checked 
for invalid pointer values, uninitialized pointer values, out-of-bounds 
pointer expressions, and freed pointer values, and an error is reported on 
any of these occurrences. The pointer information for the right side of 
the expression is also copied into the Pointer Info data structure for the 
left side pointer. 
In the case of a structure assignment, the functions .sub.-- 
PtrIgnoreStruct and .sub.-- PtrAssignStruct perform the following 
operations. Referring now to FIG. 13, the function .sub.-- PtrIgnoreStruct 
called in step 350 first determines if the Info field of the pointer is a 
special value in step 352. If not, the function sets the Info field of the 
Pointer Info data structure for all of the members of the structure to the 
IGNORE special value in step 354. If so, then operation completes. As 
mentioned above, this function is inserted by the compiler when a 
structure assignment is encountered and the right side of the expression 
has no pointer information. 
Referring now to FIG. 14, if the right side of the expression does have 
pointer information, the function .sub.-- PtrAssignStruct is called in 
step 360. In step 362 the function determines if the Info field of the 
left side is a special value. If so, then operation completes. If the Info 
field of the left side is determined not to be a special value in step 
362, then the function determines if the Info field of the right side 
contains the IGNORE special value in step 364. If so, then the function 
.sub.-- PtrlgnoreStruct is called on the left side of the Pointer Info 
data structure in step 370. If not, then the function copies the 
information in the Pointer Info data structure from the right side of the 
expression to the left side of the expression in step 366. 
The functions .sub.-- PtrlgnoreStruct and .sub.-- PtrAssignStruct are found 
on pages 19 and 20 of Appendix B in the file up.sub.-- run1.c. 
Pointer Comparisons and Subtractions 
1. CompilerOperation 
When the compiler encounters a pointer comparison operation in the user's 
source code in step 242, the compiler inserts a function call to the 
function .sub.-- PtrChkCompare in step 244 passing the pointer information 
for both the left and fight hand side, the block information for both the 
left and fight hand side, and the coordinates of this comparison 
expression. The code which inserts this function call is found in Appendix 
A in the file up.sub.-- comp.c at pages 27 and 28. 
When a pointer subtraction expression is encountered in step 246, the 
compiler inserts a function call to the function .sub.-- PtrChkSubtract in 
step 248 passing the same arguments as in the function .sub.-- 
PtrChkCompare. This code is also found at page 27 of Appendix A in the 
file up.sub.-- comp.c. 
2. Runtime Checking Operation 
Referring now to FIG. 15, the runtime function .sub.-- PtrChkCompare is 
called in step 380. The function checks the validity of pointer 
comparisons using the Pointer Info data structure for the left side and 
the right side of the pointer comparison expression. If the Info field of 
either of the Pointer Info data structures is determined to be the IGNORE 
special value in step 382, the function does nothing. If the Info field of 
either of the Pointer Info data structures indicates that the respective 
pointer is uninitialized in step 386, then a compare uninitialized error 
is reported in step 388. If the Info field of either of the pointer 
information data structures is determined to be INVALID in step 392, a 
compare invalid error is reported in step 394. If the Info field of either 
of the pointer information data structures is determined to be NULL in 
step 398, then a null comparison error is reported in step 400. If the 
block information for the left hand side or the right hand side is 
determined to be EMPTY in step 404, then a compare to scalar objects error 
is reported in step 406. If the block information is determined not to be 
equivalent for both the left hand side and the right hand side in step 
410, a comparison to different object error is reported in step 412. 
Finally, if the block information for either side is determined to be a 
freed dynamic object in step 416, then a comparison to freed object error 
is reported in step 418. 
Referring now to FIG. 16, the function .sub.-- PtrChkSubtract function is 
called in step 422. The function performs the same checks for pointer 
subtraction and is essentially equivalent to pointer comparison except 
that subtraction errors are reported instead of comparison errors. The 
above two functions are located at pages 28 and 29 of the file up.sub.-- 
run1.c, Appendix B. 
Therefore, in the case of pointer comparisons and subtractions, if either 
of the pointers involved in the comparison or subtraction is invalid, then 
an error is reported. If the Block Info for both pointers is not identical 
then the pointers do not point to the same object and an error is 
reported. If the Block Info has its Size field set to 0, then the object 
was deallocated from dynamic memory and an error is reported. Additional 
errors are reported in comparisons or subtractions involving uninitialized 
pointers, freed pointers, or those involving addresses of non-array 
objects. 
Function Calls and Returns 
Passing parameters which are pointers or structures involves a protocol 
which includes copying the pointer information to a global array by the 
caller function and then copying from this global array by the callee 
function. Returning pointers and structures from functions involves a 
similar protocol between the callee function and the caller function. This 
protocol does not entail any error checking in itself, but rather is used 
to pass information between the caller and callee functions to enable 
error checking on arguments provided to a callee function and also on 
return values provided back to the caller function. 
1. Compiler Operation 
Referring now to FIG. 2C, when the compiler encounters a function call in 
the user's code having either pointer or structure arguments that are 
passed to a callee function in step 252, the following function calls are 
inserted. For each argument that is either a pointer or structure, the 
compiler inserts a call to the function .sub.-- PassParamlnfo in step 254 
passing the pointer information and the type information for the argument. 
The code for this is found on page 29 in the function PtrPassParamlnfo 
which is in Appendix A in the file up.sub.-- comp.c. 
When the compiler encounters a function call in the user's code where the 
value to be returned is either a structure or a pointer in step 256, the 
compiler inserts a call to the function .sub.-- ReceivePtrInfo in step 257 
passing the address of the function that is called. The compiler then 
inserts a pointer dereference to the return value of that function in step 
260, and this value is used as the value in the Pointer Info data 
structure for the resulting expression. The code for this is found on page 
29 in Appendix A in the file up.sub.-- comp.c. When the compiler 
encounters a function definition, either a caller or callee function, in 
step 264, the compiler inserts into the prologue of the function 
definition a call to the function .sub.-- GetParamInfo in step 266 for 
each one of its arguments that are either pointer or structure type. 
When a return statement is encountered in a callee function that returns 
either a pointer or a structure value in step 268, the compiler inserts a 
call to the function .sub.-- ReturnPtrInfo. This code is found on page 28 
in the function PtrReturnInfo in Appendix A, file up.sub.-- comp.c. 
For any function that is called, i.e., a callee function that either 
receives pointer or structure arguments or returns a pointer or structure 
value, the compiler inserts before the function call a call to the 
function .sub.-- PtrPreCall, and after the function call the compiler 
inserts a call to the function .sub.-- PtrPostCall. 
2. Runtime Checking Operation 
Referring now to FIG. 17, when a function call is encountered, the runtime 
function .sub.-- PassParamlnfo is called in step 470. The function 
receives pointer information and block information for a particular 
parameter to a function in step 472 and inserts this information into a 
global array of parameter information that is part of the Current Runtime 
Information data structure in step 474. This information is used by the 
function that is being called to obtain pointer information for its 
arguments, as described below. This function is found on page 21 of 
Appendix B in the file up.sub.-- run1.c. 
Referring now to FIG. 18, inside of function prologues, the function 
.sub.-- GetParamInfo is called in step 480. The function obtains the 
pointer information for parameters to the function. This function accesses 
the global array in the Current Runtime Information data structure to 
obtain the pointer information and block information for individual 
arguments to the function in step 482. This information was placed there 
by the .sub.-- PassParamlnfo function. Thus the .sub.-- GetParamInfo 
function obtains the pointer information for parameters received by a 
callee function. This pointer and block information is stored in a local 
variable in step 484. The function .sub.-- GetParamInfo is found on page 
22 in Appendix A in the file up.sub.-- run1.c. 
Referring now to FIG. 19, the .sub.-- ReturnPtrInfo function is called 
inside a callee function in step 520. The function stores the function 
pointer to be returned in the Current Runtime Information data structure 
in step 522. The function .sub.-- ReturnPtrInfo then places the pointer 
information, i.e., the contents of the Pointer Info data structure, for 
the return value of the callee function into the Current Runtime 
Information data structure in step 524. 
Referring now to FIG. 20, the function .sub.-- ReceivePtrInfo is called 
inside a caller function in step 500. This function compares the function 
pointer passed in with the pointer stored in the Current Runtime 
Information data structure in step 502. If they are determined to be the 
same in step 504, then the function accesses the Pointer Info data 
structure in the Current Runtime Information data structure in step 508. 
If they are not the same, then the IGNORE special value is returned in 
step 506. The runtime functions .sub.-- ReturnPtrInfo and .sub.-- 
ReceivePtrInfo, are found on pages 23 and 24 in Appendix B in the file 
up.sub.-- run1.c. 
The functions .sub.-- PtrPreCall and .sub.-- PtrPostCall are also involved 
in this protocol for initializing and restoring the global data 
structures. Specifically, .sub.-- PtrPreCall sets the Called Function 
Pointer field of the Current Runtime Information data structure to the 
address of the called function, and .sub.-- PtrPostCall resets the 
Argument Info field of the Current Runtime Information data structure to 
NULL. These functions pass respective information into the Current Runtime 
Information data structure so that this information can be used by other 
runtime error checking functions to facilitate the runtime checking 
capabilities of the present invention. 
Therefore, for passing pointers and structures between functions, the 
present invention copies the pointer information for the arguments to a 
function into the Current Runtime Information data structure before the 
function call. Inside of the prologue of the callee function, instructions 
are generated to copy that information into local objects allocated by the 
compiler. The Called Function Pointer field in the Current Runtime 
Information data structure is checked to determine if those arguments were 
meant for the current function. 
In order to return pointers and structures from functions, at return 
statements instructions are generated that copy the pointer information 
for the return value into the Current Runtime Information object before 
returning. The caller function accesses this information for the return 
value. The Return Function Pointer field of the Current Runtime 
Information data structure is checked to determine if the information is 
meant for the current function. 
Dynamic Memory 
Dynamic memory operations are handled differently than the operations 
described above because, unlike other objects, the type and size of 
objects that are stored in dynamic memory are not known at compile time. A 
source code listing of one embodiment of the invention which performs 
runtime checking on dynamically allocated objects is included as Appendix 
C in the file up.sub.-- run2.c. Referring now to FIG. 21, when an object 
is dynamically allocated in step 582, i.e., when an object is created at 
runtime by a call to functions malloc or calloc, the present invention 
dynamically allocates a Dynamic Block Info data structure for the 
respective object in step 584. 
Referring now to FIG. 22, when an object is deallocated or freed in step 
592 by a call to function free, a function referred to as PtrChkFree is 
called in step 594. This function implements various runtime checking 
operations according to the present invention, as described below. In step 
596, the Size field of the Dynamic Block Info structure is set to 0 to 
indicate that it is a freed object. The Dynamic Block Info data structure 
is never deallocated itself until the program terminates. 
Referring now to FIG. 23, when the function PtrChkFree is called in step 
520, the function obtains the Pointer Info data structure for the pointer 
being freed in step 522. This is accomplished using the .sub.-- 
GetParamInfo function. If the Info field is UNINIT or INVALID in step 524, 
then an error is reported in step 526. If the Info field is IGNORE or NULL 
instep 528, operation completes. In step 530 the function determines if 
Block Info is available. If not, then operation completes. If so, then in 
step 532 the function determines if the object is a freed object, i.e. , 
if the object has already been freed. If so, then an error is reported in 
step 542. If the object is determined to not be a freed object in step 
532, then in step 534 the function determines if the pointer points to the 
beginning of the block specified by Block Info. If so, then operation 
completes. If not, then an error is reported in step 536. 
Referring now to FIG. 24, when function realloc is called during runtime 
that reallocates an object in step 581, a function referred to as 
PtrChkRealloc is called in step 583. If the PtrChkRealloc function does 
not report an error, then in step 585 the Pointer Info data structure is 
reorganized for the dynamic block that is being reallocated. 
Referring now to FIG. 25, the function PtrChkRealloc is similar to the 
function PtrChkFree described in step 23. When the function PtrChkRealloc 
is called in step 550, the function obtains the Pointer Info data 
structure for the pointer being freed in step 552. This is accomplished 
using the .sub.-- GetParamInfo function. If the Info field is UNINIT or 
INVALID in step 554, then an error is reported in step 556. If the Info 
field is IGNORE or NULL instep 558, operation completes. In step 560 the 
function determines if Block Info is available. If not, then operation 
completes. If so, then in step 562 the function determines if the object 
is a freed object. If so, then an error is reported in step 572. If the 
object is determined to not be a freed object in step 562, then in step 
564 the function determines if the pointer points to the beginning of the 
block specified by Block Info. If so, then operation completes. If not, 
then an error is reported in step 566. 
Memory allocation routines have no information about the type of the object 
that will be stored in memory. In order to better track dynamic memory 
objects, the present invention keeps track of cast expressions from 
pointers into dynamic memory as described above. The first time a pointer 
into dynamic memory is cast into a (non-void) type, this type is recorded 
in the Dynamic Block Info data structure, and any additional informational 
objects which are necessary for providing runtime checking are provided. 
The dynamic memory runtime checking functions of the present invention 
catch illegal operations with dynamic memory and detect corrupted dynamic 
memory during allocation and deallocation. Memory deallocation errors 
include attempts to free uninitialized pointers, previously freed 
pointers, invalid pointers, pointers not allocated with malloc or calloc, 
and addresses not at the start of an allocated block. 
FUNTIME CHECKING FOR LIBRARY FUNCTIONS 
In the programming environment of the present invention, a number of 
library files are included where runtime checking is desirable. The 
present invention includes a library protection function generator program 
referred to as libprot which helps implement runtime checking operations 
for library functions. If runtime checking is desired to be implemented 
for a library routine, pragmas according to the present invention are 
inserted into the library source code which specify the desired 
restrictions on arguments to the function. If the source code for the 
library is not available, the pragmas according to the present invention 
are placed in a separate source code file. 
Referring now to FIG. 26, if the source code for the library is available, 
the library source code including the desired pragmas 1002 is compiled by 
the libprot compiler 1004 to generate runtime checking source code 1006. 
This runtime checking source code 1006 is then combined with the original 
library source code 1002, and this combined source code is compiled with a 
standard compiler 1010. The compiler produces an object file or executable 
file 1012 that includes calls to runtime checking operations according to 
the present invention. Therefore, for each library function that has one 
of these pragmas, the libprot program generates a runtime checking 
function that performs the checks specified by the pragma. If there are 
multiple pragmas for a function, then the generated function performs the 
checks for each pragma in the order in which they appear in the input 
file. The libprot compiler also expands all macros in the pragma line. 
Referring now to FIG. 27, if the source code for the respective library is 
not available, but rather only library object code 1108 is available, the 
following operations occur. A source file 1100 including the desired 
pragmas and the respective library header is compiled by the libprot 
compiler 1004 to generate runtime checking source code 1102. The runtime 
checking source code 1102 is compiled by a standard compiler 1104 to 
generate runtime checking object code. The runtime checking object code is 
combined with the library object code 1108 to generate a library with 
runtime checking capabilities. 
In the preferred embodiment of the invention, pragmas are used to insert 
argument restrictions into library routines to insulate the user from 
having to write routines that specify the desired restrictions. However, 
other methods for allowing the user to specify argument restrictions are 
also contemplated. 
Library Runtime Checking Pragmas 
The various pragmas which can be inserted into library routines according 
to the present invention are described below. As mentioned above, these 
pragmas are inserted into a library function to specify restrictions on 
arguments to the library functions. The words in italics in the pragma 
specifications below have the following meanings: 
ident--an identifier. 
function--the name of the function for which the pragma applies. This 
function must be previously declared. 
parameter--a parameter identifier for the function. Must be previously 
declared (either in a prototype or function definition). 
Cexp--a C expression (may include references to globals, function 
parameters and other pragma primitives). The expression should not involve 
references to structure members or any other expression involving constant 
offsets from a fixed address. 
errorcode--one of the error code constants listed in a later section of 
this document. 
1) #pragma CheckLibSize function parameter Cexp 
Restrictions: parameter is a pointer or array type, Cexp is an integer 
type. 
The function generated by this pragma generates a fatal error if the array 
starting at location given by parameter does not have at least Cexp 
elements. If Cexp evaluates to zero, then parameter must have at least 1 
element. 
2) #pragma CheckLibCond function Cexp errorcode parameter 
Restrictions: Cexp has integer type. 
The function generated by this pragma evaluates the expression Cexp. If the 
expression Cexp evaluates to zero, a fatal error of type errorcode is 
generated for the argument corresponding to parameter. 
3) #pragma CheckLibGen function Cexp 
The function generated by this pragma evaluates the expression Cexp before 
calling function. 
4) #pragma CheckLibReturn function Cexp 
Restrictions: Cexp must be of the same type as the return type of function. 
The value for the expression Cexp is returned as the value of the function 
call (function is not called). 
5) #pragma CheckLibBkptCond function Cexp ident 
Restrictions: Cexp must have integer type ident must be a parameter that 
has pointer type, a previously declared global variable (may be static), 
or the primitive RETVAL if function is a non-void function. 
This pragma differs from the other pragmas described above in that the 
pragmas introduced above specify restrictions on arguments into functions, 
whereas the pragma CheckLibBkptCond checks for errors after a function 
executes. The expression Cexp is evaluated after the call to function. If 
the expression evaluates to a non-zero value and an option referred to as 
"Break on library errors" has been enabled, then a non-fatal runtime error 
is generated for the function call. The identifier ident (or "return 
value" ifident is RE TVAL) and its value are included in the error 
message. If ident is a parameter, then the value of the dereference of the 
parameter is displayed. 
Special Cases: An asterisk .sup.* may be used in place of function to 
indicate that the pragma should apply to all function declarations in the 
file, except for those that have an explicit CheckLibBkptCond pragma. 
Also, if Cexp is 0, then ident is not required and no breakpoint checking 
is performed for the function. 
6) #pragma CheckLibRetPtrArg function parameter 
This pragma is intended for library functions that return pointers. This 
pragma specifies that the pointer information to be returned from the 
function is the pointer information for the argument corresponding to 
parameter. 
This is necessary because a library function cannot be compiled by the 
compiler of the present invention, and thus the protocol used for 
returning pointers between caller and callee functions cannot be used. 
Pragma Primitives 
The following primitives are available for use within the C expressions in 
pragma lines to aid in specifying restrictions on arguments to functions. 
In each case, par am refers to a parameter of pointer or array type. 
1. NELEMS(param) 
Returns the number of elements (unsigned int) in the array specified by 
param. If unknown, it returns INT.sub.-- MAX. If invalid array, then it 
returns 0. 
2. NBYTES(param) 
Returns the number of bytes (unsigned int) in the array specified by param. 
If unknown, it returns INT.sub.-- MAX. If invalid array, then it returns 
0. 
3. ELEMTYPE(parara) 
Returns an integer value that corresponds to the type of the object hat the 
pointer argument param points to. This is useful for determining what type 
of argument was passed to a parameter of type void .sup.*. The return 
value is one of the following enumeration constants as defined in 
"libprot.h". The constant .sub.-- UPLibOtherType is returned if the type 
is not known or does not correspond to any of the other enumeration 
constants. 
______________________________________ 
enum UPLibArgTypes { 
.sub.-- UPLibCharType, 
.sub.-- UPLibShortType, 
.sub.-- UPLibIntType, 
.sub.-- UPLibFloatType, 
.sub.-- UPLibDoubleType, 
.sub.-- UPLibOtherType 
}; 
______________________________________ 
4. CHKPTR(param) 
Performs basic checks on the pointer parameter. Returns non-zero if no 
errors were found. Reports a fatal runtime error (and does not return) 
otherwise. 
5. CHKSTR(param) 
Performs same checks as CHKPTR, but also verifies that the array is null 
terminated. Returns non-zero if no errors. Reports a fatal runtime error 
(and does not return) otherwise. 
6. CHKSIZE(param, size) 
Determines whether the array specified by param is large enough to hold 
size elements of the indicated type. A size of 0 is treated the same as if 
size were 1. A negative size is treated as the corresponding unsigned 
integer quantity. Returns non-zero if no errors were found; reports a 
fatal error (and does not return) otherwise. 
7-9. CHKPTRNULL(param), CHKSTRNULL(parara), CHKSIZENULL(param,size) 
These are exactly the same as CHKPTR, CHKSTR, and CHKSIZE, except they 
return non-zero and do not report an error if param was assigned NULL. 
NOTE: It is incorrect to replace CHKPTRNULL(param) with the C expression 
param==NULL CHKPTR(param). Although param may be NULL, it is possible that 
it was never initialized and that it has this value by chance. These 
primitives report an error if param was never initialized, even if it has 
the NULL value. 
10. BLKEND(param) 
Attempts to obtain the address of the element just past the end of the 
array specified by param. Reports a fatal runtime error (and does not 
return) if param is not a valid array. Returns 0 if unable to determine 
the address. Note: BLKEND(param) (CHKPTR(param), param+NELEMS(param)) 
11. RETVAL 
Valid only in CheckLibBkptCond pragmas. Contains the return value of the 
function for which the pragma applies. Its type is the return type of the 
function. It is not valid for void functions. 
Error Codes The following error codes defined in the file libprot.h are 
recognized by the CheckLibCond pragma. In addition, the function 
UPLibReportError (int errcode, int parampos, . . . ) can be used to report 
any of these errors directly. The first argument errcode must be one of 
the following errors. The second argument parampos indicates which 
argument to highlight when the error occurs and is the 1-based position of 
a parameter. If it is zero, then the entire function call expression is 
highlighted. Additional arguments may be passed to UPLibReportError if 
errcode expects them. 
______________________________________ 
Error Code Message Text 
______________________________________ 
UPLibGenError 
Illegal argument(s) to library function. 
UPLibArgNull 
Null pointer argument to library function. 
UPLibArgInvalid 
Invalid pointer argument to library function. 
UPLibArgInit 
Uninitialized pointer argument to library 
function. 
UPLibArgFree 
Pointer to free memory passed to library 
function. 
UPLibArgUnder 
Negative index into array argument to 
library function. 
UPLibArgOver 
Array too small as argument to library 
function. 
UPLibArgScalar 
Scalar argument to library function, 
expected array. 
UPLibArgArray 
Array argument to library function, 
expected scalar. 
UPLibArgNotString 
Missing terminating null in string argument. 
UPLibArgTooSmall 
Array argument too small. 
UPLibArgNotChar 
Argument must be character. 
______________________________________ 
Example Pragmas 
The following are examples of pragmas used to implement runtime checking 
operations according to the present invention. 
EXAMPLE 
______________________________________ 
int isalnum(int ch); 
#pragma CheckLibCond isalnum (ch &gt;= EOF && ch &lt;= 0xff) 
.sub.-- UPLibArgNotChar ch 
______________________________________ 
Function isalnum expects its argument to be a valid character (between 0 
and 255) or the special integer value EOF, which is -1. The CheckLibCond 
pragma indicates that the given condition must hold before the function is 
called. If the condition is false at runtime, then an error is reported 
corresponding to the constant UPLibArgNotChar and the argument 
corresponding to the parameter ch is highlighted in the source code. 
EXAMPLE 
______________________________________ 
int ValidatePanel(int panel, int *valid); 
#pragma CheckLibGen ValidatePanel 
CHKPTR(valid) 
______________________________________ 
The library function ValidatePanel expects a valid integer pointer as its 
second argument valid. The CheckLibGen pragma indicates that the 
expression CHKPTR(valid) should be evaluated before ValidatePanel is 
called. The libprot program converts the CHKPTR primitive into an 
appropriate call into an internal function that verifies that the second 
argument is a valid pointer. 
EXAMPLE 
______________________________________ 
int strcmp(const char *s1, 
const char *s2); 
#pragma CheckLibGen 
(CHKSTR(s1) && CHKSTR(s2)) 
LWPREFIX(strcmp) 
______________________________________ 
The string comparison function strcmp requires that both its arguments be 
NULL terminated strings. This requirement is specified by the CHKSTR(s1) 
CHKSTR(s2)) C expression. 
EXAMPLE 
______________________________________ 
int ResetTextBox(int panel, 
int control, char *text); 
#pragma CheckLibGen ResetTextBox 
CHKSTRNULL(text) 
______________________________________ 
The library function ResetTextBox takes a third argument text that must be 
either a NULL terminated string or NULL. This is easily checked with the 
CHKSTRNULL primitive. (The C expression text==0 .parallel.CHKSTR(text) is 
not equivalent to CHKSTRNULL(text. See information about primitives 
above.) 
EXAMPLE 
______________________________________ 
void *memset(void *s, int c, size.sub.-- t n); 
#pragma CheckLibGen memset CHKSIZE(s, n) 
#pragma CheckLibRetPtrArg memset s 
______________________________________ 
The library function memset requires that the first argument s point to an 
allocated block of memory that is at least n bytes in length. The first 
pragma specifies this requirement with the CHKSIZE primitive. The second 
pragma indicates that memset returns s as its return value and that the 
pointer information passed into the function for s should be returned with 
the return value. 
EXAMPLE 
______________________________________ 
FILE *tmpfile(void); 
#pragma CheckLibBkptCond tmpfile (RETVAL == 0) RETVAL 
______________________________________ 
When the function tmpfile fails for some reason, it returns 0. The 
CheckLibBkptCond pragma specifies the conditions under which the function 
fails and indicates what value to display in the error message. In the 
case of tmpfile this pragma indicates that a runtime error should be 
displayed including the return value of the function if the return value 
is 0. (Note: the present invention reports this type of runtime error only 
if the "Break on library errors" runtime option is enabled.) 
EXAMPLE 
______________________________________ 
double log(double x); 
#pragma CheckLibBkptCond log 
(errno == ERANGE .parallel. errno == EDOM) errno 
______________________________________ 
The logarithm function log fails and sets the global variable errno to 
ERANGE or EDOM if the input is negative or too large. This ChkLibBkptCond 
pragma specifies the conditions under which the log function fails and 
indicates that the value of errno should be displayed at runtime if it 
does fail. 
EXAMPLE 
______________________________________ 
int XGraphPopup(char *popupTitle, void *xArray, 
int numPoints, int xDType); 
int32 CalcArithArraySize(int32 lwtype, int32 numPoints); 
int32 VerifyType(int32 lwtype, int32 elemtype); 
#pragma CheckLibGen XGraphPopup 
CHKSTRNULL(popupTitle) 
#pragma CheckLibCond XGraphPopup VerifyType(xDType, 
ELEMTYPE(xArray)) .sub.-- UPLibArgTypeMismatch xArray 
#pragma CheckLibSize XGraphPopup xArray 
CalcArithArraySize(xDType, numPoints) 
______________________________________ 
The pragmas for the library function XGraphPopup demonstrate the ability to 
make complex runtime checks. The function takes a void pointer xArray that 
must point to an array with numPoints items. The size of each item is 
given by the parameter xDType which is an integer whose value corresponds 
to one of the predefined constants (VAL.sub.-- CHAR, VAL.sub.-- INTEGER, 
etc.) defined in the "userint.h" header file. 
The first of the pragmas above simply verifies that the argument popupTitle 
is either NULL or points to a valid NULL terminated string. The second 
pragma verifies that the type specified by parameter xDType corresponds to 
the actual type of the elements in array xArray. It assumes the existence 
of a function VerifyType that returns 0 if the type given by ELEMTYPE does 
not correspond to the type given by xArray. Finally, the last pragma 
verifies that the array xArray has at least numPoints elements. The 
function CalcArithArraySize computes the number of bytes required to hold 
numPoints elements of size given by xDType. 
The Libprot Compiler 
The libprot compiler is used for an input file referred to as in.sub.-- 
file by typing in the following command: 
EQU Usage: libprot&lt;in.sub.-- file&gt;-o&lt;out.sub.-- src.sub.-- file&gt;-h &lt;out.sub.-- 
hdr.sub.-- file&gt; 
The input file must be legal ANSI C source code. The macro .sub.-- 
LIBPROT.sub.-- is predefined by libprot for conditional compilation. The 
pragmas in the input file are used to generate runtime checking functions 
as described above. The output header file contains declarations of the 
generated functions. 
The file "libprot.h" in Appendix F includes declarations necessary for 
compiling the generated output file. 
How libprot Creates Source Code 
1. The Libprot compiler compiles the input source code, but does not 
generate any object code. As #pragmas are encountered they are parsed and 
all primitives are translated as described below. The resulting 
information is stored in the symbol table with the functions for which 
they were specified. 
______________________________________ 
Primitive Transformations: 
______________________________________ 
fun 
the function in whose pragma 
the primitive occurs 
POS(param) 
the parameter position of param 
NELEMS (param) 
=&gt;.sub.-- UPLibGetNumElems (param, POS(param), &fun) 
NBYTES (param) 
=&gt;.sub.-- UPLibGetNumBytes (param, POS(param), &fun) 
ELEMTYPE (param) 
=&gt;.sub.-- UPLibGetPtrArgType (param, POS(param), &fun) 
BLKEND (param) 
=&gt;.sub.-- UPLibGetBlkEnd (param, POS(param), &fun) 
CHKPTR (param) 
=&gt;.sub.-- UPLibChkSize (param, sizeof *param, 
1, 0, POS(param), &fun) 
CHKPTRNULL (param) 
=&gt;.sub.-- UPLibChkSize (param, sizeof *param, 
1, 1, POS(param), &fun) 
CHKSTR (param) 
=&gt;.sub.-- UPLibChkString (param, 0, POS(param), &fun) 
CHKSTRNULL (param) 
=&gt;.sub.-- UPLibChkString (param, 1, POS(param), &fun) 
CHKSIZE (param, size) 
=&gt;.sub.-- UPLibChkSize (param, sizeof *param, size, 
0, POS(param), &fun) 
CHKSIZENULL (param, size) 
=&gt;.sub.-- UPLibChkSize (param, sizeof *param, size, 
1, POS(param), &fun) 
RETVAL 
=&gt;.sub.-- UPLibRetVal 
______________________________________ 
2. After reading the entire input file, libprot goes through each function 
in the symbol table and checks whether there is any #pragma information 
associated with the function. If so, then libprot generates a function 
definition. If the function name is "fun" then the generated function has 
the name ".sub.-- UP.sub.-- fun" and has the same prototype as "fun". Each 
pragma for the function is then transformed into C code as described 
below. 
______________________________________ 
Pragma Transformations: 
______________________________________ 
fun 
the function in whose pragma 
the primitive occurs 
POS(param) 
the parameter position of param 
ReturnType 
the function return type (assuming not void) 
#pragma CheckLibSize fun param exp 
=&gt;.sub.-- UPLibChkSize (param, sizeof *param, exp, 0, 
POS(param), &fun) 
#pragma CheckLibCond fun exp errorcode param 
=&gt;if (!exp) 
.sub.-- UPLibReportError (errorcode, POS(param)); 
#pragma CheckLibGen fun exp 
=&gt;exp; 
#pragma CheckLibReturn fun exp 
=&gt;return exp; 
#pragma CheckLibBkptCond fun exp ident 
=&gt; ( 
ReturnType.sub.-- UPLibRetBal; 
.sub.-- UPLibRetVal = fun (param1, 
&lt;other parameters&gt;); 
if (exp) 
.sub.-- UPLibBreakpoint ("ident", ident, (void*)&fun); 
return .sub.-- UPLibRetVal; 
______________________________________ 
Compiler pragmas 
The compiler of the present invention uses the following pragmas to enable 
and disable runtime checking for library functions. If runtime checking is 
enabled for a function, then the runtime checking or "protected" version 
that was generated with the libprot program is used in place of the 
regular library function. These pragmas are ignored when compiling without 
debugging information, i.e., when the debugging level in a menu referred 
to as the Run Options menu is set to None. 
#pragma EnableLibraryRuntimeChecking 
#pragma DisableLibraryP-untimeChecking 
These two pragmas enable and disable library checking for all the function 
declarations that occur after the pragma within a header or source file. 
The pragmas affect only the functions declared in the file in which the 
pragmas occur (nested include files are not affected). 
#pragma EnableFunctionRuntimeCheckingfunction 
#pragma DisableFunctionRuntimeCheckingfunction 
These pragmas enable and disable library checking for a particular 
function. The function must be declared before the occurrence of the 
pragma. Protection for statically linked library functions cannot be 
disabled except by placing the DisableLibraryRuntimeChecking pragma in the 
library header file. 
Operation of the Present Invention 
As mentioned above, the present invention is implemented in a computer 
system to perform runtime checking on various operations within a program 
to aid the user in debugging. FIGS. 28-31 illustrate the computer's video 
screen showing programming errors and an error notification by the runtime 
checking method of the present invention. Each of FIGS. 28-31 include two 
illustrations A and B wherein FIG. A illustrates the initial error 
notification provided by the method of the present invention and FIG. B 
illustrates operation of the runtime checking method in highlighting the 
portion of the expression causing the error. FIGS. 28A-B illustrate an 
error of passing an uninitialized pointer to a library function, FIGS. 
29A-B illustrate an error indexing an array out-of-bounds, FIGS. 30A-B 
illustrate an error assigning a pointer to an out-of-bounds pointer, and 
FIGS. 31A-B illustrate an error of dereferencing an out-of-bounds pointer. 
It is noted that the error in FIG. 28 is a fatal error, and thus the 
user's only choice is to break out of the program. The errors in FIGS. 
29-31 are non-fatal errors, and thus the user is given the choice of 
breaking or continuing. 
Additional Runtime Checking Operations in an Alternate Embodiment 
1. Union members and pointers to union members 
In the preferred embodiment, runtime checking is not provided for unions or 
pointers to unions. However, in an alternate embodiment unions are treated 
like structures by allocating full pointer information for the union and 
all of its members. Whenever one union member is assigned a value, the 
pointer information for the other union members is updated accordingly. 
This method was not implemented in the preferred embodiment because of 1) 
the complexity involved in updating pointer information for all union 
members whenever any member is updated, 2) the possibly large amount of 
extra storage required to maintain information for all union members 
(unions are normally used when the user needs to save space) and 3) unions 
are rarely used in practice. 
2. Pointers to local objects of function that has already returned 
In the preferred embodiment runtime checking is not provided to catch 
errors where operations are performed on pointers that point to a local 
variable of a function that has already returned. However, in an alternate 
embodiment, the present invention maintains a list of pointers for each 
function call that contains all the pointers that point into an object 
that is local to the function call. When the function returns, all the 
pointers on the list are invalidated. For each pointer assignment 
(including pointers in arrays and structures), the pointer must be removed 
from the list it was previously in (if any) and added to the list for the 
function call to which the object belongs (if it is a local). This large 
amount of bookkeeping was deemed too expensive for the benefits provided, 
and thus this runtime checking function was not included in the preferred 
embodiment. 
In another embodiment, an extra field is added to the Pointer Info and 
Block Info data structures that contains a serial number of the function 
call to which the object pointed to belongs (if local). Each function call 
is assigned a unique serial number during execution, which is stored in 
the Block Info data structure for all objects local to the function. For 
each pointer assignment, the serial number of the object being assigned is 
stored with the Pointer Info data structure for the pointer. Whenever a 
pointer is referenced, the serial number in the Pointer Info data 
structure is compared to a list of serial numbers for the currently active 
functions. If the serial number is not in the list, then an error is 
reported. The large amount of extra storage required for this method was 
deemed too expensive for the benefits provided, and thus this runtime 
checking function was not included in the preferred embodiment. 
In yet another embodiment, when a pointer is referenced, the present 
invention ensures that the pointer points to an address that is within the 
current stack. This method was not implemented because: 1) it does not 
catch the more subtle errors, and 2) this type of access will likely cause 
a system access violation error, which the present invention handles 
through another mechanism. 
3. Pointers that are initialized to point within objects that have 
undefined size at compile time, e.g. 
extern int a[]; 
int *ptr=&a[3 ]; 
If the array `a` in the above example does not have at least four elements, 
then the initialization is an error. The preferred embodiment does not 
include a method for detecting this error. However, in an alternate 
embodiment, this error is detected. This error cannot be caught at compile 
time, but can be caught at link time using the following method. During 
compilation, the present invention according to this alternate embodiment 
maintains a list of pointers that were initialized to objects of undefined 
size. After resolving all global symbols during linking, the present 
invention examines the list and verifies that the objects into which the 
pointers point are large enough. The present invention then reports errors 
for those that are not. This method was not implemented in the preferred 
embodiment because this type of error rarely occurs. 
4. Comparison and subtraction of pointers to the same scalar object, e.g. 
&obj&gt;&obj. 
The preferred embodiment does not include a method for detecting this 
error. However, in an alternate embodiment, the present invention passes 
the values of the pointers for the comparison or subtraction to the 
.sub.-- PtrChkCompare or .sub.-- PtrChkSubtract functions. The present 
invention then uses the pointer values and the special values stored in 
the Info field for the pointers to determine if the comparison or 
subtraction is valid. This method was not implemented because of the extra 
overhead required to pass two more parameters to these functions and 
because this type of comparison or subtraction rarely occurs. 
5. Unspecified pointer parameters in variable argument functions. For 
example, in the following function, runtime checking for the local 
variable `ptr`: 
______________________________________ 
void f(int a, . . .) 
( 
va.sub.-- list ap; 
int *ptr; 
va.sub.-- start (ap, a); 
ptr = va.sub.-- arg (ap, int*); 
) 
______________________________________ 
In the preferred embodiment, no runtime checking is performed for these 
errors. However, in an alternate embodiment, the compiler recognizes the 
special macros `va.sub.-- start` and `va.sub.-- arg`, and inserts calls to 
special functions that are passed both the type information and the 
parameter number that is being accessed. These functions determine whether 
the argument passed to the function is compatible with the type that is 
being referenced and accesses the pointer information (if any) for the 
argument, which is used for the expression. Special care is taken to 
ensure that the pointer information is available for all of the arguments 
throughout the function. If another variable argument function is called 
from within a variable argument function, then information for the 
arguments are pushed onto a stack when it is called and popped off 
afterwards so that when the function returns information for the arguments 
to the current function is still available. This method was not 
implemented because of the complexity involved and because variable 
argument functions are rarely used. 
It is also noted that present invention is language specific and cannot be 
used on programs in other languages. Further, the present invention does 
not provide error checking for object code that was not compiled by the 
compiler of the present invention (except for certain library 
restrictions). Finally, because of the integrated nature of the present 
invention, both the linker and debugger of the present invention must be 
used when performing runtime checking of programs. 
Conclusion 
Therefore, a method and apparatus for providing runtime checking in a 
compiled programming development environment is disclosed. Runtime 
checking is supported for a full text-based programming language, such as 
the ANSI-C programming language. Runtime checking is also provided for 
arguments passed to library functions. 
Although the method and apparatus of the present invention has been 
described in connection with the preferred embodiment, it is not intended 
to be limited to the specific form set forth herein, but on the contrary, 
it is intended to cover such alternatives, modifications, and equivalents, 
as can be reasonably included within the spirit and scope of the invention 
as defined by the appended claims.