如何实际检测musl libc?

9
musl团队声称不需要检测musl libc,因为他们只实现标准功能而没有需要检测的怪异行为。直到今天,这种说法可能是真的,但现在不再是真的了。正常的功能检测已经无效,因为该功能是存在的,但是已经损坏了。我不想在编译时要求root并且禁止交叉编译,也不想探测它。这个错误已经被报告,并提供了最小化的样本代码,而维护者们不想修复它,也不会采纳我的补丁。我不打算因为musl有一个损坏的功能而惩罚每一个其他的libc。
从逻辑上讲,我想要做的是:
#if MUSL || APPLE
    pid = fork();
#else
    pid = vfork();
#endif

我已经有了#if APPLE,因为Mac OSX有一个不可信的vfork()

告诉我vfork()不好没有任何意义。自2008年以来,情况已经改变,无论涉及到多少复杂性,vfork()都是更好的选择。一些来源:https://gist.github.com/nicowilliams/a8a07b0fc75df05f684c23c18d7db234


1
你想用 vfork() 做什么?请记住,它已被 POSIX.1-2008 规范宣布为过时的,并且即使在此之前也受到广泛限制。 - user149341
1
@duskwuff:每次我生成一个子进程二进制文件时,都要支付一千兆字节的页面错误,这是怎么回事。vfork() 正在大放异彩。 - Joshua
1
撇开vfork/fork的讨论不谈,值得一提的是,符合POSIX标准的系统具有posix_spawn,它涵盖了vfork(vfork后跟exec)的大多数常见用例。对于这种用例,posix_spawn可能比vfork更有效率,因为无需复制当前进程。 - dash-o
@dash-o:不行,那是个死胡同。那是我尝试的第一件事情。 - Joshua
更像是一个hack而不是解决方案;考虑检查_GNU_SOURCE但不是_GLIBC_(或_GLIB_MINOR,或__GNU_LIBRARY)。您可以将此放入单独的文件中,该文件将使用_GNU_SOURCE编译,并定义'my_fork'。 - dash-o
@无人选手:CEO 正试图强行推行政治。这种氛围是由上而下形成的。 - Joshua
2个回答

6

我已经很晚了,但是在寻找答案时,我注意到 musl libc 的一个特点(在我的情况下,它是 musl 没有实现 pthread_setname_np)。当 _GNU_SOURCE 被定义时,musl libc 在 features.h 中不定义 __USE_GNU,并且使用 _GNU_SOURCE 来保护 GNU 扩展。

因此,这个过程应该可以用来创建一个 musl libc 宏(如果另一个 libc 实现具有相同的行为或某些原因而没有 libc 包含存根 features.h,则会产生误报;但是当 _GNU_SOURCE 被定义时,Glibc、uClibc 和 bionic 都在 features.h 或其他被 features.h 包含的头文件中定义 __USE_GNU)

因此,这对我目前正在运行的程序有效:

#define _GNU_SOURCE
#include <features.h>
#ifndef __USE_GNU
    #define __MUSL__ 
#endif
#undef _GNU_SOURCE /* don't contaminate other includes unnecessarily */

4

我想可能有点晚了,但这是一个解决方案:

#! /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}"

这个Shell脚本提取了一些有趣的信息,并输出-D友好的值,以便头文件/源代码文件可以从中受益。请参见,
$ ./detect-musl.sh 
-D__MUSL__ -D__MUSL_VER_MAJOR__=1 -D__MUSL_VER_MINOR__=1 -D__MUSL_VER_PATCH__=24

请在您的Makefile中尽早调用此命令,并根据需要调整CFLAGS
此脚本执行ldd脚本,获取名称中含有musl的库,然后执行该库。所有libc库都是可执行的(因为它们实际上包含了_start()),甚至会产生输出。通常,这是版本信息。例如,GNU显示如下:
$ /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>.

而musl则显示如下:

$ /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]

我已经在Alpine和Linux Mint上测试了我的脚本,看起来它运行良好。

就像你一样,我也讨厌教条主义阻碍实际。如果你发现更好的解决方案,请随时发布它。:)

编辑:在交叉编译的情况下,ldd无法工作。需要进行一些额外的工作,生成一个测试程序并读取其ELF链接,然后检查它是否包含musl libc字符串。有关详细信息,请参见GitHub代码片段。


我确实在进行交叉编译;否则我现在就会直接检测破损的vfork了。主机也无法运行musl二进制文件。 - Joshua
@Joshua,SO 不会给你糖果。:) - Unmanned Player

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接