The embedded software development process just described is illustrated in Figure 3-1. In this figure, the three steps are shown from top to bottom, with the tools that perform them shown in boxes that have rounded corners. Each of these development tools takes one or more files as input and produces a single output file. More specific information about these tools and the files they produce is provided in the sections that follow.
Figure 3-1. The embedded software development process
Each of the steps of the embedded software build process
is a transformation performed by software running on a general-purpose computer. To distinguish this development computer (usually a PC or Unix workstation) from the target embedded system, it is referred to as the host computer. In other words, the compiler, assembler, linker, and locator are all pieces of software that run on a host computer, rather than on the embedded system itself. Yet, despite the fact that they run on some other computer platform, these tools combine their efforts to produce an executable binary image that will execute properly only on the target embedded system. This split of responsibilities is shown in Figure 3-2.
Figure 3-2. The split between host and target
In this chapter and the next I'll be using the GNU tools (compiler, assembler, linker, and debugger) as examples. These tools are extremely popular with embedded software developers because they are freely available (even the source code is free) and support many of the most popular embedded processors. I will use features of these specific tools as illustrations for the general concepts discussed. Once understood, these same basic concepts can be applied to any equivalent development tool.
3.2 Compiling
Of course, each processor has its own unique machine language, so you need to choose a compiler that is capable of producing programs for your specific target processor. In the embedded systems case, this compiler almost always runs on the host computer. It simply doesn't make sense to execute the compiler on the embedded system itself. A compiler such as this that runs on one computer platform and produces code for another is called a cross-compiler. The use of a cross-compiler is one of the defining features of embedded software development.
The Gnu C/C++ compiler (gcc ) and assembler (as ) can be configured as either native compilers or cross-compilers. As cross-compilers these tools support an impressive set of host-target combinations. Table 3-1 lists some of the most popular of the supported hosts and targets. Of course, the selections of host platform and target processor are independent; these tools can be configured for any combination.
Table 3-1. Hosts and Targets Supported by the GNU Compiler
| Host Platforms | Target Processors |
|---|---|
| DEC Alpha Digital Unix HP 9000/700 HP-UX IBM Power PC AIX IBM RS6000 AIX SGI Iris IRIX Sun SPARC Solaris Sun SPARC SunOS X86 Windows 95/NT X86 Red Hat Linux | AMD/Intel x86 (32-bit only) Fujitsu SPARClite Hitachi H8/300, H8/300H, H8/S Hitachi SH IBM/Motorola PowerPC Intel i960 MIPS R3xxx, R4xx0 Mitsubishi D10V, M32R/D Motorola 68k Sun SPARC, MicroSPARC Toshiba TX39 |
The contents of an object
file can be thought of as a very large, flexible data structure. The structure of the file is usually defined by a standard format like the common object file format (COFF) or extended linker format (ELF). if you'll be using more than one compiler (i.e., you'll be writing parts of your program in different source languages), you need to make sure that each is capable of producing object files in the same format. Although many compilers (particularly those that run on Unix platforms) support standard object file formats like COFF and ELF (gcc supports both), there are also some others that produce object files only in proprietary formats. If you're using one of the compilers in the latter group, you might find that you need to buy all of your other development tools from the same vendor.
Most object files begin with a header that describes the sections that follow. Each of these sections contains one or more blocks of code or data that originated within the original source file. However, these blocks have been regrouped by the compiler into related sections. For example, all of the code blocks are collected into a section called text , initialized global variables (and their initial values) into a section called data , and uninitialized global variables into a section called bss .