Dynamic-Loading Functions

Introduction

Although most C programs are linked together as a single load module, for large applications it is often convenient to divide a program into several load modules that can be loaded into memory and unloaded independently. The SAS/C Compiler and Library support multiple load module programs, but you must develop such programs carefully.

As with any C program, program execution always begins with a function named main. The load module containing the main function must remain in memory at all times. Each subordinate (dynamically loaded) load module must contain a function named _dynamn (which is a contraction for dynamic main). From the perspective of the compiler, the _dynamn function serves the same role in the subordinate load module as main serves in the module containing the main program.

Cautions

Note that load modules linked with the SAS/C Library are of two distinct types: main modules and dynamically loadable modules. A main module is one which contains a main function, or which can cause the C environment to be created in conjunction with using the indep compiler option. A dynamically loadable module is one which contains a _dynamn function. It is not possible to create a load module which is of both types. Any attempt to do so will fail at link time or at execution time. Two consequences of this requirement are: This restriction is imposed so that dynamically loadable modules are not forced to include the large amount of code needed to create a new C framework.

Dynamic Load Modules

Load modules subordinate to the main program module can be loaded by use of the loadm and unloadm functions. The second argument to loadm is a pointer to a function pointer in which the address of the loaded _dynamn routine will be stored.

In addition to loadm and unloadm, the library provides functions to load and unload modules containing only data (loadd and unloadd ) and a function that converts a load module entry point address to a function pointer (buildm). Two other functions, addsrch and delsrch, are provided, primarily for CMS, to define the load module search order. The search order consists of the possible locations of dynamically loaded modules and the order in which they are processed. Before any of these routines can be used, the source program must include the header file <dynam.h> using the #include statement.

Transfers of control between load modules are possible only by using function pointers. However, through the use of appropriate pointers, a routine in one load module can call a routine in any other load module. The inability of one load module to call another directly is a special case of a more general restriction; namely, load modules cannot share external variables. More precisely, two external variables (or functions) of the same name in different load modules are independent of each other. (There are a few special cases such as the variable errno, which contains the number of the most recent run-time problem.) See Appendix 4, "Sharing extern Variables among Load Modules," in the SAS/C Compiler and Library User's Guide, Fourth Edition for additional information. An external variable or function can be accessed directly only by functions that have been linked in that module. All functions in other modules can gain access to them only by use of pointers.

The functions for dynamic loading, especially addsrch and delsrch, are highly operating-system-dependent. The definition of each dynamic-loading function highlights aspects of the function that depend in some way on the operating system. addsrch and delsrch , for example, are of interest to users working under MVS only when a program needs to be portable to CMS. For CMS, these functions are quite useful but are not needed typically in an MVS environment.

Function Descriptions

Descriptions of each dynamic-loading function follow. Each description includes a synopsis and description, discussions of return values and portability issues, and an example. Also, errors, cautions, diagnostics, implementation details, and usage notes are included if appropriate.

addsrch -- Indicate a "Location" from which Modules May Be Loaded

SYNOPSIS

 #include <dynam.h>

 SEARCH_P addsrch(int type, const char *loc,
                  const char *prefix);
 

DESCRIPTION

addsrch adds a "location" to the list of "locations" from which modules can be loaded. This list controls the search order for load modules loaded via a call to loadm. The search order can be described additionally by the third argument, prefix.

The first argument type must be a module type defined in <dynam.h>. The module type defines what type of module is loaded and varies from operating system to operating system. The character string specified by the second argument loc names the location. The format of this string depends on the module type. The third argument prefix is a character string of no more than eight characters.

addsrch is of interest primarily to CMS users and to MVS users writing programs portable to CMS. The remainder of this discussion, therefore, focuses on the use of addsrch under CMS.

CMS Argument Values

Under CMS, the defined module types for the first argument are the following:

The module type also controls the format of the second argument loc, which names the location to be searched by loadm. If the module type is

All location strings may have leading and trailing blanks. The characters are uppercased. addsrch does not verify the existence of the location.

The third argument is a character string of no more than eight characters. It may be "". If it is not null, then it specifies that the location indicated is searched only if the load module name (as specified by the first argument to loadm) begins with the same character or characters specified in the third argument.

At C program initialization, a default location, defined by the following call, is in effect:

 sp = addsrch(CMS_LDLB, "DYNAMC *","");
 

RETURN VALUE

addsrch returns a value that can be passed to delsrch to delete the input source. Under CMS, this specifically means a value of the defined type SEARCH_P, which can be passed to delsrch to remove the location from the search order. If an error occurs, a value of 0 is returned.

CAUTION

The above arguments to addsrch are defined only under CMS. The use of addsrch under MVS with a CMS module type has no effect.

USAGE NOTES

addsrch does not verify that a location exists (DYNAMC LOADLIB, for example) or that load modules may be loaded from that location. The loadm function searches in the location only if the load module cannot be loaded from a location higher in the search order. addsrch fails only if its parameters are ill-formed.

EXAMPLE

 #include <dynam.h>

 SEARCH_P mylib;
 .
 .
 .
    /* Search for modules in a CMS LOADLIB. */
 mylib = addsrch(CMS_LDLB, "PRIVATE *", "");
 

buildm -- Create a Function Pointer from an Address

SYNOPSIS

 #include <dynam.h>

 void buildm(const char *name, __remote /* type */  (**fpp)(),
             const char *ep);
 

DESCRIPTION

buildm converts the entry point address in ep to a __remote function pointer. The created function pointer can then be used to transfer control to a function or module located at this address. buildm is normally used to generate a function pointer for a C load module that has been loaded without the assistance of the SAS/C Library (for instance, by issuing the MVS LOAD SVC), but it can also be used with a non-C load module or with code generated by the program. buildm also assigns a load module name to the entry point address, and use of this name in subsequent calls to loadm or unloadm is recognized as referring to the address in ep. Note that a load module processed with buildm should always include a _dynamn function.

buildm stores the function pointer in the area addressed by fpp. Note that fpp may reference a function returning any valid type of data. If the function pointer cannot be created, a NULL value is stored.

name points to a name to be assigned to the built load module. If name is "", then a unique name is assigned by buildm. If the name is prefixed with an asterisk, then buildm does not check to see if the name is the name of a previously loaded module (see "ERRORS", below).

RETURN VALUE

buildm stores the function pointer in the area addressed by fpp. If an error occurs, buildm stores NULL in this area.

ERRORS

If the string addressed by name does not start with an asterisk and is the same as a previously built or dynamically loaded module, the request is rejected unless the value of ep is the same as the entry point of the existing load module. If the entry points are the same, a pointer to the previously loaded or built module is stored in the area addressed by func.

CAUTIONS

The name argument must point to a null-terminated string no more than eight characters long, not counting a leading asterisk. Leading and trailing blanks are not allowed.

The fpp argument must be a pointer to an object declared as "pointer to function returning (some C data type)".

EXAMPLE

This example illustrates a method of using inline machine code (see Chapter 13, "In-Line Machine Code Interface," in SAS/C Compiler and Library User's Guide, Fourth Edition ) to load a load module under MVS or a TEXT file or TXTLIB member under CMS using SVC 8. The machine code instruction sequence is coded as a macro to enhance readability.

The example assumes that SIMPLE is a C _dynamn function returning void.

 #include <svc.h>
 #include <code.h>
 #include <stdio.h>

 #define LOAD(n,ep) (_ldregs(R0+R1,n,0),_ossvc(8),
         _stregs(R0,ep))

 main()
 {

    void (*fp)();
    char *ep;

       /* The name "SIMPLE" must be uppercased, left-adjusted, */
       /* and padded to eight characters with blanks when      */
       /* used by SVC 8.                                       */
    LOAD("SIMPLE  ",&ep);

       /* The name passed to buildm does not have to match     */
       /* the name of the loaded module, but it helps.         */
    buildm("simple",&fp,ep);
    if (fp)         /* If no errors, call SIMPLE               */
       (*fp)();
    else
       puts("simple didn't load.");

 }
 

delsrch -- Delete a "Location" in the Load Module Search Order List

SYNOPSIS

 #include <dynam.h>
 void delsrch(SEARCH_P sp);
 

DESCRIPTION

delsrch removes the "location" sp pointed to by the argument from the load module search order list. sp is a value returned previously by addsrch.

PORTABILITY

delsrch is not portable. delsrch is used primarily in a CMS environment as a counterpart to addsrch or by MVS programs that can port to CMS.

EXAMPLE

The following example illustrates the use of delsrch under CMS:
 #include <dynam.h>

 SEARCH_P source;
 char *new_source;
 .
 .
 .
    /* Delete old search location.  */
 if (source) delsrch(source);
    /* Add new search location.     */
 source = addsrch(CMS_LDLB, new_source, "");
 

loadd -- Dynamically Load a Load Module Containing Data

SYNOPSIS

 #include <dynam.h>

 void loadd(const char *name, char **dp, MODULE *mp);
 

DESCRIPTION

loadd is similar to loadm (load executable module) except that it is intended for data modules. loadd loads the module named by the argument name and stores the address of the load module's entry point in the location pointed to by the second argument dp.

If the module has been loaded already, the pointer returned in the second argument points to the previously loaded copy. If the module name in the first argument string is prefixed with an asterisk, a private copy of the module is loaded.

The third argument addresses a location where a value is stored that can be used later to remove the module from memory via unloadd. loadd should be used only to load modules that contain data (for example, translation tables) rather than executable code.

RETURN VALUE

loadd indirectly returns a value that is stored in the location addressed by the third argument mp. This value can be used later to remove the module from memory via unloadd. If the module to be loaded cannot be found, 0 is returned.

ERRORS

Various user ABENDs, notably 1217 and 1218, may occur if overlays of library storage are detected while dynamic loading is in progress.

CAUTION

The first argument string may be no more than eight characters long, not counting a leading asterisk. Also note that a module coded with loadd must contain at least 16 bytes of data following the entry point, or library validation of the module may fail.

PORTABILITY

loadd is not portable. As with other dynamic-loading functions, be aware of system-specific requirements for the location of modules to be loaded.

IMPLEMENTATION

The implementation of loadd necessarily varies from operating system to operating system. Under MVS, modules to be loaded must reside in STEPLIB or the system link list. Under CMS, modules to be loaded may reside in DYNAMC LOADLIB or in other locations defined by use of the addsrch routine.

Under CICS, modules to be loaded must reside in a library in the DFHRPL concatenation, and must be defined to CICS.

EXAMPLE

The following example illustrates a general case of using loadd :
 #include <dynam.h>
 #include <lcstring.h>

 char *table;
 char *str;
 MODULE tabmod;
    /* Load a translate table named LC3270AE.                     */
 loadd("LC3270AE",&table,&tabmod);
 str = strxlt(str, table);
 unloadd(tabmod);                      /* Unload module after use.*/
 

loadm -- Dynamically Load a Load Module

SYNOPSIS

 #include <dynam.h>;

 void loadm(const char *name, __remote /* type */  (**fpp)());
 

DESCRIPTION

loadm loads an executable module named by the argument string name and stores a C function pointer in the location pointed to by the argument fpp. If the module has been loaded already, the pointer stored in fpp points to the previously loaded copy. If the module name in the first argument string is prefixed with an asterisk, a private copy of the module is loaded. Note that fpp may reference a function returning any valid type of data.

RETURN VALUE

loadm provides an indirect return value in the form of a function pointer that addresses the entry point of the loaded module. If the module is in C, calling the returned function always transfers control to the _dynamn function of the module.

If the module to be loaded cannot be found, a NULL is stored in the location addressed by fpp.

ERRORS

Various user ABENDs, notably 1217 and 1218, may occur if overlays of library storage are detected while dynamic loading is in progress.

CAUTIONS

The first argument string may be no more than eight characters long, not counting a leading asterisk.

The second argument must be a pointer to an object declared as "pointer to function returning (some C data type)."

Note that a module to be loaded by loadm cannot have the entry point defined in the last 16 bytes of the load module. The library inspects this portion of the loaded module, and may ABEND if 16 bytes of data are not present. This situation can arise only if the entry point is an assembler (or other non-C) routine.

PORTABILITY

loadm is not portable. Be aware of system dependencies involving where load modules may be located and how module names are specified for your operating system.

IMPLEMENTATION

The implementation of loadm necessarily varies from operating system to operating system. Under MVS, modules to be loaded must reside in STEPLIB, a task library, or the system link list. Under CMS, modules to be loaded may reside in DYNAMC LOADLIB or in other locations defined by use of the addsrch routine.

Under CICS, modules to be loaded must reside in a library in the DFHRPL concatenation and must be defined to CICS.

USAGE NOTES

addsrch does not verify the existence of a location, for example, DYNAMC LOADLIB. Because in some circumstances the logic of a program may not require that a location be searched, no verification is done until loadm cannot find a load module in any location defined earlier in the search order. addsrch fails only if its parameters are ill-formed.

If loadm determines that a location is inaccessible (for example, the LOADLIB does not exist), the location is marked unusable, and no attempt is made to search it again.

EXAMPLES

The use of loadm is illustrated by three examples. The first demonstrates the use of the command for a very simple situation without operating-system dependencies, while second and third examples are designed to run under MVS and CMS respectively.

The second example creates a dynamic load module that includes a table of pointers to functions which may be called dynamically from the calling load module. This example runs under MVS and has been designed to provide a framework that can be expanded upon in complete application.

The third example presents a hypothetical situation under CMS in which

  1. the load module is created
  2. the load module's location is added to the list of locations from which modules can be loaded (addsrch )
  3. the load module is loaded (loadm )
  4. the load module is deleted from the search order list (delsrch ).

Example 1.1 simple case

 #include <dynam.h>

 int (*fp)();
    /* Load a load module named "ADD" and call it. */
 loadm("ADD",&fp);
 sum = (*fp)(1, 3);
 .
 .
 .
 

Example 1.2 dynamic loading modules with multiple functions

Example 1.2 illustrates techniques for managing load modules containing multiple functions. This example includes illustrative MVS JCL, but the techniques illustrated in the example are also applicable to CMS.

STEP I. Put the following declarations in a common header file and name it DYNTABLE:

 struct funcdef {           /* structure definition for functions     */
    int (*func1)();
    int (*func2)();
                             /* More functions can go here.           */
 };

 typedef struct funcdef *fptrtable;  /* pointer to list of funcdefs   */
 
Make sure the header library containing DYNTABLE is allocated so that it will be included when you compile the following source code.

STEP II. Create the following C source file and name it DYNAMIC. This file will be compiled and linked to create a dynamic load module. The _dynamn function returns to its caller a structure of function pointers that can be used to call the individual functions of the load module.

 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <lcio.h>
 #include <dynam.h>
 #include "dyntable.h"

    /* _dynamn function that returns a list of function pointers which */
    /* can be used to invoke other functions in the load module        */
 fptrtable _dynamn(void) {
       /* Initialize function pointer table.                           */
    static struct funcdef dyntab = {&func1, &func2 /* ... */ };
       /* Return pointer to table with the new contents.               */
    return(&dyntab);
 }

 int func1() {
   printf("func1() was successfully dynamically called!!!\n");
   return(0);
 }

 int func2() {
   printf("func2() was successfully dynamically called!!!\n");
   return(0);
 }
 
DYNAMIC must be compiled using the sname compiler option to override the default assignment of _dynamn as the section name. It may be compiled as re-entrant using the rent compiler option. (Note that JCL changes are required if norent compilation is needed.) During linking, the ENTRY=DYN (or DYNNK for norent) parameter must be specified.

STEP III. Create the following C source file and name it DYNMLOAD. This code demonstrates how to dynamically load the DYNAMIC load module and how to call the functions pointed to by the dyntab table:

 #include <stdio.h>
 #include <string.h>
 #include <dynam.h>
 #include "dyntable.h"

 fptrtable(*fpdyn)();  /* _dynamn function pointer prototype       */

 int rc1, rc2;         /* return codes from calling func_1         */
                       /* and func_2                               */

 fptrtable table;      /* table of function pointer and names      */
                       /* returned from _dynamn                    */
 main()
 {
       /* Load DYNAMIC.                                            */
    loadm("DYNAMIC", &fpdyn);

       /* Call _dynamn and return table of function pointers and   */
       /* name of load module.                                     */
    table = (*fpdyn)();

       /* Call func1 using function pointer from table.            */
    rc1 = (*table->func1)();
    printf("Dynamically called func1() with rc = %d \n", rc1);

       /* Call func2 using function pointer from table.            */
    rc2 = (*table->func2)();
    printf("Dynamically called func2() with rc = %d \n", rc2);

    unloadm(fpdyn);
    return;
 }
 
STEP IV. Modify the following JCL, which is used to compile and link the DYNAMIC and DYNMLOAD source files and then execute the resulting load module. (Note that site-dependent job statements should be added.)
 //*
 //*   COMPILE and LINK module that uses a _dynamn routine that passes
 //*   its caller back a list of function pointers which are used to
 //*   invoke other functions in the load module
 //*
 //COMPDYNM EXEC LC370CL,ENTRY=DYN,PARM.C='RENT SNAME(DYNAM)'
 //C.SYSLIN   DD DSN= userid .SASC.OBJ(DYNAMIC),DISP=OLD
 //C.SYSIN    DD DSN= userid .SASC.SOURCE(DYNAMIC),DISP=SHR
 //*
 //LKED.SYSLMOD  DD DSN= userid .SASC.LOAD(DYNAMIC),DISP=OLD
 //*
 //*
 //*   COMPILE and LINK module that dynamically loads the DYNAMIC
 //*   load module and calls functions within the load module
 //*   using the structure of function pointers returned.
 //*
 //CLEMAIN EXEC LC370CLG
 //C.SYSLIN   DD DSN= userid .SASC.OBJ(DYNMLOAD),DISP=OLD
 //C.SYSIN    DD DSN= userid .SASC.SOURCE(DYNMLOAD),DISP=SHR
 //*
 //LKED.SYSLMOD  DD DSN= userid .SASC.LOAD(DYNMLOAD),DISP=OLD
 //
 

Example 1.3 dynamic loading under CMS

A C source file, NEPTUNE C (listed below), is to be dynamically loaded and executed. The file contains the function neptune.
 #include <stdio.h>

 void neptune(char *p) {
    puts(p);
    return;
 }
 
STEP I. Make the function neptune into a separate, loadable module by link-editing the TEXT file into a CMS LOADLIB file. The following steps are required:
  1. Rename the function to _dynamn.
     #include <stdio.h>
    
     void _dynamn(char *p) {
        puts(p);
        return;
     }
     
    All C load modules (except the one that includes main, of course) must define one function named _dynamn.
  2. Recompile NEPTUNE C, using the sname compiler option to override the default assignment of _dynamn as the sname. See Chapter 7, "Compiler Options," in the SAS/C Compiler and Library User's Guide, Fourth Edition for more information about the sname compiler option.
  3. Link-edit the resulting NEPTUNE TEXT file using the CMS command LKED. The LIBE and NAME options of the LKED command can be used to specify the name of the output LOADLIB file and member name, respectively. For example,
     LKED NEPTUNE (LIBE DYNAMC NAME NEPTUNE
     
The LKED command creates the file DYNAMC LOADLIB, containing the member NEPTUNE (assuming the LOADLIB did not already exist).

STEP II. The function neptune now exists in a form loadable by loadm. Invoke loadm to load the module as follows:

 #include <dynam.h>
 main()
 {
    int (*fp)();               /* Declare a function pointer */
    loadm("NEPTUNE",&fp);      /* Load NEPTUNE           */
       if (fp) {               /* Check for errors           */
          (*fp)("Hello, Neptune, king of the C!");
          unloadm(fp);         /* Delete NEPTUNE             */
       }
       else
          puts("NEPTUNE failed to load.");
     exit(0);
 }
 
STEP III. The previous step used the default search location DYNAMC LOADLIB (see addsrch ); thus, no call to addsrch is required. If you use some other filename for the LOADLIB, specify it in a call to addsrch before invoking loadm. The following is an example:
 #include <dynam.h>

 main()
 {
    SEARCH_P sp;                    /* Declare a SEARCH_P value  */
    int (*fp)();
       /* specify "NEWLIB LOADLIB *" */
    sp = addsrch(CMS_LDLB,"NEWLIB *","");
    loadm("NEPTUNE",&fp);
    if (fp) {
       (*fp)("Hello, Neptune, king of the C!");
       unloadm(fp);
    }
    else
       puts("NEPTUNE failed to load.");
    delsrch(sp);  /* remove NEWLIB LOADLIB from the search order */
    exit(0);
 }
 
Since the NEPTUNE load module is relocatable, it can be loaded as a CMS nucleus extension by the NUCXLOAD command. Then the following calls cause NEPTUNE to be accessed from the nucleus extension:
 sp = addsrch(CMS_NUCX,"","");
 loadm("NEPTUNE",&fp);
 
The function must exist as a nucleus extension before invoking loadm. This facility is useful, for example, in testing a single load module that you plan to replace in an existing LOADLIB.

unloadd -- Discard a Previously Loaded Data Module

SYNOPSIS

 #include <dynam.h>;

 void unloadd(MODULE mp);
 

DESCRIPTION

unloadd unloads the data module identified by the argument mp. If the module is no longer in use, it deletes the module from memory.

RETURN VALUE

None

ERRORS

If the argument to unloadd is invalid, a user 1211 ABEND is issued. Various other ABENDs, such as 1215 or 1216, may occur during unloadd if library areas used by dynamic loading have been overlaid.

CAUTION

If an attempt is made to use data in the unloaded module, the results are undefined but probably disastrous.

EXAMPLE

See loadd.

unloadm -- Discard a Previously Loaded Module

SYNOPSIS

 #include <dynam.h>

 void unloadm(__remote /* type */  (*fp)());
 

DESCRIPTION

unloadm unloads the executable module containing the function addressed by the argument fp. If the module is no longer in use, unloadm deletes it from memory. Note that fp may reference a function ruturning any valid type of data.

unloadm may be used to unload a module that has been built by buildm, but unloadm will not delete the module from memory.

RETURN VALUE

None

ERRORS

If the argument to unloadm is invalid, a user 1211 ABEND is issued. Various other ABENDs, such as 1215 or 1216, may occur during unloadm if library areas used by dynamic loading have been overlaid.

CAUTION

If an attempt is made to call a function in the unloaded module, the results are undefined but probably disastrous.

EXAMPLE

The following example, which is not system-specific, illustrates the general use of unloadm :
 #include <dynam.h>

 int (*fp)();
    /* Load a load module named "IEFBR14", */
    /* call it, and unload it.             */
 loadm("iefbr14",&fp);
 (*fp)();
 unloadm(fp);
 

Copyright (c) 1998 SAS Institute Inc. Cary, NC, USA. All rights reserved.