Section 12.1. Cross-Development Environment


12.1. Cross-Development Environment

Developers new to embedded development often struggle with the concepts and differences between native and cross-development environments. Indeed, there are often three compilers and three (or more) versions of standard header files such as stdlib.h. Debugging an application on your target embedded system can be difficult without the right tools and host-based utilities. You must manage and separate the files and utilities designed to run on your host system from those you intend to use on your target.

When we use the term host in this context, we are referring to the development workstation that is sitting on your desktop and running your favorite Linux desktop distribution.[1] Conversely, when we use the term target we are referring to your embedded hardware platform. Therefore, native development denotes the compilation and building of applications on and for your host system. Cross-development denotes the compilation and building of applications on the host system that will be run on the embedded system. Keeping these definitions in mind will help you stay on track through this chapter.

[1] Webster's defines nonsense as "an idea that is absurd or contrary to good sense." It is my opinion that developing embedded Linux platforms on a non-Linux/UNIX host is nonsensical.

Figure 12-1 shows the layout of a typical cross-development environment. A host PC is connected to a target board via one or more physical connections. It is most convenient if both serial and Ethernet ports are available on the target. Later when we discuss kernel debugging, you will realize that a second serial port can be a very valuable asset.

Figure 12-1. Cross-development setup


In the most common scenario, the developer has a serial terminal on the host connected to the RS-232 serial port, possibly one or more Telnet terminal sessions to the target board, and potentially one or more debug sessions using Ethernet as the connection medium. This cross-development setup provides a great deal of flexibility. The basic idea is that the host system provides the horsepower to run the compilers, debuggers, editors, and other utilities, while the target executes only the applications designed for it. Yes, you can certainly run compilers and debuggers on the target system, but we assume that your host system contains more resources, including RAM, disk storage, and Internet connectivity. In fact, it is not uncommon for a target embedded board to have no human-input devices or output displays.

12.1.1. "Hello World"Embedded

A properly configured cross-development system hides a great deal of complexity from the average application developer. Looking at a simple example will help uncover and explain some of the mystery. When we compile a simple "hello world" program, the toolchain (compiler, linker, and associated utilities) makes many assumptions about the host system we are building on and the program we are compiling. Actually, they are not assumptions, but a collection of rules that the compiler references to build a proper binary.

Listing 12-1 reproduces a simple "hello world" program.

Listing 12-1. Hello World Again

#include <stdio.h> int main(int argc, char **argv) {     printf("Hello World\n");     return 0; }

Even the casual application developer will realize some important points about this C source file. First, the function printf() is referenced but not defined in this file. If we omit the #include directive containing the prototype for the printf() function, the compiler emits the familiar message:

hello.c:5: warning: implicit declaration of function 'printf'


This introduces some interesting questions:

  • Where is the file stdio.h located, and how is it found?

  • Where does the printf() function live, and how is this reference resolved in the binary executable?

Somehow it seems that the compiler just knows how to put together a proper binary file that is executable from the command line. To further complicate matters, the final executable contains startup and shutdown prologue code that we never see but that the linker automatically includes. This prologue deals with details such as the environment and arguments passed to your program, startup and shutdown housekeeping, exit handling, and more.

To build the "hello world" application, we can use a simple command line invocation of the compiler, similar to this:

$ gcc -o hello hello.c


This produces the binary executable file called hello, which we can execute directly from the command line. Defaults referenced by the compiler provide guidance on where include files will be found. In a similar fashion, the linker knows how to resolve the reference to the printf() function by including a reference to the library where it is defined. This, of course, is the standard C library.

We can query the toolchain to see some of the defaults that were used. Listing 12-2 is a partial listing of the output from cpp when passed the -v flag. You might already know that cpp is the C preprocessor component of the gcc toolchain. We have added some formatting (whitespace only) to improve the readability.

Listing 12-2. Default Native cpp Search Directories

[View full width]

$ cpp -v Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.3.3/specs Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share /info --enable-shared --enable-threads=posix --disable-checking  --disable-libunwind-exceptions --with-system-zlib --enable-__cxa_atexit  -host=i386-redhat-linux Thread model: posix gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7)  /usr/lib/gcc-lib/i386-redhat-linux/3.3.3/cc1 -E -quiet -v - ignoring nonexistent directory "/usr/i386-redhat-linux/include" #include "..." search starts here: #include <...> search starts here:  /usr/local/include  /usr/lib/gcc-lib/i386-redhat-linux/3.3.3/include  /usr/include End of search list. /usr/lib/

This simple query produces some very useful information. First, we can see how the compiler was configured using the familiar ./configure utility. The default thread model is posix, which determines the thread library your application gets linked against if you employ threading functions. Finally, you see the default search directories for #include directives.

But what if we want to build hello.c for a different architecture, such as PowerPC? When we compile an application program for a PowerPC target using a cross-compiler on our host machine, we must make sure that the compiler does not use the default host include directories or library paths. Using a properly configured cross-compiler is the first step, and having a well designed cross-development environment is the second.

Listing 12-3 is the output from a popular open-source cross-development toolchain known as the Embedded Linux Development Kit (ELDK), assembled and maintained by Denx Software Engineering. This particular installation was configured for the PowerPC 82xx toolchain. Again, we have added some whitespace to the output for readability.

Listing 12-3. Default Cross-Search Directories

[View full width]

$ ppc_82xx-cpp -v Reading specs from /opt/eldk/usr/bin/.. /lib/gcc-lib/ppc-linux/3.3.3/specs Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share /info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib  --enable-__cxa_atexit --with-newlib --enable-languages=c,c++ --disable-libgcj  --host=i386-redhat-linux -target=ppc-linux Thread model: posix gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-10)  /opt/eldk/usr/bin/../lib/gcc-lib/ppc-linux/3.3.3/cc1  -E -quiet -v -iprefix /opt/eldk/usr /bin/..  /lib/gcc-lib/ppc-linux/3.3.3/ -D__unix__ -D__gnu_linux__  -D__linux__ -Dunix  -D__unix -Dlinux -D__linux -Asystem=unix  -Asystem=posix - -mcpu=603 ignoring nonexistent directory "/opt/eldk/usr/ppc-linux/sys-include" ignoring nonexistent directory "/opt/eldk/usr/ppc-linux/include" #include "..." search starts here: #include <...> search starts here:   /opt/eldk/usr/lib/gcc-lib/ppc-linux/3.3.3/include   /opt/eldk/ppc_82xx/usr/include End of search list.

Here you can see that the default search paths for include directories are now adjusted to point to your cross versions instead of the native include directories. This seemingly obscure detail is critical to being able to develop applications and compile open-source packages for your embedded system. It is one of the most confusing topics to even experienced application developers who are new to embedded systems.



Embedded Linux Primer(c) A Practical Real-World Approach
Embedded Linux Primer: A Practical Real-World Approach
ISBN: 0131679848
EAN: 2147483647
Year: 2007
Pages: 167

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net