Skip to content

2026.8.24.6 — musl on Windows has a name, and the segment that disagreed now says so - #501

Merged
Sunrisepeak merged 11 commits into
mainfrom
feat/mcpp-target-vocabulary
Aug 25, 2026
Merged

2026.8.24.6 — musl on Windows has a name, and the segment that disagreed now says so#501
Sunrisepeak merged 11 commits into
mainfrom
feat/mcpp-target-vocabulary

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

同一个 --target x86_64-windows-gnu,两种体系下的 C 库完全不同,而 mcpp 用同一个名字称呼它们。实测同一份源码:

MinGW CRT musl / openkal
体积 587,894 9,815,552(16.7×)
依赖 DLL KERNEL32msvcrt.dll ntdllKERNEL32SHELL32

MinGW 的 C 库不是自足的——printf/malloc 在目标机自带的 msvcrt.dll 里。musl 是自足的。两者不该共用一个名字。

⚠️ 这个名字 LLVM 拼不出来,而理由不是我以为的那个

我三次说「LLVM 词表没有这个拼写」。不准确。 实测 llvm 22.1.8:

clang++ --target=x86_64-pc-windows-musl -c t.cpp
    #5  llvm::MCWinCOFFStreamer::emitCGProfileEntry(...)

三元组解析器认识它,代码生成路径没有实现它——崩在 COFF 写出器,是 ICE 不是诊断。四个非 MSVC 环境 gnu/cygnus/itanium/musl,只有 musl 死;预定义宏显示它从未被建模(无 __MINGW32__)。

⭐ 于是两个字符串必须不同,而它们本来就是:

Target x86_64-windows-musl → x86_64-w64-windows-gnu
       ^ 回答「C 库是谁」      ^ 回答「遵循哪套对象 ABI」

此前我用编译器的词表限制了 mcpp 自己的词表,把两个轴混成一个——这是同一问题三轮没改对的根子。

修复:同形的两件事,一个报一个不报

x86_64-linux-gnu     名字说 gnu,事实是 musl  →  ⚠️ 警告
x86_64-windows-gnu   名字说 gnu,事实是 musl  →  ❌ 静默

豁免的理由只对了一半:那一段捆着对象 ABI(被兑现)与 MinGW 的 C 运行时(被图替换)。判据改为「轴 ∈ {CLibrary, ObjectAbi}」。⚠️ 裸机的 elf 继续豁免——它在任何平台都不命名 C 库。误伤由既有的 fromGraph() 挡住(实测预构建路径不进警告)。

两处实测得来的约束

  • pin = llvm@22.1.8 不是偏好:全局默认为 gcc 时空 pin 会让它落到 musl-gcc 载荷并报「没有 C++ 前端」——消息谈缺前端,真问题是只有 clang 能发这个目标。
  • 档为 preview:端到端还差一环——openkal-musl@0.3.3 精确钉死 openkal-windows = "0.1.3",索引里已有的 0.1.4 到不了消费者。

文档

docs/16 新增「三套词表」(中英):x86_64-w64-mingw32 为何把实现放 OS 位、clang 如何重拼为 windows-gnugnu 正是 LLVM 拆开 mingw32 后给剩下那半起的名字。

.agents/docs 另收两份:三轴实测矩阵,以及一份待 review 的规范设计——提议 mcpp 拥有自己的目标词表(<arch>-<os>-<libc>,第三段恒定表示 C 库),与 clang 的分歧限于五处且每处有实测支撑。

单元 93/0,e2e 284 全绿。

…eed now says so

⭐ **同一个 `--target x86_64-windows-gnu`,两种体系下的 C 库完全不同**,
而 mcpp 用同一个名字称呼它们。实测同一份源码:

                  MinGW CRT      musl / openkal
    体积             587,894         9,815,552   (16.7×)
    依赖 DLL      + msvcrt.dll     (无)

MinGW 的 C 库不是自足的 —— `printf`/`malloc` 的实现在目标机自带的
`msvcrt.dll` 里,587KB 只是「你的代码 + 胶水」。musl 是自足的。
**两者不该共用一个名字。**

── ⚠️ 这个名字 LLVM 拼不出来,而理由不是我以为的那个 ────────

我三次说「LLVM 词表没有 windows-musl 这个拼写」。**不准确。** 实测:

    clang++ --target=x86_64-pc-windows-musl -c t.cpp
        #5  llvm::MCWinCOFFStreamer::emitCGProfileEntry(...)

三元组解析器**认识**它(`-dumpmachine` 原样返回),代码生成路径
**没有实现**它 —— 崩在 COFF 写出器,是 ICE 不是诊断。Windows 上四个
非 MSVC 环境 `gnu`/`cygnus`/`itanium`/`musl`,前三个能编,只有 musl 死;
预定义宏说明它从未被建模(无 `__MINGW32__`)。

⭐ 于是 mcpp 的名字与交给 clang 的三元组**必须**是两个字符串 ——
而它们本来就是,报告里那个箭头两侧就是:

    Target x86_64-windows-musl → x86_64-w64-windows-gnu
           ^ 回答「C 库是谁」      ^ 回答「遵循哪套对象 ABI」

**我此前用编译器的词表限制了 mcpp 自己的词表**,把两个轴混成一个。
这是同一个问题三轮没改对的根子。

⚠️ pin 为 `llvm@22.1.8` 不是偏好:全局默认为 gcc 时空 pin 会让它落到
musl-gcc 载荷并报「没有 C++ 前端」—— 一条关于缺前端的消息,而真正的
问题是只有 clang 能发这个目标。

⚠️ 档为 `preview`:端到端还差一环 —— `openkal-musl@0.3.3` 精确钉死
`openkal-windows = "0.1.3"`,索引里已有的 0.1.4 到不了消费者。

── 修复:同形的两件事,一个报一个不报 ────────────────────

    x86_64-linux-gnu     名字说 gnu,事实是 musl  →  ⚠️ 警告
    x86_64-windows-gnu   名字说 gnu,事实是 musl  →  ❌ 静默

豁免的理由「Windows 上 gnu 命名对象 ABI 而非 C 库」**只对了一半**:
那一段捆着对象 ABI(被兑现)与 MinGW 的 C 运行时(被图替换)。
判据改为「轴 ∈ {CLibrary, ObjectAbi}」,消息额外说明 ABI 那一半未受影响。

⚠️ 裸机的 `elf` 继续豁免 —— 它在任何平台上都不命名 C 库。

── 文档 ──────────────────────────────────────────────

docs/16 新增「三套词表」(中英):`x86_64-w64-mingw32` 为何把实现放在
OS 位(autoconf 世界观)、clang 如何重拼为 `windows-gnu`、以及
`gnu` 这个 env 值正是 LLVM 拆开 `mingw32` 后给剩下那一半起的名字。

.agents/docs 另收两份:三轴实测矩阵,以及一份**待 review 的规范设计** ——
提议 mcpp 拥有自己的目标词表(`<arch>-<os>-<libc>`,第三段恒定表示
C 库),与 clang 的分歧限于五处且每处有实测支撑。

单元测试 93/0(targetside 断言随语义反转更新),e2e 284 全绿。
两处我写错了,都是同一种错误的两个方向。

⚠️ **「苹果 SDK 不可再分发」把源码和 SDK 混成了一件事。** Apple 的 Libc
是开源的(`apple-oss-distributions/Libc`);不可再分发的是 SDK 里的头文件
与 `.tbd` 存根库 —— 那才是交叉链接 Mach-O 所需的。我把「不能交叉」的原因
说成了「libc 闭源」。另外 `libSystem` 不等于 Libc,它是一把伞,
底下含 libc、libm、libpthread、libdyld。

⚠️ **「macOS 只有一个 C 库,不需要名字」已经不成立** —— openkal 让
macOS 上也有了 musl。而我在同一份文档的 §5 里记下了这个实测,
**却没有回头改 §1.1 的表**。测出来了、没把结论传播到该改的地方,
这是本轮第二次。

── ⭐ 但结论仍是「libSystem 不该有名字」,理由换成实测的 ──────

macOS 的三元组形状与另外两个平台不同:OS 段带部署目标,且没有 env 段。
clang 接受任意 env 后缀却不赋予含义:

    arm64-apple-macos14.0-gnu   → 原样返回,未规范化
    arm64-apple-macos14.0-musl  → 原样返回,未规范化

原样保留而不像 Windows 那样重拼,说明 **Darwin 那一支根本不看 env 段**。
给 libSystem 起名会得到一个**在编译器侧没有对应物**的字符串。

于是规范定:**macOS 第三段只有一个合法值 `musl`,libSystem 由省略表达。**
三平台因此不对称,而不对称来自被映射的那一侧,不是 mcpp 的选择。

⭐ 连带:openkal 路径压根不碰 libSystem,所以这个命名问题在构建期体系里
从一开始就不存在 —— 只在预构建路径上被问起,而那条路径今天用的正是省略式。

── §6 新增:已知代价,记录在案 ──────────────────────────

「libSystem 由省略表达」的代价是**说不出它**:一个装了 openkal 依赖、
却想在 macOS 上用 libSystem 的工程无法表达这个意图,而 Linux 上 `-gnu`
恰恰能做到。

不解的理由、**重新打开它的三个触发条件**、以及届时的候选做法都写进 §6。
⭐ 并把 §5 与 §6 的性质分开:§5 是**尚未测**,§6 是**测过了并决定不做** ——
这两种「待办」看着像,后来者对它们该做的事完全不同。
…ble fact

自查时读 diff 发现 `x86_64-windows-musl` **有两行** —— 改 pin 时插入了修正
后的行,没删掉原来那行。`find_known_target` 取首命中,所以每一处行为都
正确、什么都没失败;表里多的是一行**列值与活着那行不一致的死数据**。

⭐ 靠通读 diff 发现它,机制是错的:重复是表的性质,机器看得见。
两条测试各四行:

    TheTargetTableHasNoDuplicateNames    没有两行共用一个规范名
    EveryTableRowIsItsOwnCanonicalForm   每行都能 parse 回它自己

第二条守的是另一件事:一个 `parse`/`str` 往返不还原的规范名,
是一行**任何 `--target` 都到不了**的行。

实测:临时插一行重复的,第一条立刻转红。
⭐ **一套目标词表的价值只有在第二个架构上才被检验。** x86_64 上
「按 OS 分」与「按架构分」给出相同答案,判据用错了轴也看不出来。

来源:#492 的使用者报告 —— 他要同时出 x86_64 与
aarch64 两份静态二进制。逐层剥出三处:

**7.1 ✅ 已修** x87 例程按 OS 排,而它是架构的性质。
`*xf*.c` 的排除挂在 `os = "macos"` 与 `os = "none"` 下,aarch64-linux
落进 `os = "linux"` 那一支去编只有 x86 才有的 `xf_float`。
⚠️ 两处此前都对 —— 那两个 OS 恰好都蕴含非 x87 架构;linux 段不排除
也对 —— x86 上 `__mulxc3` 会被真实调用。**两个方向都会坏,错的只是轴。**
已提 mcpplibs/openkal-llvm-runtime#5。

**7.2 ⚠️ 未修** `--no-default-config` 本身改变目标特性:

    clang --target=aarch64-unknown-linux-musl                       →  +outline-atomics
    clang --target=aarch64-unknown-linux-musl --no-default-config   →  -fmv

⭐ 根因**不是**三元组不一致(两边逐字核对过都是
`aarch64-unknown-linux-musl`),**不是**缓存串目标(键含 target_triple,
两个 aarch64 条目按工具链正确分开)—— 这两条我都先猜错并被实测否掉。

std 模块带 `--no-default-config` 编(包要求,否则宿主 C 库的头混进来),
普通 TU 不带。**两个都成立的要求相撞**,而 x86_64 上该 flag 不改变特性,
所以从未暴露。三条候选修法按代价列出,均未实测。

**7.3 / 7.4 未查**:`aarch64-linux-gnu` 仍是 planned 而使用者真正要的是
`-musl`(诊断没说「你要的那个另有拼法」);以及使用者报的另两条 ——
`std::random_device` 被 `__config_site` 关掉、GMF 里 include 标准头时
宿主头与 openkal 头混合。⭐ 后者与 7.2 是**同一个 cfg** 的两个症状。
7.2 与 7.2b 从「未修」改为已修并附实测;新增 7.5 —— `std::random_device`
的根在 openkal 规范(SURFACE.txt 全文无 random/entropy),而非运行时包:

    std::random_device → libc++ 五条后端 → getentropy → musl getrandom()
    → syscall_cp(SYS_getrandom) → openkal-musl 无替代 → 规范无该接口

⚠️ `_LIBCPP_HAS_RANDOM_DEVICE 0` **不是保守设置,是事实的准确记录**。
改成 1 只让编译期通过,链接会缺符号。`generic` 那份也关着是对的 ——
它同样跑在 openkal 之上,`generic` 不等于「宿主 Linux」。
⚠️ 我写「openkal-llvm-runtime 只能开关那个宏」,错了。
`okm_syscall.c` **已经把 69 个系统调用转给了 openkal**,移植机制现成;
真正的约束是规范里没有随机源接口可转。

并补两条:经 fs 打开 `/dev/urandom` 在设计上被挡住(`kal_fs_open` 只能
相对 preopen 目录,而 openkal-linux 只给 2 个)——⭐ 那正是能力模型要挡的
东西,不是缺口是拒绝;以及当下可做的 feature 形状,`openkal-musl` 已有
`[features]` 机制,但名字要说清它假设宿主(`host-entropy` 而非 `random`),
且 `_LIBCPP_HAS_RANDOM_DEVICE` 必须跟随它而非独立开关。
按 §7.5.1 三条判定(不加实现不了 / 不能绕过接口层 / 宿主通用裸机不通用)
给出形状,照 `openkal.time` —— 现有接口里最小的一个,问题结构同型。

⚠️ **不设 AVAILABLE 能力字**:「有没有」由接口在场与否回答(6.1 条),
这正是 openkal-opensbi@0.1.3 撤回的教训。

⭐ uefi 那一行最有意思:同一后端在不同固件上可能提供或不提供,而 6.1 条
要求「全有或全无」,所以必须在**构建期**决定 —— 运行期拒绝违反该条。

三处连带改动都写了约束:AT_RANDOM 要保留 fallback(否则裸机上 stack canary
起不来)、`_LIBCPP_HAS_RANDOM_DEVICE` 跟随目标、一致性套件那条会以 2^-256
概率误报而写清楚比省掉好。

⚠️ 草案未实测,不属于目标词表这一 PR。
The assertion that the toolchain line carries mcpp's own name for the
target had an `*) : ;;` fallback, so it printed on a match and passed on
anything else. This file's whole subject is a name that used to be
accepted by nothing, and the one line proving the mapping — mcpp's
canonical form in, the compiler's spelling out — could not fail.

The fallback is gone, and the pin is asserted beside it. The pin is what
the row is for: a global default of gcc carried onto this target
produces a true sentence about the wrong subject,

    error: toolchain payload 'xim:musl-gcc@16.1.0' has no known C++
           frontend in …/xim-x-musl-gcc/16.1.0/bin

so the report showing `llvm@22.1.8` winning is the observable form of
the row working.

Measured against a binary built from this branch:

      ok  x86_64-windows-musl is known, and the refusal names the real reason
      ok  the toolchain line carries mcpp's own name
      ok  the row's pin decided the toolchain
@Sunrisepeak
Sunrisepeak force-pushed the feat/mcpp-target-vocabulary branch from 4892f4e to ad0de57 Compare August 24, 2026 23:40
…not exist

§1.1 listed it as "显式请求 musl" and §6.1 built an argument on it: that
macOS can name musl but cannot name libSystem. Measured against a binary
built from this branch, neither is true —

    $ mcpp build --target aarch64-macos-musl
    error: unknown target 'aarch64-macos-musl'

— the table has one macOS row and it carries no env segment, so both
directions are unsayable there. Windows can name its C library because
this PR adds `x86_64-windows-musl`; macOS is missing the same row rather
than missing a policy, which is a smaller and more accurate statement of
the gap than the one that was written.

Every target name in docs/16 (both languages) and the three analysis
documents was then checked against the table. Twenty-three appear; four
short spellings are valid by the fill rule, four more are candidate
spellings in passages that say they are not adopted, and this was the
only one presented as real that is not.
并修正 §7.5.2 排在 §7.6 之后的顺序。
@Sunrisepeak
Sunrisepeak merged commit 003fc95 into main Aug 25, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants