8.4   Building, Loading, and Unloading Application Modules

In the Tornado development environment, application modules for the target system are created and maintained on a separate development host. First, the source code, generally in C or C++, is edited and compiled to produce a relocatable object module. Application modules use VxWorks facilities by virtue of including header files that define operating-system interfaces and data structures. The resulting object modules can then be loaded and dynamically linked into a running VxWorks system over the network.

The following sections describe in detail the procedures for carrying out cross-development manually (without using the project facility).

8.4.1   Using VxWorks Header Files

Many application modules make use of VxWorks operating system facilities or utility libraries. This usually requires that the source module refer to VxWorks header files. The following sections discuss the use of VxWorks header files.

VxWorks header files supply ANSI C function prototype declarations for all global VxWorks routines. The ANSI C prototypes are conditionally compiled; to use them, the preprocessor constant __STDC__ must be defined. ANSI C compilers define this constant by default. VxWorks provides all header files specified by the ANSI X3.159-1989 standard.

VxWorks system header files are in the directory installDir/target/h and its subdirectories.


*

NOTE: The notation $(WIND_BASE) is used in makefiles to refer to the Tornado installation directory. This chapter uses that notation because makefiles are the most convenient way to run the Tornado compilation tools. If you run the compiler from the Windows command prompt, write %WIND_BASE% instead.

VxWorks Header File: vxWorks.h

The header file vxWorks.h contains many basic definitions and types that are used extensively by other VxWorks modules. Many other VxWorks header files require these definitions. Thus, this file must be included first by every application module that uses VxWorks facilities. Include vxWorks.h with the following line:

#include "vxWorks.h"

Other VxWorks Header Files

Application modules can include other VxWorks header files as needed to access VxWorks facilities. For example, an application module that uses the VxWorks linked-list subroutine library must include the lstLib.h file with the following line:

#include "lstLib.h"

The manual entry for each library lists all header files necessary to use that library.

ANSI Header Files

All ANSI-specified header files are included in VxWorks. (UNIX)

This implies that many familiar UNIX header files are available under VxWorks as well. There are two file names that differ from the usual UNIX names: a_out.h (which corresponds to the UNIX a.out.h) and stdlib.h (which corresponds to the UNIX malloc.h)

The -I Compiler Flag

By default, the compiler searches for header files first in the directory of the source module and then in directories that apply only to the development host. With the GNU compiler, you can avoid these host-system include directories with the compilation flag -nostdinc. To access the VxWorks header files, the compiler must also be directed to search $(WIND_BASE)/target/h. Thus, the following option flag is standard for VxWorks compilation:

-I $(WIND_BASE)/target/h

Some header files are located in subdirectories. To refer to header files in these subdirectories, be sure to specify the subdirectory name in the include statement, so that the files can be located with a single -I specifier. For example:

#include "vxWorks.h" 
#include "sys/stat.h"

VxWorks Nested Header Files

Some VxWorks facilities make use of other, lower-level VxWorks facilities. For example, the tty management facility uses the ring buffer subroutine library. The tty header file tyLib.h uses definitions that are supplied by the ring buffer header file rngLib.h.

It would be inconvenient to require you to be aware of such include-file interdependencies and ordering. Instead, all VxWorks header files explicitly include all prerequisite header files. Thus, tyLib.h itself contains an include of rngLib.h. (The exception to this is the basic VxWorks header file vxWorks.h, which all other header files assume is already included.)

This, in turn, might lead to a problem: a header file could get included more than once, if one were included by several other header files, or if it were also included directly by the application module. Normally, including a header file more than once generates fatal compilation errors, because the C preprocessor regards duplicate definitions as potential sources of conflict. To avoid this problem, all VxWorks header files contain conditional compilation statements and definitions that ensure that their text is included only once, no matter how many times they are specified by include statements. Thus, an application module can include just those header files it needs directly, without regard for interdependencies or ordering, and no conflicts arise.

Internal Header Files

Table 8-2 lists the subdirectories of installDir/target/h used by VxWorks for internal header files. These header files are, for the most part, not intended for applications. The following subdirectories are exceptions, and are sometimes required by application programs:

VxWorks Private Header Files

VxWorks modules are designed so that you never need to know or reference the modules' internal data structures. In general, all legitimate access to a facility is provided by a module's subroutine interfaces. The internal details should be thought of as "hidden" from application developers. This means that the internal implementations can change without affecting your use of the corresponding facilities.

Internal details in VxWorks are hidden using two conventions. Some header files mark hidden code using the following comments:

/* HIDDEN */ 
... 
/* END HIDDEN */

Internal details are also hidden with private header files: files that are stored in the directory installDir/target/h/private. The naming conventions for these files parallel those in installDir/target/h with the library name followed by P.h. For example, the private header file for semLib is installDir/target/h/private/semLibP.h.


*

CAUTION: Never make references to any of the hidden definitions, or base any assumptions on those definitions. The only supported uses of a module's facilities are through the public definitions in the header file, and through the module's subroutine interfaces. Although this rule is not currently enforced in any way, it is in your interest to observe it. Your adherence ensures that your application code is not affected by internal changes in the implementation of a VxWorks module.

8.4.2   Compiling Application Modules

Tornado includes a full-featured C and C++ compiler and associated tools, collectively called the GNU ToolKit. Extensive documentation for this set of tools is printed in a separate manual: the GNU ToolKit User's Guide. This section provides some general orientation about the source of these tools, and describes how the tools are integrated into the Tornado development environment.

The GNU Tools

GNU ("GNU's Not UNIX!") is a project of the Free Software Foundation started by Richard Stallman and others to promote free software. To the FSF, free software is software whose source code can be copied, modified, and redistributed without restriction. GNU software is not in the public domain; it is protected by copyright and subject to the terms of the GNU General Public License, a legal document designed to ensure that the software remains free--for example, by prohibiting proprietary modifications and concomitant restrictions on its use. The General Public License can be found in the file COPYING that accompanies the source code for the GNU tools, and in the section titled Free Software at the back of the GNU ToolKit User's Guide.

It is important to be aware that the terms under which the GNU tools are distributed do not apply to the software you create with them. In fact, the General Public License makes no requirements of you as a software developer at all, as long as you do not modify or redistribute the tools themselves. On the other hand, it gives you the right to do both of these things, provided you comply with its terms and conditions. It also permits you to make unrestricted copies for your own use.

The Wind River GNU distribution consists of the GNU ToolKit, which contains GNU tools modified and configured for use with your VxWorks target architecture. The source code for these tools is included.

Cross-Development Commands

The GNU cross-development tools in Tornado have names that clearly indicate the target architecture. This allows you to install and use tools for more than one architecture, and to avoid confusion with corresponding host native tools. A suffix identifying the target architecture is appended to each tool name. For example, the cross-compiler for the 68K processor family is called cc68k, and the assembler as68k. The suffixes used are shown in Table 8-3. Note that the text in the GNU ToolKit User's Guide refers to these tools by their generic names (without a suffix).

Table 8-3:  Suffixes for Cross-Development Tools


Architecture
Command Suffix

MC680x
68k 
SPARC/SPARClite 
sparc 
i960 
1  
x86 
386 
MIPS 
mips 
PowerPC 
ppc  
ARM  
arm  
Simulators  
simso, hppa, simnt  

1:  See C. Intel i960.

Defining the CPU Type

Tornado can support multiple target architectures in a single development tree. To accommodate this, several VxWorks header files contain conditional compilation directives based on the definition of the variable CPU. When using these header files, the variable CPU must be defined in one of the following places:

To define CPU in the source modules or header files, add the following line:

#define CPU cputype

To define CPU on the compilation command line, add the following flag:

-DCPU=cputype

The constants shown in Table 8-4 are supported values for cputype.

Table 8-4:  Values for cputype


Architecture
Value

MC680x
MC68000, MC68010, MC680201 , MC68040, MC68LC0402 , MC68060, CPU32  
SPARC, SPARClite 
SPARC3  
i960 
I960CA, I960KB, I960KA, I960JX  
i386,i486, Pentium,
PentiumPro 
I80386, I80486, PENTIUM4  
MIPS 
R3000, R4000, R4650 
PowerPC  
PPC403, PPC603, PPC604, PPC860  
ARM  
ARM7TDMI, ARM7TDMI_T, ARMSA110, ARM710A, ARM810  
Simulators  
SIMSPARCSOLARIS, SIMHPPA, SIMNT  

1:  MC68020 is the appropriate value for both the MC68020 and the MC68030 CPUs.

2:  MC68LC040 is the appropriate value for both the MC68LC040 and the MC68EC040.

3:  SPARC is the appropriate value for both SPARC and SPARClite CPUs.

4:  PENTIUM is the appropriate value for both Pentium and PentiumPro CPUs.

With makefiles, the CPU definition can be added to the definition of the flags passed to the compiler (usually CFLAGS).

In the source code, the file vxWorks.h must be included before any other files with dependencies on the CPU flag.

As well as specifying the CPU value, you must usually run the compiler with one or more option flags to generate object code optimally for the particular architecture variant. These option flags usually begin with -m; see Compiling C Modules.

Compiling C Modules

The following is an example command to compile an application module for a VxWorks MC68020 system:

% cc68k -fno-builtin -I %WIND_BASE%\target\h -nostdinc -O \ 
-c -DCPU=MC68020 applic.c

This compiles the module applic.c into an object file applic.o. Table 8-5 shows a similar example compiler invocation for each CPU architecture family.

Table 8-5:  Compiler Invocation by Architecture Family


Architecture
Example Invocation

MC680x0  
cc68k -fno-builtin -I $(WIND_BASE)/target/h -nostdinc -O -c\
-m68040 -DCPU=MC68040 applic.c
 
SPARC  
ccsparc -fno-builtin -I $(WIND_BASE)/target/h -nostdinc -O2 -c \
-DCPU=SPARC applic.c
 
SPARClite  
ccsparc -fno-builtin -I $(WIND_BASE)/target/h -nostdinc -O2 -c \
-msparclite -DCPU=SPARC applic.c
 
i960  
See your i960 toolkit documentation and C. Intel i960.  
i386/i486  
cc386 -fno-builtin -I $(WIND_BASE)/target/h -nostdinc -O -c \
-fno-defer-pop -mno-486 -DCPU=I80386 applic.c
 
MIPS  
ccmips -fno-builtin -I $(WIND_BASE)/target/h -nostdinc -O2 -c \
-mcpu=r4000 -mips3 -G 0 -DCPU=R4000 applic.c
 
PowerPC  
ccppc -O2 -mcpu=603 -I$WIND_BASE/target/h -fno-builtin \
-fno-for-scope -nostdinc -DCPU=PPC603 -D_GNU_TOOL -c applic.c
 
ARM  
ccarm -DCPU=ARM7TDMI -mcpu=arm7tdmi -mno-sched-prolog \
-fno-builtin -O2 -nostdinc -I $WIND_BASE/target/h -c applic.c
 
Simulator  
ccsimso -DCPU=SIMSPARCSOLARIS -ansi -nostdinc -g \
-fno-builtin -fvolatile -DRW_MULTI_THREAD -D_REENTRANT \
-O2 -I. -I /wind/target/h -c applic.c
 

The following list gives summary descriptions of the compiler flags in Table 8-5. For more information, see the GNU ToolKit User's Guide, or the architecture appendices.

-c
Compile only; do not link for execution under the host. The output is an unlinked object module with the suffix ".o", in this case applic.o.

-DCPU=arch
Define the CPU type.

-DVX_IGNORE_GNU_LIBS
Define the constant used by the i960 configuration to suppress the use of the GNU libraries (cc960 only).

-D_GNU_TOOL
Required; defines the compilation toolkit used to compile VxWorks or applications (ccppc only).

-fno-builtin
Use library calls even for common library subroutines.

-fno-defer-pop
Always pop the arguments to each function call as soon as that function returns.

-fno-for-scope
Required; allows the scope of variables declared within a for loop to be outside of the for loop.

-G 0
Do not use the MIPS global pointer (ccmips only).

-I   $(WIND_BASE)/target/h
Include VxWorks header files (see 8.4.1 Using VxWorks Header Files).

-m68040
Generate code for a specific variant of the MC680x0 family.

-mcpu=
Generate MIPS R4200 or R4600 specific code (ccmips only).

-mips3
Issue instructions from level 3 of the MIPS instruction set (ccmips only).

-mno-486
Generate code optimized for an i386 rather than for an i486 (cc386 only).

-msparclite
Generate SPARClite-specific code (ccsparc only).

-nostdinc
Do not search host-system header files; search only the directories specified with the -I flag and the current directory for header files.

-O
Perform standard optimizations.

-O2
Use level 2 optimization.

Compiling C++ Modules

Tornado supports the GNU compiler, a standard part of the cross-compilation tools distributed for Tornado, compiles source programs in either C or C++. To use this compiler for C++, invoke ccarch on any source file with a C++ suffix (such as .cpp). For complete information on using C++, including a detailed discussion of compiling C++ modules, see 5. C++ Development.

Compiling C++ applications in the VxWorks environment involves the following steps:

  1. C++ source code is compiled into object code for a specific target architecture, just as for C applications.

  1. The compiled object module is munched. Munching is the process of scanning an object module for non-local static objects, and generating data structures that VxWorks run-time support can use to call the objects' constructors and destructors. The details are described in 5.2.5 Munching C++ Application Modules.

8.4.3   Static Linking (Optional)

After you compile an application module, you can load it directly into the target with the Tornado dynamic loader (through the shell or through the debugger).

In general, application modules do not need to be linked with the linker from the GNU ToolKit, ldarch. However, using ldarch may be required when several application modules cross-reference each other. The following example is a command to link several application modules, using the GNU linker for the MC680x0 family of processors.

C:\devt> ld68k -o applic.o -r applic1.o applic2.o applic3.o

This creates the object module applic.o from the object modules applic1.o, applic2.o, and applic3.o. The -r option is required, because the object-module output must be left in relocatable form so that it can be downloaded and linked to the target VxWorks image.

Any VxWorks facilities called by the application modules are reported by ldarch as unresolved externals. These are resolved by the Tornado loader when the module is loaded into VxWorks memory.


*

WARNING: Do not link each application module with the VxWorks libraries. Doing this defeats the load-time linking feature of Tornado, and wastes space by writing multiple copies of VxWorks system modules on the target.

8.4.4   Downloading an Application Module

After application object modules are compiled (and possibly linked by the host ldarch command), they can be dynamically loaded into a running VxWorks system by invoking the Tornado module loader. You can do this either from the Tornado shell using the built-in command ld( ), or from the debugger using the Debug menu or the load command.

The following is a typical load command from the Tornado shell:

-> ld <applic.o

This relocates the code from the host file applic.o, linking to previously loaded modules, and loads the object module into the target's memory. Once an application module is loaded into target memory, any subroutine in the module can be invoked directly from the shell, spawned as a task, connected to an interrupt, and so on.

The shell ld( ) command, by default, adds only global symbols to the symbol table. During debugging, you may want local symbols as well. To get all symbols loaded (including local symbols), you can use the GDB command load from the debugger. Because this command is meant for debugging, it always loads all symbols. Alternately, you can load all symbols by calling the shell command ld( ) with a full argument list instead of the shell-redirection syntax shown above. When you use an argument list, you can get all symbols loaded by specifying a 1 as the first argument, as in the following example:

-> ld 1,0,"applic.o"

In the foregoing examples, the object module applic.o comes from the shell's current working directory. Normally, you can use either relative path names or absolute path names to identify object modules to ld( ). If you use a relative path name, the shell converts it to an absolute path (using its current working directory) before passing the download request to the target server. In order to avoid trouble when the shell where you call ld( ) is not running on the same host as its target server, Tornado supplies the LD_SEND_MODULES facility; see the Tornado User's Guide: Shell. If you are using a remote target server and ld( ) fails with a "no such file" message, be sure that LD_SEND_MODULES is set to "on."


*

CAUTION: (Windows) If you call ld( ) with an explicit argument list, any backslash characters in the module-name argument must be doubled. If you supply the module name with the redirection symbol, as in the earlier example in this section, no double backslashes are needed. See the Tornado User's Guide: Shell for more discussion of this issue.

For more information about loader arguments, see the discussion of ld( ) (in the reference entry for windsh).

For information about the target-resident version of the loader (which also requires the target-resident symbol table), see the VxWorks reference entry for loadLib.

8.4.5   Module IDs and Group Numbers

When a module is loaded, it is assigned a module ID and a group number. Both the module ID and the group number are used to reference the module. The module ID is returned by ld( ) as well as by the target-resident loader routines. When symbols are added to the symbol table, the associated module is identified by the group number (a small integer). (Due to limitations on the size of the symbol table, the module ID is inappropriate for this purpose.) All symbols with the same group number are from the same module. When a module is unloaded, the group number is used to identify and remove all the module's symbols from the symbol table.

8.4.6   Unloading Modules

Whenever you load a particular object module more than once, using the target server (from either the shell or the debugger), the older version is unloaded automatically. You can also unload a module explicitly: both the Tornado shell and the target-resident VxWorks libraries include an unloader. To remove a module from the shell, use the shell routine unld( ); see the reference entry for windsh.

For information about the target-resident version of the unloader (which also requires the target-resident symbol table and loader), see the VxWorks reference entry for unldLib.

After a module has been unloaded, any calls to routines in that module fail with unpredictable results. Take care to avoid unloading any modules that are required by other modules. One solution is to link interdependent files using the static linker ldarch as described in 8.4.3 Static Linking (Optional), so that they can only be loaded and unloaded as a unit.