cportabilitymusl

How to actually detect musl libc?


The musl team claims that there is no need of a way of detecting musl libc because they only implement standard functionality and have no quirks that need detecting.

Up until today, that claim may well have been true, but it is no longer true. Normal feature detection is not working because the feature is there but broken, I'd rather not probe for it because I don't want to demand root at compile time and disallow cross-compilation. The bug has been reported with minimized sample code, and the maintainers don't want to fix it at all, nor will they take my patch.

I am not going to penalize every other libc because musl has a broken feature.

Logically speaking I want to do

#if MUSL || APPLE
    pid = fork();
#else
    pid = vfork();
#endif

where I already have #if APPLE because Mac OSX has an untrustworthy vfork().

There is no point telling me vfork() is bad. The situation has changed since 2008, and vfork() is a better choice whenever possible, no matter the complexities involved. Some Source: https://gist.github.com/nicowilliams/a8a07b0fc75df05f684c23c18d7db234


Solution

  • I guess this is a bit late, but here's a solution:

    #! /bin/sh
    
    tmpf=/tmp/musl.log
    
    # Detect Musl C library.
    libc=$(ldd /bin/ls | grep 'musl' | head -1 | cut -d ' ' -f1)
    if [ -z $libc ]; then
        # This is not Musl.
        rm -f ${tmpf}
        exit 1
    fi
    $libc >${tmpf} 2>&1
    vstr=$(cat ${tmpf} | grep "Version" | cut -d ' ' -f2)
    
    v_major=$(echo $vstr | cut -d '.' -f1)
    v_minor=$(echo $vstr | cut -d '.' -f2)
    v_patch=$(echo $vstr | cut -d '.' -f3)
    
    rm -f ${tmpf}
    
    echo "-D__MUSL__ -D__MUSL_VER_MAJOR__=${v_major} -D__MUSL_VER_MINOR__=${v_minor} -D__MUSL_VER_PATCH__=${v_patch}"
    

    This shell script extracts some interesting information and prints out -D friendly values so headers/source files can benefit from them. See,

    $ ./detect-musl.sh 
    -D__MUSL__ -D__MUSL_VER_MAJOR__=1 -D__MUSL_VER_MINOR__=1 -D__MUSL_VER_PATCH__=24
    

    Please invoke this early on in your Makefile and adjust your CFLAGS accordingly.

    This script executes ldd script, gets the library that has musl in its name, then executes that. All libc libraries are executable (because they literally contains _start()) and even produce output. Normally, this is the version information. For example, GNU shows this:

    $ /lib/x86_64-linux-gnu/libc.so.6
    GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.
    There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
    PARTICULAR PURPOSE.
    Compiled by GNU CC version 7.3.0.
    libc ABIs: UNIQUE IFUNC
    For bug reporting instructions, please see:
    <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
    

    and, musl shows this:

    $ /lib/ld-musl-x86_64.so.1 
    musl libc (x86_64)
    Version 1.1.24
    Dynamic Program Loader
    Usage: /lib/ld-musl-x86_64.so.1 [options] [--] pathname [args]
    

    I've tested my script on Alpine and Linux Mint and it seems to work fine.

    Like you, I hate when pedantic ideologies hinder practicalities. If you find a better solution, please feel free to up post it. :)

    Edit: In case of cross compiling, ldd can't work. There's a bit of extra work involved where you generate a test program and read its ELF links out then check if its contains musl libc string in it. See GitHub snippet for details.