Chapter Contents

Previous

Next
Systems Programming with the SAS/C Compiler

The SPE Framework: Creating and Terminating

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 SPE, the framework is created by a start-up routine that can be modified to allow a framework to be created in any number of ways. The framework itself can be tailored to support whatever services are needed by the program. In ANSI terms, this is called a freestanding environment.

In general, there are three methods that you can use to create an SPE framework:

Of course, all three methods can be combined. The needs of the application may be such that the standard start-up routines can be slightly modified to provide for them. You may want to write your own start-up routine that creates an INDEP framework. SPE is designed to be flexible.

No matter which method is chosen, most of the details of creating and destroying frameworks are handled by a routine called L$UMAIN. In this section, L$UMAIN is described first because it is used in all of the methods. After this discussion, each of the three methods of creating an SPE framework is discussed individually.


The L$UMAIN Routine

The compiler generates code that depends on the following conventions. L$UMAIN ensures that these conventions are met when it calls the initial functions.
R1 addresses the function's parameter list.
R12 addresses the CRAB.
R13 addresses a DSA.
R14 contains the function's return address.
R15 contains the function's entry point.
CRABPRV addresses the pseudoregister vector.

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:

  1. allocates storage for the CRAB and initializes it.

  2. saves the R13 value on entry in the CRABPENV field of the CRAB.

  3. allocates storage for the pseudoregister vector.

  4. calls the L$CPRSU routine to initialize the pseudoregister vector with initial values for extern and static variables.

  5. allocates the C stack. The stack size can be specified as the initial value of the extern variable _stack . (Note that the PRV is initialized before the stack is created, so the initial value of _stack will be available.)

  6. points R13 to the first save area on the stack and chains it to the caller's save area. L$UMAIN also saves this address in the CRABMDSA field in the CRAB.

  7. calls the function whose address was in the R15 slot of the save area on entry.

Note that you can modify L$UMAIN to add, modify, or remove steps as required by the application. Creation of a CRAB and a stack is always required.

L$UMAIN calls the initial function using a BALR 5,15 instruction. This places the return address for L$UMAIN in R5. The return address for L$UMAIN's caller (the start-up routine) remains in R14. This enables the first C function to return directly to the start-up routine.

The start-up routine then has the following options:
Option 1 Branch to the address in R5, thereby returning to L$UMAIN. This path enables L$UMAIN to terminate the C framework and return to the start-up routine's caller.
Option 2 Load R13 from the chain field of L$UMAIN's save area, restore registers, and return to the address in R14. This path bypasses C framework termination. The save area for L$UMAIN remains allocated, and the C framework continues to exist. You can destroy the framework later by calling exit or L$UEXIT.


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.

If a program is entered via an INDEP function, L$UEXIT can be called as a function to terminate the framework. L$UEXIT forces a return to L$UMAIN by loading R13 from the CRABMDSA field (thus addressing L$UMAIN's save area), reloading L$UMAIN's registers from this save area, and branching to the address in R5.

L$UMAIN performs the following steps to terminate the C framework:

  1. calls any defined atexit cleanup routines.

  2. loads R13 from the CRABPENV field. Normally, this addresses the save area belonging to the caller of the start-up routine.

  3. frees any memory allocated for library control blocks, including heap and stack storage, the pseudoregister vector, and the CRAB.

  4. restores registers and returns. Note that control returns to the caller of the start-up routine and not to the start-up routine itself.

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 standard start-up routines are provided as generic examples. For programs running under OS/390, the standard start-up routine is named L$UOSEP. For CMS programs, the standard start-up routine is named L$UCMSE. For USS, the standard start-up routine is L$UOEEP. The standard start-up routine for CICS is named L$UCICE. These routines are written to provide a minimal environment with linkage and parameters that are appropriate for the operating system.

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

[IMAGE]

Do the following to use the standard SPE start-up routine:


The standard OS/390 start-up routine

The standard OS/390 start-up routine L$UOSEP expects to be entered with R1 addressing a standard OS VL-format parameter list containing the addresses of one or more parameters, with the last address indicated by the presence of the VL bit. L$UOSEP also accepts a 0 in R1, indicating that there are no parameters.

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 OS/390 start-up routine

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 standard CMS start-up routine

The standard CMS start-up routine L$UCMSE expects to be entered with R1 and R0 set up by SVC 202 as described in VM/SP CMS for System Programming, or by SVC 204 as described in the VM/XA SP CMS Application Development Guide for CMS. In 370 mode CMS, R1 contains a code in the high-order byte and a pointer to a tokenized PLIST in the 3 low-order bytes, and R0 may contain a pointer to an extended PLIST. In XA CMS, R1 addresses a tokenized PLIST, R0 may contain a pointer to an extended PLIST, and additional information is stored at offset 96 from R13.

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.)

The standard CICS start-up routine

The standard CICS start-up routine L$UCICE should be entered with R1 addressing the standard CICS parameter list that contains the address of the EXEC interface block and the address of any COMMAREA, or the value X'FF000000', if no COMMAREA exists.

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 indep compiler option can be used together with the armode compiler option. The result is a function that can be called before a C framework has been created and that runs in access register mode. Note that such a function must still be entered in primary address space mode. Once it completes initialization, the generated code will zero all the access registers and switch into access register mode. It is possible that an implementation of L$UPREP could support calls to an indep C function in access register mode if it was capable of saving the access registers and returning to primary address space mode before creating or restoring a SAS/C framework.

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

[IMAGE]

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

[IMAGE]

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.

Writing a start-up routine with the indep option

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

[IMAGE]

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

[IMAGE]

The framework can be destroyed with L$UEXIT.

If you use a start-up routine, you need to be sure to store the CRAB address in a location where L$UPREP can locate it on future calls. For example, the CRAB address can be stored in the user word of an operating system control block associated with the program. When entered, L$UMAIN expects the address of this location to be in R1. If, instead, the value in R1 is 0, then L$UMAIN does not store the CRAB address. In this case, the CRAB address can be recovered from R12 by the start-up routine after L$UMAIN is complete and then stored at the appropriate location.

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

Writing your own start-up routine to initialize the C framework may be desirable for any of several reasons, including the following:

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

The following example start-up routines illustrate several uses for SPE and show a number of techniques for using SPE efficiently.

Example 1: An OS/390 SVC start-up routine

L$USVCE is a start-up routine that creates the C framework for an OS/390 type 3 or type 4 SVC written in the C language. To execute C code in this environment, the following problems must be solved:

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:

  1. L$USVCE saves registers temporarily in the RB extended save area in order to issue a GETMAIN for two save areas. The memory is obtained from a key 0 subpool to avoid integrity problems.

  2. L$USVCE points register 13 to the area created by GETMAIN and copies all saved registers to the second save area. It then calls L$UMAIN, specifying that the first C routine to call is #SVCMAIN and that R1 addresses the second save area created by GETMAIN.

  3. L$USVCE contains #SVCMAIN, a static routine which is defined to use the CENTRY and CEXIT macros. It points R1 to a word containing the address of the register save area and calls the user's svcmain function.

  4. When #SVCMAIN returns, it bypasses L$UMAIN, returning directly to L$USVCE. L$USVCE stores the address of the label SVCEXIT in the R14 slot of L$UMAIN's save area. Then, L$USVCE branches to R5 to return to L$UMAIN for destruction of the framework. After the framework has been destroyed, L$UMAIN returns to SVCEXIT rather than to L$USVCE's caller.

  5. After L$UMAIN returns to the label SVCEXIT, L$USVCE copies registers possibly modified by the program out of their save area and into the RB. It then frees its save areas, reloads the necessary registers, and returns.

The techniques used by L$USVCE are applicable to a wide variety of situations in which a C program must be called with nonstandard linkage conventions.

Example 2: A CMS nucleus extension start-up routine

L$UNUXE is a start-up routine that creates the C framework for a CMS nucleus extension written in C. L$UNUXP is a specialized version of L$UPREP for the same environment. To execute C code in this environment, the following problems must be solved:

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.

L$UNUXE solves the problems of defining the necessary nucleus extension attributes as follows. First, it presumes that the MODULE (that is, its own code) has already been defined as a nucleus extension via the CMS NUCXLOAD command.

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.