Building your own toolchain
Overview
A cross-compiler is a compiler that runs on one platform (e.g. x86_64) and generates code for another platform (e.g. ARM). This would be the first step that we have to do if we want to do development on a new platform.
Once we have this cross-compiler, we can build various software targets for the new platform: the kernel, the shell, the utilities, and others, including the native compiler for that platform.
A native compiler is a compiler that generates code for the same platform that it runs on. A native compiler is usually called just as "the compiler" - without the prefix cross- it is understood that a compiler is native. To build a native compiler on a new platform, one needs to have a cross-compiler first.
So it seems obvious that any new platform development effort must start at creating the first compiler. In the FirstBoot page, this was short-circuited by using a ready-made cross-compiler from Aboriginal and Linaro. In the NativeCompiler page, again this was short-circuited by using the Aboriginal's native compiler.
That's why I started with a known good compiler. Once that works and we can be sure indeed that the configuration etc is correct, then we can build our own compiler at ease, knowing that any failure later would be the problem at the compiler level and not because software configuration.
So now that we have a working system using those shortcuts, in this page I will go ahead and explain those steps that I have previously skipped.
Building the Cross Compiler
Nowadays, there are many ways to build your own cross-compiler. There are many scripts and environments that will do that for you, for example (in no particular order of preference):
- Buildroot
- Crosstool-NG
- embtoolkit
- and of course, Aboriginal Linux itself
And if that's all that you want to know, that's great - go and grab and use them. They are very useful tools indeed.
But except for Aboriginal Linux, these tools have two problems:
- They make the cross-compiler for your but don't explain how it gets made and what's involved in making it.
- They don't tell you how to make the jump from cross-compiler to native compiler.
The second deficiency is especially problematic, because sooner or later we want to get a native compiler going. To know about these things, there is no substitute to doing it the LinuxFromScratch (LFS) way. In fact, LFS has subproject called Cross LFS (CLFS) that does exactly that.
To build a cross-compiler using the CLFS, follow Chapter 5 (after doing the
preparation in Chapter 4 and reading the earlier chapters). By the end of
Chapter 5, you will get a working cross-compiler located in /cross-tools
.
There is no instruction for building CLFS for the ARM platform,
but you can use other non-x86 platform as a template (suitably modifying the
platform reference to ARM). For example, I used the MIPS CLFS successfully.
Follow the CLFS for MIPS; the only change you need to do is CLFS_TARGET:
instead of mips-unknown-linux-gnu
, change that to
arm-lfs-linux-gnueabi
. Obviously you can skip building MIPS-specific
packages (like the Colo bootloader).
This native compiler is as good as Aboriginal's or Linaro's one, although for making static binaries (e.g. static busybox) I still recommend to use Aboriginal cross compiler as it uses uClibc and thus creates smaller static binaries.
Building the Staging Native Compiler
Once you've gotten the cross-compiler, the next step would be to prepare the native compiler. In (C)LFS, there are two native compilers:
- the staging/temporary native compiler, located in
/tools
, which is used to compile and build packages before the final compiler is build (including the final compiler itself). - the final native compiler, located in
/usr
, which is the compiler that will be shipped as part of the distribution. Most of the software in the distro should be built using this compiler.
Follow CLFS Chapter 6; by the end of Chapter 6 you would get a working
staging native compiler hosted in /tools
, which you can copy into your
bootable root filesystem and use it on the real ARM machine.
If you are going to follow the FirstBoot method and you build a static busybox with all applets enabled, then you don't need to build all the packages listed in Chapter 6; as some of those are provided by busybox and are adequate for building the full system later. Other than the toolchain (binutils, gcc and eglibc), the ones you need to build are: file, flex, bison, make, m4 (these aren't provided busybox), patch (patch from busybox is buggy), and bash/ncurses (some GNU tools explicitly require bash for building) . If you wish you can build gettext and texinfo. If you don't use busybox you need to build all of them.
This staging native compiler is as good as Aboriginal's one, and it is as handy too - you can put it to SD Card anywhere, and symlink /tools to the directory that contains the tool, and you can use it when you use the SD Card to boot your platform.
Building the Final Native Compiler
Once you have the native compiler, you can boot as described in FirstBoot page. From there, you have two options to go on and create the final native compiler:
- to continue with LFS Chapter 6 as described in NativeCompiler, or
- to continue with CLFS Chapter 9 and 10.
They are (almost) identical from that point onwards.
Final Notes
The default instructions in CLFS will create default compilers and libraries;
and in this case, it will be soft-float toolchain. If you want to make a
hardfloat toolchain instead, use the modifications I've put in NativeCompiler
page. Basically it is about adding some --with-xxx
settings when building
the compiler, and adding CFLAGS=-mfloat-abi
settings when building glibc.