Repository navigation
obj/std.o 被无条件链进每个单元:纯 C 的 compat 包因此依赖 libstdc++.so.6 #416
Description
Activity
实施前按方案先复现,结论是这条 issue 的因果不成立 —— 症状是真的,归因是错的。
实测(mcpp 2026.8.15.1,本分支)
一个纯 C 的共享库工程(两个
.c,没有一行 C++,没有import std):$ readelf -d bin/libpurec.so | grep NEEDED libstdc++.so.6 ← 症状确实在 libm.so.6 libgcc_s.so.1 libc.so.6 $ grep -c 'obj/std.o' target/*/*/build.ninja 0 ← build.ninja 里根本没有 std.o $ nm -D bin/libpurec.so | grep -c _ZGIW 0 ← 产物里没有模块初始化器std.o没有被链进去,而libstdc++.so.6照样在。真因
std.o本身也不可能是来源:$ nm --defined-only obj/std.o → 1 个符号:_ZGIW3std(模块 std 的全局初始化器) $ nm --undefined-only obj/std.o → 0 个 $ ls -l obj/std.o → 1280 字节零个未定义符号,它拖不动任何共享库。
真正的来源是链接驱动。生成的规则是:
cxx = .../xim-x-gcc/16.1.0/bin/g++ rule cxx_shared command = $cxx -shared @$out.rsp -o $out $ldflags $soname_flag $unit_ldflagsg++隐式追加-lstdc++,而全仓--as-needed只用在-latomic那一处
(src/build/flags.cppm:240),所以-lstdc++会无条件成为NEEDED—— 即使
没有任何符号引用它。这条 issue 该怎么拆
原判据「纯 C 的 compat 包产出的
.so,readelf -d里没有libstdc++.so.6」
不可能通过收窄std.o达成,两者是独立的两件事:std.o无条件链进每个单元 —— 仍然该修(一个不该在那里的对象),但后果比
本 issue 描述的小得多。风险也比预想低:std.o唯一的符号是模块初始化器,
而import std的 TU 会引用它,所以谓词若漏判会得到链接期 undefined
_ZGIW3std—— 响亮的失败,不是静默错误。- 纯 C 链接单元用 C++ 驱动 —— 这才是
libstdc++.so.6的来源。修法要么让
纯 C 单元改用 C 驱动,要么引入--as-needed(后者会影响 dlopen 的库,
需要单独评估)。
建议按 2 另开一条,把本 issue 的判据收窄到 1。
分析全文:
.agents/docs/2026-08-15-issues-412-422-analysis.md症状已消除,分两步落地 —— 关闭得晚了,补记在此。
归因的修正
Sunrisepeak 的复现是对的,本 issue 的因果不成立:
std.o有 0 个未定义符号,它拖不动任何共享库。纯 C 包上的NEEDED libstdc++.so.6来自链接驱动 ——cxx_shared/cxx_link一律用g++,与链接输入里有没有std.o无关。症状是真的,归因是错的。这条 issue 因此被拆成两半。
落地的两半
本 issue(
std.o按需链接) —— 2026.8.15.1(commitc459cf2)。src/build/ninja_backend.cppm:2001:const bool takesStd = cxxUnit && has_std_artifacts && unit_needs_std(lu);
has_std_artifacts只回答「这个工具链有没有预建的 std 模块」,与单元自身的需求无关;unit_needs_std才是关于这个链接单元的谓词,并且按 issue 要求覆盖了传递可达性,不只看本单元源码。回归测试tests/e2e/235_std_object_only_when_needed.sh。后续在同一处发现
std.compat.o完全没有unit_needs_std收窄 —— 本 issue 修了std.o而把它旁边那个漏了,于是一个 C++ TU 的全局初始化器进了每一个「工具链恰好带 std.compat」的链接单元。同一谓词、同一理由,已一并收拢,注释就在那两行上。驱动那一半 —— 拆出 #426,已修复关闭。非 C++ 的链接单元现在走
c_link/c_shared。判据
issue 写的三条都成立:纯 C 包产出的
.so的readelf -d里没有libstdc++.so.6;import std的工程正常链接运行;间接经依赖的模块接口用到 std 的工程也正常 —— 最后一条就是 235 里的传递性用例。
现象
obj/std.o(import std的模块对象)被无条件链进每一个 Binary / TestBinary /SharedLibrary 单元,完全不看该单元是否真的
import std。src/build/ninja_backend.cppm:1362-1381:has_std_artifacts只表示"这个工具链有预构建的 std 模块",与单元自身的需求无关。实测:
bin/libXau.so的链接输入 = 8 个纯 C 的.o+ 一个obj/std.o。compat.xau里没有一行 C++。后果
在 #414 之前,这与共享库的
-static-libstdc++叠加,把整个libstdc++.a拖进纯 C 的compat 包:libXau.so 39KB → 9.5MB,并导出 777 个 GLOBAL 标准库定义。
#414 把 ELF 共享库改判
toolchain-coupled之后体积已经回落(实测 libXau 39 368 B),但 std.o 仍在,于是纯 C 的 compat 包(libXau / libXdmcp / libX11)凭空多一条
NEEDED libstdc++.so.6:一个纯 C 的库不该依赖 C++ 运行时。它现在依赖,只是因为链接输入里多了一个对象。
建议
把
std_o_dst的追加条件从「工具链有 std 模块」收窄到「这个链接单元真的需要它」。需要先确认的两点:
import std的传递性 —— 依赖的 BMI 传递import std是有历史坑的(见 dep BMI 缓存跨版本毒化那次)。判据不能只看本单元的源码。
StaticLibrary走ar,本来就不追加,不受影响。判据
.so,readelf -d里没有libstdc++.so.6import std的工程仍正常链接、运行背景
.agents/docs/2026-08-11-origin-precedence-implementation-plan.md§C3