Document revision date: 30 March 2001

## OpenVMS Programming Concepts Manual

Previous | Contents | Index

## 33.3 Declaring Initialization Routines

Any library or user program module can declare an initialization routine. This routine is called when the image is started. The declaration is made by making a contribution to the LIB$INITIALIZE program section, which contains a list of routine entry point addresses to be called before the main program or main routine is called.

The following VAX MACRO example declares an initialization routine by placing the routine entry address INIT_PROC in the list:

    .EXTRN LIB$INITIALIZE         ; Cause library initialization
                                  ; Dispatcher to be loaded

    .PSECT LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG

    .LONG INIT_PROC               ; Contribute entry point address of
                                  ; initialization routine.
    .PSECT ...

The .EXTRN declaration links the initialization routine dispatcher, LIB$INITIALIZE, into your program's image. The reference contains a definition of the special global symbol LIB$INITIALIZE, which is the routine entry point address of the dispatcher. The linker stores the value of this special global symbol in the initialization list along with the starting address of the debugger and the main program. The GBL specification ensures that the PSECT LIB$INITIALIZE contribution is not affected by any clustering performed by the linker.

Note that moving modules and PSECTS around to affect symbol resolution may result in unintended memory placement within your image. If, for example, you add a CLUSTER statement to your linker options file, the initialization code may not run because the CLUSTER statement in the linker option file may cause the various LIB$INITIALIZE PSECTS to become separated. To remedy this possible condition, either add to your options file a CLUSTER or COLLECT statement like the following:

      CLUSTER = <cluster name>,,,<module>, SYS$LIBRARY:STARLET.OLB/include = LIB$INITIALIZE

      COLLECT = <cluster name>, LIB$INITIALIZDZ, LIB$INITIALIZD_, LIB$INITIALIZE,LIB$INITIALIZE$

## 33.4 Dispatching to Initialization Routines

The LIB$INITIALIZE dispatcher calls each initialization routine in the list with the following argument list:

`CALL init-proc (init-coroutine ,cli-coroutine [, image-info])`

The init-coroutine argument is the address of a library coroutine to be called to effect a coroutine linkage with LIB$INITIALIZE.

The cli-coroutine is the address of a CLI coroutine used to obtain command arguments.

The image-info argument is useful image information, such as the program name.

## 33.5 Initialization Routine Options

An initialization routine can be used to do the following:

- Set up an exit handler by calling the Declare Exit Handler ($DCLEXH) system service, although exit handlers are generally set up by using a statically allocated first-time flag.
- Initialize statically allocated storage, although this is done preferably at image activation time using compile-time and link-time data initialization declarations or by using a first-time call flag in its statically allocated storage.
- Call the initialization dispatcher (instead of returning to it) by calling the init-coroutine argument. This achieves a coroutine link. Control returns to the initialization routine when the main program returns control. Then the initialization routine should also return control to pass back the completion code returned by the main program (to the debugger or CLI, or both).
- Establish a condition handler in the current frame before performing the preceding actions. This leaves the initialization routine condition handler on the image stack for the duration of the image execution. This occurs after the CLI sets up the catchall stack frame handler and after the debugger sets up its stack frame handler. Thus, the initialization routine handler can override either of these handlers, because it will receive signals before they do.

## 33.6 Initialization Example

The following VAX MACRO code fragment, which works on both VAX and Alpha systems, shows how an initialization routine does the following:

- Establishes a handler
- Calls the init-coroutine argument routine, so that the coroutine calls the initialization dispatcher
- Gains control after the main program returns
- Returns to the normal exit processing

          .ENTRY INIT_PROC, ^M<>     ; No registers used
          MOVAL HANDLER, (FP)        ; Establish handler
          ...                        ; Perform any other initialization

          CALLG (AP), @INIT_CO_ROUTINE(AP)
                                     ; Continue initialization which
    10$:                             ; then calls main program or
                                     ; routine.
         ...                         ; Return here when main program
                                     ; returns with R0 = completion
         RET                         ; Status return to normal exit
                                     ; processing with R0 = completion
                                     ; status

         .ENTRY HANDLER, ^M<...>     ; Register mask
         ...                         ; handle condition
                                     ; could unwind to 10$
         MOVL #..., R0               ; Set completion status with a
                                     ; condition value
         RET                         ; Resignal or continue depending
                                     ; on R0 being SS$_RESIGNAL or
                                     ; SS$_CONTINUE.

## Part 6
Appendices: Macros and Examples of 64-Bit Programming

These appendices describe the following information:

- Macros used in 64-bit programming
- Examples of 64-bit programming
- Generic macros used for calling system services
- OpenVMS data types
- Distributed name services on OpenVMS VAX

## Appendix A
C Macros for 64-Bit Addressing

This appendix describes the following C macros for manipulating 64-bit addresses, for checking the sign extension of the low 32 bits of 64-bit values, and for checking descriptors for the 64-bit format:

- $DESCRIPTOR64
- $is_desc64
- $is_32bits

## DESCRIPTOR64

> Constructs a 64-bit string descriptor.

### Format

> $DESCRIPTOR64 name, string

### Description

> > name is name of variable
> > string is address of string

> Example:

>             int status;
>             $DESCRIPTOR64 (gblsec, "GBLSEC_NAME");

>             ...

>             /* Create global page file section */
>             status = sys$create_gpfile (&gblsec, 0, 0, section_size, 0, 0);

>             ...

> This macro resides in descrip.h in SYS$LIBRARY:DECC$RTLDEF.TLB.

## $is_desc64

> Distinguishes a 64-bit descriptor.

### Format

> $is_desc64 desc

### Description

> > desc is address of 32-bit or 64-bit descriptor

> Returns:

> > 0 if descriptor is 32-bit descriptor
> > 1 if descriptor is 64-bit descriptor

> Example:

>     #include <descrip.h>
>     #include <far_pointers.h>
>     ...
>             if ($is_desc64 (user_desc))
>             {
>                     /* Get 64-bit address and 64-bit length from descriptor */
>                     ...
>             }
>             else
>             {
>                    /* Get 32-bit address and 16-bit length from descriptor */
>                    ...
>             }

> This macro resides in descrip.h in SYS$LIBRARY:DECC$RTLDEF.TLB.

## $is_32bits

> Tests if a quadword is 32-bit sign-extended.

### Format

> $is_32bits arg

### Description

> Input: arg is 64-bit value

> Output:

> > 1 if arg is 32-bit sign-extended
> > 0 if arg is not 32-bit sign-extended

> Example:

>             #include <starlet_bigpage.h>
>             ...
>             if ($is_32bits(user_va))
>                     counter_32++;   /* Count number of 32-bit references */
>             else
>                     counter_64++;  /* Count number of 64-bit references */

> This macro resides in starlet_bigpage.h in SYS$LIBRARY:SYS$STARLET_C.TLB.

## Appendix B
64-Bit Example Program

This example program demonstrates the 64-bit region creation and deletion system services. It uses SYS$CREATE_REGION_64 to create a region and then uses SYS$EXPREG_64 to allocate virtual addresses within that region. The virtual address space and the region are deleted by calling SYS$DELETE_REGION_64.

    /*
        This program creates a region in P2 space using the region creation
        service and then creates VAs within that region.  The intent is to
        demonstrate the use of the region services and how to allocate virtual
        addresses within a region.  The program also makes use of 64-bit
        descriptors and uses them to format return values into messages with the
        aid of SYS$GETMSG.

        To build and run this program type:

        $ CC/POINTER_SIZE=32/STANDARD=RELAXED/DEFINE=(__NEW_STARLET=1) -
            REGIONS.C
        $ LINK REGIONS.OBJ
        $ RUN REGIONS.EXE
    */

    #include    <descrip.h>             /* Descriptor Definitions               */
    #include    <far_pointers.h>        /* Long Pointer Definitions             */
    #include    <gen64def.h>            /* Generic 64-bit Data Type Definition  */
    #include    <iledef.h>              /* Item List Entry Definitions          */
    #include    <ints.h>                /* Various Integer Typedefs             */
    #include    <iosbdef.h>             /* I/O Status Block Definition          */
    #include    <psldef.h>              /* PSL$ Constants                       */
    #include    <ssdef.h>               /* SS$_ Message Codes                   */
    #include    <starlet.h>             /* System Service Prototypes            */
    #include    <stdio.h>               /* printf                               */
    #include    <stdlib.h>              /* malloc, free                         */
    #include    <string.h>              /* memset                               */
    #include    <syidef.h>              /* $GETSYI Item Code Definitions        */
    #include    <vadef.h>               /* VA Creation Flags and Constants      */

    /*  Module-wide constants and macros.                                       */

    #define     BUFFER_SIZE         132
    #define     HW_NAME_LENGTH       32
    #define     PAGELET_SIZE        512
    #define     REGION_SIZE         128

    #define     good_status(code)   ((code) & 1)

    /*  Module-wide Variables                                                   */

    int
        page_size;

    $DESCRIPTOR64 (msgdsc, "");

    /*  Function Prototypes                                                    */

    int get_page_size (void);
    static void print_message (int code, char *string);

    main (int argc, char **argv)
    {
        int
            i,
            status;

        uint64
            length_64,
            master_length_64,
            return_length_64;

        GENERIC_64
            region_id_64;

        VOID_PQ
            master_va_64,
            return_va_64;

    /*  Get system page size, using SYS$GETSYI.                                 */

        status = get_page_size ();
        if (!good_status (status))
            return (status);

    /*  Get a buffer for the message descriptor.                                */

        msgdsc.dsc64$pq_pointer = malloc (BUFFER_SIZE);
        printf ("Message Buffer Address = %016LX\n\n", msgdsc.dsc64$pq_pointer);

    /*  Create a region in P2 space.                                            */

        length_64 = REGION_SIZE*page_size;
        status = sys$create_region_64 (
            length_64,                  /* Size of Region to Create             */
            VA$C_REGION_UCREATE_UOWN,   /* Protection on Region                 */
            0,                          /* Allocate in Region to Higher VAs     */
            &region_id_64,              /* Region ID                            */
            &master_va_64,              /* Starting VA in Region Created        */
            &master_length_64);         /* Size of Region Created               */
        if (!good_status (status))
        {
            print_message (status, "SYS$CREATE_REGION_64");
            return (status);
        }

        printf ("\nSYS$CREATE_REGION_64 Created this Region:  %016LX - %016LX\n",
            master_va_64,
            (uint64) master_va_64 + master_length_64 - 1);

    /*  Create virtual address space within the region.                         */

        for (i = 0; i < 3; ++i)
        {
            status = sys$expreg_64 (
                &region_id_64,      /* Region to Create VAs In                  */
                page_size,          /* Number of Bytes to Create                */
                PSL$C_USER,         /* Access Mode                              */
                0,                  /* Creation Flags                           */
                &return_va_64,      /* Starting VA in Range Created             */
                &return_length_64); /* Number of Bytes Created                  */
            if (!good_status (status))
            {
                print_message (status, "SYS$EXPREG_64");
                return status;
            }
            printf ("Filling %016LX - %16LX with %0ds.\n",
                return_va_64,
                (uint64) return_va_64 + return_length_64 - 1,
                i);
            memset (return_va_64, i, page_size);
        }

    /*  Return the virtual addresses created within the region, as well as the
        region itself.                                                          */

        printf ("\nReturning Master Region:  %016LX - %016LX\n",
            master_va_64,
            (uint64) master_va_64 + master_length_64 - 1);

        status = sys$delete_region_64 (
            &region_id_64,      /* Region to Delete                             */
            PSL$C_USER,         /* Access Mode                                  */
            &return_va_64,      /* VA Deleted                                   */
            &return_length_64); /* Length Deleted                               */

        if (good_status (status))
            printf ("SYS$DELETE_REGION_64 Deleted VAs Between:  %016LX - %016LX\n",
                return_va_64,
                (uint64) return_va_64 + return_length_64 - 1);
        else
        {
            print_message (status, "SYS$DELTE_REGION_64");
            return (status);
        }

    /*  Return message buffer.                                                  */

        free (msgdsc.dsc64$pq_pointer);
    }

    /*  This routine obtains the system page size using SYS$GETSYI.  The return
        value is recorded in the module-wide location, page_size.               */

    int get_page_size ()
    {
    int
        status;

    IOSB
        iosb;

    ILE3
        item_list [2];

    /* Fill in SYI item list to retrieve the system page size.                  */

        item_list[0].ile3$w_length       = sizeof (int);
        item_list[0].ile3$w_code         = SYI$_PAGE_SIZE;
        item_list[0].ile3$ps_bufaddr     = &page_size;
        item_list[0].ile3$ps_retlen_addr = 0;
        item_list[1].ile3$w_length       = 0;
        item_list[1].ile3$w_code         = 0;

    /* Get the system page size.                                                */

        status = sys$getsyiw (
                    0,                              /* EFN                      */
                    0,                              /* CSI address              */
                    0,                              /* Node name                */
                    &item_list,                     /* Item list                */
                    &iosb,                          /* I/O status block         */
                    0,                              /* AST address              */
                    0);                             /* AST parameter            */

        if (!good_status (status))
        {
            print_message (status, "SYS$GETJPIW");
            return (status);
        }
        if (!good_status (iosb.iosb$w_status))
        {
            print_message (iosb.iosb$w_status, "SYS$GETJPIW IOSB");
            return (iosb.iosb$w_status);
        }

        return SS$_NORMAL;
    }

    /*  This routine takes the message code passed to the routine and then uses
        SYS$GETMSG to obtain the associated message text.  That message is then
        printed to stdio along with a user-supplied text string.                */

    #pragma inline (print_message)
    static void print_message (int code, char *string)
    {
        msgdsc.dsc64$q_length = BUFFER_SIZE;
        sys$getmsg (
            code,                                       /* Message Code         */
            (unsigned short *) &msgdsc.dsc64$q_length,  /* Returned Length      */
            &msgdsc,                                    /* Message Descriptor   */
            15,                                         /* Message Flags        */
            0);                                         /* Optional Parameter   */
        *(msgdsc.dsc64$pq_pointer+msgdsc.dsc64$q_length) = '\0';
         printf ("Call to %s returned:  %s\n",
            string,
            msgdsc.dsc64$pq_pointer);
    }

## Appendix C
VLM Example Program

This example program demonstrates the memory management VLM features described in Chapter 16.

    /*
        This program creates and maps to a memory-resident global section using
        shared page tables.  The program requires a reserved memory entry
        (named In_Memory_Database) in the Reserved Memory Registry.

        The entry in the registry is created using SYSMAN as follows:

        $ MCR SYSMAN
        SYSMAN> RESERVED_MEMORY ADD "In_Memory_Database"/ALLOCATE/PAGE_TABLES -
            /ZERO/SIZE=64/GROUP=100

        The above command creates an entry named In_Memory_Database that is
        64M bytes in size and requests that the physical memory be allocated
        during system initialization.  This enables the physical memory to
        be mapped with granularity hints by the SYS$CRMPSC_GDZRO_64 system
        service.  It also requests that physical memory be allocated for
        page tables for the named entry, requests the allocated memory be
        zeroed, and requests that UIC group number 100 be associated with
        the entry.

        Once the entry has been created with SYSMAN, the system must be
        re-tuned with AUTOGEN.  Doing so allows AUTOGEN to re-calculate
        values for SYSGEN parameters that are sensitive to changes in
        physical memory sizes.  (Recall that the Reserved Memory Registry
        takes physical pages away from the system.)  Once AUTOGEN has
        been run, the system must be rebooted.

        Use the following commands to compile and link this program:

        $ CC/POINTER_SIZE=32 shared_page_tables_example
        $ LINK shared_page_tables_example

        Since 64-bit virtual addresses are used by this program, a Version
        5.2 Compaq C compiler or later is required to compile it.
    */
    #define     __NEW_STARLET   1

    #include    <DESCRIP>
    #include    <FAR_POINTERS>
    #include    <GEN64DEF>
    #include    <INTS>
    #include    <PSLDEF>
    #include    <SECDEF>
    #include    <SSDEF>
    #include    <STARLET>
    #include    <STDIO>
    #include    <STDLIB>
    #include    <STRING>
    #include    <VADEF>

    #define     bad_status(status) (((status) & 1) != 1)
    #define     ONE_MEGABYTE    0x100000

    main ()
    {
        int
            status;

        $DESCRIPTOR (section_name, "In_Memory_Database");

        uint32
            region_flags = VA$M_SHARED_PTS,   /* Shared PT region. */
            section_flags = SEC$M_EXPREG;

        uint64
            mapped_length,
            requested_size = 64*ONE_MEGABYTE,
            section_length = 64*ONE_MEGABYTE,
            region_length;

        GENERIC_64
            region_id;

        VOID_PQ
            mapped_va,
            region_start_va;

        printf ("Shared Page Table Region Creation Attempt:  Size = %0Ld\n",
            requested_size);

    /*  Create a shared page table region. */

        status = sys$create_region_64 (
            requested_size,                 /* Size in bytes of region */
            VA$C_REGION_UCREATE_UOWN,       /* Region VA creation and owner mode */
            region_flags,                   /* Region Flags:  shared page tables */
            &region_id,                     /* Region ID */
            &region_start_va,               /* Starting VA for region */
            &region_length);                /* Size of created region */

        if (bad_status (status))
        {
            printf ("ERROR:  Unable to create region of size %16Ld\n\n",
                requested_size);
            return;
        }

        printf ("Shared Page Table Region Created:  VA = %016LX, Size = %0Ld\n\n",
            region_start_va,
            region_length);

    /* Create and map a memory-resident section with shared page tables
           into the shared page table region. */

        printf ("Create and map to section %s\n", section_name.dsc$a_pointer);
        status = sys$crmpsc_gdzro_64 (
                &section_name,              /* Section name */
                0,                          /* Section Ident */
                0,                          /* Section protection */
                section_length,             /* Length of Section */
                &region_id,                 /* RDE */
                0,                          /* Section Offset; map entire section */
                PSL$C_USER,                 /* Access Mode */
                section_flags,              /* Section Creation Flags */
                &mapped_va,                 /* Return VA */
                &mapped_length);            /* Return Mapped Length */

        if (bad_status (status))
            printf ("ERROR:  Unable to Create and Map Section %s, status = %08x\n\n",
                    section_name.dsc$a_pointer,
                    status);
        else
        {
            printf ("Section %s created, Section Length = %0Ld\n",
                    section_name.dsc$a_pointer,
                    section_length);
            printf ("    Mapped VA = %016LX, Mapped Length = %0Ld\n\n",
                    mapped_va,
                    mapped_length);
        }

        /* Delete the shared page table.  This will cause the mapping to the
           section and the section itself to be deleted. */

        printf ("Delete the mapping to the memory-resident global section");
        printf (" and the shared\n    page table region.\n");
        status = sys$delete_region_64 (
                &region_id,
                PSL$C_USER,
                &region_start_va,
                &region_length);

        if (bad_status (status))
            printf ("ERROR:  Unable to delete shared page table region, status = %08x\n\n", status);
        else
            printf ("Region Deleted, Start VA = %016LX, Length = %016LX\n\n",
                    region_start_va,
                    region_length);
        printf ("\n");
    }

This example program displays the following output:

    Shared Page Table Region Creation Attempt:  Size = 67108864
    Shared Page Table Region Created:  VA = FFFFFFFBFC000000, Size = 67108864

    Create and map to section In_Memory_Database
    Section In_Memory_Database created, Section Length = 67108864
        Mapped VA = FFFFFFFBFC000000, Mapped Length = 67108864

    Delete the mapping to the memory-resident global section and the shared
        page table region.
    Region Deleted, Start VA = FFFFFFFBFC000000, Length = 0000000004000000

Previous | Next | Contents | Index

| privacy and legal statement |
5841PRO_086.HTML