Herdiansyah


Wyvertux part 1: The toolchain

As I mentioned in my 16 January post, I envisioned a GNU-less Linux distribution, laid out its problems such as toolchain, GTK, “GPL”, etc. And now onto the elephant in the room: toolchain.

The GNU toolchain is the staple of almost every Linux distributions ever existed. GCC is de facto free compiler for many years before its competitors even exist. Many BSD distrbution also have GCC by default (and still does on certain architectures), Apple Mac OS used latest GCC until the latter’s switch to GPLv3, leading to Apple (and the rest of BSDs, IIRC) staying on GCC 4.2.1 (the last GCC under GPLv2) and Clang’s launch.

And that’s just the compiler. We have GNU Make, de facto Make program in Linux distributions, binutils (as, ld, et al.), glibc (probably the only reason we have -gnu in a Linux triplet), bison (GNU yacc, basically), and GNU m4. There’s also GNU debugger and GNU autotools but we’re going into those later on. The current elephants in the room are GCC, glibc, and binutils.

Since I’m writing this assuming that one already use musl-libc instead of GNU libc, additional steps (which probably would include cross-compiling) would be needed if you’re from GNU libc.

The replacement

So, if I’m going to replace the GNU toolchain, I need replacements. What are the viable competitors to the GNU toolchain? Obviously, Clang is the answer to GCC, and it has been tested with the Linux kernel, but what else?

There are many more parts of GCC I haven’t mentioned yet (such as libatomic and libgomp), but that’s for later time. The current LLVM stack I’m building don’t need this, yet.

So, how do I replace this? Good news is, as of 9.0+, the LLVM stack has all of these. So I only need one component to rule them all.

GNU binutils will be replaced by LLVM tools, which has all of binutils except ld, which would be handled by LLVM lld instead. GCC libstdc++ is going to be replaced by LLVM libc++, and GCC libgcc_s is going to be replaced by two different components, LLVM libunwind and LLVM compiler-rt.

Actually building it

NOTE: This is what I could do when I first time built these bootstraps, I might have a better way since then. these steps are from my kiss-llvm repo.

As of 9.0.1, you can’t just throw all LLVM-related tarballs and compile it through gcc - it won’t work. You’ll need to build a temporary LLVM stack before you can make a unified LLVM build. Since I’m using KISS Linux as my host, I’ll be using their packaging format.

First step is building LLVM by itself, packaged as @llvm-pass1. To make sure our temporary LLVM doesn’t link to GNU ncurses, libedit (not part of GNU), et al., we can add -DLLVM_ENABLE_LIBEDIT=OFF to reduce dependencies on stray libraries further, such aslibxml2, we can add -DLLVM_ENABLE_LIBXML2=OFF. This way, we can get an LLVM binary which only links to the C library and C++ library. In current case, musl and libstdc++.

After LLVM, we build clang. It’s necessary to build Clang before compiler-rt since my experience building compiler-rt with gcc has been, less than stellar. If I tried to compile something with compiler-rt build with gcc, it’ll segfault. So I’m doing it the other way around (build clang before compiler-rt). The options are just building and linking the dynamic library, and turning off the examples, documentations, tests, and disable static build.

After clang, we build compiler-rt, packaged as @compiler-rt. Since we’re building on a musl-libc, we need to modify the CMake config a little bit. Make sure we build it with Clang and Clang++ (thanks to Void Linux).

sed -i 's/set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE)/set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE)/' cmake/config-ix.cmake

The next part involves libunwind, packaged as @llvm-libunwind. As usual, we build it with clang and clang++, but add a flag -DLIBUNWIND_USE_COMPILER_RT=ON, so libunwind is built with compiler_rt instead of libgcc.

After that, we’re building libc++abi, packaged as @libc++abi, the low-level support for libc++. We still fetch libc++ there since we need the libc++ headers. Make sure it uses both LLVM libunwind and compiler-rt by adding the flags -DLIBCXXABI_USE_LLVM_UNWINDER=ON and -DLIBCXXABI_USE_COMPILER_RT=ON.

After building libc++, I usually did a test with Pass 1 clang first. I tested it by building a simple C++ hello world program, and ldd’d it. If it returns libc++, libunwind, and libc++abi, then the it’s successful. Time for LLVM pass 2.

We’ve come to the Pass 2 build of our temporary LLVM build, this time, we force LLVM to use libc++ we built earlier. (Note: building @lld and llvm (in Clang, as GCC doesn’t support these options) with --rtlib=compiler-rt and --stdlib=libc++ as CXXFLAGS might be sufficient, I haven’t tried this.), and then Pass 2 Clang. We still build it from GCC (since Clang won’t be able to link with the pass 2 LLVM library) with libgcc, but this time, we link it with libc++.

Last but not least, @lld, the LLVM linker. Not much to tell it apart of telling it to use clang.

Well, that’s it. We can build the full unified llvm package. Compiled with Clang, linked with ld.lld. It’ll link with the proper compiler-rt and libc++ libraries. You have a full LLVM toolchain, GNU-free.

Next time, I want to look into other GNU components, if it can be replaced at all. Maybe later.