libc++abi v.s. libcxxrt

If you’ve read my previous posts, you may already know that a C++ standard library needs something called an “ABI library” to do certain low-level work for it. In the case of libc++, it supports libsupc++, libc++abi or libcxxrt as its ABI library. Among them, libsupc++ is not very well-known because it’s a part of GCC and usually not shipped standalone. In this post, we’ll be concentrating on libc++abi and libcxxrt.

libc++abi is the following effort by LLVM after it successfully developed a new C++ standard library — libc++. Developed by the same party, libc++abi works seamlessly with libc++. It’s now used on Apple’s platforms.

libcxxrt is developed by PathScale [1], a commercial compiler vendor, together with the BSD communities.  It’s now used in the three BSD derivatives and proved to be of industry quality.

While refactoring the ebuild for libc++, I hit a tough question: which ABI library to use as the default. libc++abi seems a more natural choice to me because it’s from the same bloodline as libc++. But libcxxrt should also be solid enough since it’s used in those rock-steady BSDs without any problem. Out of curiosity, I decided to do a simple benchmark between the two rivals.

I’m using a C++ program suggested by lu_zero for this benchmark [2]. I linked this program with libc++abi or libcxxrt (dynamically or statically), ran it three times and recorded the average runtime under each configuration. OpenMP is disabled for static linking because the OpenMP library used by clang is currently shared only on Gentoo. The test machine is a Gentoo VM with 4 cores and 4GB of memory. The parameter SPP (in source code) is altered from its default to 100 to reduce runtime. Below are the benchmark figures:

libc++abi, shared: 50.178s
libcxxrt, shared: 54.558s

libc++abi, static: 1m38.484s
libcxxrt, static: 1m35.134s

Apparently, libc++abi is slightly faster with dynamic linking, but a bit slower with static linking. Considering system fluctuation, the difference is nearly negligible. Due to its same bloodline as libc++, I’m going to keep libc++abi as the default ABI library in Gentoo 🙂



GSoC 2016: code submission status

This post serves as a tracker of code submitted in the domain of this GSoC project.

This summer, I worked out a bunch of patches that enhance clang/llvm with support for musl-libc, and had those patches contributed upstream. With these patches, clang is now able to correctly link binaries with musl.

llvm musl-libc support:

clang musl-libc support:

There’s still a pending compatibility issue that prevents llvm itself from being built on musl as is. musl and llvm’s developers have different views on this issue [1], and I haven’t yet found a solution that pleases both side. Currently we’re using a downstream patch in Gentoo to make llvm and musl compatible [2].

To have clang not link binaries with libgcc, I contributed another patch to clang that allows compiler-rt to be used as the default runtime library:

With those upstream enhancements, I wrote several ebuilds for Gentoo to construct a GCC-free C++ runtime environment, including:

  • create new packages for LLVM’s libunwind and libc++abi
  • enhance libc++ to support libc++abi and libunwind
  • make llvm compatible with musl-libc
  • enhance clang to support libc++ as the default stdlib and compiler-rt as the default rtlib
  • create a profile for using clang as the default compiler in Gentoo

Code submitted to Gentoo:

Code under reviewing for Gentoo:

Once the pending pull requests are merged, I’ll deliver a proper Gentoo stage3 with clang as the default compiler and all packages (except for kernel) built with clang.



Use clang as a native compiler in Gentoo

So far my GSoC project for Gentoo this year is soon coming to an end. There are still a few missing pieces, but the goal — supporting clang as a native compiler — is almost achieved. There’s nothing preventing you from using clang natively in your system now, and I’ll show you how in this post 🙂

First, you should install a GCC-free C++ runtime stack, composed of libunwind, libcxxrt and libcxx. There’re two versions of libunwind, one from nongnu and the other from LLVM. I encourage you to use the LLVM version, which proved more robust in my previous experiments [1]. To explicitly install LLVM’s libunwind, type:

$ emerge llvm-libunwind

followed by:

$ USE=libunwind emerge libcxx

libcxxrt will be pulled in automatically by emerge since libcxx depends on it. If we don’t explicitly install llvm-libunwind, the nongnu version will be pulled in instead.

Then it’s time to install clang:

$ USE='clang default-libcxx default-compiler-rt' emerge llvm

The USE flags default-libcxx and default-compiler-rt tell clang to use libc++ as the default C++ library and compiler-rt as the default runtime library, in place of libstdc++ and libgcc respectively, thus getting rid of dependence on GCC. If your system is based on musl-libc, you need also add -sanitize to the USE flags to disable LLVM’s sanitizers, which won’t compile on musl at the moment.

Remember to apply keyword ~amd64 on all the packages involved, since the features mentioned above are only available in the latest version of those packages. Additionally, the latest version of LLVM is currently masked for testing; you need to put the following line in package.unmask to unmask it:

# in file /etc/portage/profile/package.unmask

Then we are ready to go! Now you can use clang to compile any C/C++ program and the resulting binary will be GCC-free: no dependence on libgcc or libstdc++. Also put CC=clang and CXX=clang++ to ensure all your future packages are compiled by clang. Actually I encourage you to rebuild @world instantly, after which all packages in your system will be GCC-free:

$ emerge -e @world

NOTE: there’s one package you should pay attention to: libmnl. It’s pulled in by another package iproute2, and unfortunately is mis-compiled by clang [1]. There’re two solutions: 1) apply keyword ~amd64 on libmnl so the latest version is used, which works with clang; 2) apply USE flag minimal on iproute2 so it doesn’t need libmnl at all.

At this moment, you may wonder if we can just uninstall GCC once and for all. Unfortunately, the answer is no. There are two cases where GCC is still irreplaceable. First is when you upgrade your kernel. Currently clang is not capable of compiling the kernel without heavy patching, so you still need the good old GCC.

The second case is when you compile C++ code. When I say the binary built by clang is GCC-free, it’s actually not 100% true 🙁 There’re two pieces from GCC needed by every C++ program: crtbegin and crtend; unfortunately they’re not provided by any library mentioned above. I tried borrowing implementation of these two files from NetBSD, and they seem to work right out of the box. But another problem is that clang on Linux is hardcoded to use GCC’s crtbegin/end; NetBSD’s crtbegin/end won’t be recognized unless clang’s behavior is altered. This issue isn’t unresolvable and I’ll see if I can find a workaround for it. For now, just don’t remove your GCC 🙂

This project is not finished. There’re two pieces to be delivered soon: 1) a new package libcxxabi, which is developed by LLVM, will replace libcxxrt as the default C++ ABI library; 2) a new profile for native clang where USE flags, keywords and masks are all set appropriately so you don’t need to do it yourself.

Stay tuned!