Building older versions of GCC compatible with Linux Kernel 2.4 =============================================================== This document explains how to build an older supported version of the GCC compiler from a distribution which only offers incompatible recent versions. Context ======= When Linux 2.4.0 was released in early 2001, GCC 2.95.2 was mainstream and GCC 2.91.66 was still widely used. The GCC development model was evolving and still confused. Since then, GCC has evolved a lot and stabilized. New versions are regularly released, and some old features from the early code get deprecated then removed. The kernel heavily relies on GCC's capabilities and behaviour. Some of the code in Linux looks strange but is in fact intended to workaround early GCC bugs. For these reasons, almost every new major GCC release breaks the kernel build process. GCC 3.4 was a real pain to introduce as it required a lot of rewriting in sensible areas, and GCC 4 required a lot of work, though this work was less complicated thanks to the cleanup efforts invested in GCC 3.4. Starting with GCC 4.2, the output code randomly fails depending on section ordering, which itself depends on the declaration order of functions, module parameters and many other things. The nasty part is that the code builds but randomly fails at runtime, so it is almost impossible to fix it and ensure that everything works, especially in the drivers area where most of the problems lie. As of 2008, GCC 4.3.2 is advertised as the current release and 4.2 the previous release. Most distributions have been shipping with 4.2 and 4.3 for some time, so building Linux 2.4 on a recent distribution has become a real problem for users who still have to support kernel 2.4 on servers, firewalls or any other system. Solution : the two-minutes process ================================== If it is not possible to adapt the kernel to GCC, let's adapt GCC to the kernel. We're lucky, building GCC to build just a kernel is not hard and is rather fast. I call that a two-minutes process because building an older GCC takes about 1 minute, and the kernel with that GCC also takes one minute. First, you have to select which version of GCC you want to build your kernel with. Here are some comments on possible versions : - 2.95.3 : very well tested for the kernel, builds kernels very fast, requires a lot of patches and is rather hard to build.. - 3.0 : very buggy, avoid it. - 3.1 & 3.2 : apparently less buggy but rarely used so bugs might have remained unnoticed. - 3.3 : used and tested for a long time. A bit slow but easy to build. - 3.4 : was recently introduced, received less testing, though seems OK. Builds kernels faster than 3.3, and is easy to build too. - 4.0 & 4.1 : received little testing, particularly slow but may produce smaller kernels when compiled with -Os. Always take the last maintenance version of a compiler (eg: 3.4.6 for 3.4). For best reliability and less hassle, I tend to recommend GCC 3.3.6. For improved build times (about 30% lower) and improved kernel performance, I'd recommend 3.4.6. It tends to produce more efficient code on i386, but has had a long history of causing annoyances with inline declarations. It seems OK though, and I build all my kernels with it. We'll assume 3.4 is used for the rest of this document, though what is described will work with 3.3 to 4.1 unless stated otherwise. Instructions ============ 1) Download gcc sources from the nearest mirror ----------------------------------------------- Find a mirror address here : [ http://gcc.gnu.org/mirrors.html ] or download from this directory : ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.4.6/ Get gcc-core-3.4.6.tar.bz2. It only contains the C compiler, which is what you want. 2) Prepare your build environment --------------------------------- Create a temporary directory where you'll extract the sources. Don't build on NFS, it may be slow. Use /tmp if you want. You'll need about 150 MB of free space. You'll have to extract the sources in that new directory, and create a temporary build directory aside it : $ mkdir /tmp/gcc-build $ cd /tmp/gcc-build $ tar jxf /tmp/gcc-core-3.4.6.tar.bz2 $ mkdir build 3) Configure gcc ---------------- You don't want your new gcc to conflict with the one already in place. I recommend simply prefixing it with "kernel-", and not installing it in /usr/bin, but rather /opt/kgcc/bin or anywhere else (/usr/local/bin will be used by default). I recommend choosing a place you already have in your PATH (such as the default /usr/local/bin), so that you don't have to pass the full path to the binary when building. $ cd /tmp/gcc-build/build $ ../gcc-3.4.6/configure --disable-locale --disable-shared --disable-nls \ --enable-languages=c \ --prefix=/opt/kgcc --program-prefix=kernel- If you're using GCC 3.3, you may see strange messages indicating that some programs were not found (eg: kernel-objdump). Simply ignore them. Note that you can set a lot of options, even use it as a cross-compiler. While very frequent, such a build will not be covered by this document. 4) Build GCC ------------ Both GCC 3.3 and 3.4 support parallel building, which reduces build time on SMP systems : $ make -j 4 If the build fails here because of some options you added above, you'll have to remove the build dir and recreate it. 5) Install your new GCC ----------------------- The binaries may be a bit big, but you can strip them. Both GCC 3.3 and 3.4 support a trick on the command line during the installation process, which consists in passing the "-s" flag to "install" : $ sudo make install INSTALL_PROGRAM='${INSTALL} -s' It will be installed under the directory referred to by the "prefix" option above, or /usr/local/bin if none was specified : $ ls -l /opt/kgcc/bin/kernel-gcc -rwxr-xr-x 3 root root 73124 Sep 6 22:45 /opt/kgcc/bin/kernel-gcc $ /opt/kgcc/bin/kernel-gcc -v Reading specs from /tmp/gcc-3.4.6-build/tmp-inst/opt/kgcc/bin/... Configured with: ../gcc-3.4.6/configure --disable-shared --disable-... Thread model: posix gcc version 3.4.6 6) Using your new compiler -------------------------- The compiler just has to be passed to "make" via the "CC" variable for all commands : $ make CC=/opt/kgcc/bin/kernel-gcc -j 4 dep bzImage modules $ sudo make CC=/opt/kgcc/bin/kernel-gcc modules_install install or more simply, when you have it in your path : $ make CC=kernel-gcc -j 4 dep bzImage modules $ sudo make CC=kernel-gcc -j 4 modules_install install Note: make modules_install needs a 2.4-compatible depmod. If your distro is 2.6-based and says it does not find depmod or depmod.old, it means that either modutils or module-init-tools have not been correctly installed. You can still force the path to depmod by passing it in the DEPMOD variable during make modules_install if you know where to find a good one. 7) I want to use a really old compiler, but compiling it breaks! ----------------------------------------------------------------- Tackle the problem in stages. Compile 3.x.y as above. Then use that to compile 2.95.x (CC=/opt/kgcc/bin/kernel-gcc ./configure ...), install, use 2.95.x to compile the next compiler in the chain, continue as far as you'd like. Conclusion ========== Building an older GCC on to build an older kernel on a newer machine is not really hard. It becomes harder when you have to cross-build (eg: you're building on a 64-bit machine for a 32-bit one). But for this, I would recommend that you check the excellent "crosstool" utility from Dan Kegel. It supports a wide variety of compilers, contains a lot of fixes and will do all the hard patching and configuration work for any combination you want or need. Suggestions and comments ======================== If you find mistakes or want to send comments about this document, please mail me at .