PEP 571 – manylinux2010 平台标签
- 作者:
- Mark Williams <mrw at enotuniq.org>, Geoffrey Thomas <geofft at ldpreload.com>, Thomas Kluyver <thomas at kluyver.me.uk>
- BDFL 委托:
- Alyssa Coghlan <ncoghlan at gmail.com>
- 讨论至:
- Distutils-SIG 邮件列表
- 状态:
- 已取代
- 类型:
- 信息性
- 主题:
- 打包
- 创建日期:
- 2018年2月5日
- 发布历史:
- 取代者:
- 600
- 决议:
- Distutils-SIG 消息
摘要
本 PEP 提议创建 manylinux2010 平台标签,以取代 PEP 513 引入的 manylinux1 标签。它还提议更新 PyPI 和 pip 以支持在兼容平台上上传、下载和安装 manylinux2010 分发版。
基本原理
manylinux1 平台标签名副其实,它使得在许多 Linux 系统上安装二进制扩展模块成为可能。像 cryptography [2] 和 numpy [3] 这样的库现在对 Python 开发者来说更加易于访问,因为它们在常见架构上的安装不再依赖于脆弱的开发环境和构建工具链。
manylinux1 wheel 通过允许其包含的扩展模块仅链接到一组较小的、导出具有足够旧版本号的符号的系统级共享库来实现其可移植性,这些符号受益于向后兼容性策略。例如,在 manylinux1 wheel 中依赖 glibc 的扩展模块必须以 2.5 或更早版本进行构建;然后它们可以在提供最新 glibc 版本但仍导出 2.5 版本所需符号的系统上运行。
PEP 513 从 CentOS 5.11 中提取了其白名单共享库及其符号版本,这是编写 PEP 时最旧的支持的 CentOS 版本。不幸的是,CentOS 5.11 已于 2017 年 3 月 31 日结束生命周期,并明确警告不要继续使用。 [4] 不会再有进一步的更新,例如安全补丁。这意味着其软件包将保持过时版本,阻碍了使用 manylinux1 Docker 镜像的 Python 软件打包者的努力。
CentOS 6 现在是最旧的支持的 CentOS 版本,并将通过 2020 年 11 月 30 日收到维护更新。 [5] 我们提议创建一个新的 PEP 425 风格的平台标签,名为 manylinux2010,该标签将源自 CentOS 6,并更新 manylinux 工具链、PyPI 和 pip 以支持它。
最初提议将其命名为 manylinux2,但版本控制已更改为使用日历年(也称为 CalVer [22])。这使得更容易按任意顺序定义未来的 manylinux 标签:例如,一个假设的 manylinux2017 标准可以通过新的 PEP 在 manylinux2014 之前定义,或者可以定义一个 manylinux2007 标准,该标准针对比本 PEP 更早但比 manylinux1 更新的系统。
日历版本控制也大致说明了哪些 Linux 发行版版本支持哪个标签:manylinux2010 将在 2010 年之后发布的大多数发行版版本上运行。但这只是一种近似说法:实际的兼容性规则在下面定义,并且一些较新的发行版可能不符合这些规则。
manylinux2010 策略
以下标准决定了一个 linux wheel 是否符合 manylinux2010 标签
- 该 wheel 只能包含为 CentOS 6 支持的两种架构之一编译的二进制可执行文件和共享对象:x86_64 或 i686。 [5]
- 该 wheel 的二进制可执行文件或共享对象不得链接到外部提供的库,除了以下白名单中的库
libgcc_s.so.1 libstdc++.so.6 libm.so.6 libdl.so.2 librt.so.1 libc.so.6 libnsl.so.1 libutil.so.1 libpthread.so.0 libresolv.so.2 libX11.so.6 libXext.so.6 libXrender.so.1 libICE.so.6 libSM.so.6 libGL.so.1 libgobject-2.0.so.0 libgthread-2.0.so.0 libglib-2.0.so.0
此列表与
manylinux1的外部提供的库白名单相同,但libncursesw.so.5和libpanelw.so.5除外。 [7] 出于与 PEP 513 中概述的相同原因,libpythonX.Y仍然不符合包含条件。在 Fedora 30 发布了
libcrypt.so.2而非libcrypt.so.1后,libcrypt.so.1已被追溯性地从白名单中移除。在基于 Debian 的系统上,这些库由以下软件包提供
包 库 libc6 libdl.so.2, libresolv.so.2, librt.so.1, libc.so.6, libpthread.so.0, libm.so.6, libutil.so.1, libnsl.so.1 libgcc1 libgcc_s.so.1 libgl1 libGL.so.1 libglib2.0-0 libgobject-2.0.so.0, libgthread-2.0.so.0, libglib-2.0.so.0 libice6 libICE.so.6 libsm6 libSM.so.6 libstdc++6 libstdc++.so.6 libx11-6 libX11.so.6 libxext6 libXext.so.6 libxrender1 libXrender.so.1 在基于 RPM 的系统上,它们由这些软件包提供
包 库 glib2 libglib-2.0.so.0, libgthread-2.0.so.0, libgobject-2.0.so.0 glibc libresolv.so.2, libutil.so.1, libnsl.so.1, librt.so.1, libpthread.so.0, libdl.so.2, libm.so.6, libc.so.6 libICE libICE.so.6 libX11 libX11.so.6 libXext libXext.so.6 libXrender libXrender.so.1 libgcc libgcc_s.so.1 libstdc++ libstdc++.so.6 mesa libGL.so.1 - 如果 wheel 包含链接到也导出版本化符号的任何白名单库的二进制可执行文件或共享对象,它们可能仅依赖于以下最大版本
GLIBC_2.12 CXXABI_1.3.3 GLIBCXX_3.4.13 GCC_4.5.0
例如,
manylinux2010wheel 可以包含需要GLIBC_2.4版本glibc符号的二进制工件,因为这是早于GLIBC_2.12最大值的版本。 - 如果 wheel 是为 CPython 2 的任何版本或 Python 3.0(包括 3.2)的版本构建的,那么它必须包含一个 CPython ABI 标签,指示其 Unicode ABI。因此,为 Python 2 构建的
manylinux2010wheel 必须包含cpy27mu标签(指示它是为具有 UCS-4 ABI 的解释器构建的)或cpy27m标签(指示它是为具有 UCS-2 ABI 的解释器构建的)。(PEP 3149, [9]) - 一个 wheel 不得 要求
PyFPE_jbuf符号。这是通过在不使用--with-fpectlconfigure标志编译的 Python 上构建它来实现的。
兼容 Wheel 的编译
与 manylinux1 一样,auditwheel 工具会将 manylinux2010 平台标签添加到在 manylinux2010 Docker 容器中通过 pip wheel 或 bdist_wheel 构建的 linux wheel 中。
Docker 镜像
提供了两个基于 CentOS 6 的 manylinux2010 Docker 镜像,用于构建可以可靠地转换为 manylinux2010 wheel 的二进制 linux wheel。 [10] x86_64 和 i686 镜像配备了新的编译器套件(来自 devtoolset-8 的 gcc、g++ 和 gfortran),以及最新版本的 Python 和 pip。
与缺少 vsyscall 的内核的兼容性
Docker 容器假定其用户空间与其主机的内核兼容。不幸的是,日益常见的内核配置破坏了 x86_64 CentOS 6 Docker 镜像的这种假设。
glibc 的 2.14 及更早版本要求内核在 x86_64 上提供一种过时的系统调用优化,称为 vsyscall。 [11] 为了实现这种优化,内核会将一个包含频繁调用的系统调用(最显著的是 time(2))的只读页面映射到每个进程,位于一个固定的内存位置。然后 glibc 通过解引用函数指针到 vsyscall 页面的适当偏移量并调用它来调用这些系统调用。这避免了正常系统调用调用中影响到的调用内核的开销。vsyscall 早已因一种等效的机制 vDSO(或“虚拟动态共享对象”)而过时,在这种机制中,内核会将一个包含优化系统调用的可重定位的虚拟共享对象映射到每个进程。 [12]
vsyscall 页面存在严重的安全隐患,因为它不参与地址空间布局随机化 (ASLR)。其可预测的位置和内容使其成为返回导向编程攻击中常用的 gadget 来源。 [13] 同时,它的移除破坏了 x86_64 ABI,因为依赖 vsyscall 的 glibc 版本在尝试将系统调用指针解引用到不存在的页面时会发生段错误。作为一种妥协,Linux 3.1 实现了一个“模拟”的 vsyscall,它减少了映射到进程的可执行代码(从而减少了 ROP gadget 的材料)。 [14] vsyscall=emulated 多年来一直是大多数发行版内核中的默认配置。
不幸的是,vsyscall 模拟仍然在可靠的内存位置暴露了可预测的代码,并继续对返回导向编程有用。 [15] 由于大多数发行版现已升级到不依赖 vsyscall 的 glibc 版本,它们开始提供不再支持 vsyscall 的内核。 [16]
CentOS 5.11 和 6 都包含依赖 vsyscall 页面的 glibc 版本(分别为 2.5 和 2.12.2),因此基于这两者的容器无法在许多发行版即将发布版本提供的内核上运行。 [17] 如果 Travis CI,例如,开始在不提供 vsyscall 接口的内核下运行作业,Python 打包者将无法在那里使用我们的 Docker 镜像来构建 manylinux wheel。 [18]
我们从 glibc git 仓库中派生了一个补丁,它将所有对 vsyscall 的依赖移除回溯到我们 manylinux2010 镜像中包含的 glibc 版本。 [19] 重建 glibc,从而重建 manylinux2010 镜像本身,仍然需要一个提供 vsyscall 机制的主机内核,但生成的镜像可以在提供该机制和不提供该机制的主机上运行。由于 vsyscall 接口只是一个应用于运行进程的优化,因此使用此修改后的镜像构建的 manylinux2010 wheel 应该与在未修改的 CentOS 6 系统上构建的 wheel 相同。此外,vsyscall 问题仅影响 x86_64;它不属于 i686 ABI。
Auditwheel
auditwheel 工具也已更新以生成 manylinux2010 wheel。 [20] 其行为和目的与 PEP 513 相比没有变化。
安装程序的平台检测
平台可以在 PEP 513 中描述的 _manylinux 模块上定义一个 manylinux2010_compatible 布尔属性。如果该属性为 False,则该平台被认为与 manylinux2010 不兼容。
如果找不到 _manylinux 模块,或者它没有 manylinux2010_compatible 属性,工具可以回退到检查 glibc。如果平台具有 glibc 2.12 或更新版本,则假定其兼容,除非 _manylinux 模块另有说明。
具体来说,我们提议的算法是
def is_manylinux2010_compatible():
# Only Linux, and only x86-64 / i686
from distutils.util import get_platform
if get_platform() not in ["linux-x86_64", "linux-i686"]:
return False
# Check for presence of _manylinux module
try:
import _manylinux
return bool(_manylinux.manylinux2010_compatible)
except (ImportError, AttributeError):
# Fall through to heuristic check below
pass
# Check glibc version. CentOS 6 uses glibc 2.12.
# PEP 513 contains an implementation of this function.
return have_compatible_glibc(2, 12)
与 manylinux1 wheel 的向后兼容性
正如 PEP 513 中所述,manylinux1 白名单库的指定符号版本构成一个上限。同样适用于本 PEP 中为 manylinux2010 定义的符号版本。因此,manylinux1 wheel 被视为 manylinux2010 wheel。因此,一个识别 manylinux2010 平台标签的 pip,即使在明确设置的情况下,当没有可用的 manylinux2010 wheel 时,也会为 manylinux2010 平台安装 manylinux1 wheel。 [21]
PyPI 支持
PyPI 应该允许上传包含 manylinux2010 平台标签的 wheel,就像它允许 manylinux1 一样。它不应尝试验证 manylinux2010 wheel 的兼容性。
PEP 571 变更摘要
根据 PEP 批准后收到的反馈,对该 PEP 进行了以下更改
libgcc_s的最大版本符号从GCC_4.3.0更新到GCC_4.5.0,以解决 32 位 Cent OS 6 问题。这对 x86_64 没有影响,因为libgcc_s对于 x86_64 在GCC_4.3.0到GCC_4.5.0之间没有额外的符号。
参考资料
版权
本文档已进入公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0571.rst