Alpaquita Linux: Libc implementations
Alpaquita Linux has two libc variants based on two different libc implementations, namely
musl is further represented by two variants, such as
musl-perf, and can be easily changed on the installed system using the
apk package manager.
In general, the choice is between
musl is mentioned in the text below, both musl variants are implied.
This document provides a general comparative analysis of the most important differences in libc implementations. For more information and more details, see the following documents:
Both libc variants use the "С.UTF-8" locale by default. In
musl it is built in, but in
glibc it is generated on the package level. Changing the locale in
musl is difficult and there is still limited support via the
MUSL_LOCPATH environment variable.
Usually, features that are not in the
musl library itself, are in the third-party projects including the locales. See the musl-locales project for details.
On the contrary, glibc has an impressive set of locales and it is already available in the
musl makes parallel requests to the nameservers found in
/etc/resolv.conf and only returns the first accepted answer. In glibc, the
requests are sequential, that is the next server is requested only after the previous one times out. Both versions are limited to 3 nameservers.
Parallel queries provide better performance in
musl, but in some setups it may be unacceptable, in which case caching local DNS server can come to the rescue. Caching reduces the CPU and network load and can even speed-up name resolution in
glibc to overcome a sequential nature of the requests.
Consider a simple setup of a caching server using a flexible tool like dnsmasq as follows.
Setting up a caching DNS server
apk add dnsmasq
cat > /etc/resolv.conf << EOF
cat >> /etc/dnsmasq.conf << EOF
rc-update add dnsmasq default
rc-service dnsmasq start
musl did not support TCP prior to version 1.2.3-r15, but now when a UDP response is received with the
musl sends a new request over TCP.
DNS directives in
search directive is available in
musl since version 1.1.13, hence Alpaquita supports it from the start.
domain directive (obsolete) behaves identically to the
search one, even though it was only supposed to have a single entry.
ndots:n: default is 1, meaning that if there are any dots in a name, a search list is not used.
timeout:n: default is 5 seconds. The amount of time the resolver waits for the response. Retry interval is calculated as
timeout / attempts.
attempts:n: default is 2.
The default values for the above options are quite similar to
musl is a lightweight library, which is much smaller than glibc. There are no overloaded or tricky functions, versioning, and overuse of malloc.
|stream-glibc docker image
|stream-musl with musl-perf
The resource consumption is small too, for example, the default stack size in
musl is only 128K, unlike the default in
glibc. This can provide additional
benefits for applications with a large number of threads. If necessary,
supports changing the stack size with
pthread_attr_setstacksize or when
linking with the
-wl,-z,stack-size=N option. The size can be increased or decreased.
memcpy, memmove, memset and others
Speaking of performance, we must mention
musl-perf. Some consider
glibc to be faster than
musl due to optimized string functions for various CPU features, such as avx2, avx512, etc. Now such optimizations are available in the
musl-perf package that distinguishes it from a regular
musl implementation. The full list of optimized string functions can be found in the Collection of glibc optimized asm string implementations document.
The best implementation of a string function is chosen at runtime depending on the capabilities of the CPU. The same as in
glibc. Although, there is a cost in terms of size to having multiple implementations of the functions.
Let’s look at the results of Phoronix Stream-Copy benchmarks on an VX2-capable machine. This is just an example of a benchmark that uses only
The performance of
musl-perf is pretty much the same as in the
glibc variant or in other glibc-based distributions.
5. Memory allocators
ptmalloc2 (pthreads malloc) general-purpose memory allocator.
This is one of the most proven and fastest malloc implementation for multiple
threads without lock contention. It has a good speed/memory balance and
tunable parameters via
GLIBC_TUNABLES env var.
musl has the so called
mallocng allocator introduced and used by default since version 1.2.1 in 2020. It is known for its strengthened protection against heap-based overflows, use-after-free, double-free errors, and better fragmentation avoidance.
In addition to the built-in allocators, there are a number of other external
allocators that are available for both
mimalloc: outperforms other leading allocators (jemalloc, tcmalloc, etc.), and often uses less memory.
mimalloc-secure: mimalloc that has a built-in secure mode, which adds guard pages, randomized allocation, and encrypted free lists to protect against various heap vulnerabilities. Though it has a performance penalty compared to mimalloc around 10% on average over various benchmarks.
jemalloc: implementation that emphasizes fragmentation avoidance and scalable concurrency support.
rpmalloc: a general purpose allocator with lock free thread caching.
To switch to one of these allocators, for example, to mimalloc, we can run the following command in Alpaquita Linux:
apk add mimalloc-global
Make sure it is used globally:
/lib/libmimalloc.so.1.7 => /lib/libmimalloc.so.1.7 (0x7f2725e04000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f2725f02000)
For more information, see the following links:
6. Indirect Function (ifunc)
musl support ifunc.
musl-default did not support it, but since version 1.2.3-r12 ifunc support was added to Alpaquita as well. This is because the GCC Function Multi-Versioning feature was added to the packages, for example the gzip package, which now requires ifunc support to work since
7. DT_RELR relative relocation format
The new format can optimally encode R_*_RELATIVE relocations in shared objects and position independent executables (PIE), saving the size of the resulted binary.
musl supports this feature since version 1.2.3-r9 in Alpaquita Linux. glibc variant with the current version 2.34, does not provide such support yet.
In order to enable compact relative relocations for your builds in Alpaquita musl environment, simply pass the
-Wl,-z,pack-relative-relocs flag to the linker. As a result, you get a new relocation section
.relr.dyn and save on the size of the built binary. In some cases the shrinkage can be as much as 5% without additional effort.
Consider the busybox, an app that has over a thousand relative relocations:
808 696 (-2.9%)
The use of RELR results in a 79% reduction in the relocation section size and approximately 2.9% smaller in total size.
For more information, see Relative relocations and RELR.
Musl’s simplicity and lightweight design makes its mechanism easy to audit, and more importantly, less likely to fail, reducing the attack surface.
The FORTIFY_SOURCE macro helps detect buffer overflows in various functions that manipulate memory and strings in libc, such as
strcpy, etc., providing an additional level of validation for such functions, which are potentially a source of buffer overflow bugs.
To enable it, set
-D_FORTIFY_SOURCE=2 (above 0) and enable the compiler optimizations by the
glibc supports it natively, but
musl depends on the external headers of another package. To make sure the compiled binary is built with the extra protections enabled, first install the
fortify-headers package if it is not already installed as follows:
fortify-headers package is automatically installed if the
build-base package is installed on
musl. So it might already be present on your machine.
apk add fortify-headers
You can review what is included in the package:
apk info -L fortify-headers
Now you can compile the applications with
-D_FORTIFY_SOURCE=2 as usual.
clang provide a built-in
usr/include/fortify include on
musl. Therefore, it is not necessary to additionally specify this path to compilers.
Unfortunately, you need to disassemble the binary to make sure FORTIFY_SOURCE is used in
glibc, you can check the
*_chk symbols in the relative functions.