Skip to content

宿主 -L 链上的库与运行期加载的库可以是两份不同构建,而构建期一言不发 #537

Description

@Sunrisepeak

实测环境:mcpp 2026.8.30.1(本分支),gcc 16.1.0,Linux x86_64,SubOS 里装了 Mesa。

概述

[build] ldflags = ["-L/usr/lib/...", "-lgbm"]运行期闭包能被满足时静默通过。产物的 ABI 取自宿主的 /usr/include + /usr/lib,而它自己的私有加载器把同一个 soname 解析到 registry 里的另一份构建。链接的是 A,加载的是 B,没有任何东西说一句话。

这个守卫本身是好的 —— 它只是按闭包可满足性判,不按宿主路径判,于是当 soname 两边都存在时,它在结构上就不可能触发。

复现

[package]
name    = "linkab"
version = "0.1.0"

[build]
cxxflags = ["-I/usr/include"]
ldflags  = ["-L/usr/lib/x86_64-linux-gnu", "-lgbm"]
#include <cstdio>
extern "C" void* gbm_create_device(int);
int main(){ std::printf("%p\n", gbm_create_device(-1)); }

构建输出全文:

   Resolving toolchain
    Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
      Target x86_64-linux-gnu → x86_64-unknown-linux-gnu
    Inferred sources [src/**/*.{cppm,cpp,cc,c,S,s,asm}]
    Inferred target linkab (bin from src/main.cpp)
   Compiling linkab v0.1.0 (.)
    Finished dev [unoptimized + debuginfo] in 0.05s

exit 0,零告警。

两份库确实不是同一个

链接期(-L 找到的)   /usr/lib/x86_64-linux-gnu/libgbm.so.1              22976 字节
运行期(私有 loader)  ~/.mcpp/registry/subos/default/lib/libgbm.so.1      39401 字节

soname 都是 libgbm.so.1。产物的 DT_NEEDEDlibgbm.so.1,RPATH 里有 subos/default/lib,于是私有加载器拿到的是后者。

与现有守卫的关系

守卫在宿主独有的库上工作得很好。同一个工程换成 -lcap(SubOS 里没有):

error: runtime closure validation failed (proven Linux ELF defect)
runtime closure for …/bin/hostonly cannot be satisfied: libcap.so.2 not found
on the search path this artifact will actually use.
       Its PT_INTERP is a private loader, so the host's /usr/lib is NOT
       consulted — the program will fail to start with "cannot open shared
       object file".
       Fix: install the provider into the selected SubOS …

消息很好,出路也点明了。缺口只在两边都有同名 soname的那一格 —— 而那恰好是图形栈这类工程最常落进去的一格。

为什么值得单独修

  1. 它是 [Bug & RFC] 系统工具链 build.mcpp 执行失败缺陷、原生宿主模式与工作区继承规范提案 #527 §1 里真正属于 mcpp 的那部分。 docs(examples): 09 —— 系统级图形栈,用推荐方式做完整条链 (#527) #532 的论证里写着「-L/usr/lib 是错误用法,mcpp 在构建期就明说了」;在装了 Mesa 的机器上,mcpp 什么都没说。不该写宿主 -L 的理由不是 mcpp 会拒,而是 mcpp 拒不了

  2. 失败被推迟到最坏的位置。 两份构建的差异要到运行期才显形,可能是崩溃,也可能是行为不同而不崩 —— 而构建日志里没有任何线索指回那一行 ldflags

  3. 它让「零 Host 闭包」这类断言不可核验。 在开发机上用私有加载器 --list 数出来的闭包,分不开「由声明的包满足」和「由这台机器碰巧装了的东西满足」。

可能的方向(未验证,供讨论)

  • 链接期记录每个 -l 实际解析到的文件,与运行期闭包解析到的文件比对,不一致就报。信息在两侧都有,只是从没对过。
  • 或者更前置:[build] ldflags 里出现宿主搜索路径(/usr/lib/lib64/usr/local/lib)时,不论闭包是否可满足都给一条 degraded 诊断,说明产物的 ABI 来源与加载来源可能不是同一份。这条不需要任何新信息。

后者更便宜,前者才是判据落在事实上。

相关

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions