关注了就能看到更多这么棒的文章哦~
Adding package information to ELF objects
By Jake Edge
November 2, 2021
DeepL assisted translation
https://lwn.net/Articles/874642/
虽然在 Fedora 和其他 Linux 发行版上通常是有一些直接方法来确定某个出现问题的二进制文件(例如 crash 了)是来自哪个软件包的,但在有些情况下可能却很难。最近为 Fedora 36 提出的一项功能(目前计划在 2022 年 4 月底完成)计划把一些信息嵌入到二进制文件本身里面,就可以显示它们是来自哪里的了。这是多个发行版的共同目标之一,目的是将这种信息存储在二进制文件(以及它们使用的库)中的方式给标准化定下来,从而有助于报告 crash 或者其他场景。
10 月 25 日,Fedora 项目经理 Ben Cotton 在 Fedora 开发邮件列表中发布了这个提案,也可以在 wiki 上看到细节。基本的想法是,每个为 RPM 包创建的 ELF 对象都会新增到一个.note.package ELF section。该部分将包含一个 JSON 格式的描述,用来说明它是由哪个 RPM 发布的。因此,这些二进制文件就包含了可以将它们直接与软件包对应起来的信息,哪怕在系统上没有 RPM metadata 数据也没关系
当 crash 发生时,这个机制将会被 systemd-coredump 工具利用来记录软件包的版本。对于那些通常来说都已经具有了 RPM metadata 数据的普通 Fedora 系统来说,这并没有什么大的好处。但是在其他情况下,Fedora 创建的二进制文件可能在运行中崩溃,这个机制就可以帮助管理员和工具能识别出二进制文件的确切来源。
这项功能最初是在 4 月为 Fedora 35 提出的,但被 Fedora 工程指导委员会(FESCo)拒绝了,并明确邀请其重新提交 "一个更详细、更易理解的'对 Fedora 的好处' 的描述"。于是这个功能的两位负责人 Zbigniew Jędrzejewski-Szmek 和 Lennart Poettering 在重新提交的提案中加入了更多信息。修改过的 "Benefit to Fedora" 部分详细说明了为什么它对这个发行版是有意义的:
这里增加了一个简单而可靠的方法来收集程序包版本的信息。它增强了现有的机制,而不是取代了它。在报告 crash dump 的时候特别有用,但也可用于 image introspection (映像文件内省)和[forensics (取证)]、license check (许可证检查),以及检查容器里的版本等等。
如果我们在 Fedora 中采用了这个方案,那么 Fedora 就会在实施该标准方面处于领先。无论是用在哪里的 Fedora 二进制文件都能被轻易识别出来。Fedora 二进制文件就为 build 过程提供了一个更好的基础。
如果其他发行版采用这个标准,我们就可以依据 Fedora 的做法来很容易地对这些二进制文件进行内省并报告出来。例如,当有人使用带有一些源自 Debian 生态系统的程序的容器时,我们将能够识别这些程序,而不再需要用 `apt` 或 `dpkg-query` 之类工具。在 Fedora 主机中执行的 Core dump 分析就可以很容易地提供关于来自外界 build 的程序的一些有用的信息。
当某个程序崩溃时,现在就已经使用了一个标识符,那就是存储在 .note.gnu.build-id ELF section 的 build ID。但这个 ID 是一个很长的十六进制字符串,人类无法直接理解。此外,这个 ID 只能通过使用安装在系统上的 RPM 数据库或经过网络查询才能确认出它来自的是哪个 RPM。提案中的一个例子展示了 note.package 中人类可读的 JSON 内容是什么样子的:
{
"type": "rpm",
"name": "hello",
"version": "0-1.fc35.x86_64",
"osCpe": "cpe:/o:fedoraproject:fedora:33"
}
该提案指出,"直接动机就是用在 core dump 场景"。如果有 RPM 数据库的话,可以使用 build ID,但是,使用 build ID 的话也可能会有问题。含有 crash 的程序的软件包在后来又被升级了的情况并不少见,因此安装的二进制文件与当时运行的二进制文件并不相同,此外还有其他可能会导致匹配失效,因此无法绝对保证这里的对应关系。另外,在没有数据库的环境中发生的 crash,例如在 initrd 或 sandboxed container 中运行的时候,还可以使用 JSON note 来提取 crash 中每个组件的确切版本。
对于自己来编译生成软件包的用户来说,人类可以理解信息要比 build ID 更有用,后者需要在某种数据库中维护,以便将来把 ID 映射到来源版本上。此外,二进制文件有时会从 Fedora 放到其他发行版中来执行,反过来的情况也有。在这种情况下,能够轻松找到一个二进制文件的来源也是非常有用的:
虽然大多数发行版都提供了一些机制来找出源码构建信息,但这些机制因发行版而异,而且可能不容易从其他的陌生系统中获取到。container 容器、flatpaks、snaps、Python 二进制 wheels、Anaconda 包,以及经常有人编译二进制文件后放到网上供其他人下载时,这些情况都会混杂在一起用。
David Cantrell 对这个提议有几个疑问。他想知道为什么 Fedora 要关心它的二进制文件在其他系统上是否混合起来用了,以及匹配关系是什么。如果没有办法在 vanilla Fedora 系统上重现这个问题的话,那么错误报告就不会是完全有效的。Poettering 说,拥有这些信息还是会有用的,因为它将有助于表明问题是发生在一个混合系统上。这将使 Fedora 有机会来尝试重现这个错误,也许是配合其他发行版一起来查,或者至少允许 Fedora 可以 "更有效率地'忽略' 那些非 Fedora 问题"。
Cantrell 还问到 "NEVRA"(name-epoch-version-release-architecture)这个软件包信息是否足够,因为它可能并不是唯一的,并想知道 build ID 加上 debuginfod server 配合起来使用是否足够了。Debian 开发者 Luca Boccassi 指出,从 systemd-coredump 所运行的 sandbox 环境来看,不一定有网络访问能力,有时也也不希望能访问网络。将 debuginfod 信息的 URL 添加到 package note 中也是一种可能。
Jędrzejewski-Szmek 说,还有一个隐私问题需要考虑。"向查询 debuginfo 服务器发起查询,可能会暴露自己的信息(比如我在运行什么,是在什么版本,哪些东西导致了 crash,等等),因此这种查询需要是可选打开的,并在用户控制之下。" 他还指出,Fedora Koji build system 就确保了其创建的软件包的 NEVRA 信息是唯一的。
然而,Kevin Kofler 对该功能有一些反对意见。实际上,他是反对在没有 RPM 数据库的环境中运行 Fedora 二进制文件的这种使用场景。他还声称,在不提供源代码的情况下从 RPM 中提取二进制文件,这种做法本身就是违反了代码的 license。但 license 问题其实基本上是与此无关的,Jędrzejewski-Szmek 认为。在某些情况下可能违反了许可,但在很多其他情况下并不违反许可。他继续说,二进制文件中的软件包信息实际上在有这类问题的时候可以帮助弄清情况。
没有 RPM 数据库的话会导致 Fedora 的安装无法正常运行,Kofler 说。"这怎么可能是个合理要求呢?" 但实际上经常会出现 RPM 数据库不存在的情况,特别是在 container 的使用场景里,Daniel P. Berrangé说。有的时候,有这个数据库是很有用的,但是在 container 领域,这不是一个很重要的事情,这并不意味着这个使用场景是不合理的:"这只是对使用和维护软件栈的不同方法/态度/权衡的决策。"
Kofler 说,为了支持这个使用场景而 "增大(bloating)" Fedora 中所有的 ELF 对象文件是不合理的做法。该提案指出,每个 ELF 对象的开销约为 200 字节,如果发行版中的每个对象都添加了软件包信息,则会增加 13MB。由于 Kofler 似乎不赞同这个使用场景,在他看来,为了支持这个场景而占用额外空间都是没有必要的。但是 Boccassi 指出,为了支持一个普遍存在的使用场景,这个代价是很小的了:
[……]它已经发生了,也正在发生,还将继续发生,因为对其他人来说,它是完全合乎逻辑和非常可取的做法。因此,人们要么呆坐着整天抱怨容器不好、他们都做错了、如果他们肯听听道理的话一切都不是问题,要么就动手做一些事情来大大改善每个人的工作基础,而这个成本低得可以忽略不计,如果这个做法采用了编译器改动或改变 build flag 或其他类似方法的话,基本上其他什么都不会变动。
此外,Poettering 指出,即使是 vanilla Fedora 系统也有一部分是没有 RPM 数据库的:
你也可以运行一个没有 RPM 数据库的系统,一直都可以,而且那东西还自称是 Fedora:dracut initrd 正是这么一个东西,它是由 RPM 建立起来的,但没有任何 RPM 数据库。
问题是,有多种不同方式来进行更新。rpm/dnf 是一种,dracut image rebuilt 是另一种,container 的更新方式通常也会更加不一样。rpm 是一个有用的工具(通过将 rpm metadata 嵌入 ELF 对象,它会变得更加强大),但你假设基于 rpm/dnf 的更新是升级的唯一正确方式,这根本不是现实,甚至不可取。
Cantrell 还提出了该提案可能存在的安全问题。他想知道使用 JSON 是否会有安全问题,JSON 在过去曾是一些安全问题的来源。"我关注的是编码格式、大小限制或报告、以及结构的格式。" Poettering 说,选择 JSON 是因为其拥有 "久经考验的解析器",这些解析器已经在 systemd 和其他地方使用起来了。Jędrzejewski-Szmek 补充说,"systemd 中的实现正在利用 oss-fuzz 来不断进行 fuzzing 测试",所以我们有理由认为多数解析器的错误都已经被发现并被 fix 了。
总的来说,虽然还有一些担忧,但人们的反应基本上是正面的。这是一个对二进制文件的影响相当小的变化,200 字节似乎并不重要,而它可以在许多情况下会有用处。它也为跨发行版的一些努力做出了贡献。微软的 CBL-Mariner 容器发行版就已经增加了对该功能的支持,它将被推荐给 Debian,其他发行版也可能会效仿。当然,这将取决于 FESCo,但反对意见和担忧似乎并不能抵消它将带来的好处。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~