Chapter Contents |
Previous |
Next |
Systems Programming with the SAS/C Compiler |
When a C program is invoked, the C execution framework
is created by a start-up routine that is executed before the
main
function is called.
When
main
returns or when the
exit
function is called, the framework is destroyed.
The full C framework provides the support environment needed by the full
C library, including I/O, command-line parsing, signal handling, stack and
heap storage management, dynamic loading, and many other services. In ANSI
terminology, this is called a hosted environment.
In general, there are three methods that you can use to create an SPE framework:
exit
is called.
exit
or a special routine called L$UEXIT. See The L$UEXIT Routine for more information.
This method is
most appropriate for applications structured
as a package of subroutines without a main routine. The first subroutine
called creates the framework, which is thereafter shared between all subroutines.
Each function compiled using the
indep
option can be separately invoked and can return
control independently to the operating system (or whatever program called
it) without destroying the C framework.
The L$UMAIN Routine |
When L$UMAIN is entered, certain values must be in the general registers, as follows:
R1 | points to a word in which the address of the CRAB will be stored. If R1 is 0, then the CRAB address will not be stored. |
R13 |
points to a save area in which the
registers have already been saved using a
STM 14,12,12(13)
instruction. The
R15 slot of this save area (offset X'10') must address the initial function
to be executed under the C framework. The R1 slot (offset X'18') must contain
the value that is to be placed in R1 when the initial function is called.
This value will be used as the address of the initial function's parameter
list. The initial function can be either a C function or an assembler function
that uses the CENTRY and CEXIT macros. |
R14 | contains the return address. |
R15 | contains the address of L$UMAIN. |
L$UMAIN performs the following steps:
extern
and
static
variables.
extern
variable
_stack
. (Note that the
PRV is initialized before the stack is created, so the initial value of
_stack
will be available.)
The start-up routine then has the following options:
The L$UEXIT Routine |
Termination
of the C framework occurs when control returns to L$UMAIN. In most frameworks,
control returns to L$UMAIN when the initial function returns or when the
exit
function is called.
L$UMAIN performs the following steps to terminate the C framework:
atexit
cleanup routines.
L$UEXIT can be called from a non-C routine. The
exit
function calls L$UEXIT, but the
exit
function is designed to be called only from
a C function. Note that L$UEXIT can be used only in an INDEP framework.
The Standard Start-up Routines |
The general flow of control of these routines is shown in Standard Start-up Flow of Control. The start-up routines create the framework by calling L$UMAIN and destroy it by allowing control to return to L$UMAIN after the initial function returns.
Standard Start-up Flow of Control
Do the following to use the standard SPE start-up routine:
<osmain.h>
for OS/390
<oemain.h>
for USS
OS/390
<cmsmain.h>
for CMS
<cicsmain.h>
for CICS.
oemain
,
osmain
,
cmsmain
, or
cicsmn
. This function is
equivalent to the
main
function in the full C framework. An individual description
for each system's function is described in the next sections.
The
osmain
function should be declared as follows:
int osmain(int argc, void **argv);
argc
is the argument count, that is, the number of pointers in the
register 1 argument list.
argv
is a pointer to the unchanged list of arguments.
Note that none of the arguments are tokenized, and that, in contrast to the
hosted C environment, the first argument is
argv [0]
, not
argv [1]
.
The standard USS start-up routine L$UOEEP expects
to be entered with R1 addressing a parameter list in the format passed by
the USS
exec
system call. (See the IBM publication OS/390 UNIX System
Services Assembler Callable Services, for information on this parameter
list format.)
The
oemain
function should be declared as follows:
int oemain(int argc, char **argv);
argc
and
argv
have the same meanings as for a regular C
main
function:
argc
is an argument count,
and
argv
is a list of pointers to arguments as passed by the caller of
exec
.
The first argument
argv[0]
will contain a pointer to the program name,
assuming it was set properly by the caller of
exec
.
The
exec
system call passes L$UOEEP a list of environment
variables specified by its caller. These environment variables can be accessed
by the program using the standard
getenv
function.
The
cmsmain
function should be declared as follows:
int cmsmain(int ecode, char *plist, char **eplist, char *userinfo);
ecode
is the entry code from the high-order
byte of register 1 or from the save area extension in XA CMS.
plist
is the tokenized
PLIST address from register 1.
eplist
is the contents of register 0 on entry to
L$UCMSE, which, depending on the value of
ecode
, may or may not address an extended
PLIST.
userinfo
addresses the save area extension at 96 from R13 in XA CMS.
(In 370 mode CMS, this argument is not meaningful.)
Declare the function
cicsmn
as follows:
int cicsmn (int argc, void **argv);
argc
is set to 2.
argv
is a pointer to the unchanged
list of arguments. None of the arguments is tokenized; the first argument
is
argv[0]
, not
argv[1]
.
Using the indep Compiler Option with SPE |
The
indep
compiler option causes the compiler to generate code so that
C functions have the following special properties:
When a function compiled with the
indep
option is called,
it immediately transfers control to a routine named L$UPREP. If R13 addresses
a C DSA, L$UPREP takes no special action. If R13 does not address a DSA,
but the framework has been created, L$UPREP loads the CRAB address into R12
and returns to the function. If the framework does not yet exist, L$UPREP
calls L$UMAIN to create the framework and save the CRAB address where it can
be located on future calls. After L$UPREP has completed processing, the called
function resumes with the same value in R1 (and therefore the same arguments)
as when it was entered.
Because any function compiled with the
indep
option can cause
the framework to be created or restored, any such function can serve as a
program's initial entry point or as an entry point for subsequent calls.
The flow of control on the first call to the program is shown in Flow Control on the First Call to the INDEP Program.
Flow Control on the First Call to the INDEP Program
With one exception, L$UMAIN
executes as described in
Option 2 under The L$UMAIN Routine.
The exception is that when the SPE framework is created due to a call to
a function compiled with the
indep
option, the framework is not destroyed on return
from that function. This means that additional calls can be made to C functions
and that external variables, memory allocated with
malloc
, and so on, will
be available. To destroy the framework, you must call L$UEXIT, either directly
or indirectly with
exit
. If, however, the first C function is named
main
, then the C framework
is destroyed when
main
returns.
Flow of Control on Subsequent Calls shows the flow of control for a call when the framework already exists and when L$UEXIT is called to destroy the framework.
Flow of Control on Subsequent Calls
This method enables SPE programs to be invoked
without
a start-up routine. In general, the caller is responsible only for creating
a parameter list that can be used in a C function. If this is not possible,
L$UPREP can be modified to create a usable parameter list. (See Example 2: A CMS nucleus extension start-up routine.) L$UMAIN takes on the entire
responsibility for creating the framework.
Of course, frameworks can be created with both a start-up
routine and one or more entry points compiled with the
indep
option. Again, this
means that the framework can be retained across multiple calls to the program.
Flow of Control in First Call to an INDEP Program Using a Start-up Routine shows the flow of control for the first call to this type of program. Because the initial function was called from L$UMAIN, L$UPREP does nothing and returns control to the initial function. Control is not returned to L$UMAIN after the initial function returns, so the framework is not destroyed.
Flow of Control in First Call to an INDEP Program Using a Start-up Routine
The flow of control for a subsequent call to this program is shown in Flow of Control on Subsequent Calls.
Flow of Control on Subsequent Calls
The framework can be destroyed with L$UEXIT.
L$UPREP uses the L$UCENV macro to locate the CRAB address. Refer to L$UPREP: Framework Creation and Recovery for information about how the L$UCENV macro is used.
Writing Your Own Start-up Routine |
To reiterate, the start-up
routine calls L$UMAIN to
create the framework. The framework can be destroyed on return to the start-up
routine or left active until deletion by
exit
or L$UEXIT.
Example Start-up Routines |
L$USVCE solves the parameter and return value problems
in the following manner. When the main C program,
svcmain
, is called, it
receives a single argument (declared
void *regs [16]
) that defines a 64-byte
save area in which the SVC's input registers have been saved, in the order
0 through 15. Thus, the contents on register 1 on entry can be accessed as
regs [1]
. Return values are specified by modifying the contents of this array.
(Only some modifications will have any effect because the OS/390 SVC handler
always restores registers 2 through 14 itself.)
L$USVCE deliberately ignores any value returned by
svcmain
. This avoids assigning a random return code when
svcmain
returns without
specifying a return value.
L$USVCE does not allow the use of
exit
because the GETMAIN
technique is ineffective if
exit
is used. A technique could be devised that
would allow the use of
exit
, but it is easier to assume that SVC writers
are disciplined enough to avoid it.
L$USVCE implements this linkage and solves the other problems as well, in the following manner:
svcmain
function.
Some of these problems can be solved by using an initial
function compiled with the
indep
option, but the other requirements are best
solved by a specialized start-up routine and L$UPREP.
Then, it defines a second, overriding nucleus extension
with the same name, whose entry point is defined as the C function
nucxep
. This extension
is always defined with the SERVICE attribute so that it gets control when
the nucleus extension is dropped. The environment is not destroyed automatically
when this happens. Instead, the application is expected to detect the RESET
call itself (by testing for the RESET parameter) and call
exit
to destroy the framework.
To allow attributes in addition to SERVICE, attribute bits from the external
variable
_nucxopt
can be added to the NUCEXT argument list. (Note that the overriding
nucleus extension is defined only after the C framework has been created because
the values of externals are not available before this time.) The address of
the CRAB is stored in the user word of the SCBLOCK associated with the nucleus
extension.
L$UNUXP solves the problem of reaccessing the C framework
after it has been created by the first call. The second and subsequent calls
invoke
nucxep
directly, which, because it is compiled with the
indep
option, immediately
calls the L$UPREP entry point of L$UNUXP. L$UNUXP retrieves the CRAB address
from the user word of the SCBLOCK (addressed via register 2 on entry). Because
the framework must have already been created, if this value is 0, L$UNUXP
issues a diagnostic abend.
The problem of getting the R0 and R1 values to the first
C function is solved jointly by L$UNUXE and L$UNUXP. In each case, the value
of R1 on entry to
nucxep
is modified to address a parameter list containing the contents
of R0, the original contents of R1, and the address of the save area extension.
On the first call to
nucxep
, the call is made from L$UNUXE, and
nucxep
builds the new argument list. On subsequent calls,
nucxep
always calls L$UPREP
immediately. L$UNUXP modifies R1 to address the R0 slot of the previous save
area before returning to
nucxep
, stores the save area extension address in
the R2 slot, and updates the DSAPARMS field of the DSA accordingly.
The techniques of L$UNUXE and L$UNUXP are applicable
to a wide variety of situations in which the C framework must be retained
for a number of separate invocations and the use of unmodified
indep
is not adequate.
Chapter Contents |
Previous |
Next |
Top of Page |
Copyright © 2001 by SAS Institute Inc., Cary, NC, USA. All rights reserved.