开发者尝试让 musl 发行版兼容 NVIDIA 专有驱动
来源标题:Making musl and proprietary NVIDIA drivers work together
一位开发者尝试让基于 musl 的 Linux 发行版支持 NVIDIA 专有显卡驱动。由于 NVIDIA 专有驱动针对 glibc 构建,musl 发行版在 NVIDIA 显卡上无法正常工作。该开发者称已进行实验,并发布文章总结进展。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
你可能已经知道,基于 musl 的发行版的一个局限是:在装有各种 NVIDIA 显卡的电脑上使用时,它们无法完全正常工作。这是因为该驱动是专有的,且是为 glibc 构建的。我一直在尝试让这件事行得通,本文就是这项工作的总结。
NVIDIA 显卡在 Linux 上比较棘手。虽然大多数其他 GPU 厂商默认使用自由软件图形驱动(内核驱动在 Linux 内核树内,用户空间部分由 Mesa 项目提供),但 NVIDIA 通常不这么做,而是依赖树外内核驱动和厂商提供的专有用户空间库。确实存在一个自由软件驱动,叫做 Nouveau(内核和 OpenGL Mesa 驱动)和 NVK(Vulkan 驱动)。这能让显卡工作,但由于各种原因,性能通常会显著降低。目前的情况很大程度上取决于你拥有的是哪一代 NVIDIA 显卡。更简短地说,如果你有 RTX 2000 及以上,用 FOSS 驱动基本没问题但会损失性能;如果你的卡比 GTX 600 新但比 GTX 1600 旧,那就很麻烦,需要专有驱动;如果你的卡真的很老,它可能工作得不错,也可能完全无法使用。
我们现在在 580 和 620 分支上都有一个开放的 pull request。它还没有完全准备好,但已经能工作,并且按照 NVIDIA 提供的方式,可用于 x86_64 和 aarch64 架构。
要做到这一点,需要处理多件事情。特别是,使用原版 musl 是不可能的,而且很可能永远不会工作。我会逐项详细说明。
GLVND 库由 NVIDIA 创建,用于实现厂商中立的 OpenGL 分发。它提供应用程序链接的库,并暴露 OpenGL(及其他)API。然后它把调用分发给具体实现,这些实现可能是 Mesa 或专有驱动。如今大多数发行版都使用 GLVND。Mesa 自带内置的分发代码,如果你不把它和 GLVND 一起构建,就不一定需要 GLVND。musl 发行版通常使用后者。Chimera 目前不是这样,Void Linux 的 musl 分支也不是,因为它与 glibc 分支共享打包。Chimera 于 2026 年 11 月 5 日切换到 GLVND。切换相当直接,不需要太多文档。只需要打包 GLVND,用它重新构建 Mesa(以避免使用其内置的分发库),并处理依赖 mesa-devel 的软件包。
GLVND 不仅对 NVIDIA 驱动有用,总的来说它是用户会发现最有用的主要东西。ARM 和其他地方也有一些厂商驱动,但那些是小众。专有驱动多年前就强制要求 GLVND(这在混合显卡笔记本上也很有用,因为那时你可能仍然想为 iGPU 保留 Mesa)。
gcompat 项目提供了一个兼容层,允许有限地使用与 glibc 链接的程序。对我们来说不幸的是,gcompat 最新的 tagged release 没有提供专有驱动解析所需的所有符号。幸运的是,最新的 Git 分支提供了。如今开发非常缓慢,所以我们直接把它升到了最新修订版。
你可以在 pull request 中看到打包是如何工作的,那部分目前相当不重要,不需要详细说明。它的工作方式与其他发行版类似:从 NVIDIA 下载 .run 归档,并把所有东西安装到正确的位置。当然,在我们的情况下,我们不能直接安装它,因为它依赖 glibc 和其他东西。我们也不能直接引入 gcompat,因为驱动的大部分是库,没有解释器。
让我们看看对 libnvidia-glcore.so.580.196.07(核心 OpenGL 库)运行 ldd 会发生什么。
$ ldd libnvidia-glcore.so.580.196.07 ldd (0x739c4a8b1000) libm.so.6 => ldd (0x739c4a8b1000) libc.so.6 => ldd (0x739c4a8b1000) libdl.so.2 => ldd (0x739c4a8b1000) libnvidia-tls.so.580.196.07 => /lib/libnvidia-tls.so.580.196.07 (0x739c4a8a3000) libnvidia-gpucomp.so.580.196.07 => /lib/libnvidia-gpucomp.so.580.196.07 (0x739c3fa00000) libpthread.so.0 => ldd (0x739c4a8b1000) librt.so.1 => ldd (0x739c4a8b1000) Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by /lib/libnvidia-gpucomp.so.580.196.07) Error relocating /lib/libnvidia-gpucomp.so.580.196.07: mallinfo: symbol not found Error relocating /lib/libnvidia-gpucomp.so.580.196.07: __strftime_l: symbol not found Error relocating libnvidia-glcore.so.580.196.07: dladdr1: symbol not found Error relocating libnvidia-glcore.so.580.196.07: gnu_get_libc_version: symbol not found Error relocating libnvidia-glcore.so.580.196.07: dlvsym: symbol not found Error relocating libnvidia-glcore.so.580.196.07: backtrace: symbol not found Error relocating libnvidia-glcore.so.580.196.07: unsupported relocation type 37
这里有一堆东西。所以 libm.so.6、libc.so.6、libdl.so.2 等解析到 ldd,因为它们在 musl ldso 中是保留的:
static const char reserved[] = "c.pthread.rt.m.dl.util.xnet.";
动态链接器会忽略后缀。出于某种原因,一些库还对 ld-linux-x86-64.so.2 有 DT_NEEDED,而那是 glibc 的动态链接器。还有一些重定位失败,我们稍后会处理,现在先拿出 patchelf。
patchelf 是一个工具,可以让你动态修补 ELF 文件,用于处理库依赖(DT_NEEDED)、动态链接器(DT_INTERP)、RPATH,甚至一些花哨的动态符号重映射。我们用它来清理每个被安装的库和可执行文件。让我们从类似下面的命令开始:
$ patchelf --remove-needed ld-linux-x86-64.so.2 --remove-needed libdl.so.2 --remove-needed libm.so.6 --remove-needed libpthread.so.0 --remove-needed librt.so.1 --replace-needed libc.so.6 libc.so --add-needed libgcompat.so.0 libnvidia-glcore.so.580.196.07
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。