ChorusOS 4.0 Introduction

Using Dynamic Libraries

There are two types of library in the ChorusOS operating system:

Relocatable code can be contained in two types of executable:

An actor that uses dynamic libraries is called a dynamic actor. A relocatable actor uses only static libraries.

Static and Dynamic Linking

The following table summarizes the actions performed by the static linker, which runs on the development host, and by the runtime linker, which runs on the target.

 Link Relocatable Executable Dynamic executable
 Static Linker.a Static linker adds necessary objects (.o) of a static library (.a) to the executable. .a Static linker adds necessary objects (.o) of a static library (.a) to the executable. .so Static linker adds the library to the list of libraries to load at actor start-up (afexec).
Runtime Linker (afexec) -.so At actor start-up, libraries are loaded and linked by the runtime linker. Libraries to load are defined either at static link, or in the LD_PRELOAD environment variable. The runtime linker uses a library search path to find dynamic and shared libraries.
Runtime Linker (dlopen) -.so Application explicitly asks the runtime linker to dynamically load and link a dynamic library, using the dlopen() function of the dynamic linking API.

Dynamic linking of libraries applies recursively to library dependencies: when a library is loaded, all the libraries it uses are also loaded.

Building a Dynamic Library

This section describes how to build dynamic and shared programs using the imake tool. See "Developing ChorusOS Applications" for more general information about using imake.

The following imake macro builds dynamic libraries:

DynamicLibraryTarget(dlib, objs, staticLibs, dynamicLibs, dlDeps, options)

The following example builds a dynamic library named libfoo.so from the binary objects files a.o and b.o. When this library is loaded dynamically, the runtime linker will also load the dynamic library libdyn.so, which must be in its search path.

DynamicLibraryTarget(
        libfoo.so, 
        a.o  b.o, ,
        libdyn.so, , )

Building a Dynamic Program

The following imake macros build dynamic executables:

DynamicUserTarget(prog, objs, staticLibs, 
        dynamicLibs, dlDeps, options)
DynamicSupTarget(prog, objs, staticLibs, 
        dynamicLibs, dlDeps, options)
DynamicCXXUserTarget(prog, objs, staticLibs, 
        dynamicLibs, dlDeps, options)
DynamicCXXSupTarget(prog, objs, staticLibs, 
        dynamicLibs, dlDeps, options)
DynamicLibraryTarget(prog, objs, staticLibs, 
        dynamicLibs, dlDeps, options)

The prog argument is the name of the resulting program. Other arguments are the same as the DynamicLibraryTarget() macro. For the options argument, the following options are particularly useful:

The following example builds a dynamic program named prog from the binary object files a.o and b.o. The program is statically linked with the static ChorusOS operating system library. When this program is started, the runtime linker will load the dynamic library libdyn.so. In the target file system, this library can be located in the /libraries directory, as this directory is added to the search path of the runtime linker.

DynamicUserTarget(
        prog, 
        a.o  b.o, ,
        libdyn.so, ,
        -Xlinker -rpath -Xlinker /libraries)

Dynamic Programming

The previous sections described how to use dynamic objects (dynamic and shared libraries) that are loaded by the runtime linker at actor start-up. In addition to this mechanism, an actor can also bind a dynamic or shared library explicitly during its execution. This on-demand object binding has several advantages:

Typically, an application performs the following sequence to access an additional dynamic object, using the dynamic library API:

Runtime Linker

This section describes the functions performed by the runtime linker, as well as the features it supports for dynamic applications.

Dynamic applications consist of one or more dynamic objects. They are typically a dynamic executable and its dynamic object dependencies. As part of the initialization of a dynamic application, the runtime linker completes the binding of the application to its dynamic object dependencies.

In addition to initializing an application, the runtime linker provides services that allow the application to extend its address space by mapping additional dynamic objects and binding to symbols within them.

The runtime linker performs the following functions:

The runtime linker uses a prescribed search path for locating the dynamic dependencies of an object. The default search paths are the runpath recorded in the object, followed by /usr/lib. The runpath is specified when the dynamic object is constructed using the -rpath option of the linker. The environment variable LD_LIBRARY_PATH can be used to indicate directories to be searched before the default directories.


Note -

The runtime linker needs a file system to load dynamic objects. This file system can be on a host and accessed through NFS from the target. In embedded systems without a network connection, a bank of the system image can be used. For example: /image/sys_bank.


Environment Variables

The following environment variables are used by the runtime linker:

Supported Features

Examples

This section contains two examples of dynamic programs. In all these examples, it is assumed that a standard development environment has been set up: system build tree, search path, boot and initialization of target machine. It also assumes that the chorus_root_directory is the path of the target root directory on the NFS host (for example /home/chorus/root), the name of the target is jericho, and the environment variable WORK refers to the directory used for building these examples

Dynamic Link at Actor Start-up

The following dynamic program uses a custom dynamic library which will be loaded and linked at actor start-up. It uses a function foo() which is defined in the dynamic library. This function calls a function bar() defined in the main program.

This is the dynamic program progdyn.c:

#include <chorus.h> 

extern void foo();

main() {
    foo();              /* calling foo defined in the library */
}

void bar() {
    printf ("bar called\n");
}

This is the dynamic library libdyn.c:

#include <chorus.h> 

extern void bar();

void foo() {
    printf ("Calling bar\n");
    bar();              /* calling bar defined in the main program */
}

Building the Dynamic Library

Create a directory libdyndir in $WORK, containing libdyn.c and the following Imakefile:

SRCS = libdyn.c
DynamicLibraryTarget (libdyn.so, libdyn.o, , , ,-Xlinker -soname=libdyn.so )
Depend(libdyn.c)

In the libdyndir directory, build the dynamic library libdyn.so using the ChorusOSMkMf, make depend, and make commands.

Building the Dynamic Program

Create a directory progdyndir in $WORK, containing progdyn.c and the following Imakefile:

SRCS = progdyn.c
DynamicUserTarget (progdyn, progdyn.o, , 
        $(WORK)/libdyndir/libdyn.so,
        $(WORK)/libdyndir/libdyn.so, )
Depend()

In the progdyndir directory, build the dynamic program progdyn using the ChorusOSMkMf, make depend and make commands.


% ChorusOSMkMf $WORK
% make depend
% make

Running the Dynamic Program

Copy the dynamic program into the /bin subdirectory of the chorus_root_directory directory:


% cp $WORK/progdyndir/progdyn chorus_root_directory/bin 

Copy the dynamic library into the /lib subdirectory of the chorus_root_directory directory:


% cp $WORK/libdyndir/libdyn.so chorus_root_directory/lib 

Then, the following command will tell the runtime linker where to find the libdyn.so dynamic library:


% rsh jericho setenv LD_LIBRARY_PATH /lib 

Alternatively, set the runpath to /lib in the ldopts argument of the program macro (-rpath /lib).

Finally, the following command will start the program and dynamically load the libdyn.so library:


% rsh jericho arun /bin/progdyn

Explicit Link Using dlopen

The following program explicitly loads a dynamic library at runtime, using the function dlopen(). It searches for the address of the dynfunc() function defined in the library and calls this function.

This is the dynamic program progdyn2.c:

#include <chorus.h>
#include <cx/dlfcn.h>

int main()
{
    void    (*funcptr)();       /* pointer to function to search */
    void    *handle;            /* handle to the dynamic library */

        /* finding the library */
    handle = dlopen ("libdyn2.so", RTLD_NOW);
    if !(handle) { printf ("Cannot find library libdyn2.so\n"); exit(1); } 

        /* finding the function in the library */
    funcptr = (void (*)()) dlsym (handle, "dynfunc");
    if !(funcptr) { printf ("Cannot find function dynfunc\n"); exit(1); }

        /* calling library function */
    (*funcptr)();
}

This is the dynamic library libdyn2.c:

#include <chorus.h> 

void dynfunc() {
    printf ("Calling dynfunc\n");
}

Building the Program and the Library

The program and library above are built in the same way as in the previous example, using two Imakefiles:

Create a directory libdyn2dir in $WORK, containing libdyn2.c and the following Imakefile:

SRCS = libdyn2.c
DynamicLibraryTarget (libdyn2.so, libdyn2.o, , , , )
DependTarget(libdyn2.c)

Create a directory progdyn2dir in $WORK, containing progdyn2.c and the following Imakefile:

SRCS = progdyn2.c
DynamicUserTarget (progdyn2, progdyn2.o, , , , )
Depend(progdyn2.c)

Running the Dynamic Program

Copy the dynamic program into the /bin subdirectory of the chorus_root_directory directory:


% cp $WORK/progdyn2dir/progdyn2 chorus_root_directory/bin 

Copy the dynamic library into the /lib subdirectory of the chorus_root_directory directory:


% cp $WORK/libdyn2dir/libdyn2.so chorus_root_directory/lib 

Then, the following command will tell the runtime linker where to find the libdyn2.so dynamic library:


% rsh jericho setenv LD_LIBRARY_PATH /lib

Finally, the following command will start the program:


% rsh jericho arun /bin/progdyn2 

At program start-up, the runtime linker will only load the executable progdyn2. The libdyn2.so library will be loaded when the dlopen() function is called.