关于二进制分发包的重建 (Binary Distribution Rebuilds)
关于二进制分发包的重建 (On Binary Distribution Rebuilds)
发布于 2025-03-31,作者:simon — 暂无评论 ↓
几个月前,我重建了 (popcon 排名靠前的 50 个) Debian 和 Ubuntu 软件包,分别在 amd 和 arm64 架构上,并比较了结果。从那以后,Reproduce.Debian.net 项目启动了。与我的小型实验不同,该项目是更大规模的重建,涉及更多架构。他们的目标是重现 Debian 档案库中发布的内容。
这两种方法的一个区别在于构建输入:Reproduce Debian 项目使用与构建已发布软件包相同的构建输入。我则使用已发布软件包的最新版本进行重建。
这种差异意味着什么?我相信 reproduce.debian.net 将能够重现档案库中的更多软件包。如果你使用一个版本的 GCC 构建一个 C 程序,你会得到一些二进制输出;如果你使用更高版本的 GCC,你很可能会得到不同的二进制输出。这是一件好事:我们希望 GCC 不断发展,并在时间推移产生更好的输出。然而,这意味着为了重现我们发布和使用的二进制文件,我们需要使用准备这些二进制文件时使用的任何构建依赖项来重建它们。结论是,我们需要使用旧的 GCC 来重建程序,这似乎是 Reproduce.Debian.Net 的方法。
如果 Reproduce.Debian.net 项目能够达到 100% 的可重现性,那将是一个巨大的成功,而且这似乎是可以实现的。
但我认为我们需要做得更多。仅仅能够使用较旧的二进制包来可重现地重建软件包,只会引出一个问题:我们能否重建这些较旧的软件包?我担心尝试这样做最终会导致需要重建 20 多年前的软件包,其中有相当一部分是非法分发的,或者由于 bit-rot 而无法再构建。如果我们的重建工作假设了一些我们无法再从源代码构建的初始二进制 blob,我们就无法解决 Trusting Trust 问题。
我绘制了一个插图,来说明我正在考虑的工作,以实现比可重现重建更强大的目标。我将这个概念称为 Idempotent Rebuild,这是一个古老的概念,我相信它与 John Gilmore 多年前描述的 相同。
该图显示了如何使用 Debian 主档案库作为输入来重建另一个“stage #0”档案库。这个 stage #0 档案库可以与主档案库使用 diffoscope 进行比较,所有差异都是最好能解决的问题。stage #0 档案库中的软件包用于准备包含构建工具的新容器镜像,并且 stage #0 档案库用作输入来重建自身的另一个版本,称为“stage #1”档案库。stage #0 和 stage #1 之间的差异也对分析和解决问题很有用。这个过程可以重复多次。我相信如果这个过程在某个时候终止,即 stage #N 档案库与 stage #N-1 档案库相同,那将是一个有用的属性。如果发生这种情况,我将输出档案库标记为分发的 Idempotent Rebuild。
今天的 N 有多大?最简单的假设是它是无穷大。嵌入到二进制包中的任何构建时间戳都会在每次迭代中更改。这将导致该过程永远不会终止。修复嵌入的时间戳也是 Reproduce.Debian.Net 项目将会遇到的问题,并且必须解决。
还有什么其他原因可能导致差异?很容易看出,一般来说,如果某些输出不是确定性的,例如二进制文件中汇编程序目标代码的排序顺序,那么输出将是不同的。 reproduce.debian.net 项目也将捕获此问题的简单实例。
是否存在导致无限 N 的更高阶链?很容易想象这些链的存在,但我不知道它们在实践中会是什么样子。
理想的情况是我们可以将 N 降至 1。这在技术上可行吗?比较构建 GCC,它执行一个初始的 stage 0 构建,使用系统编译器来生成一个 stage 1 中间版本,该版本用于再次构建自身到 stage 2。比较 stage 1 和 2,如果成功(二进制文件相同),则编译成功。这里 N=2。但是这是使用一些未知的系统编译器执行的,该编译器通常与正在构建的 GCC 版本不同。重建二进制分发时,您从相同的源版本开始。因此,N=1 似乎是可能的。
我现在无法报告任何进一步的技术进展,我感到不快。此工作的下一步是在存储库中发布 stage #0 构建工件,以便可以使用它们来构建 stage #1。我已经表明,与官方二进制文件相比,stage #0 的可重现性约为 30%,但我没有将工件保存在可重用的存储库中。由于官方二进制文件不是使用最新版本构建的,因此预计可重现性数字较低。但是 stage #1 会发生什么?百分比应该会上升:我们现在将重建与早期重建进行比较,使用相同的构建输入。我渴望看到这种情况实现,并希望最终在此方面取得进展。但是,为了构建 stage #1,我相信我需要在 stage #0 中重建更多软件包,这可能与“build-essentials-depends”软件包集大致相似。
我认为 Idempotent Rebuilds 的最终目标是能够从其他 bootstrappable 环境(如 Guix)重新引导像 Debian 这样的二进制分发。在并行实现 Debian 的 100% Idempotent Rebuild 的同时,我们可以设置一个 Guix 环境,该环境使用 Guix 二进制文件构建 Debian 软件包。这些构建最终应该收敛到相同的 Debian 二进制软件包,否则会发生一些非常成问题的事情。这种重新引导像 Debian 这样的二进制分发的方法似乎比重建该分发从一开始的所有二进制文件更简单。
你怎么看?
PS. 我担心 Debian main 可能已经进入一种根本无法再重建自身的状态:非自由固件和非 Debian 签名的二进制文件的存在和假设可能已经破坏了 Debian main 重建自身的能力。为了能够完成 Debian 的幂等和自举重建,需要解决这个问题。