Runtime Link Library System

RLL PROGRAMMERS GUIDE

Contents


Overview

The use of the RLL system is reasonable transparent from the view of the application programmer who is writing C programs, and the user who uses the resulting C programs. These users will find that the system is transparent in operation and needs the minimum of consideration.

Special considerations have to be taken by the programmer who is trying to produce routines that are suitable for inclusion into Runtime Link Libraries.

These constraints arise from a number of underlying causes:

The net effect is


Programming Constraints

There are various constraints that have to be met for routines that are to be placed into a RLL library. These are necessary because of a combination of the way the Runtime Linking process is implemented, and the need for libraries to be simultaneously shareable amongst many programs. These restrictions are:

Routines may, however, include references to functions or variables that are in the other modules that are in the same RLL library as themselves, or in other RLL libraries that were specified as being used by this RLL library when it was built.

If you are writing library routines in C, then this means that effectively these constraints translate into the following:

Many routines will automatically satisfy these constraints and will already be suitable for inclusion into a RLL library. A very useful tool in helping you decide whether routines will be suitable for inclusion in a RLL is SLB, the SROFF Librarian Program that is provided as part of the C68 package. If run using the -W parameter, it can give you an analysis of the external references made by a module (XREFs), and also of the symbols in the module that are globally visible (XDEFs).


Programming around constraints

The obvious question arises is what do you do with a routine which does not satisfy all the above constraints. An obvious simple example is a C routine which sets the global variable errno for reporting diagnostic information on an error return.

There are fundamentally two techniques available. These are:

It is quite likely that both of these techniques could be used in parallel. These techniques are now discussed in more detail.

Using the C Pre-processor to call RLL routines

The C pre-processor allows for any calls to library routines to be expanded into slightly different variants for routines that need additional parameters when present in a RLL. To assist the library programmer in implementing this, the preprocessor symbol RLL_LIBS will be set if the RLL version of a library routine is to be used.

You could have an entry in the header file that defines your library function in the form:

  #ifdef RLL_LIBS 
   #define myfunc(p1,p2) RLL_myfunc(&errno,p1,p2)
  #else 
   #define myfunc(p1,p2) RLL_myfunc(p1,p2)
  #endif

This means that all references to myfunc in the original source code will have been replaced to calls to RLL_myfunc, and an additional parameter will have been added to the call. Having done this, you would then write the RLL version of the function that is to go into the library along the lines of:

/* 
* This piece of code is written so that the RLL 
* version is generated if RLL_LIBS is defined, and 
* the standard version if it is not 
*/ 
#ifdef RLL_LIBS 
 #define ERRNO * errnoptr 
 int RLL_myfunc (int *errnoptr, char *par1, int par2) 
#else 
 #define ERRNO errno 
 int myfunc (char * par1, int par2) 
#endif 
 { 
   ...... 
   ERRNO = error_code; 
   ...... 
 }

Points to note about this are that:

It is recommended that normally you provide both standard and RLL versions of any libraries you create so that the application programmer can make the final decision on whether to use RLL(s) or not. Making sure both versions are based on the same file helps ensure that the two versions are kept consistent with each other.

The above way of doing things is reasonable clear to understand and has the advantage that it can all be done at the C level. It has the disadvantage that parameters need to be copied with the attendant runtime overheads, and that it relies on the appropriate header file being included.

Using stubs to call RLL routines

There is an alternative technique that can be used either instead of, or in addition to, the one mentioned in the previous section.

The technique is to break the call down into two stages:

If written in C the stub routine for the example would then look something like:

int myfunc (char * param1, int param2) 
{ 
    return RLL_myfunc (&errno, NULL, param1, param2)l 
} 

You can do this more efficiently if you use assembler for the stub routine. In this case you need to exploit knowledge of the way parameters are stored and write a routine for the AS68 assembler that looks something like:

    .globl _myfunc 
_myfunc: 
    pea  errno         ;set up pointer to errno 
    jsr _RLL_myfunc    ;call main routine 
    lea  4(sp),sp      ;remove parameter from stack 
    rts

This stub routine should then be included into a special stub library that will have the same name as the corresponding RLL library, but will have the _rls extension. Any time that a RLL library is linked in, any relevant stubs in the (stub( library will be statically linked into the main program to establish the link.

Note the fact that underscores need to be added to the C names when written in assembler. The corresponding code for the routine in the RLL library now becomes:

/* 
* This piece of code is written so that the RLL 
* version is generated if RLL_LIBS is defined, and 
* the standard version if it is not 
*/ 
#ifdef RLL_LIBS 
 #define ERRNO * errnoptr 
 int RLL_myfunc (int *errnoptr, char * dummy, char *par1, int par2) 
#else 
 #define ERRNO errno 
 int myfunc (char * par1, int par2) 
#endif 
 { 
   ...... 
   ERRNO = error_code; 
   ...... 
 }

Note that in this example the extra parameter which is a pointer to errno appears at the front of the parameter list (because the parameters on the stack are accessed from right to left when at the C level - which means that the additional parameter is the first one declared in the rll_myfunc() routine). There is also a dummy parameter between those added by the stub routine and the original parameters to allow for the fact that the return address for the stub_myfunc() routine is still there.

If you do not understand the above, then you should get the SOURCE disks that are available for use with C68 and look at some of the source code for routines in the LIBC_A library. A good example might be that for the ?????() routine.


RLL THING Name and Version Number

When you build a RLL, it has to be assigned both a name for use by the THING system, and a version number.

The Version Number should always be of the form "n.mm" where "n" is the major version number and "mm" the minor version number. This Version Number information is used by the RLL system as follows:

The THING name of a RLL is made up of the main part of the filename plus the most significant part of the Version Number. Thus version 4.14 of the LIBC_RLL library would have a THING name of "LIBC4". There is a limit or 16 characters (including the character for the major version number) in a RLL THING name - if necessary the name will be truncated on the left. As the major version number is part of the THING name, then it is possible to have RLLs that are for different major numbers of the same library loaded simultaneously.


RLL Initialisation Routines

The system allows for you to have special user provided initialisation routines invoked at certain points. These routines are executed at the following points:

These routines are optional. If not included in the RLL when it is built, then no special additional routines will be called at these points. It is not expected that most RLLs will require these routines, but the facility is provided for special cases.


Building a Runtime Link Library.

Once you have written your RLL compatible library module(s) and have done the compile to produce the SROFF versions of the files (the ones whose name end in _o), the next stage is to actually build all these modules into a RLL library.

Where for a standard library you use the SLB SROFF Librarian to build a library, the process of building a RLL library from its constituent models is more akin to building a program in that the LD linker is used rather than the SLB librarian program.

o get the LD linker to build a RLL library, you proceed as if you were about to link all the relevant modules into a single program, except that (at a minimum) you additionally specify the -R parameter to LD. The -R parameter is followed by the version number that is to be given to the generated RLL. Once you have done that the rest is done automatically by LD. Of course you still have to test the resultant RLL library!

To illustrate this you might well build a RLL library by using parameters to LD of the form:

-olibmine -R1.00 -rm

This would build the file LIBMINE_RLL (the _RLL extension is added automatically if not explicitly specified), giving it a version number of '1.00'. The -rm parameter has been used to specify that this library is to have a dependency on the library file LIBM_RLL which is the RLL version of the standard maths library. The system will also automatically set up a dependency on LIBC_RLL - the RLL version of the standard C library (Tip:- If you want to suppress the dependency on LIBC_RLL you can do this by explicily giving the -lc parameter to the linker).

Note that the above example illustrates the fact that it is possible to build one RLL library that has a dependency on other ones. However also note that it is necessary to explicitly state any such dependencies when you link a library - LD will not automatically follow any dependencies already set up in a RLL you link in.

Another point to note is that the RLL system treats symbol names as case sensitive. For situations in which this would be a problem, the LD linker provides an option to treat all case names as though they were upper case. If this option is used when building the RLL, then all names will be stored in upper case. You should then use the same option when linking programs that will use this RLL so that they only ask for symbols using upper case.


Building a Stub library

This is built in the same way as standard C68 library, using the SLB SROFF librarian program. The only difference is that the extension of stub libraries should be _rls.

It is not compulsory to have a stub library corresponding to each RLL library. If one is present it will be used, and if not no error message will be output. It really depends on the programmer producing the RLL library and the techniques that he used.

Note that is not necessary that the "Stub" libraries associated with a RLL library be present at runtime. This is because the stubs are statically linked into the main program at link time.


Testing a Runtime Link Library

Testing a RLL is rather like testing any library routine - you have to write a suitable test program.

You may find that the Interactive and Debug facilities providing control of the RLM system from the SuperBasic level are useful while testing your code. You are likely to be continually loading/unloading any RLL that contains routines that you are trying to test.

There is a useful trick if you are working on producing an updated version of a RLL that you have already used in your current programs. Give the test version of your RLL a different major version number to your current "production" version. That way existing programs can still pick up the old tested version of the library, while your test program which you have linked to use the new one will (because it has a different major version number) select your test version.

When you have finished testing your new RLL, you can make a decision whether it is backward compatible with your existing RLL. If it is, then merely by giving it the same major version number as the existing RLL, but an updated minor version number all your programs will switch to using the new RLL. Do not forget, however, to unload the old copy of the RLL and put the new one in its place!