What is cross compiling?
Cross compilation is where the compiler produces executable for running on a different platform. Let me explain it by using an example. Assume that PingWu wants to compile the Linux kernel for an embedded Linux system (based on say, powerpc). She has a PPC-based board where already a Linux kernel and applications are running.
Ping starts compiling the latest and greatest kernel from kernel tree. But soon she realizes that her embedded system, although pretty smart for the task it was designed to perform, has limited resources and would easily take a couple of hours just to compile the kernel with minimal options. At the same time she discovers that she has a workstation running Linux with AMD64 dual core 8way processor with 128GB RAM. Linux kernel compiles in that machine within less than 5 minutes. (I tested it, believe me it took around 4:30 minutes for full compilation of Linux 2.6.18-rc3). She compiles the kernel, but she immediately realizes that the output from her workstation is for AMD64 arch, and would not run on PPC.
What she needs is a Cross Compiler -- A compiler that runs in architecture A, producing output executable for architecture B. (substitute AMD64 for A and PPC for B in the above example).
Another common case is people compiling programs for their WRT64G wireless router. Not only do those have a fairly slow CPU, but they do not even have enough space to store or run a compiler. Cross-compiling the programs for the wireless router on your x86 PC is your best option in this case.
GCC and binutils in cross platform incarnations
For compiling the Linux kernel, you require gcc and binutils. GCC is extremely smart and can produce output in a variety of architectures, but it must be instructed at the time of compilation of gcc itself. So is the case of binutils. (BTW, if you do not know what binutils are, they are the basic binary utilities like the assembler, linker, loader, etc. Assuming you are running a Linux distro based on RPM (package manager):
[om@turyxsrv ~/work]$ rpm -qf /usr/bin/as binutils-2.16.91.0.6-4 [om@turyxsrv ~/work]$ rpm -ql binutils-2.16.91.0.6-4 ... /usr/bin/ar /usr/bin/as /usr/bin/c++filt /usr/bin/gprof /usr/bin/ld /usr/bin/nm /usr/bin/objcopy /usr/bin/objdump /usr/bin/ranlib /usr/bin/readelf /usr/bin/size ...
Now you know how to check for the binutils and constituent files.
Getting a cross compiler
Pre-built Cross Compilers
There are many available on the net, including the ELDK (DENX's Embedded Linux Development Kit). This toolchain is available on various target platforms including PPC, Arm, etc. Other pre-built toolchains are available at: Bootlin kernel.org 01.org
Building Your Own Cross-compiler
Crosstool is a set of scripts that assist in building a cross-compiler from scratch. These scripts can also produce a matching cross-glibc, which means you can cross-compile userspace applications as well as kernels.
64bit/32bit compilers
GCC is capable of generating both 32 and 64 bit code (eg.for AMD64 and IA32, or PPC and PPC64) using a compiler flag (-m32/-m64). When you take part in kernel development, you might want to compile your patchsets for both 32 and 64 bit architectures. Cross Compilation comes to your rescue here too. Rather than trying to modify the kernel makefile options to produce 32 bit output using 64 bit compiler or vice versa, (I could not figure it out after spending a couple of hours ), one should have a cross compiler to compile the kernel for 32 / 64 bit outputs on same platform.
NOTE: (this works at least on Ubuntu 8.04.1 amd64) To compile a 32 bit kernel for x86 on a x86_64 host, run i386 (which is a link to /usr/bin/setarch), then proceed as normal (make config; make). Alternatively, on an x86_64 host, you can use make ARCH=i386 <build_targets> to generate i386 binary output.
Testing the setup
Once you have set up the cross compiler, you should test it using a small hello.c to make sure that compiler indeed works. What I did is,
[om@turyxsrv ~/prg/tmp]$ export cross=i686-unknown-linux-gnu- [om@turyxsrv ~/prg/tmp]$ echo $cross i686-unknown-linux-gnu- [om@turyxsrv ~/prg/tmp]$ ${cross}gcc mathtest.c -o math [om@turyxsrv ~/prg/tmp]$ file math math: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.4.3, dynamically linked (uses shared libs), for GNU/Linux 2.4.3, not stripped
So, it is working.
Now let's configure and compile the linux kernel
[om@turyxsrv ~]$ cd src/i386-linux-2.6 [om@turyxsrv /home/src/i386-linux-2.6]$ uname -a Linux turyxsrv 2.6.18-rc3 #3 SMP Sun Jul 30 11:54:32 PDT 2006 x86_64 x86_64 x86_64 GNU/Linux [om@turyxsrv /home/src/i386-linux-2.6]$ make CROSS_COMPILE=i686-unknown-linux-gnu- ARCH=i386 xconfig [om@turyxsrv /home/src/i386-linux-2.6]$ make CROSS_COMPILE=i686-unknown-linux-gnu- ARCH=i386 V=1 -j4 all
I used V=1 to make the compilation verbose and -j4 to make use of my 4CPU machine.
After the compilation, make prints the message,
Kernel: arch/i386/boot/bzImage is ready (#1)
If you are still paranoid, make sure the architecture you compiled for is correct.
[om@turyxsrv /home/src/i386-linux-2.6]$ file vmlinux vmlinux: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
All done. I compiled on AMD64 and the target kernel would run on i386.
In fact, not really all. If your setup needs kernel modules you may want to install them on an arbitrary directory from where you can copy them easily to your target system. The following command line does this for you.
[om@turyxsrv /home/src/i386-linux-2.6]$ make INSTALL_MOD_PATH=<WhereTheModulesShallGo> modules_install
In this case, CROSS_COMPILE and ARCH are not necessary.
Generating Preprocessed files
Yet another facet of kernel compilation is that it helps you to generate preprocessed files. This is extremely useful when you suspect something could be wrong with your macros. In 2.4 days, we could get the command line and add -c option and redirect the result of gcc preprocessor to a file. In 2.6, it is built into the kernel. Here is how.
Say, I want to generate the preprocessor output for kernel/dma.c,
[om@kartel /space/kernel.org/linux-exp]$ make kernel/dma.i CHK include/linux/version.h CHK include/linux/utsrelease.h CALL scripts/checksyscalls.sh CPP kernel/dma.i
Done. Open kernel/dma.i to see what preprocessor did to dma.c
This is available for a loadable module (not a part of kernel) too:
[root@hydra1 linuxdriver]# make -C /lib/modules/2.6.18-92.el5/build/ M=$(pwd)/src hxge_main.i make: Entering directory `/usr/src/kernels/2.6.18-92.el5-x86_64' CPP [M] /root/hydra-src/linuxdriver/src/hxge_main.i make: Leaving directory `/usr/src/kernels/2.6.18-92.el5-x86_64'