<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>供应链 · XB 现场笔记</title><link>https://hk.xbcatm.com/zh/tags/%E4%BE%9B%E5%BA%94%E9%93%BE/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://hk.xbcatm.com/zh/tags/%E4%BE%9B%E5%BA%94%E9%93%BE/index.xml" rel="self" type="application/rss+xml"/><item><title>镜像签名把摘要绑定到受信任签名者</title><link>https://hk.xbcatm.com/zh/posts/signed-images-prove-who-built-them/</link><pubDate>Thu, 21 Oct 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/posts/signed-images-prove-who-built-them/</guid><description>校验精确镜像摘要和获授权的签名者；构建来源与内容安全仍需要额外证据。</description><content:encoded>&lt;p&gt;容器镜像标签是可变指针，摘要则标识内容。有效签名把一份关于该摘要的声明绑定到签名密钥；这把密钥是否代表获授权的人或工作负载，要由信任策略判断。单靠签名，不能证明谁构建了镜像、构建是否遵循所宣称的流程，也不能证明软件内容安全。这些问题需要不同证据，不能因为校验成功就合并成一个结论。&lt;/p&gt;
&lt;p&gt;真正有用的边界，是部署之前的验证决定。校验方需要检查内容绑定和预期信任根，再执行身份与来源策略。registry 地址、熟悉的标签，或者存在某份签名，都不足以独立构成放行依据。必须知道自己信任谁、允许其声明什么，以及这份声明究竟覆盖哪一个产物。&lt;/p&gt;
&lt;h2 id="摘要是身份标签是指针"&gt;摘要是身份，标签是指针&lt;/h2&gt;
&lt;p&gt;OCI 清单摘要对精确清单字节进行哈希，清单中的描述符再通过各自摘要标识配置与层，客户端也须核对这些绑定。在哈希抗碰撞假设下，清单改变会得到不同摘要。如果只修改存储层中的字节却不更新描述符，应触发内容完整性失败，而不是悄悄把原镜像重新定义成另一份内容。内容寻址依赖实际校验，不只是路径看起来像摘要。&lt;/p&gt;
&lt;p&gt;Cosign 的普通镜像签名载荷包含镜像摘要，验证时会把它与请求的镜像比较。不同标签可以指向同一个已签名摘要；标签一旦移动，先前的校验不能覆盖新对象。应解析并验证目标摘要，再部署同一个摘要。如果校验之后重新按标签拉取，就留下检查与使用之间的竞争窗口；重复查询标签也不能彻底关闭这个窗口。&lt;/p&gt;
&lt;h2 id="对标签签名绑不住任何东西"&gt;标签字符串不能绑定镜像内容&lt;/h2&gt;
&lt;p&gt;对字面字符串 app:v2 签名，认证的是这个声明，并不是名字后面不断变化的对象。签名也可以包含其他上下文，所以需要检查真实载荷格式与校验器行为，而不是把所有方案都说成只签一个裸哈希。不要为了让不匹配的载荷通过，就关闭摘要声明校验，否则签名有效与目标镜像之间的关键联系会丢失。&lt;/p&gt;
&lt;p&gt;基于密钥的校验，需要由策略说明为什么信任所给公钥。无密钥校验还要检查预期证书身份与 OIDC 颁发者，以及证书链和所要求的透明日志证据。Fulcio 证书把身份关联到密钥，不会检查构建过程或认证软件质量。要判断构建声明，还需要适当的来源证明、受信任的构建者，以及证明中主体摘要与待运行产物一致的检查。&lt;/p&gt;
&lt;h2 id="校验才是控制签名不是"&gt;校验才是控制，签名不是&lt;/h2&gt;
&lt;p&gt;强制检查可以放在准入或部署前，但效果取决于实际覆盖范围。需要确认哪些工作负载与 registry 被纳入策略，以及校验失败时如何处理。只记录摘要不能证明发布获授权；即使签名正确、身份也获授权，仍不能据此排除所有漏洞或恶意行为。安全测试与构建来源验证应各自提供证据，不能相互替代。&lt;/p&gt;
&lt;p&gt;密码学签名有效与当前策略接受，是两回事。退役密钥需要更新所有相关验证方的信任策略，仅生成一把新密钥不会自动完成撤销。短期证书配合可信签名时间证据，可以支持证书到期之后验证历史签名。透明日志让活动可检查，却不会自动撤销泄露身份或阻挡恶意发布。事故响应仍须决定保留哪些身份、时间窗口、摘要和证明，不能把日志存在等同于风险已解决。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;部署步骤先验证一个摘要，随后又按可变标签拉取；另一次推送就可能让最终运行的字节变掉。需要把经过验证的摘要一直传到部署交接处，并明确它指向单平台清单还是多平台索引，确认运行时怎样选择具体平台镜像。这样才能说明实际运行对象与被检查对象之间的关系。&lt;/p&gt;
&lt;p&gt;另一种失败是接受任何能通过密码学检查的签名者。应把信任限制到获授权密钥，或明确的身份与颁发者组合，并测试错误签名者和未批准摘要会被拒绝。发现泄露后需要调查并改变策略；这并不自动证明该密钥以前签过的所有产物都恶意，也不能承诺继续接受它们一定安全。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;
&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-signed-images-prove-who-built-them.detail.zh.svg" alt="摘要绑定与签名者授权先于部署同一摘要；构建来源、内容安全、信任更新及对象发现需分别检查。" width="560" height="1755"&gt;
&lt;figcaption&gt;镜像签名信任：机制与证据边界。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;下面是命令模板，不是可直接运行的夹具：registry 是示例，摘要也被省略。进行获授权的 registry 检查之前，需提供已有测试镜像的完整摘要、可信公钥，并确认所安装的 Cosign 与 Skopeo 版本。本次源码审阅没有签名、操作 registry 或执行部署，因此没有新的集成运行结果。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cosign verify --key cosign.pub registry.example.com/app@sha256:9ab5a134...
cosign tree registry.example.com/app@sha256:9ab5a134...
skopeo inspect --format &amp;#39;{{.Digest}}&amp;#39; docker://registry.example.com/app:latest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;预期结果是指定摘要在选定密钥下通过校验，不一定打印可读的人类身份。&lt;code&gt;cosign tree&lt;/code&gt; 发现关联对象，列出对象不等于验证其中声明。Skopeo 可以查看标签当下指向；如果不同，只说明它与刚才检查的摘要不同，另一个摘要仍可能有自己的签名。即使两者相同，也只是观察时刻的快照，无法阻止标签随后移动。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.sigstore.dev/cosign/verifying/verify/"&gt;Sigstore：用 cosign 校验签名&lt;/a&gt;，基于密钥与无密钥的摘要校验，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://slsa.dev/spec/v1.0/"&gt;SLSA v1.0 规范&lt;/a&gt;，来源信息、摘要，以及签名到底在证明什么，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sigstore.dev/policy-controller/overview/"&gt;Sigstore policy-controller&lt;/a&gt;，在准入阶段强制签名与身份策略，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://specs.opencontainers.org/image-spec/descriptor/"&gt;OCI 镜像描述符规范&lt;/a&gt;，摘要与所引用内容的绑定，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://slsa.dev/spec/v1.0/verifying-artifacts"&gt;SLSA v1.0：验证产物&lt;/a&gt;，可信构建者与来源策略不等于签名有效，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sigstore.dev/about/overview/"&gt;Sigstore 概览&lt;/a&gt;，身份证书与透明日志的边界，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sigstore.dev/cosign/verifying/timestamps/"&gt;Sigstore 时间戳&lt;/a&gt;，可信签名时间与证书有效期，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sigstore.dev/cosign/signing/signing_with_containers/"&gt;Sigstore：容器签名&lt;/a&gt;，tree 发现关联签名，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/containers/skopeo/blob/main/docs/skopeo-inspect.1.md"&gt;Skopeo inspect 手册&lt;/a&gt;，顶层清单摘要与平台相关字段，已核验 2026-10-18。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>可复现构建把源码与产物对应起来</title><link>https://hk.xbcatm.com/zh/posts/reproducible-builds-make-bins-trustable/</link><pubDate>Sun, 15 Aug 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/posts/reproducible-builds-make-bins-trustable/</guid><description>比较声明范围内的完整产物，并控制编译和链接输入；字节相同不代表源码安全。</description><content:encoded>&lt;p&gt;可复现构建的含义是：另一方依据声明的源码、构建步骤和相关环境，能够逐字节重建指定产物。这个承诺必须说明比较哪些输出；如果交付的可执行文件不同，只比较目标文件相同是不够的。复现结果为源码与分发内容之间的联系提供证据，而源码审阅、来源证明和签名分别回答其他问题。两次相同的恶意构建仍然有害，被污染的工具链也可能稳定复现自己的异常行为。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-reproducible-builds-make-bins-trustable.detail.zh.svg" alt="已记录的 arm64 macOS 实验在两个目录中比较三种处理后的完整目标文件和可执行文件：不映射时两类都不同，仅编译器映射时目标文件相同但可执行文件不同，完整编译与链接处理后两类均相同。这是本机历史证据，不是独立复现或安全保证；文章审阅未重跑平台专用构建。" width="560" height="2820"&gt;&lt;figcaption&gt;图示为文章记录的 Apple 工具链比较，包含仅做编译器处理时最终可执行文件仍不一致的情况。平台专用重跑仍受阻，本次绘图也未重跑构建。本地一致不等于独立复现，也不证明源码安全。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="记录真正影响字节的输入"&gt;记录真正影响字节的输入&lt;/h2&gt;
&lt;p&gt;源码版本与编译器版本只是输入的一部分。依赖、目标架构、SDK、链接器、参数、文件遍历顺序、目录路径、时间戳、区域设置和打包工具，都可能进入最终结果。应固定实际相关的输入，并说明比较边界。容器镜像有助于描述环境，却不会自动消除网络下载、宿主机挂载或变化的时间戳。一个镜像标签也不必然代表永远不变的镜像内容。&lt;/p&gt;
&lt;p&gt;时间宏和调试信息是常见差异来源。&lt;code&gt;SOURCE_DATE_EPOCH&lt;/code&gt; 为支持该约定的工具提供稳定时间，并不会改写所有程序对当前时间的理解。编译器的前缀映射能够规范化嵌入的源文件路径；链接器还可能独立记录目标文件位置或其他元数据。因此，编译阶段消除了差异，仍应继续检查实际链接输出，不能在中间产物相同时就停止验证。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;只比较挑选出的某个段，可能掩盖真正发布文件中的不同。应明确可复现性覆盖可执行文件、调试符号、压缩归档还是带签名安装包。如果签名过程加入会变化的数据，就说明并论证可复现负载的边界，同时单独校验签名后的分发包。还应记录 SDK 和链接器的构建标识；仅有编译器版本，无法重建另外两者的输入。不能悄悄删除难以处理的字节，再宣称原始文件完全相等；比较范围本身也是需要审阅的契约。&lt;/p&gt;
&lt;p&gt;同一台机器上的两次摘要一致，是这些输入条件下本地重复构建的证据，不等于多个独立构建方已经验证，也不能证明源码与工具正确。反过来，摘要不一致首先意味着需要调查，不能立即断定发生了攻击。应定位差异来自哪个组件，再判断是不是有输入没有纳入环境声明。没有记录 SDK、链接参数或打包规则，可能就足以让相同源码生成不同文件。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;此前记录的实验使用 arm64 macOS、Apple clang 21.0.0 及其本机链接器；本轮没有重新执行这个平台专用构建。保存为 &lt;code&gt;repro-build.py&lt;/code&gt;，安装命令行开发工具后运行 &lt;code&gt;python3 repro-build.py&lt;/code&gt;。链接参数属于 Apple 工具链，不应假设 Linux 链接器也接受。脚本创建两个临时构建目录，用三种处理方式编译同一小段 C 程序；目录名差异是刻意保留的输入扰动。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TemporaryDirectory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;int main(void) { puts(__FILE__); puts(__DATE__ &amp;#34; &amp;#34; __TIME__); }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;TemporaryDirectory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;executables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unmapped&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;compiler-only&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;one&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;two&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;main.c&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;PATH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;PATH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;LC_ALL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SOURCE_DATE_EPOCH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1700000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unmapped&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-ffile-prefix-map=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;=/src&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;clang&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-g&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-O0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1700000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1700000000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;linker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-Wl,-reproducible&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;linker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-Wl,-oso_prefix,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;clang&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;linker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;executables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;equality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;equality&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unmapped&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;executables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;compiler-only&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;executables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;objects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;executables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/src/main.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Nov 14 2023 22:13:20&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sha256&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;equal_between_directories&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;equality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;complete_program_output&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;same_host_only&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所有处理都使用时间值 &lt;code&gt;1700000000&lt;/code&gt;，并固定目标文件的修改时间。没有路径映射时，目标文件和可执行文件都不同。只做编译器映射后，目标文件摘要已经相等，最终可执行文件仍然不同：链接器调试映射中还保留着各自的目标文件路径。这个中间失败说明，编译阶段通过的检查不能直接为实际发布的二进制文件背书。&lt;/p&gt;
&lt;p&gt;三种处理均已开启链接器的可复现模式；完整处理还从它记录的目标文件调试映射里去除构建目录前缀。此时两边的目标文件与可执行文件摘要都相等。执行完整处理后的程序，两次均输出 &lt;code&gt;/src/main.c&lt;/code&gt; 和 &lt;code&gt;Nov 14 2023 22:13:20&lt;/code&gt;。断言比较的是整个文件，包括生成的 Mach-O 元数据，而不是构建完成后再剪掉差异位置。&lt;/p&gt;
&lt;p&gt;这些结果只覆盖本机的一个编译器、SDK、架构和简单程序，没有证明大型安装包、另一种工具链或独立签名发布也能复现。脚本打印每个摘要和相等判断，让中间与最终结果都可检查。读者在不同环境执行时，应先确认工具版本和参数语义，再讨论结果差异；不能把这里出现的某个摘要当成所有平台必须一致的常量。&lt;/p&gt;
&lt;h2 id="定位差异后再扩大承诺"&gt;定位差异后再扩大承诺&lt;/h2&gt;
&lt;p&gt;生产比较失败时，应保留两份产物及各自的构建输入记录，逐步定位归档、段和元数据中的差异，再控制相应输入并重新构建。调查过程中保持声明的输出集合不变，避免通过减少比较对象来制造成功。本地可重复之后，再由独立环境按照公开步骤重建并比较相应发布产物。这样增加的是另一方能够复现结果的证据，而不是用可复现性取代安全审阅或来源验证。&lt;/p&gt;
&lt;h2 id="延伸阅读"&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hk.xbcatm.com/zh/notes/hsts-should-be-rolled-out-with-a-recovery-plan/"&gt;HSTS 应带着恢复计划来灰度发布&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hk.xbcatm.com/zh/notes/a-release-manifest-should-name-every-published-byte/"&gt;发布清单应点名每一个已发布的字节&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reproducible-builds.org/docs/definition/"&gt;Reproducible Builds: definitions&lt;/a&gt;，明确产物范围并逐字节重建的定义，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reproducible-builds.org/docs/build-path/"&gt;Reproducible Builds: build path&lt;/a&gt;，编译器和调试信息中嵌入路径的影响，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reproducible-builds.org/docs/source-date-epoch/"&gt;Reproducible Builds: SOURCE_DATE_EPOCH&lt;/a&gt;，由支持该约定的工具使用稳定时间戳，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://clang.llvm.org/docs/ClangCommandLineReference.html"&gt;Clang command-line reference&lt;/a&gt;，编译阶段的路径映射参数，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/apple-oss-distributions/ld64/main/src/ld/Options.cpp"&gt;Apple ld64 options&lt;/a&gt;，可复现模式将调试映射的修改时间清零，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/apple-oss-distributions/ld64/main/doc/man/man1/ld-classic.1"&gt;Apple ld64 manual&lt;/a&gt;，OSO 调试映射路径前缀移除，已核验 2026-10-15。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>