gcc
invokes all the required GNU passes for you with the following utilities.
cpp
The preprocessor which processes
all the header files and macros that your target requires.
gcc
The compiler which produces
assembly language code from the processed C files. For more information,
see Using GNU CC in
GNUPro Compiler Tools.
gas
The assembler which produces
binary code from the assembly language code and puts it in an object file.
ld
The linker which binds the
code to addresses, links the startup file and libraries to the object file,
and produces the executable binary image.
There are several machine-independent
compiler switches, among which are, notably,
-fno-exceptions (for C++), -fritti (for C++) and -T
(for linking).
You have four implicit file
extensions: .c , .C , .s , and .S.
For more information, see Using
GNU CC in GNUPro Compiler Tools.
gcc,
the GNU compiler
When you compile
C or C++ programs with gnu C, the compiler quietly inserts a call at the
beginning of mainto a gcc
support subroutine called __main . Normally this is invisible--you
may run into it if you want to avoid linking to the standard libraries,
by specifying the compiler option, -nostdlib.
Include -lgcc at the end of your compiler command line to resolve
this reference. This links with the compiler support library libgcc.a.
Putting it at the end of your command line ensures that you have a chance
to link first with any of your own special libraries.
__main is the initialization
routine for C++ constructors. Because GNU
C is designed to interoperate with GNU C++, even C programs must have this
call: otherwise C++ object files linked with
a C main might fail. For more information on gcc, see Using
GNU CCin GNUPro Compiler Tools.
cpp,
the GNU preprocessor
cpp merges in the
#include files, expands all macros
definitions, and processes the #ifdef sections. To see the output
of cpp, invoke gcc with the -E option,
and the preprocessed file will be printed on stdout.
There are two
convenient options to assemble handwritten files that require C-style preprocessing.
Both options depend on using the compiler driver program, gcc,
instead of calling the assembler directly.
Name
the source file using the extension .S (capitalized) rather than
.s. gcc recognizes files with this extension as assembly
language requiring C-style preprocessing.
Specify the "source language"
explicitly for this situation, using the gcc option, -xassembler-with-cpp
.
For more information
on cpp, see The
C Preprocessor in GNUPro Compiler Tools.
gas,
the GNU assembler
gas can be used
as either a compiler pass or a source-level assembler.
When used as a source-level
assembler, it has a companion assembly language preprocessor called gasp.
gasp has a syntax similar to most other assembly language macro
packages.
gas emits a relocatable
object file from the assembly language source
code. The object file contains the binary code and the debug symbols.
For more information
on gas, see Using as
in GNUPro Utilities.
ld,
the GNU linker
ldresolves
the code addresses and debug symbols, links the startup code and additional
libraries to the binary code, and produces an executable binary
image.
For more information
on ld, see Usingld
in GNUPro Utilities.
.coff
for object file formats
.coffis
the main object file format when using the
tools on embedded target systems. For more information on object files
and object file formats, see Using
binutils in GNUPro Utilities.
binutils,
the GNU binary utilities
The following
are the binary utilities, although they are
not included on all hosts: ar, nm, objcopy,
objdump, ranlib, size, strings, and
strip.
For more information
on the GNU binary utilities, see Using
binutils in GNUPro Utilities.
The most important of these
utilities are objcopyand
objdump.
objcopy
A few ROM monitors, such
as a.out, load executable binary
images, and, consequently, most load an S-record. An S-record is a printable
ASCII representation of an executable binary image.
S-records
are suitable both for building ROM images for standalone boards and for
downloading images to embedded systems. Use the following example's input
for this process.
objcopy
-O srec infileoutfile
infile in
the previous example's input is the executable binary filename, and outfile
is the filename for the S-record.
Most
PROM burners also read S-records or some similar
format. Use the following example's input to get a list of supported object
file types for your architecture.
objdump
-i
For more information on S-records,
see the discussions for FORMAT output-formatin
the documentation for MRI
compatible script files and the discussion for BFD
in Usingld in
GNUPro Utilities. For more discussion of making an executable
binary image, see objcopy
in Using binutils
in GNUPro Utilities.
objdump
objdump displays information about one or more
object files. The options control what particular information to display.
This information is mostly useful to programmers who are working on the
compilation tools, as opposed to programmers who just want their program
to compile and work.
When
specifying archives, objdump shows information on each of the
member object files. objfile... designates the object files to
be examined.
A few of the
more useful options for commands are: -d,
-disassembleand --prefix-addresses.
-d
--disassemble
Displays the assembler mnemonics for the machine instructions
from objfile . This option only disassembles those sections that
are expected to contain instructions.
--prefix-addresses
For disassembling, prints the complete address on each
line, starting each output line with the address it's disassembling. This
is the older disassembly format. Otherwise, you only get raw opcodes.
gdb,
the debugging tool
To run gdbon
an embedded execution target, use a gdb backend with the gdb
standard remote protocol or a similar protocol. The most common are the
following two types of gdb backend.
A gdb stub
This is an exception handler for breakpoints,
and it must be linked to your application. gdb stubs use the gdb
standard remote protocol.
An existing ROM monitor
used as a gdb backend
The most common approach means using the following processes.
With a similar protocol to the
gdb standard remote protocol.
With an interface that uses the
ROM monitor directly. With such an interface,
gdb only formats and parses commands.
For more information
on debugging tools, see Debugging
with GDB in GNUPro Debugging Tools.
Useful
debugging routines
The following
routines are always useful for debugging a
project in progress.
print()
Runs standalone in libglosswith
no newlibsupport. Many times
print() works when there are problems that make printf()
cause an exception.
outbyte()
Used for low-level debugging.
putnum()
Prints out values in hex
so they are easier to read.
libgloss,
newlib
and libstd++,
the GNU libraries
GNUPro Toolkit
has three libraries: libgloss,
newliband libstd++.
libgloss
libgloss , the library for GNU Low-level OS
Support , contains the startup code, the I/O
support for gcc and newlib (the C library), and the target
board support packages that you need to port the GNU tools to an embedded
execution target.
The C library used throughout
this manual is newlib , however libgloss could easily
be made to support other C libraries. Because libgloss resides
in its own tree, it's able to run standalone, allowing it to support GDB's
remote debugging and to be included in other GNU tools.
Several functions that are
essential to gcc reside in libgloss. These include the
following functions.
The Cygnus libraries, including the C library,
libc, and the C math library,
libm.
libstd++
The C++ library in development by Cygnus
crt0,
the main startup file
The crt0
(C RunTime 0) file contains the initial startup
code.
Cygnus provides
a crt0 file, although you may want to write your own crt0
file for each target. The crt0 file is usually written in assembler
as 'crt0.S', and its object gets linked in first and bootstraps
the rest of your application. The crt0 file defines a special
symbol like _start, which is both
the default base address for the application and the first symbol in the
executable binary image.
If you plan to use any routines
from the standard C library, you'll also need to implement the functions
on which libgloss depends. The crt0 file accomplishes
the following results.
crt0initializes everything in your program that needs it.
This initialization section varies. If you
are developing an application that gets downloaded to a ROM monitor, there
is usually no need for special initialization because the ROM monitor handles
it for you. If you plan to burn your code in a ROM, the crt0 file
typically does all of the hardware initialization required to run an application.
This can include things like initializing serial ports and running a memory
check; however, results vary depending on your hardware.
The following is a typical
basic initialization of crt0.S.
1. Set up concatenation
macros.
#define CONCAT1(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a ## b
Later, you'll use these macros.
2. Set up label
macros, using the following example's input.
#ifndef __USER_LABEL_PREFIX__
#define __USER_LABEL_PREFIX__ _
#endif
#define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
These macros make the code
portable between coff and a.out. coff always
has an __ (underline) prepended to the front of its global symbol
names. a.out has none.
3. Set up register
names (with the right prefix),
using the following example's input.
#ifndef __REGISTER_PREFIX__
#define __REGISTER_PREFIX__
#endif
/* Use the right prefix for registers. */
#define REG(x) CONCAT1 (__REGISTER_PREFIX__, x)
#define d0 REG (d0)
#define d1 REG (d1)
#define d2 REG (d2)
#define d3 REG (d3)
#define d4 REG (d4)
#define d5 REG (d5)
#define d6 REG (d6)
#define d7 REG (d7)
#define a0 REG (a0)
#define a1 REG (a1)
#define a2 REG (a2)
#define a3 REG (a3)
#define a4 REG (a4)
#define a5 REG (a5)
#define a6 REG (a6)
#define fp REG (fp)
#define sp REG (sp)
Register names are for portability
between assemblers. Some register names have a % or $
prepended to them.
4. Set up space
for the stack and grab a chunk of memory.
.set stack_size, 0x2000 .
comm SYM (stack), stack_size
This can also be done in
the linker script, although it typically gets done at this point.
5. Define an
empty space for the environment, using the following example's input.
.data
.align 2
SYM (environ):
.long 0
This is bogus on almost any
ROM monitor, although it's best generally
set up as a valid address, then passing the address to main().
This way, if an application checks for an empty environment, it finds one.
6. Set up a few
global symbols that get used elsewhere.
.align 2
.text
.global SYM (stack)
.global SYM (main)
.global SYM (exit)
.global __bss_start
This really should be __bss_start,
not SYM (__bss_start.
__bss_start needs
to be declared this way because its value is set in the linker script.
7. Set up the
global symbol, start, for the
linker to use as the default address for the .text section. This
helps your program run.
SYM (start):
link a6, #-8
moveal #SYM (stack) + stack_size, sp
crt0zeroes the .bsssection
Make sure the
.bss section is cleared for uninitialized data, using the following
example's input. All of the addresses in the .bss section need
to be initialized to zero so programs that forget to check new variables'
default values will get predictable results.
moveal #__bss_start, a0
moveal #SYM (end), a1
1:
movel #0, (a0)
leal 4(a0), a0
cmpal a0, a1
bne 1b
Applications can get wild
side effects from the .bss section being left uncleared, and it
can cause particular problems with some implementations of malloc()
.
crt0calls main()
If your ROM
monitor supports it, set up argc and argv for command
line arguments and an environment pointer before the call to main(),
using the following example's input.
For g++, the code
generator inserts a branch to __main at the top of your main()
routine. g++ uses __main to initialize its internal tables
and then returns control to your main() routine.
For crt0 to call
your main() routine, use the following example's input. First,
set up the environment pointer and jump to main(). Call the main
routine from the application to get it going, using the following example's
input with main (argc, argv, environ), using argvas
a pointer to NULL.
pea 0
pea SYM (environ)
pea sp@(4)
pea 0
jsr SYM (main)
movel d0, sp@-4
crt0calls (exit)
After main()
has run, the crt0 file cleans things up and returns control of
the hardware from the application. On some hardware there is nothing to
return to--especially if your program is in ROM--and if that's the case,
you need to do a hardware reset or branch back to the original start address.
If you're using
a ROM monitor, you can usually call a user trap to make the ROM take over.
Pick a safe vector with no sides effects. Some ROM's have a built-in trap
handlerjust
for this case.
Implementing (exit)
here is easy. First, with _exit, exit from the application. Normally,
this causes a user trap to return to the ROM monitor for another run. Then,
using the following example's input, you proceed with the call.
SYM (exit):
trap #0
Both rom68kand
bugcan handle a user-caused
exception of 0 with no side effects. Although the bug
monitor has a user-caused trap that returns control to the ROM monitor,
the bug monitor is more portable.
The
linker script
The linker
script accomplishes the following processes
to result.
When your application is
loaded into memory, it allocates some RAM, some disk space for I/O, and
some registers. The linker script makes a memory map of this memory allocation
which is important to embedded systems because, having no OS, you have
the ability then to manage the behavior of the chip.
The actual section names
vary depending on your object file format. For a.out and coff,
the three main sections are .text,
.dataand .bss.
Sets the default values
for variables
used elsewhere.
These default variables are
used by sbrk()and the crt0
file, typically called by _bss_startand _end.
Memory
map
There
are two ways to ensure the memory map is correct.
By having the linker create the
memory map by using the option, -Map.
By, after linking, using the
nm utility to check critical addresses
like start, bss_end and _etext.
The following
is an example of a linker script for an m68k-based target board.
1. Use the STARTUP
command, which loads the file so that it executes
first.
STARTUP(crt0.o)
The m68k-coff configuration
default does not link in crt0.o because it assumes that a developer
has crt0. This behavior is controlled in the config file
for each architecture in a macro called STARTFILE_SPEC.
If STARTFILE_SPEC is set to NULL, gcc formats its command
line and doesn't add crt0.o. Any filename can be specified with
STARTUP, although the default is always crt0.o .
If
you use only ld to link, you control whether or not to link in
crt0.o on the command line.
If
you have multiple crt0 files, you
can leave STARTUP out, and link in crt0.o in the makefile
or use different linker scripts. Sometimes this option is used to initialize
floating point values or to add device support.
2. Using GROUP,
load the specified file.
GROUP(-lgcc-liop-lc)
In this case, the file is
a relocated library that contains the definitions for the low-level functions
needed by libc.a . The file to load could have also been specified
on the command line, but as it's always needed, it might as well be here
as a default.
3. SEARCH_DIRspecifies
the path in which to look for files.
SEARCH_DIR(.)
4. Using _DYNAMIC,
specify whether or not there are shared dynamic libraries.
In the following example's case, there are no shared libraries.
__DYNAMIC = 0;
5. Set _stack,
the variable for specifying
RAM for the ROM monitor.
6. Specify a
name for a section that can be referred to later in the script. In the
following example's case, it's only a pointer to the beginning of free
RAM space with an upper limit at 2M. If the
output file exceeds the upper limit, MEMORYproduces
an error message. First, in this case, we'll set up the memory map of the
board's stack for high memory for both the rom68k and mon68k
monitors.
MEMORY
{
ram : ORIGIN = 0x10000, LENGTH = 2M
}
Constructor
and destructor tables for g++
The following discusses
setting up constructor and destructor tables for g++.
Set up the .text
section, using the following example's input.
SECTIONS
{
.text :
{
CREATE_OBJECT_SYMBOLS
*(.text)
etext = .;
__CTOR_LIST__ = .;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
*(.ctors)
LONG(0)
__CTOR_END__ = .;
__DTOR_LIST__ = .;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
*(.dtors)
LONG(0)
__DTOR_END__ = .;
*(.lit)
*(.shdata) }
> ram
.shbss SIZEOF(.text) + ADDR(.text) : {
*(.shbss)
}
In
a coff file, all the actual instructions reside in .text
for also setting up the constructor and destructor tables for g++.
Notice that the section description redirects itself to the RAM variable
that was set up in Step 5 of the earlier process for the crt0
file, variable for
specifying RAM for the ROM monitor.
Set
up the .datasection.
.talias : { } > ram
.data : {
*(.data)
CONSTRUCTORS
_edata = .;
} > ram
In a coff
file, this is where all of the initialized data goes. CONSTRUCTORS
is a special command used by ld.
Default
values for variables, _bss_startand_end
The following documentation discusses
setting default values for variables, _bss_start and _end.Set
up the .bss section, using the following
example code.
In a coff file,
this is where uninitialized datagoes.
The default values for _bss_start and _end are set here
for use by the crt0 file when it zeros the .bss section.
.bss SIZEOF(.data) + ADDR(.data) :
{
__bss_start = ALIGN(0x8);
*(.bss)
*(COMMON)
end = ALIGN(0x8);
_end = ALIGN(0x8);
__end = ALIGN(0x8);
}
.mstack : { } > ram
.rstack : { } > ram
.stab . (NOLOAD) :
{
[ .stab ]
}
.stabstr . (NOLOAD) :
{
[ .stabstr ]
}
}
I/O
support code
Most applications
use calls to the standard C library. However, when you initially link libc.a,
several I/O functions are undefined. If you
don't plan on doing any I/O, you're OK; otherwise, you need to create two
I/O functions: open() and close(). These don't need to
be fully supported unless you have a file system, so they are normally
stubbed out, using kill().
sbrk()is
also a stub, since you can't do process control
on an embedded system, and it is only needed by applications that do dynamic
memory allocation. It uses the variable, _end,
which is set in the linker script.
The following
routines are also used for optimization.
-inbyte
Returns a single
byte from the console.
-outbyte
Used for low-level
debugging, takes an argument for print()
and prints a byte out to the console (typically used for ASCII text).
Memory
support
The following
routines are for dynamic memory allocation.
sbrk()
The functions, malloc(), calloc(),
and realloc() all call sbrk() at their lowest levels.
sbrk() returns a pointer to the last memory address your application
used before more memory was allocated.
caddr_t
Defined elsewhere as char *.
RAMSIZE
A compile-time option that moves a pointer
to heap memory and checks for the upper limit.
Miscellaneous
support routines
The following
support routines are called by newlib,
although they don't apply to the embedded environment.
isatty()
Checks for a terminal device.
kill()
Simply exits.
getpd()
Can safely return
any value greater than 1, although the value doesn't effect anything in
newlib.