Are alternatives to gcc, libstdc++ and glibc viable yet? And how do I use them?
uClibc as an alternative is already “traditional”.
binutils is still needed, especially ld. Alternatives: lld and mclink(?), but they’re not quite there yet. gold (also from binutils) is a viable alternatives. It has a bit of use outside of LTO: it has code folding options, and it is a little bit faster to link. gas is sometimes needed because the LLVM assembler doesn’t support all things you still encounter in assembly code. Tools like nm need to deal with LLVM bytecode and gcc interim code (for LTO) in addition to traditional object files. We can get this with a wrapper script that determines the type of object file and calls into llvm or gcc.
gcc can mostly be replaced by clang. E.g. OpenMandriva 3 is almost fully built with clang 3.7. Problems usually due to bad code or gcc extensions. Many patches have been upstreamed, a couple of patches are still maintained out of tree in OpenMandriva. Packages that are really too difficult to handle are just built with gcc – which is possible as long as they use the same libc (doesn’t matter who compiled it).
clang’s __GNUC__ macros are too conservative, so some projects will think the compiler version is too old to enable some feature. Proper solution is to detect features instead.
Things to avoid to be compatible with both compilers:
- Nested functions
- Variable length arrays of structs and non-POD types
- Empty scruts
- array subscripts of type char (cast it to int)
- Some reserved words (e.g. _Nullable is used in Qt)
- Declared but undefined static functions or variables (error in clang, not even a warning in gcc)
- build gcc with –with-default-libstdcxx-abi=gcc4-compatible – gcc5 changed ABI (will be changed in future)
- C89-isms or C++98-isms, e.g. ‘extern inline’ – clang defaults to new standard versions, gcc defaults to C90.
It can be a good idea to just build with clang as an extra warnings pass, even if you build the final thing with gcc.
Use gcc or clang for new code? clang tends to be faster at compiling, its source code is easier to read, it has backends for GPU. gcc has better OpenMP support and supports more targets. Best solution: try both, try the results, and go for the one that produced the best executable.
musl is now a viable replacement of glibc. It’s not binary compatible with glibc. clang doesn’t support musl directly,but patches are available from OpenMandriva (just 5 patches). gcc trunk supports musl.
bionic is also a libc that starts to be usable. It is highly optimised (especially for ARM). It still lacks SysV shared memory – so no X server. But you can take code from e.g. musl to implement that part.
To ensure libc compatibility:
- Really include all headers you use, don’t rely on implicit inclusion through another header file (esp. with musl).
- Avoid using deprecated API.
- Don’t assume that _GNU_SOURCE, _BSD_SOURCE are defined.
- __linux__ != __GLIBC__
- Some functions may not exist, e.g. locale variants.
glibc supports most targets and almost everything is compatible with it. musl tends to be faster and smaller, without cruft. bionic is even smaller and faster (on ARM), but is designed for Android needs so you may have to import some functions from another libc. uClibc is even smaller, and it’s configurable, but doesn’t support aarch64.
libstdc++ can be replaced with LLVM’s libc++, but binary compatibility doesn’t exist. Problem for libraries: e.g. Qt linked with libc++, other application linked with libstdc++, at DLL load time you get symbol clashes. So libc++ is only viable if you can rebuild everything.
To ensure libstdc++ compatibiilty:
- Code for C++11 or C++14
- Don’t assume one header includes another
libc++ is often a better choice: 50% smaller with full C++14 support. Android is switching to it, but they have to since STLport is unmaintained. But it’s almost exclusively with clang, so only use it if you also compile with clang.
Cross-compiling with clang is easy because the compiler always supports all possible target backends. So you just need to point to the correct sysroot – except that –sysroot doesn’t work very well. Need a wrapper to add -nostdinc -isystem XXX -L XXX.
When cross-compiling, if it compiles, it doesn’t mean it works. One thing you can do is to run it in qemu.