About This Book xviiTypographical conventions xixNote to Windows users xxWhat you’ll find in this guide xxiNote to Windows users xxiiRecommended reading xxii 1 Choosing the version of th
Trang 1PROGRAMMER’S GUIDE
Trang 2Operating System
Programmer’s Guide
For QNXNeutrino6.3
2005, QNX Software Systems
Trang 3July 2004 First edition
Electronic edition published 2005
Technical support options
To obtain technical support for any QNX product, visit the Technical Support section in the Services area on our website
(www.qnx.com) You’ll find a wide range of support options, including our free web-based Developer Support Center.
QNX, Momentics, Neutrino, and Photon microGUI are registered trademarks of QNX Software Systems in certain jurisdictions All other trademarks and trade names belong to their respective owners.
Printed in Canada.
Part Number: 002512
Trang 4About This Book xvii
Typographical conventions xixNote to Windows users xxWhat you’ll find in this guide xxiNote to Windows users xxiiRecommended reading xxii
1
Choosing the version of the OS 3Conforming to standards 4Header files in/usr/include 7Self-hosted or cross-development 8
A simple example 8Self-hosted 10Cross-development with network filesystem 10Cross-development with debugger 11
Cross-development, deeply embedded 11Using libraries 14
Static linking 15Dynamic linking 15Runtime loading 15Static and dynamic libraries 15Platform-specific library locations 17Linking your modules 18
Creating shared objects 19
Trang 5Debugging 20Debugging in a self-hosted environment 20Debugging in a cross-development environment 21The GNU debugger (gdb) 23
The process-level debug agent 23
A simple debug session 30Configure the target 30Compile for debugging 30Start the debug session 30Get help 32
Sample boot image 34
Suspending a running thread 47When the thread is blocked 47When the thread is preempted 47When the thread yields 48Scheduling algorithms 48FIFO scheduling 49Round-robin scheduling 50Why threads? 51
Trang 6Process creation 55Concurrency 56
Using fork() and forkpty() 57Inheriting file descriptors 57Process termination 58
Normal process termination 59Abnormal process termination 59Affect of parent termination 61Detecting process termination 61
4
What is a resource manager? 75Why write a resource manager? 77Under the covers 79
The types of resource managers 84Components of a resource manager 85iofunc layer 85
resmgr layer 86dispatch layer 87thread pool layer 89Simple examples of device resource managers 90Single-threaded device resource manager example 90Multi-threaded device resource manager example 96Data carrying structures 99
The Open Control Block (OCB) structure 100The attribute structure 101
The mount structure 107Handling the IO READmessage 109Sample code for handling IO READmessages 110Ways of adding functionality to the resource manager 114Handling the IO WRITEmessage 119
Sample code for handling IO WRITEmessages 119Methods of returning and replying 122
Trang 7Returning with an error 122Returning using an IOV array that points to your data 123Returning with a single buffer containing data 123Returning success but with no data 124
Getting the resource manager library to do the reply 124Performing the reply in the server 125
Returning and telling the library to do the default action 127Handling other read/write details 127
Handling the xtype member 128
Handling pread*() and pwrite*() 130
Handling readcond() 132Attribute handling 133Updating the time for reads and writes 133Combine messages 134
Where combine messages are used 134The library’s combine-message handling 136Extending Data Control Structures (DCS) 142Extending the OCB and attribute structures 142Extending the mount structure 145
Handling devctl() messages 145Sample code for handling IO DEVCTLmessages 148
Handling ionotify() and select() 152Sample code for handling IO NOTIFYmessages 156Handling private messages and pulses 164
Handling open(), dup(), and close() messages 167Handling client unblocking due to signals or timeouts 168Handling interrupts 170
Sample code for handling interrupts 170Multi-threaded resource managers 173Multi-threaded resource manager example 173Thread pool attributes 175
Thread pool functions 177
Trang 8Filesystem resource managers 178Considerations for filesystem resource managers 178Taking over more than one device 179
Handling directories 180Message types 186
Connect messages 187I/O messages 187Resource manager data structures 188
Transparent Distributed Processing Using
5
Qnet 191
What is Qnet? 193Benefits of Qnet 193What works best 194What type of application is well-suited for Qnet? 195Qnet drivers 195
How does it work? 196Locating services using GNS 200Quality of Service (QoS) and multiple paths 209Designing a system using Qnet 212
The product 212Developing your distributed system 213Configuring the data cards 213
Configuring the controller card 214Enhancing reliability via multiple transport buses 215Redundancy and scalability using multiple controller cards
217Autodiscovery vs static 218When should you use Qnet, TCP/IP, or NFS? 219Writing a driver for Qnet 222
Writing an Interrupt Handler 227
6
What’s an interrupt? 229
Trang 9Attaching and detaching interrupts 229Interrupt Service Routine (ISR) 230Determining the source of the interrupt 231Servicing the hardware 233
Updating common data structures 236Signalling the application code 236Running out of interrupt events 241Advanced topics 241
Interrupt environment 241Ordering of shared interrupts 242Interrupt latency 242
Common sources 250Detecting and reporting errors 252Using themallocdebug library 253Controlling the level of checking 257Other environment variables 263Caveats 264
Manual checking (bounds checking) 265Getting pointer information 266Getting the heap buffer size 267Memory leaks 268
Tracing 268Causing a trace and giving results 269Analyzing dumps 270
Compiler support 271C++ issues 271
Trang 10Bounds checking GCC 273Summary 274
Freedom from Hardware and Platform
A
Dependencies 275
Common problems 277I/O space vs memory-mapped 277Big-endian vs little-endian 278Alignment and structure packing 279Atomic operations 280
Solutions 280Determining endianness 280Swapping data if required 281Accessing unaligned data 282Examples 283
Accessing I/O ports 286
Conventions for Makefiles and
B
Directories 289
Structure 291Makefile structure 293Therecurse.mkfile 293Macros 294
Directory structure 296The project level 296The section level (optional) 296The OS level 296
The CPU level 296The variant level 297Specifying options 297Thecommon.mkfile 297The variant-level makefile 298Recognized variant names 298
Trang 11Using the standard macros and include files 300Theqconfig.mkinclude file 301
Theqrules.mkinclude file 304Theqtargets.mkinclude file 309Advanced topics 310
Collapsing unnecessary directory levels 311Performing partial builds 312
More uses forLIST 313GNUconfigure 314
Developing SMP Systems 321
C
Introduction 323Building an SMP image 323The impact of SMP 324
To SMP or not to SMP 324Processor affinity 325SMP and synchronization primitives 325SMP and FIFO scheduling 325
SMP and interrupts 326SMP and atomic operations 326Designing with SMP in mind 327Use the SMP primitives 328
Assume that threads really do run concurrently 328Break the problem down 328
Using GDB 331
D
GDB commands 334Command syntax 334Command completion 335Getting help 337
Running programs under GDB 340Compiling for debugging 341Setting the target 341
Trang 12Starting your program 342Your program’s arguments 343Your program’s environment 344Your program’s input and output 345Debugging an already-running process 346Killing the child process 347
Debugging programs with multiple threads 347Debugging programs with multiple processes 349Stopping and continuing 350
Breakpoints, watchpoints, and exceptions 350Continuing and stepping 365
Signals 370Stopping and starting multithreaded programs 372Examining the stack 373
Stack frames 374Backtraces 375Selecting a frame 376Information about a frame 378MIPS machines and the function stack 379Examining source files 380
Printing source lines 380Searching source files 382Specifying source directories 383Source and machine code 384Shared libraries 386
Examining data 387Expressions 388Program variables 389Artificial arrays 390Output formats 392Examining memory 393Automatic display 395
Trang 13Print settings 398Value history 405Convenience variables 406Registers 408
Floating point hardware 410Examining the symbol table 411Altering execution 415
Assignment to variables 415Continuing at a different address 417Giving your program a signal 418Returning from a function 418Calling program functions 419Patching programs 419
ARM Memory Management 421
E
ARM-specific restrictions and issues 423
NTO TCTL IObehavior 423Implications of the ARM Cache Architecture 424ARM-specific features 427
The<sys/netmgr.h>header file 436Booting over the network 439
Overview 439Creating directory and setting up configuration files 440Building an OS image 441
Booting the client 445Troubleshooting 445What doesn’t work 445
Trang 14Glossary 447
Trang 16Debugging in a self-hosted environment 21Debugging in a cross-development environment 22Running the process debug agent with a serial link at 115200baud 24
Null-modem cable pinout 25Several developers can debug a single target system 26Running the process debug agent with a TCP/IP static port 26For a TCP/IP dynamic port connection, theinetdprocess willmanage the port 27
The Neutrino architecture acts as a kind of “software bus” thatlets you dynamically plug in/out OS modules This pictureshows the graphics driver sending a message to the fontmanager when it wants the bitmap for a font The fontmanager responds with the bitmap 39
Thread priorities range from 0 (lowest) to 63 (highest) Althoughinterrupt handlers aren’t scheduled in the same way asthreads, they’re considered to be of a higher priority because
an interrupt handler will preempt any running thread. 44The ready queue for six threads (A-F) that are READY All otherthreads (G-Z) are BLOCKED Thread A is currentlyrunning Thread A, B, and C are at the highest priority, sothey’ll share the processor based on the running thread’sscheduling algorithm 46
Thread A blocks, Thread B runs 49FIFO scheduling Thread A runs until it blocks 50Round-robin scheduling Thread A ran until it consumed itstimeslice; the next READY thread (Thread B) now runs.50
Trang 17Under-the-cover communication between the client, the processmanager, and the resource manager 80
You can use the resmgr layer to handle IO *messages 87You can use the dispatch layer to handle IO *messages, select,pulses, and other messages 88
Multiple clients with multiple OCBs, all linked to one mountstructure 100
Returning the optionalstruct statalong with thestruct dirententry can improve efficiency 186
A simple GNS setup 201
A redundant GNS setup 206Separate global domains 208Interrupt request assertion with multiple interrupt sources 231Source tree for a multiplatform project 292
Trang 20Typographical conventions
Throughout this manual, we use certain typographical conventions todistinguish technical terms In general, the conventions we useconform to those found in IEEE POSIX publications The followingtable summarizes our conventions:
Code examples if( stream == NULL )
Command options -lR
Environment variables PATH
File and pathnames /dev/null
Function names exit()
Keyboard chords Ctrl – Alt – Delete
Keyboard input something you type
Program output login:
Programming constants NULL
Programming data types unsigned short
Programming literals 0xFF,"message string"
Variable names stdin
User-interface components Cancel
We format single-step instructions like this:
We use an arrow (→) in directions for accessing menu items, like this:
Trang 21You’ll find the Other menu item under
Perspective→Show View.
We use notes, cautions, and warnings to highlight importantmessages:
Notes point out something important or useful
☞
CAUTION: Cautions tell you about commands or procedures that
may have unwanted or undesirable side effects
!
WARNING: Warnings tell you about commands or procedures that could be dangerous to your files, your hardware, or even yourself.
Note to Windows users
In our documentation, we use a forward slash (/) as a delimiter in all
pathnames, including those pointing to Windows files
We also generally follow POSIX/UNIX filesystem conventions
Trang 22What you’ll find in this guide
The Neutrino Programmer’s Guide is intended for developers who are
building applications that will run under the QNX Neutrino RealtimeOperating System
Depending on the nature of your application and target platform, you
may also need to refer to Building Embedded Systems If you’re using the Integrated Development Environment, see the IDE User’s Guide.
☞
This table may help you find what you need in the Programmer’s
Guide:
Get started with a “Hello,world!” program
Compiling and Debugging
Get an overview of the Neutrinoprocess model and schedulingmethods
Programming Overview
Create and terminate processes ProcessesDevelop a device driver and/or
resource manager
Writing a Resource Manager
Use native networking Transparent Distributed
Processing Using QnetLearn about ISRs in Neutrino Writing an Interrupt Handler
Analyze and detect problemsrelated to dynamic memorymanagement
Heap Analysis: MakingMemory Errors a Thing of thePast
continued .
Trang 23When you want to: Go to:
Deal with non-x86 issues (e.g
big-endian vs little-endian)
Appendix A: Freedom fromHardware and PlatformDependencies
Understand our makefilemethodology
Appendix B: Conventions forMakefiles and DirectoriesWrite programs for SMP
machines
Appendix C: Developing SMPSystems
Learn how to use the GDBdebugger
We assume that you’ve already installed Neutrino and that you’re
familiar with its architecture For a detailed overview, see the System
Architecture manual.
☞
Note to Windows users
In the QNX documentation, we use a forward slash (/) as a delimiter
in all pathnames, including those pointing to Windows files.
We also generally follow POSIX/UNIX filesystem conventions
Recommended reading
For the most part, the information that’s documented in the
Programmer’s Guide is specific to QNX For more general
information, we recommend the following books:
Trang 24¯ Butenhof, David R 1997 Programming with POSIX Threads.
Reading, MA: Addison-Wesley Publishing Company ISBN0-201-63392-2
TCP/IP programming (note that some of the advanced API featuresmentioned in the following books might not be supported):
¯ Hunt, Craig 2002 TCP/IP Network Administration Sebastopol,
CA: O’Reilly & Associates ISBN 0-596-00297-1
¯ Stevens, W Richard 1997 Unix Network Programming:
Networking APIs: Sockets and XTI Upper Saddle River, NJ:
Prentice-Hall PTR ISBN 0-13-490012-X
¯ — 1993 TCP/IP Illustrated, Volume 1 The Protocols Reading,
MA: Addison-Wesley Publishing Company ISBN 0-201-63346-9
¯ — 1995 TCP/IP Illustrated, Volume 2 The Implementation.
Reading, MA: Addison-Wesley Publishing Company ISBN0-201-63354-X
Trang 26Compiling and Debugging
Trang 28Choosing the version of the OS
The QNX Momentics development suite lets you install and workwith multiple versions of Neutrino Whether you’re using thecommand line or the IDE, you can choose which version of the OS tobuild programs for
Coexistence of 6.3.0 and 6.2.1 is supported only on Windows andSolaris hosts
☞
When you install QNX Momentics, you get a set of configuration filesthat indicate where you’ve install the software The
QNX CONFIGURATION environment variable stores the location
of the configuration files for the installed versions of Neutrino; on aself-hosted Neutrino machine, the default is/etc/qconfig
If you’re using the command-line tools, use theqconfigutility toconfigure your machine to use a specific version of Neutrino
On Windows hosts, useQWinCfg, a graphical front end forqconfig.You can launch it from the Start menu
☞
Here’s whatqconfigdoes:
¯ If you run it without any options,qconfiglists the versions thatare installed on your machine
¯ If you use the-eoption, you can useqconfigto set up theenvironment for building software for a specific version of the OS.For example, if you’re using the Korn shell (ksh), you can
configure your machine like this:
eval qconfig -n "QNX Neutrino 6.3.0" -e
When you start the IDE, it uses your currentqconfigchoice as thedefault version of the OS; if you haven’t chosen a version, the IDEchooses an entry from the directory identified by
QNX CONFIGURATION If you want to override the IDE’s choice,
Trang 29you can choose the appropriate build target For details, see “Version
coexistence” in the Concepts chapter of the IDE User’s Guide Neutrino uses these environment variables to locate files on the host
When the-ansioption is used,qcccompiles strict ANSI code Usethis option when you’re creating an application that must conform tothe ANSI standard The effect on the inclusion of ANSI- andPOSIX-defined header files is that certain portions of the header filesare omitted:
¯ for ANSI header files, these are the portions that go beyond theANSI standard
¯ for POSIX header files, these are the portions that go beyond thePOSIX standard
You can then use theqcc -Doption to define feature-test macros to
select those portions that are omitted Here are the most commonlyused feature-test macros:
Trang 30POSIX C SOURCE=199506Include those portions of the header files that relate to the
POSIX standard (IEEE Standard Portable Operating System
Interface for Computer Environments - POSIX 1003.1, 1996)
FILE OFFSET BITS=64Make the libraries use 64-bit file offsets
#define POSIX C SOURCE=199506
#include <limits.h>
#include <stdio.h>
.
#if defined( QNX SOURCE)
#include "non POSIX header1.h"
#include "non POSIX header2.h"
#include "non POSIX header3.h"
#endif
The source code is then compiled using the-ansioption
The following ANSI header files are affected by the
POSIX C SOURCEfeature test macro:
¯ <limits.h>
¯ <setjmp.h>
¯ <signal.h>
Trang 31¯ <stdio.h>
¯ <stdlib.h>
¯ <time.h>
The following ANSI and POSIX header files are affected by the
QNX SOURCEfeature test macro:
Header file Type
Trang 32Header files in /usr/include
The ${QNX TARGET}/usr/includedirectory includes at leastthe following subdirectories (in addition to the usualsys):
arpa ARPA header files concerning the Internet, FTP and
¯ Files ending in*intr.hdescribe interrupt vector
numbers for use with InterruptAttach() and
net Network interface descriptions
netinet,netinet6,netkey
Header files concerning TCP/IP
photon Header files concerning the Photon microGUI; for more
information, see the Photon documentation
snmp Descriptions for the Simple Network Management
Protocol (SNMP)
Trang 33Self-hosted or cross-development
In the rest of this chapter, we’ll describe how to compile and debug aNeutrino system Your Neutrino system might be anything from adeeply embedded turnkey system to a powerful multiprocessor server.You’ll develop the code to implement your system using developmenttools running on the Neutrino platform itself or on any other
supported cross-development platform
Neutrino supports both of these development types:
¯ self-hosted — you develop and debug on the same system
¯ cross-development — you develop on your host system, then
transfer and debug the executable on your target hardwareThis section describes the procedures for compiling and debuggingfor both types
A simple example
We’ll now go through the steps necessary to build a simple Neutrinosystem that runs on a standard PC and prints out the text
“Hello, world!” — the classic first C program
Let’s look at the spectrum of methods available to you to run yourexecutable:
If your environment is: Then you can:
Self-hosted Compile and link, then run on
hostCross-development, network
filesystem link
Compile and link, load overnetwork filesystem, then run ontarget
continued .
Trang 34If your environment is: Then you can:
Cross-development, debuggerlink
Compile and link, use debugger
as a “network filesystem” totransfer executable over totarget, then run on targetCross-development, rebuilding
You can choose how you wish to compile and link your programs:you can use tools with a command-line interface (via theqcc
command) or you can use an IDE (Integrated DevelopmentEnvironment) with a graphical user interface (GUI) environment Oursamples here illustrate the command-line method
☞
The “Hello, world!” program itself is very simple:
#include <stdio.h>
int main (void) {
printf ("Hello, world!\n");
return (0);
}
You compile it for PowerPC (big-endian) with the single line:
qcc -V gcc ntoppcbe hello.c -o hello
This executes the C compiler with a special cross-compilation flag,
-V gcc ntoppcbe, that tells the compiler to use thegcccompiler,Neutrino-specific includes, libraries, and options to create a PowerPC(big-endian) executable using the GCC compiler
Trang 35To see a list of compilers and platforms supported, simply execute thecommand:
hello
Cross-development with network filesystem
If you’re using a network filesystem, let’s assume you’ve already set
up the filesystem on both ends For information on setting this up, see
the Sample Buildfiles appendix in Building Embedded Systems.
Using a network filesystem is the richest cross-development methodpossible, because you have access to remotely mounted filesystems.This is ideal for a number of reasons:
¯ Your embedded system requires only a network connection; nodisks (and disk controllers) are required
¯ You can access all the shipped and custom-developed Neutrinoutilities — they don’t need to be present on your (limited)embedded system
¯ Multiple developers can share the same filesystem server
Trang 36For a network filesystem, you’ll need to ensure that the shell’s PATH
environment variable includes the path to your executable via thenetwork-mounted filesystem At this point, you can just type thename of the executable at the target’s command-line prompt (if you’rerunning a shell on the target):
hello
Cross-development with debugger
Once the debug agent is running, and you’ve established connectivitybetween the host and the target, you can use the debugger to downloadthe executable to the target, and then run and interact with it
Download/upload facility
When the debug agent is connected to the host debugger, you cantransfer files between the host and target systems Note that this is ageneral-purpose file transfer facility — it’s not limited to transferringonly executables to the target (although that’s what we’ll be
describing here)
In order for Neutrino to execute a program on the target, the programmust be available for loading from some type of filesystem Thismeans that when you transfer executables to the target, you mustwrite them to a filesystem Even if you don’t have a conventionalfilesystem on your target, recall that there’s a writable “filesystem”present under Neutrino — the/dev/shmemfilesystem This serves as
a convenient RAM-disk for downloading the executables to
Cross-development, deeply embedded
If your system is deeply embedded and you have no connectivity tothe host system, or you wish to build a system “from scratch,” you’llhave to perform the following steps (in addition to the common step
of creating the executable(s), as described above):
1 Build a Neutrino system image
Trang 372 Transfer the system image to the target.
3 Boot the target
Step 1: Build a Neutrino system image.
You use a buildfile to build a Neutrino system image that includes
your program The buildfile contains a list of files (or modules) to beincluded in the image, as well as information about the image Abuildfile lets you execute commands, specify command arguments,set environment variables, and so on The buildfile will look like this:
[virtual=ppcbe,elf] bootstrap = { startup-800fads
PATH=/proc/boot procnto-800 }
[+script] script = { devc-serppc800 -e -c20000000 -b9600 smc1 &
reopen hello }
[type=link] /dev/console=/dev/ser1 [type=link] /usr/lib/ldqnx.so.2=/proc/boot/libc.so [perms=+r,+x]
libc.so [data=copy]
[perms=+r,+x]
devc-serppc800 hello
The first part (the four lines starting with[virtual=ppcbe,elf]),contains information about the kind of image we’re building
The next part (the five lines starting with[+script]) is the startupscript that indicates what executables (and their command-lineparameters, if any) should be invoked
The[type=link]lines set up symbolic links to specify the serialport and shared library file we want to use
Trang 38The runtime linker is expected to be found in a file called
ldqnx.so.2, but the runtime linker is currently contained within the
libc.sofile, so we make a process manager symbolic link to it
☞
The[perms=+r,+x]lines assign permissions to the binaries thatfollow — in this case, we’re setting them to be Readable andExecutable
Then we include the C shared library,libc.so.Then the line[data=copy]specifies to the loader that the datasegment should be copied This applies to all programs that follow the
[data=copy]attribute The result is that we can run the executablemultiple times
Finally, the last part (the last two lines) is simply the list of filesindicating which files should be included as part of the image Formore details on buildfile syntax, see themkifsentry in the Utilities
Reference
Our sample buildfile indicates the following:
¯ A PowerPC 800 FADS board and ELF boot prefix code are beingused to boot
¯ The image should containdevc-serppc800, the serialcommunications manager for the PowerPC 80x family, as well as
hello(our test program)
¯ devc-serppc800should be started in the background (specified
by the&character) This manager will use a clock rate of 20 MHz,
a baud rate of 9600, and ansmc1device
¯ Standard input, output, and error should be redirected to
/dev/ser1(via thereopencommand, which by default redirects
to/dev/console, which we’ve linked to/dev/ser1)
¯ Finally, ourhelloprogram should run
Let’s assume that the above buildfile is calledhello.bld Using the
mkifsutility, you could then build an image by typing:
Trang 39mkifs hello.bld hello.ifs
Step 2: Transfer the system image to the target.
You now have to transfer the imagehello.ifsto the target system
If your target is a PC, the most universal method of booting is to make
a bootable floppy diskette
If you’re developing on a platform that has TCP/IP networking andconnectivity to your target, you may be able to boot your Neutrinotarget system using a BOOTP server For details, see the “BOOTP
section” in the Customizing IPL Programs chapter in Building
Embedded Systems.
☞
If your development system is Neutrino, transfer your image to afloppy by issuing this command:
dinit -f hello.ifs /dev/fd0
If your development system is Windows NT or Windows 95/98,transfer your image to a floppy by issuing this command:
dinit -f hello.ifs a:
Step 3: Boot the target.
Place the floppy diskette into your target system and reboot yourmachine The message “Hello, world!” should appear on yourscreen
Using libraries
When you’re developing code, you almost always make use of a
library — a collection of code modules that you or someone else has
already developed (and hopefully debugged) Under Neutrino, wehave three different ways of using libraries:
Trang 40static linking The word “static” implies that it’s not going to change
— all the required modules are already combined into one executable.
Dynamic linking
Rather than build a self-contained executable ahead of time, you cantake your modules and link them in such a way that the ProcessManager will link them to the library modules before your program
runs We call this dynamic linking The word “dynamic” here means
that the association between your program and the library modules
that it uses is done at load time, not at linktime (as was the case with
the static version)
Runtime loading
There’s a variation on the theme of dynamic linking called runtime
loading In this case, the program decides while it’s actually running
that it wishes to load a particular function from a library
Static and dynamic libraries
To support the two major kinds of linking described above, Neutrino
has two kinds of libraries: static and dynamic.
Static libraries
A static library is usually identified by a.a(for “archive”) suffix (e.g
libc.a) The library contains the modules you want to include inyour program and is formatted as a collection of ELF object modules