Solo – 静态 Linux 二进制文件的 .so 加载器
SoLo — 静态 Linux 二进制文件的 .so 加载器 仅需一个 musl 链接的可执行文件。在运行时,加载用户现有的 glibc 链接的 GPU 驱动程序。无需容器、无需 AppImage,并且在进程中没有第二个 libc。静态二进制文件是部署 Linux 软件的一个非常乏味的方法:一个文件,无依赖项,无破坏风险。我们使用 IX 构建我们自己的静态 Linux 二进制文件,IX 是一种源为先的构建系统,旨在生成完全静态的 Linux 二进制文件。乏味在应用程序需要 GPU 的时候就结束了:Vulkan 和 OpenGL 驱动程序由主机以共享对象的形式提供,通常是针对 glibc 构建的,而完全静态的 musl 二进制文件通常无法通过 dlopen() 加载它们。SoLo 跨越了这个边界。它提供一个 dlfcn 风格的源 API,由其自己的 ELF 加载器(x86-64 和 aarch64)和一个基于 musl 的 glibc ABI 桥实现。结果仍然是一个普通的静态可执行文件,但它可以使用已安装在机器上的图形驱动程序。该库包含一个端到端的 Vulkan 示例:一个完全静态的可执行文件加载主机未修改的 Vulkan 驱动程序,运行一个计算着色器,并将结果写入 PNG。在 Linux 下对 AMD radv、radeonsi、Intel 和 NVIDIA GPU 进行了测试,并在 Asahi Linux 下对 Apple M1 进行了测试。主机保持硬件特定的代码。您只需发送其他所有内容。而不仅仅是依赖于演示的说法:在每次提交时,CI 都会通过 SoLo 加载 1,000 个安装量最多的 Debian 包的共享库——超过 2,100 个主机对象——在 x86-64 和 aarch64 上测试。 查看其工作原理 获取预构建的二进制文件——无需克隆,无需工具链,任何安装了 Vulkan 驱动程序的 Linux(mesa-vulkan-drivers 就足够):curl -LO https://github.com/pg83/solo/releases/latest/download/vulkan-x86_64 chmod +x vulkan-x86_64 ./vulkan-x86_64 hello.png vulkan-aarch64 是用于 arm64 机器的相同演示。该命令以通常的方式发现发行版安装的 Vulkan ICD,并生成 512×512 RGBA 图像。这就是我们构建 Shitty 发布二进制文件的方式——顺便说一下,这是一个闪电般快速的终端模拟器!要强制使用特定驱动程序:./vulkan-x86_64 --driver /usr/share/vulkan/icd.d/radeon_icd.x86_64.json radeon.png ./vulkan-x86_64 --driver /usr/share/vulkan/icd.d/lvp_icd.json lavapipe.png ICD 清单名称在不同发行版之间略有不同。传递没有 --driver 使嵌入的 Khronos 加载器执行其正常发现。您可以验证可执行文件本身不是动态链接的:readelf -lW ./vulkan-x86_64 | grep INTERP # 无输出 readelf -dW ./vulkan-x86_64 # "没有动态部分" 或者从源代码构建相同的演示,要求在 PATH 中有 Python 3 和 C/C++ 编译器:git clone https://github.com/pg83/solo.git cd solo ./build vulkan ./vulkan hello.png 这不是一个玩具调用 vkCreateInstance。演示:进入静态链接的 Khronos Vulkan 加载器;通过 SoLo 加载主机的 Vulkan ICD 及其非 glibc 依赖项;创建设备、存储缓冲区、描述符集合和计算管道;调度已注册的SPIR-V 着色器;映射结果并通过静态链接的 libpng 进行写入。完整示例位于 bin/vulkan,Vulkan 程序本身位于 main.cpp。 如何工作 ┌──────────────────── 完全静态可执行文件 ────────────────────┐ │ │ │ 应用程序 → 嵌入式 Vulkan 加载器 → SoLo dlopen/dlsym │ │ ├─ x86-64 ELF 映射 │ │ └─ glibc ABI → musl │ │ │ │ └───────────────────────────────────────────┬─────────────────────┘ │ 运行时映射 ▼ 系统 Mesa/Vulkan ICD.so + DSO elf_loader.cpp 映射 ELF 段,遍历 DT_NEEDED,解析版本化符号,应用 x86-64 重定位,支持 ELF TLS 和 TLSDESC,具体化 IFUNC,应用 RELRO,并运行初始化程序。依赖项本身是 ELF DSO 的会递归加载。故意不加载 glibc。像 malloc@GLIBC_2.2.5 这样的导入通过 glibc_shim.cpp 解决为过程现有 musl 运行时的 ABI 正确适配器。未支持的 glibc 函数有唯一生成的存根,如果被调用将会发出明确的错误,而不是静默地破坏进程。由于 musl 使其同步对象的大小符合每个架构的 glibc ABI,因此桥接不会遮蔽它们:驱动程序创建的 pthread_mutex_t 被直接使用。因此,一个锁对于已加载的 DSO 和可能共享其 Static executable 都是一个锁,并且在第一次使用时采纳 glibc 的静态递归和错误检查初始化程序。在从磁盘加载 DSO 之前,SoLo 会检查其静态提供程序注册表。这让应用程序能够满足依赖项——例如 Wayland——使用已经链接到可执行文件中的函数。LD_LIBRARY_PATH 和 DL_ELF_LIBRARY_PATH 会被认可,用于标准系统目录外的库。 有趣的部分足够小,值得一读:lib/dlfcn.cpp — dlopen、dlsym、错误和静态提供者 lib/elf_loader.cpp — ELF 映射、符号、重定位和 TLS lib/glibc_shim.cpp — 实现的 glibc ABI 适配器 lib/glibc_stubs.cpp — 其余 ABI 的显式回退 将其用作库
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡