Introduction
There are some cases when you want to compile programs for a computer (we say
target then), but can't compile them directly on the target for miscellaneous reasons :
- it doesn't run Linux (yet!)
- you don't have a compiler (yet!)
- it doesn't have enough CPU, RAM or disk space
- you want to use a faster computer for compilation
As long as you are compiling, for instance, x86 code for another x86 computer, things are trivial ; but when you want to compile a kernel for your powermac on a x86 PC, or when you want to compile binaries for a small embedded platform (not enough RAM and disk space) on your x86 (or anything) workstation, you have to
cross-compile. You will simply use a compiler running on your workstation to produce binaries which will run on another architecture. The very popular GCC supports cross-compilation easily, but for each target (powerpc, mips, sparc...) you have to compile (or install) a new cross-compiler.
Follow the following indications ; this router is a MIPS32-compatible CPU running in big-endian mode, so the arch is
mips.
Compilation of a cross-compiler
I will assume that you're using
Debian ; if you use another distro, you have three(+1) choices :
- find out if they have a similar mechanism to produce cross-compiler packages
- find out if they directly supply cross-compiler packages
- compile the whole toolchain by hand
- switch to Debian ;-)
- (update): download precompiled rpm binaries from Steven J. Hill's ftp; you need the toolchain and toolchain-mips packages, not the mipssel one.
Instead of supplying every possible cross-compiler (which would mean a very large number of packages), Debian offers a simple way to compile your own cross-compiler. You can read the
HOWTO, or follow those simple steps :
- apt-get install toolchain-source toolchain-source-gdb toolchain-source-newlib (add gdb if you want a debugger, and add newlib if you want it - it's an embedded libc, similar to UcLibC ; I didn't try it yet).
- go to some place where you have at least 500 MB of available disk space, preferably a fast disk ;
- there, tpkg-make mips-linux (remplace mips-linux with mipsel-linux for little-endian MIPS, powerpc-linux for powerpc, etc. ; warning, if you supply a wrong name, it will run anyway, but you will have problems later). If you encounter errors involving autoconf, you may need to run apt-get install autoconf2.13.
- then, cd binutils-mips-linux-*/ ; debuild -us -uc (you can remove the -us -uc if dpkg-build is setup to sign packages with your pgp keys). Note : if you run this as non-root, you might need to install the package fakeroot before. Also, if it says that packages foo and bar are missing, just install them and rerun debuild -us -uc.
- once debuild has finished, get root, and run debi ; this will install the package.
- now run TPKG_SERVER=ftp://ftp.fr.debian.org tpkg-install-libc mips-linux ; replace ftp.fr.debian.org with your favourite Debian mirror, and mips-linux with your target architecture.
- now, go to the gcc-mips-linux-* directory, and run debuild -us -uc and debi there too.
- if you installed the source for gdb or newlib, go to their respective directories and run debuild+debi too.
- test ! Write a very simple program (like int main () { return puts("Hello world!"); }) and try to compile it, running mips-linux-gcc hello.c -o hello. Congratulations, you have cross-compiled something :-)
Notes
A couple of things to know :
- if you want to use libraries (X11, glib, who knows), you have to install them for the target architecture ; you can do that with tpkg-install-libc mips-linux libglib1.2-dev (for instance). It will fetch the libglib1.2-dev package for mips architecture, mangle it with dpkg-cross and install it so it won't conflict with your architecture, but is usable by the cross-compiler.
- every library used need to be installed on the target, too ; if you don't want or can't install the libraries, you can compile statically (opposed to the default mode, "dynamically") ; that means "including all the required libraries to the compiled program". If you're not familiar with this concept, it's very useful when you want to distribute your programs to people who might not have the required libraries (or might have old or incorrect versions of them), but that means that your binaries will be much larger (as each library is included). If you compile stuff for embedded systems, with very low RAM and disk space, you will probably find that the GNU libc is too big, and you will want to try UcLibC. I didn't find (yet) a very clean way to setup a UcLibC cross-compilation environment, but I explain on the UcLibC page a simple (if not clean) method to do it.
Kernel cross-compilation
Go to your kernel sources ; and when running
make menuconfig, append
ARCH=mips (or
ARCH=powerpc, etc.) on the command-line. Then, when compiling the kernel, try to append
ARCH=mips CROSS_COMPILE=mips-linux- to the
make bzImage invocation. Note that when specifying ARCH, you don't add the
-linux suffix ; and don't forget the trailing dash in CROSS_COMPILE. You might have to modify the top-level
Makefile to set the CROSS_COMPILE variable here. Another way to do it (which works reliably) is to use
make-kpkg ; if you don't use it yet to compile your kernels, don't hesitate to learn it ! It is a very convenient way to produce Debian packages of kernel images + modules + configuration + system.map, ready to be installed. You can use it for cross-compilation like that :
make-kpkg clean && make-kpkg --arch powerpc --cross-compile=powerpc-linux- --append-to-version -g3 kernel_image kernel_headers ; that's actually the line I used to cross-compile a kernel for a g3 powermac. Note that you can omit the
kernel_image kernel_headers parts ; it will then compile the kernel but won't create packages.