INtime SDK Help
RSLs (real-time shared libraries)
INtime SDK v6 > About INtime > INtime Kernel > RSLs (real-time shared libraries)

Overview

Real-time Shared Libraries (RSLs) are images containing code and/or data which may be mapped into the address space of one or more processes. They share many of the features of Windows DLLs, and are created in a similar fashion. There is also a Visual Studio Wizard to create a project with the appropriate settings to create an RSL.

RSLs are mapped to a process' address space whenever a Real-Time Application (RTA) is loaded which refers to the RSL. If an RSL with the same name is not present in memory then the loader loads the RSL before loading the RTA.

Note:The default loader behavior is to check only the filename of the RSL against those already loaded. This behavior can be changed to check the full path of an RSL against those already loaded so that it is possible to load two RSLs with the same name, but different path locations. This behavior is enabled by setting a registry variable as follows:

Key: HKLM\Software\TenAsys\RtAppLoader (64-bit case: HKLM\Software\Wow6432Node\TenAsys\RtAppLoader)

DWORD Value name: StrictRslPathCheck

Value: 1 = enable full path name check, 0 = check only filename

When the loader is looking for a file to load the RSL image from and the full path has not been specified, it looks first in the directory from where the RTA was loaded. If the RSL is not there, it looks in the INtime\bin directory for the file. The search may be extended by configuring additional paths, defining other locations where RSLs may be sought. If this configuration exists and the location exists, it is searched before the INtime\bin location. For INtime for Windows this value is configured in the INtime Configuration utility in the Node Management applet, in the System table, value "RSL search path". For INtime Distributed RTOS the loader uses the values defined in the PATH environment variable.

Explicit linking, like the Win32 LoadLibrary function, can be accomplished in INtime with LoadRtLibrary. 

Entry/exit function

Each RSL may optionally have a single entry/exit function. The system calls this function at various times during the life of a process. These calls are usually used by an RSL to perform any per-process and per-thread initialization and cleanup. This function may be omitted if not required. If the function is included, it must have the following form:

BOOLEAN __stdcall RslMain( RTHANDLE hModule,
                        DWORD  dwReason,
                        LPVOID lpReserved)
{
  switch (dwReason)
  {
  case RSL_PROCESS_ATTACH:
    // The RSL is being mapped into the process' address space
    break;

  case RSL_THREAD_ATTACH:
    // A thread is being created
    break;

  case RSL_THREAD_DETACH:
    // A thread is being deleted
    break;

  case RSL_PROCESS_DETACH:
    // The RSL is being unmapped from the process' address space
    break;
  }

  return TRUE;  // value only tested for RSL_PROCESS_ATTACH
}

The INtime kernel calls the function at various times. Whenever the function is called the hModule parameter contains the handle for the RSL object. This object identifies the RSL image in the process's address space. The dwReason parameter indicates the event which caused the system to call the function. This parameter may take one of these values:

RSL_PROCESS_ATTACH

When an RSL is mapped to a process' address space, the RSL's RslMain function is called with the value of RSL_PROCESS_ATTACH passed in the dwReason parameter. When processing RSL_PROCESS_ATTACH, the RSL should perform any per-process initialization required for functions in the RSL, and then return a value indicating whether or not the initialization was successful. On success, the value TRUE should be returned, otherwise a value of FALSE should be returned.

In the case where a library is being loaded implicitly while loading an application, the thread which executes the RslMain function is the process' initial thread. The RslMain function of each RSL mapped to the process' address space is called with the dwReason parameter set to RSL_PROCESS_ATTACH. After all of the RSLs have been successfully initialized, the thread continues to initialize the C library for this process and then calls the process' main function. If any of the initialization functions returns a value of FALSE, the process will be terminated immediately.

In the case where a library is being loaded explicitly by way of a call to LoadRtLibrary, a return value of FALSE causes the LoadRtLibrary call to fail.

RSL_PROCESS_DETACH

When an RSL is unmapped from a process' address space, the system calls the RSL's RslMain function passing a value of RSL_PROCESS_DETACH in the dwReason parameter. An RSL should perform any process-specific cleanup actions when this value is processed. The thread which executes the RslMain function with the RSL_DETACH_PROCESS code is the thread which calls the exit function in the process. The RslMain function is called after any atexit processing and after any C++ cleanup code but before the C library resources are released at exit.

RSL_THREAD_ATTACH

When a thread is created in a process, the system calls the RslMain function for each RSL mapped into the process' memory space with the dwReason parameter set to RSL_THREAD_ATTACH. The function should be used to perform thread-specific initialization of the RSL. The thread which calls the RslMain function is the thread just created, before the thread function is called. If the thread returns from its thread function, the RslMain function for each mapped RSL is called again with the RSL_THREAD_DETACH reason code and then deletes itself.

Note:   The initial thread of a process does not call RslMain with dwReason set to RSL_THREAD_ATTACH. Any RSL's which are mapped into the process memory space when the process is started receive the RSL_PROCESS_ATTACH notification, but do not receive the RSL_THREAD_ATTACH notification.

RSL_THREAD_DETACH

When a thread deletes itself by calling DeleteRtThread with either its own handle or a null handle specified then the RslMain function of any RSL's mapped to the process' memory space receives the RSL_THREAD_DETACH notification.

Notes:   

Exporting Variables and Functions from an RSL

When an RSL is created it contains functions that are available to RTAs and other RSLs. When an RSL function is made available to other RTA or RSL files, it is said to be exported. It is also possible to export global data variables as well as functions. This can be done with the Microsoft compiler's __declspec(dllexport) storage class specifier. When another application or RSL wants to call the function or access the variable which has been exported, it declares it using the __declspec(dllimport) storage class specifier. A common header file for the RSL is commonly created as in the following example:

// myrsl.h
// MYRSL is defined in the settings for the RSL project, so 
// the exported symbols are defined as __declspec(dllexport) in
// the implementation, and __declspec(dllimport) to an external
// application. 

#ifdef MYRSL
#define MYRSLAPI __declspec(dllexport)
#else
#define MYRSLAPI __declspec(dllimport)
#endif

MYRSLAPI int Func(int n);
MYRSLAPI int nCount;

When this file is included in the RSL implementation file, the source looks like this:

// myrsl.c
// Including the file myrsl.h exports the symbols from this RSL

#include "myrsl.h"

int nCount = 0;

int Func(int n)
{
  return n + 2;
}
.
.
.

Function names can have their names mangled by the Visual C++ compiler, which is not always desirable. For example the function:

__declspec(dllexport) int __stdcall Func(int p);

is exported by the linker as _Func@4 in the export table.

To avoid this, and to export a "clean" name, it is possible to use one of two methods. The first is to create a .DEF file and create a list of exported names in the EXPORTS sections, like this:

EXPORTS
  Func

When the Visual C++ linker interprets the .DEF file it sees that the names Func and _Func@4 have been exported, which match apart from the name mangling, so it exports the name using the DEF file definition, and not the mangled version.

An alternative method to use is to put a linker directive in the source code, by adding a line like this:

#pragma comment(linker, "/export:Func=_Func@4")

For further details of these Visual C++ compiler features, see the Visual Studio documentation.

Sharing data between processes using RSLs

When the loader maps image sections from an RSL to an RTA, it only creates multiple mappings of read-only sections, such as those used for code or constant data. It is possible to coerce the linker into creating a writable shareable section so that different processes may share writable data. This is achieved by using the data_seg pragma in the compiler to create a segment with special "Shared" attributes, and the /SECTION switch in the linker to cause it to group the special segments into a section with shared attributes.

For example, if we wish to create a variable which has global scope across all mapping of the RSL, we create a segment for it, called "Shared", in the compiler:

#pragma data_seg("Shared")
LONG g_nCount = 0;
#pragma data_seg()

Notice two things about this example. The first is that the variable to be shared (g_nCount in this example) is initialized. If it is not initialized then the variable will not be placed in the Shared segment; all uninitialized variables are always placed in the .bss section. Secondly, the data_seg pragma is reset after the definition, so that subsequent variables are treated normally.

We now need to specify the /SECTION switch to the linker to tell it that the segment with the name "Shared" should be placed in a section with the shared attribute. The name Shared, given by the data_seg pragma, is only a name and does not imply any special attributes by itself. The /SECTION switch associates the name with a section type. In our case the switch would look like:

/SECTION:Shared,RWS

This associates the segment named "Shared" with a section with attributes RWS - readable, writeable and shared. You can also embed the linker switch in the source code:

#pragma comment(linker, "/SECTION:Shared,RWS")

Again, refer to the Microsoft documentation for further details. An illustration of shared variables may be found in the RSL example project installed with the product.

System calls

This lists common operations related to Real-time Shared Libraries (RSLs):

To . . . Use this system call . . .
Load a library module LoadRtLibrary
Unload (free) a module FreeRtLibrary
Find the address of an exported symbol GetRtProcAddress
Get the handle of a previously loaded module GetRtModuleHandle
Get the full path of a loaded module, given its handle GetRtModuleFilename
Get information about a loaded module GetRtModuleInformation
Allocate a thread-local storage (TLS) index RtTlsAlloc
Release a thread-local storage (TLS) index RtTlsFree
Store a value in the calling thread's thread local storage RtTlsSetValue
Retrieve a value from the calling thread's thread local storage RtTlsGetValue
See Also