This chapter demonstrates the use of imake macros to build different types of ChorusOS applications written in the C and C++ languages. The examples in this chapter are installed with the ChorusOS product as part of the SUNWewp examples package.
As previously discussed, ChorusOS applications can be POSIX processes or actors. This chapter provides the build information for both types of applications.
After completing the examples provided in this chapter, you will understand how to use imake to build an application with the ChorusOS product.
The default installation directory for all examples used in this book is install-dirchorus_family/src/opt/examples/. You can use the SolarisTM tool pkginfo(1)as follows:
% pkginfo | grep SUNWewp application SUNWewps Sun Embedded Workshop for Solaris/SPARC Examples |
To build a ChorusOS component, use the make and imake tools. All development tools are provided in the install_dir/5.0/chorus-family/tools directory of your ChorusOS installation.
The make environment is defined by a file containing variable definitions and rules. This file provides the rules for compiling C, C++, and assembly language. The rules are specific to your compiler - the name of the file indicates the compiler used. For example, when using the gcc compiler, the make environment file is named tgt-make/gcc-devsys.mk. This file contains the variables and rules required for building the component. The following variables are defined:
CFLAGS and CXXFLAGS specify the compilation options for C and C++ files, respectively. The compilation options are shown in Table 6-1.
Table 6-1 Compilation OptionsOption | Possible Settings | Default Setting |
---|---|---|
WARN | $(WARN_ON), $(WARN_OFF) | $(WARN_ON) |
DEBUG | $(DEBUG_ON), $(DEBUG_OFF) | $(DEBUG_OFF) |
PROF | $(PROF_ON), $(PROF_OFF) | $(PROF_OFF) |
OPT | $(OPT_ON), $(OPT_OFF) | $(OPT_ON) |
INCLUDES and DEFINES specify include and depend values. These variables can be replaced by different variables (overloaded) at the application level. The variables are contained in the CPPFLAGS flag, which is used in compilation and in computing dependencies. Both INCLUDES and DEFINES can be initialized at the application level.
LD_UCRT0, LD_SCRT0, LD_DCRTO, LD_PSCRT0, LD_PUCRT0, LD_CRTI, and LD_CRTN are used to manage different
types of crt object files. A crt object
file (crto) contains the entry point of the application
(_start
or _main
).
LD_UCRT0 |
Specifies the entry point for an embedded user actor |
LD_SCRT0 |
Specifies the entry point for an embedded supervisor actor |
LD_DCRT0 |
Specifies the entry point for drivers |
LD_PSCRT0 |
Specifies the entry point for a supervisor actor or process |
LD_PUCRT0 |
Specifies the entry point for a user actor or process |
LD_CRTI |
Contains the crti.o file. This file is always the first object file of a link list and should contain the beginning of the .init and .fini sections |
LD_CRTN |
Contains the .fini section |
LD_U_ACTOR and LD_S_ACTOR specify link information for user and supervisor actors.
EBD_U_LIBS, EBD_S_LIBS, CLX_U_LIBS, CLX_S_LIBS, EBD_CXX_LIBS, and CLX_LIBS are provided to manage the libraries for embedded actors, actors and processes.
Table 6-3 Library Variables
EBD_U_LIBS |
Manages the C library for embedded user actors |
EBD_S_LIBS |
Manages the C library for embedded supervisor actors |
CLX_U_LIBS |
Manages the C library for user actors and processes |
CLX_S_LIBS |
Manages the C library for supervisor actors and processes |
EBD_CXX_LIBS |
Manages the C++ library for embedded actors |
CLX_LIBS |
Manages the C++ library for actors and processes |
The make environment includes the following commands:
cc
ld
as
mkctors
The ChorusOS imake environment enhances the make environment by
providing template rules for common ChorusOS build operations using generic
names. When using the predefined imake rules, it is not
necessary to know which libraries, crt
files, or
entry points should be used to build an application, because they are selected
automatically.
Instead of creating Makefiles you create Imakefiles, and imake generates Makefiles from them.
The imake environment is defined by four files containing sets of variables and rules. These files are located in the tools/imake directory. The rules are independent of the compiler you use.
Imake.tmpl contains definitions of variables. See "imake Variable Definitions".
Imake.rules contains build rules. See "imake Build Rules".
Package.rules contains rules used to build a binary distribution. See "imake Packaging Rules".
The Imake.tmpl file contains the following definitions:
FAMILY, indicates the target family (x86, usparc, ppc60x, mpc860).
COMPILER, identifies the compiler to be used (for example, gcc).
REL_DIR, specifies the path of the current directory. This variable is automatically set in subdirectories by imake.
HOST, indicates the host operating system (SOLARIS).
DEVTOOLS_DIR, indicates the path of the ChorusOS development tools.
The Imake.rules file contains macros known as Imake build rules. A list of Imake build rules and their functions is displayed in Table 6-4.
Table 6-4 imake Build RulesMacro name | Function |
---|---|
MakeDir(dir) | Creates a directory named dir. |
LibraryTarget(lib, objs) | Adds the objects indicated by objs into the library lib. |
Depend(srcs) | Computes the dependencies of srcs and adds them to the dependency list in the Makefile (using makedepend). |
ActorTarget(prog, objs, options, crt0, libs) | Uses objs to create a C actor called prog, and passes options, crt0 and libs to the linker. |
UserActorTarget(prog, objs, libs) | Creates a user C actor or process. |
SupActorTarget(prog, objs, libs) | Creates a supervisor C actor or process. |
EmbeddedUserActorTarget(prog, objs, libs) | Creates an embedded user C actor. |
EmbeddedSupActorTarget(prog, objs, libs) | Creates an embedded supervisor C actor. |
BuiltinDriver(prog, objs, libs) | Creates a ChorusOS operating system driver. |
BspProgTarget(prog, entry, objs, libs) | Creates a BSP program. |
CXXActorTarget(prog, objs, options, crt0, libs) | Uses objs to create a C++ actor or process named prog, and passes options, crt0 and libs to the linker. |
CXXUserActorTarget(prog, objs, libs) | Creates a user C++ actor or process. |
CXXSupActorTarget(prog, objs, libs) | Creates a supervisor C++ actor or process. |
CXXEmbeddedUserActorTarget(prog, objs, libs) | Creates an embedded user C++ actor. |
CXXEmbeddedSupActorTarget(prog, objs, libs) | Creates an embedded supervisor C++ actor. |
DynamicUserTarget(prog, objs, libs, dynamicLibs, dlDeps, options) | Creates a dynamic user C process. |
DynamicSupTarget(prog, objs, libs, dynamicLibs, dlDeps, options) | Creates a dynamic supervisor C process. |
DynamicCXXUserTarget(prog, objs, libs, dynamicLibs, dlDeps, options) | Creates a dynamic user C++ process. |
DynamicCXXSupTarget(prog, objs, libs, dynamicLibs, dlDeps, options) | Creates a dynamic supervisor C++ process. |
DynamicLibraryTarget(dlib, objs, staticLibs, dynamicLibs, dlDeps, options) | Creates a dynamic library. |
SharedUserTarget(prog, shobjs, staticLibs, sharedLibs, slDeps, options) | Creates a shared user C process. |
SharedSupTarget(prog, objs, staticLibs, sharedLibs, slDeps, options) | Creates a shared supervisor C process. |
SharedCXXUserTarget(prog, objs, staticLibs, sharedLibs, slDeps, options) | Creates a shared user C++ process. |
SharedCXXSupTarget(prog, objs, staticLibs, sharedLibs, slDeps, options) | Creates a shared supervisor C++ process. |
SharedLibraryTarget(shlib, shobjs, sharedLibs, staticLibs, slDeps, options) | Creates a shared library. |
The rules for building actors and processes use the following common arguments:
obj is the list of binary objects included in the actor/process.
prog is the name of the actor/process.
libs is the list of additional libraries used to build the actor/process. The application is linked by default with the library to provide either the basic or extended environment.
The rules used to build dynamic or shared libraries for actors and processes are described in more detail in "Building a Dynamic Process".
The Package.rules file contains macros known as Imake packaging rules which are used for building a binary distribution. Their names and functions are listed in Table 6-5.
Table 6-5 imake Packaging RulesMacro name | Function |
---|---|
DistLibrary(lib, dir) | Creates the directory dir and copies the library lib into it. |
DistActor(actor, dir) | Creates the directory dir and copies the actor actor into it. |
DistFile(file, dir) | Creates the directory dir and copies the file file into it. |
DistRenFile(file, nFile, dir) | Creates the directory dir, copies file into it, changing the name of file to nFile. |
DistProgram(program, dir) | Creates the directory dir and copies program into it. |
The following examples demonstrate how to create an Imakefile using single and multiple source files.
The application in this example is composed of a single C source file, myprog.c, in the directory myprog. Writing an Imakefile is fairly straightforward.
Set the SRCS variable to the list of source files.
In this case there is only one source file:
SRCS = myprog.c
Specify how to build the executable.
The macro you use depends on the type of binary you want. If you want to build a user-mode binary (for example myprog_u), use the UserActorTarget() macro, as illustrated in the following file extract. The first argument is the name of the executable and the second lists the object files. The third argument enables you to specify which libraries your program requires. In this example there is no library, therefore the argument is empty (you could also pass a NullParameter).
UserActorTarget(myprog_u,myprog.o,)
In the following example, we will build a user-mode binary. If you want to build a supervisor-mode binary (for example, myprog_s.r), use the SupActorTarget() macro as shown. The arguments are the same as for UserActorTarget().
SupActorTarget(myprog_s.r,myprog.o,)
Use the Depend() macro to generate the Makefile dependencies.
Depend($(SRCS))
The Imakefile is now complete and looks as follows:
SRCS = myprog.c UserActorTarget(myprog_u,myprog.o,) Depend($(SRCS))
Generate the Makefile with the ChorusOSMkMf tool.
See the ChorusOSMkMf(1CC) man page for details of how to do this. In the myprog directory, type:
% ChorusOSMkMf build_dir |
Where build_dir is the directory where you have built the ChorusOS system image on which your application will run.
Generate the make dependencies
To do this type:
% make depend |
Compile and link the program
To do this type:
% make |
The compiled program is now in your myprog directory and ready to be executed.
If an application used source files located in several subdirectories, you need to create a root Imakefile in the root directory, containing only the following:
#define IHaveSubdirs SUBDIRS = subdir1 subdir2 ...
Where subdir1, subdir2, ... are the subdirectories containing the source files (or other intermediate root Imakefile files). Next, create an Imakefile in each subdirectory containing source files. To generate the first Makefile, go to the root directory and type:
% ChorusOSMkMf build_dir |
Next, populate the tree with Makefile files, generate dependencies and finally compile the programs by typing make Makefiles, make depend, and then make.
% make Makefiles % make depend % make |
The compiled program is now ready to be executed.
Examples of Imakefiles which can be modified and used to build your own applications are provided in install_dir/chorus_family/src/opt/examples.
Actors and processes are defined according to the types of libraries they use. This section describes the different library types in the ChorusOS operating system and illustrates the kinds of actors and processes that would use each library type.
There are three types of library in the ChorusOS operating system:
Static library names are suffixed by .a. The static library is a collection of binary object files (.o). The linker concatenates all required binary objects within the static libraries into an executable program file.
Dynamic
Dynamic library names are suffixed by .so and can be linked with a program at runtime. Dynamic linking is supported by a ChorusOS operating system component named the runtime linker (see "Runtime Linker"). There are two two cases in which dynamic linking occurs:
At actor start-up - in order to build the executable, the runtime linker loads and links a list of libraries. These libraries are known as the dependencies of the executable.
During actor execution - with the dynamic linking API, an actor can explicitly load and link to dynamic libraries using the dlopen() function. This facilitates dynamic programming.
Dynamic libraries are loaded at runtime and are not included in the executable. This reduces the size of executable files. Dynamic libraries use relocatable code format. This code is turned into absolute code by the runtime linker.
In the ChorusOS operating system, both user and supervisor actors (but not boot actors) can use dynamic libraries.
Shared
Shared libraries are also suffixed by .so. Like dynamic libraries, shared libraries can be dynamically linked but unlike dynamic libraries, they are not duplicated in physical memory. When two actors, each with its own private copy of data, use the same shared library, only one instance of the library is present in physical memory.
Shared libraries use Position Independent Code (PIC). In PIC, references to global symbols are made indirectly through symbol tables. References to global symbols, as well as local jumps are made relative to the program counter. This code, as a result, can run anywhere in memory without being modified, however, the tables must be relocated first before program execution can begin. Using PIC in shared libraries does not perform as well as the code in dynamic libraries because the global symbol information is accessed through indirect references.
To compile an application in PIC, you must set the following feature in your Imakefile:
FPIC = ON
The FLARGEGOT feature complements the FPIC feature. Setting FLARGEGOT = ON enables support for large tables of global symbols.
Both dynamic and shared library names are suffixed by .so. However, each library type is built in a different way and is readily distinguished. The imake tool does not check that library components are PIC format binary object files. The resulting library may contain .o objects, which are not in PIC format, that can still be dynamically loaded but can no longer be shared.
It is possible to check whether or not a library is shared. To check whether or not the libfoo.so library is shared, for example, run the following GNU command:
% objdump -x libfoo.so | grep TEXTREL |
objdump is located in the chorus_family/tools tree. The specific path includes a family-dependent prefix.
If the library is shared, no output is generated by this command. Only dynamic library object files contain references to TEXTREL.
Actors or processes can be divided into two groups, according to the types of libraries they use.
Relocatable actors and processes
Upon actor start-up, the runtime linker loads the actor and performs the necessary relocations. A relocatable actor or process uses only static libraries.
Dynamic actors/processes
Upon actor start-up, the runtime linker loads the actor or process and performs the necessary relocations. It also loads and links the actor dependencies (the required dynamic libraries).
When you build a process or actor other than an embedded actor, you must specify one of the following macros in your Imakefile:
Relocatable processes/actors:
UserActorTarget
SupActorTarget
CXXUserTarget
CXXSupTarget
Dynamic processes/actors:
DynamicUserTarget
DynamicSupTarget
DynamicCXXUserTarget
DynamicCXXSupTarget
Shared processes/actors:
SharedUserTarget
SharedSupTarget
SharedCXXUserTarget
SharedCXXSupTarget
Each of these rules implicitly calls the libc.a or libc.so libraries. Therefore, when creating the Imakefile for a process or actor, there is no need to think about the libc.a or libc.so libraries because this is taken care of when you select the Imakefile rule. The C++ library is automatically included by specifying CXX in the relevant Imakefile rule.
The static linker runs on the development host and the runtime linker (dynamic linker) runs on the target.
The following table summarizes the actions performed by the static linker and by the runtime linker.
Link | Relocatable executable | Dynamic executable |
Static Linker | .a Static linker adds the required objects (.o) of a static library (.a) to the executable. | .a - Static linker adds the required objects (.o) of a static library (.a) to the executable. .so - Static linker adds the library to the list of libraries to be loaded at actor start-up. |
Runtime Linker | n/a | .so - At actor start-up, libraries are loaded and linked by the runtime linker. Libraries to be loaded are defined either as static links, or in the LD_PRELOAD environment variable. The runtime linker uses a library search path to locate dynamic and shared libraries. |
Runtime Linker (dlopen) | n/a | .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 libraries it uses are also loaded.
Dynamic applications consist of one or more dynamic objects. The dynamic application is typically a dynamic executable and associated 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 enable the application to extend its address space by adding dynamic objects and binding to symbols within them.
The runtime linker performs the following functions:
Analyzes the executable's dynamic information section and determines which dynamic libraries are required.
Locates and loads the required dynamic libraries, and analyzes their dynamic information sections to determine whether additional dynamic library dependencies are required.
Performs the necessary relocations to bind dynamic libraries in preparation for actor execution (after all dynamic libraries are located and loaded).
Calls any initialization functions provided by the dynamic libraries. The initialization functions are called in the reverse order of the topologically sorted dependencies. Should cyclical dependencies exist, the initialization functions are called using the sorted order with the cycle removed.
Passes control to the application.
Calls all finalization functions on deletion of dynamic objects from the actor. The finalization functions are called in the order of the topologically sorted dependencies.
Acquires additional dynamic objects with dlopen() and binds to symbols within these objects using dlsym(), if required by the application.
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, proceeded 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 that are to be searched ahead of the default directories.
The runtime linker requires a file system to load dynamic objects. This file system can be on a host and can be 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.
The following environment variables are used by the runtime linker:
LD_LIBRARY_PATH specifies a colon-separated list of directories that are to be searched ahead of the default directories defined previously. The colon-separated list is used to enhance the search path that the runtime linker uses to locate dynamic and shared libraries.
LD_PRELOAD provides a dynamic object name that is linked after the program is loaded but before any other dynamic objects that the program references.
LD_DEBUG is a column-separated list of tokens
for debugging the runtime linking of an application. Each token is associated
with a set of traces that are displayed during runtime linking. The supported
tokens are: file-ops
, reloc
, symbol-resolution
, malloc
, segment-alloc
, dependency
, misc
, linking
, dynamic-map-op
, group
, and error
.
Wildcard substitutions can also be used, for example, s*
matches both symbol-resolution
and segment-alloc
. Using just the wildcard (*
) matches
all traces.
Immediate binding:
The runtime linker performs both data reference and function reference relocations during process initialization (before transferring control to the application). This behavior is equivalent to the LD_BIND_NOW behavior in the Solaris operating environment (lazy binding is not supported).
No version checking:
The runtime linker does not perform version dependency checking. When looking for a library, the runtime linker looks for a file name that matches the library name exactly. This behavior is equivalent to the LD_NOVERSION behavior in the Solaris operating environment.
Weak symbols and aliases:
During symbol resolution, weak symbols are silently overridden by any global definition with the same name. Weak symbols can be defined alone or as aliases to global symbols. Weak symbols are defined with pragma definitions.
Call debug:
The runtime linker obtains debug information from the dynamic libary, thereby enabling you to debug the dynamic library.
In a dynamic program, an actor can link a dynamic or shared library explicitly during its execution. This on-demand object linking has several advantages:
By processing a dynamic object when it is required rather than during the initialization of an application, start-up time can be greatly reduced. In fact, the object might not be required if its services are not required during a particular run of the application.
The application can choose between several different dynamic objects depending on the exact services required. For example, if different libraries implement the same driver interface, the application can choose a specific driver implementation and load it dynamically.
Any dynamic object added to the actor address space during execution can be freed after use, thereby reducing overall memory consumption.
Typically, an application performs the following sequence to access an additional dynamic object using the dynamic library API:
A dynamic object is located and added to the address space of a running application using dlopen(). Any dependencies this dynamic object has are also located and added at this time.
The added dynamic object and its dependencies are relocated, and any initialization sections within these objects are called.
The application locates symbols within the added objects using dlsym(). The application is then able to reference the data or call the functions defined by these new symbols.
After the application has finished with the objects, the address space can be freed using dlclose(). Any termination section within the objects being freed will be called at this time.
Any error conditions that occur as a result of using these runtime linker interface routines can be displayed using dlerror().
Both dynamic and shared libraries can be used in dynamic programs. The only difference is that shared libraries are not duplicated in memory.
The following imake macro is used to build dynamic libraries:
DynamicLibraryTarget(dlib, objs, staticLibs, dynamicLibs, dlDeps, options)
This macro includes the following arguments:
dlib: name of the resulting dynamic library (suffixed by .so).
objs: library components. A list of binary object files (suffixed by .o).
staticLibs: a list of the static libraries (.a) that can be statically linked.
dynamicLibs: a list of dependencies. This list includes the dynamic libraries that must be loaded together with the resulting library. Each library can be defined in one of two ways:
-L path -l name
On the host, the linker looks for the library path/libname.so. On the target, the runtime linker looks for lib name.so in the library search path.
path
This library path can be absolute or relative and is used on the host by the linker, and on the target by the runtime linker. A relative path containing a "/" is interpreted as relative to the current directory by the runtime linker. A path without the "/" is searched in the library search path by the runtime linker.
dlDeps: a list of dynamic libraries on which the resulting library depends. If these dynamic libraries are changed, the resulting library dlib will be rebuilt. Each library must be defined as a path on the host. Generally dlDeps duplicates the libraries described in dynamicLibs. To express the dependency without embedding a path in the executable, use the -L path -l name syntax.
options: any linker 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, , )
Dynamic libraries are supported with the gcc
compiler only.
The following imake macros are used to build dynamic applications:
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)
For details of the functions that each macro performs, refer to "imake Build Rules".
The prog argument is the name of the resulting process. Other arguments are similar to those in the DynamicLibraryTarget() macro. For the options argument, the following options are particularly useful:
-soname=<name>
Within a DynamicLibraryTarget() rule, this option sets the internal soname of the library. If a library is used as a dependency in a rule that builds a dynamic executable and has a soname defined, the executable records the soname instead of the dynamicLibs argument.
-rpath <dir>
This option defines the runpath directory that is added to the library search path.
The following example builds a dynamic application called dyn_app from the binary object files a.o and b.o. The process is statically linked with the static ChorusOS operating system library. When this application is started, the runtime linker loads the dynamic library libdyn.so. In the target file system, this library can be located in the /libraries directory because this directory will be added to the search path of the runtime linker.
DynamicUserTarget( dyn_app, a.o b.o, , libdyn.so, , -rpath /libraries)
The following imake macro is used to build shared libraries:
SharedLibraryTarget(shlib, shobjs, sharedLibs, staticLibs, slDeps, options)
shlib: the name of the resulting shared library (suffixed by .so).
shobjs: a list of binary object files in PIC format (suffixed by .o).
sharedLibs: a list of dependencies. This includes the shared libraries that must be loaded together with the resulting library. Each library can be defined in one of two ways:
-L path -l name
On the host, the linker looks for the library path/libname.so. On the target, the runtime linker looks for lib name.so in the library search path.
path
This is an absolute or relative library path used on the host by the linker, and on the target by the runtime linker. A relative path containing a "/" is interpreted as relative to the current directory by the runtime linker. A path without the "/" is searched in the library search path by the runtime linker.
staticLibs: a list of static libraries (.a) that are statically linked.
slDeps: a list of shared libraries on which the resulting library depends. If these libraries are changed, the resulting library slib will be rebuilt. Each library must be defined as a path on the host. Generally slDeps duplicates the libraries described in sharedLibs. To express the dependency without embedding a path in the executable, use the -L path -l name syntax.
options: any linker options, preceded by -Xlinker. This option must be used to supply system-specific linker options which cannot be recognized by the compiler. To pass an option that takes an argument, you must use -Xlinker twice, once for the option and once for the argument.
The following example builds a shared library named libfoo.so from the PIC binary objects files, a.o and b.o. When this library is loaded dynamically, the runtime linker will also load the libshared.so.
SharedLibraryTarget( libfoo.so, a.o b.o, , libshared.so, , )
The following imake macros are used to build shared applications:
SharedUserTarget(prog, shobjs, staticLibs, sharedLibs, slDeps, options) SharedSupTarget(prog, objs, staticLibs, sharedLibs, slDeps, options) SharedCXXUserTarget(prog, objs, staticLibs, sharedLibs, slDeps, options) SharedCXXSupTarget(prog, objs, staticLibs, sharedLibs, slDeps, options)
For more information on each of these macros, refer to "imake Build Rules".
The prog argument is the name of the resulting process. Other arguments are the same as the SharedLibraryTarget() macro. For the options argument, the following options are particularly useful:
-Xlinker -soname=<name>
Within the SharedLibraryTarget() rule, this option sets the internal soname of the library. If a library used as a dependency in a rule that builds a dynamic application has a soname defined, the executable records the soname instead of the sharedLibs argument.
-Xlinker -rpath -Xlinker <dir>
This option defines the runpath directory that is added to the library search path.
The following example builds a shared application named shr_app from the PIC binary object files a.o and b.o. When this process is started, the runtime linker loads the shared libraries libshared.so and libcx.u.so (if required). In the target file system, this library is located in the /libraries directory because this directory will be added to the search path of the runtime linker.
SharedUserTarget( shr_app, a.o b.o, , libshared.so, , -Xlinker -rpath -Xlinker /libraries)
As with shared libraries, it is possible to build shared applications which do not contain .o objects in PIC format. The applications are still able to use shared libraries, however the application code is not shared.
This section discusses two examples of dynamic and shared applications. In 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).
In these examples, 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.
The following dynamic application uses a custom dynamic library which will be loaded and linked at application start-up. It uses the function foo() which is defined in the dynamic library. This function calls the bar() function defined in the main application.
The following is the dynamic application progdyn.c:
#include <chorus.h> extern void foo(); main() { foo(); /* calling foo defined in the library */ } void bar() { printf ("bar called\n"); }
The following 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 application */ }
Create the directory and the Imakefile.
Create a directory libdyndir in $WORK, containing libdyn.c and the following Imakefile:
SRCS = libdyn.c DynamicLibraryTarget (libdyn.so, libdyn.o, , , ,) Depend(libdyn.c)
Build the dynamic library.
In the libdyndir directory, build the dynamic library libdyn.so using the commands ChorusOSMkMf, make depend, and make.
Create the directory and the Imakefile.
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(progdyn.c)
Build the dynamic application.
In the progdyndir directory, build the dynamic application progdyn using the commands ChorusOSMkMf, make depend and make.
% ChorusOSMkMf $WORK % make depend % make |
Copy the dynamic application.
Copy the dynamic application into the /bin subdirectory of the chorus_root_directory directory:
% cp $WORK/progdyndir/progdyn chorus_root_directory/bin |
Copy the dynamic library.
Copy the dynamic library into the /lib subdirectory of the chorus_root_directory directory:
% cp $WORK/libdyndir/libdyn.so chorus_root_directory/lib |
The following command will notify 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 application macro (-rpath /lib).
Start the application.
Start the application and dynamically load the libdyn.so library, using the following command:
% rsh jericho /bin/progdyn |
The following program explicitly loads a dynamic library at runtime using the function dlopen(). This program searches for the address of the dynfunc() function (defined in the library) and calls this function.
The following 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)(); }
The following is the dynamic library libdyn2.c:
#include <chorus.h> void dynfunc() { printf ("Calling dynfunc\n"); }
The program and library discussed in the previous section are built in the same way as the previous example using two Imakefiles:
Create the first directory.
Create a directory called libdyn2dir in $WORK, containing libdyn2.c and the following Imakefile:
SRCS = libdyn2.c DynamicLibraryTarget (libdyn2.so, libdyn2.o, , , , ) Depend(libdyn2.c)
Create the second directory.
Create a directory progdyn2dir in $WORK, containing progdyn2.c and the following Imakefile:
SRCS = progdyn2.c DynamicUserTarget (progdyn2, progdyn2.o, , , , ) Depend(progdyn2.c)
Copy the 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 library.
Copy the dynamic library into the /lib subdirectory of the chorus_root_directory directory:
% cp $WORK/libdyn2dir/libdyn2.so chorus_root_directory/lib |
Notify the runtime linker.
Use the following command to notify the runtime linker where to find the libdyn2.so dynamic library:
% rsh jericho setenv LD_LIBRARY_PATH /lib |
Start the program.
Use the following command to start the program:
% rsh jericho arun /bin/progdyn2 |
At program start-up, the runtime linker will only load the progdyn2 executable . The libdyn2.so library is loaded when the dlopen() function is called.
A small modification to the previous example enables a shared application to load a shared library dynamically.
Create the shared library file.
Copy the libdyn.c file into the libshareddir directory and rename it libshared.c. The ensuing Imakefile will build a PIC binary object file called libshared.o and a shared library named libshared.so as follows:
FPIC = ON SRCS = libshared.c SharedLibraryTarget (libshared.so, libshared.o, , , , ) Depend($(SRCS))
Create the shared program file.
Copy the progdyn.c file into the progshareddir directory and rename it progshared.c. The ensuing Imakefile will build a PIC binary object file called progshared.o and an executable file called progshared:
FPIC = ON SRCS = progshared.c LIBPATH = <pathname of the shared library directory> SharedUserTarget (progshared_u, progshared.o, $(UTILS_LIB) $(CLX_UTILS_LIB), -L$(LIBPATH) -Xlinker -rpath -Xlinker/shared -lshared,,) Depend($(SRCS))
Copy the shared application.
Copy the shared application into the /bin subdirectory of the chorus_root_directory directory:
% cp $WORK/progshareddir/progshared_u chorus_root_directory/bin |
Copy the shared library.
Copy the shared library into the /shared subdirectory of the chorus_root_directory directory:
% cp $WORK/libshareddir/libshared.so chorus_root_directory/shared |
There is no need to set LD_LIBRARY_PATH as the path for the runtime linker. This path was specified in the Imakefile by setting the -Xlinker -rpath option.
Start the application.
The following command starts the application and loads the libshared.so and libc.so libraries (if they have not already been loaded by another application):
% rsh jericho /bin/progshared_u |
The following procedure enables a shared program to load a shared library explicitly.
Create the library file.
Copy the libdyn2.c file into the libshared2dir directory and rename it libshared2.c. The following Imakefile will build a shared library called libshared2.so:
FPIC = ON SRCS = libshared2.c SharedLibraryTarget (libshared2.so, libshared2.o,,,,) Depend($(SRCS))
Create the program file.
Copy the progdyn2.c file into the progshared2dir directory and rename it progshared2.c. The following Imakefile will build a PIC binary object file called progshared2.o and an executable file called progshared2
FPIC = ON SRCS = progshared2.c SharedUserTarget (progshared2_u, progshared2.o, $(UTILS_LIB) $(CLX_UTILS_LIB),,,) Depend($(SRCS))
Copy the application.
Copy the shared application into the /bin subdirectory of the chorus_root_directory directory:
% cp $WORK/progshared2dir/progshared2_u chorus_root_directory/bin |
Copy the shared library.
Copy the shared library into the /lib subdirectory of the chorus_root_directory directory:
% cp $WORK/libshared2dir/libshared2.so chorus_root_directory/lib |
Notify the runtime linker.
The following command will notify the runtime linker where to find the libshared2.so shared library:
% rsh jericho setenv LD_LIBRARY_PATH /lib/shared:/lib |
Alternatively, set the runpath to /lib in the ldopts argument of the program macro (-rpath /lib).
Start the program.
The following command will start the program and load the libshared2.so and libc.so libraries (if they have not already been loaded by another actor):
% rsh jericho /bin/progshared2 |