Linux 二进制兼容性的圣杯:musl 与 dlopen

Linux 二进制兼容性的圣杯:musl 与 dlopen

Hacker News 摘要

原标题:The Holy Grail of Linux Binary Compatibility: Musl and Dlopen

作者 Splizard 在 GitHub 的 graphics.gd 项目讨论区分享了他在解决 Linux 二进制兼容性方面的进展。他将这一成果称为 Linux 兼容性的终极方案,即通过 musl 库和自定义的 dlopen 实现单一静态二进制文件。

背景与挑战

在 Linux 上分发命令行工具或服务器软件非常简单,只需运行 go build 就能生成一个单一的静态二进制文件。这种文件可以在 2012 年以后发布的、运行 3.2 以上内核版本的任何 Linux 分发版上运行。

然而,当涉及到硬件加速图形时,情况变得非常复杂。Linux 上的所有 GPU 驱动都需要通过 C ABI 访问动态库。这些 C 库是针对特定的标准 C 库构建的。目前最常见的是 glibc,但也有一些分发版使用 musl

如果你编译了一个基于 glibc 的库或可执行文件,它无法在基于 musl 的系统上运行,反之亦然。作者在将个人电脑系统更换为 musl 版的 Void Linux 后,深刻体会到了这种不兼容性,连编译 Zed 编辑器都变得非常困难。

适配 musl 的努力

为了让 graphics.gd 支持 musl,作者对 Go 运行环境进行了补丁处理,引入了专门的 GOOS=musl 构建选项。

作者放弃了之前使用的 c-shared 构建方式,转而将 Go 代码与 Godot 的 c-archive 直接链接,从而生成一个单一的二进制文件。虽然这让项目支持了 musl,但开发者仍然需要针对不同的 C 库提供两个版本的下载。作者认为这并没有真正解决碎片化问题。

追求单一静态二进制文件

作者希望实现一种能够包含图形支持的单一静态二进制文件。虽然 Godot 在 Linux 上包含了大部分依赖项,但它仍需要使用 dlopen 在运行时动态加载 X11、Wayland、OpenGL 和 Vulkan 等驱动。

问题在于,musl 官方拒绝在静态二进制文件中实现 dlopen。这是为了防止用户从 musl 环境加载 glibc 库,因为两者的线程局部存储(TLS)实现方式存在根本性的冲突。

技术突破:自定义 dlopen

为了攻克这个难题,作者参考了 C 语言中的 detour 技术和 Cosmopolitan 项目的实现。

作者在项目中扩展了这种方案,其工作原理如下:

• 在目标机器中包含或编译一个微型 C 程序。

• 在同一进程中加载并执行该程序。

• 利用这个程序引入宿主系统的动态链接器,从而窃取系统自带的 dlopen

• 使用汇编蹦床(assembly trampoline)对所有动态加载的函数进行封装。

• 在函数调用期间,将环境临时切换到宿主系统的 libc TLS,调用结束后再切换回来。

这种方法让 Go 语言编写的图形程序也能像普通的命令行工具一样,享受单一静态二进制文件的便利,同时拥有完整的硬件加速支持。

使用与反馈

开发者现在可以通过 GOOS=musl GOARCH=amd64 gd build 命令来交叉编译自己的项目。作者还提供了一个基于该技术的示例项目供用户测试。

在讨论区中,有用户报告在 Arch Linux 系统上运行该示例项目时遇到了段错误(SIGSEGV)。作者目前正在询问相关系统的详细信息,以便进一步排查和修复这个底层兼容性问题。


原文:https://github.com/quaadgras/graphics.gd/discussions/242

评论:https://news.ycombinator.com/item?id=46762882

Report Page