From 308ce01bf107cda6367ea89e211c4b0c77b13c20 Mon Sep 17 00:00:00 2001 From: speak-agent <248744407+speak-agent@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:28:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(13):=20=E5=AD=90=E9=9B=86=E4=B8=8E?= =?UTF-8?q?=E9=9B=B6=20libc=20=E6=A1=A3=E6=98=AF=E7=BB=84=E5=90=88?= =?UTF-8?q?=E5=85=B3=E7=B3=BB,=E4=B8=8D=E6=98=AF=E4=BA=92=E6=96=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #466 里这一章断言: ⚠️ `std-freestanding` 与 `std-freestanding-nolibc` 服务于**互斥**的 安排 …… 处在零 libc 档的工程根本用不了子集。 这句话描述的是 std-freestanding 0.4.0 之前的状态,而 0.4.0 的 `nolibc` feature 恰恰让子集在这一档可用——实测 103 个头里的 94 个。同一个 PR 同时带来了那个 feature 和这句否定它的话。 障碍从来不是子集需要一个 C 库:libc++ 为 C 头提供包装头,它们通过 `#include_next` 续到真正的头去取 `size_t`、`mbstate_t`、`time_t` 与 `EOF`;没有 C 库时这条链无处可续,于是死在缺**类型**上而不是缺头上, 这正是从报错看不出成因的原因。四个很小的头把链续上。 一并写入板级支持包也能服务这一档(riscv-virt-rt 0.5.0 的 `nolibc` feature),理由与「为什么要有板级包」是同一条:UART 在哪、RAM 从哪开始、 哪个模拟器启动它,没有一条是 C 库事实。 静默替换那条警告仍然成立,但重新划定了范围:它说的是把 `std-freestanding-nolibc` **直接**与 C 库并用,而不是经由该 feature。 --- docs/13-baremetal.md | 30 ++++++++++++++++++++++-------- docs/zh/13-baremetal.md | 20 ++++++++++++++++---- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/docs/13-baremetal.md b/docs/13-baremetal.md index 4c0ac8d8..40fcf95d 100644 --- a/docs/13-baremetal.md +++ b/docs/13-baremetal.md @@ -323,14 +323,28 @@ obligation rather than a convenience: `memcpy`, `memmove`, `memset` and `memcmp` must exist because the compiler lowers structure assignment and array initialisation onto them. `std-freestanding-nolibc` supplies those and `strlen`. -⚠️ `std-freestanding` and `std-freestanding-nolibc` serve **mutually exclusive** -arrangements. The subset needs the C library's headers — measured: on the -zero-libc tier it fails at compile with `'inttypes.h' file not found` — so a -project that can use the subset has a C library and does not need the five -functions, while a project on the zero-libc tier cannot use the subset at all. - -⚠️ That package is for the zero-libc tier only, and using it alongside a C -library fails silently rather than loudly. A C library ships as an archive, and +The subset composes with this tier rather than excluding it. `std-freestanding` +with `features = ["nolibc"]` compiles against no C library at all, and measured, +**94 of its 103 headers** do. The obstacle was never that the subset wants a C +library: libc++ ships wrappers for the C headers — `string.h` and its siblings — +which reach the real header through `#include_next` to obtain `size_t`, +`mbstate_t`, `time_t` and `EOF`. With no C library the chain has nothing to +continue to, and the wrapper fails on a missing *type* rather than a missing +header, which is why the cause is not evident from the error. Four small headers +restore the chain, and the feature is what pulls them in. + +A board-support package can serve this tier too, and for the same reason it is +worth having one at all. Where a machine's UART is, where its RAM begins and +which emulator boots it are not C library facts: + +```toml +[dependencies] +riscv-virt-rt = { version = "0.5.0", features = ["nolibc"] } +``` + +⚠️ `std-freestanding-nolibc` is what that feature resolves to, and adding it +**directly** alongside a C library fails silently rather than loudly. A C library +ships as an archive, and an archive member is pulled only while the symbol is still undefined; a dependency package's object files enter the link unconditionally. The package therefore defines `memcpy` first, the C library's member is never pulled, and diff --git a/docs/zh/13-baremetal.md b/docs/zh/13-baremetal.md index 18f5649e..dfe9f6b8 100644 --- a/docs/zh/13-baremetal.md +++ b/docs/zh/13-baremetal.md @@ -293,11 +293,23 @@ freestanding 翻译单元仍会用到的 C 函数中,有四个是义务而非便 `memmove`、`memset` 与 `memcmp` 必须存在,因为编译器把结构体赋值与数组初始化下降到 它们之上。`std-freestanding-nolibc` 提供这四个与 `strlen`。 -⚠️ `std-freestanding` 与 `std-freestanding-nolibc` 服务于**互斥**的安排。子集需要 -C 库的头文件 —— 实测:零 libc 档上它在编译期即死于 `'inttypes.h' file not found` -—— 因此能用子集的工程有 C 库、不需要这五个函数,而处在零 libc 档的工程根本用不了子集。 +子集与这一档是**组合**关系而非互斥。`std-freestanding` 带 `features = ["nolibc"]` +时在完全没有 C 库的情况下编译,实测其 **103 个头中的 94 个**可用。障碍从来不是子集 +需要一个 C 库:libc++ 为 C 头文件提供了包装头 —— `string.h` 及其同类 —— 它们通过 +`#include_next` 续到真正的头去取 `size_t`、`mbstate_t`、`time_t` 与 `EOF`。没有 +C 库时这条链无处可续,包装头死在缺**类型**而不是缺头文件上,这正是从报错看不出成因的 +原因。四个很小的头把链续上,而该 feature 负责把它们引进来。 -⚠️ 该包仅用于零 libc 档,而与 C 库并用时是**静默**失败而非响亮失败。C 库以归档形式 +板级支持包同样可以服务这一档,理由与"为什么要有板级包"是同一条:一台机器的 UART 在哪、 +RAM 从哪开始、哪个模拟器启动它,没有一条是 C 库事实: + +```toml +[dependencies] +riscv-virt-rt = { version = "0.5.0", features = ["nolibc"] } +``` + +⚠️ `std-freestanding-nolibc` 正是该 feature 解析到的包,而**直接**把它与 C 库并用时 +是**静默**失败而非响亮失败。C 库以归档形式 发布,归档成员只在符号仍未定义时才被拉入;而依赖包的目标文件无条件进入链接。于是该包 先定义了 `memcpy`,C 库的成员从不被拉入,构建**成功** —— 程序拿到的是逐字节实现而不是 C 库经过优化的那份,且没有任何提示。实测(picolibc 在场):冷构建链接通过,`nm` 只找到