Skip to content

Commit bcc9981

Browse files
committed
feat: 为没有操作系统的机器构建 C++ 运行时
libc++ / libc++abi / libunwind 现在也为 riscv64-none-elf 建得出来。实测: 1431 个对象,ELF 64-bit LSB relocatable UCB RISC-V,__cxa_throw 与 __cxa_begin_catch 有定义。 llvm-generated/ 变成一个配置一个目录 —— 那正是 musl-generated/ 已有的形状, 理由相同:它们是 configure 产物,而一个没有 configure 步骤的包把它们提交进来。 freestanding 与 generic 的差别只有三位加两个开关: 线程 hosted 目标下 libc++ 自己认出系统并找到 pthread;不被认出的系统上 它停在 "No thread API" 而不是猜 —— 正确,因为不猜正是 configure 存在的理由。所以 freestanding 把它写出来。 文件系统 关掉,并且实现它的源文件随之不编 —— 与 random.cpp 同一条规则, 也是 libc++ 自己的构建用同一个开关做的事。 终端 关掉。 dladdr 关掉:没有动态加载器,它接口里那个类型不存在。 _GNU_SOURCE libc++abi 为线程标识去够 syscall,而 musl 只在这个宏下声明 那个名字。LLVM 自己的 runtimes 构建出于同样理由定义它 —— 而宿主构建从别处拿到了它,这个差别直到这个目标来问才可见。 ⚠️ 一并暴露的一处不完整:__assertion_handler 也是 configure 产物,而宿主那次 构建悄悄用了工具链载荷里的那份。现在它在 llvm-generated/ 里(与模板逐字相同, 所以是收进来而不是改写)。裸机这一试才让它现形 —— 宿主上它一直是绿的。 ⚠️ import std 在这种目标上仍被拒绝,而拒绝它的不是这个包: error: `import std;` is not available on 'riscv64-none-elf' --- a freestanding target has no hosted standard library. 那句话陈述的前提,这个包让它不成立了。解除它是构建工具的改动 —— 它问的是 「目标是不是 freestanding」,而该问的是「hosted 标准库在不在」。
1 parent 84a612b commit bcc9981

7 files changed

Lines changed: 276 additions & 2 deletions

File tree

.mcpp/.xlings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"index_repos": [
3+
],
4+
"deps": ["xim:picolibc-riscv@1.8.12"],
5+
"lang": "en",
6+
"mirror": "auto"
7+
}

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,39 @@ With it at `1`, none. The second follows from openkal reporting `ENOSYS` for
5353
entropy: `std::random_device` is not built, and a program that names it is told
5454
by the linker.
5555

56+
## Two configurations, and why there are two
57+
58+
`llvm-generated/` carries one directory per configuration, which is the shape
59+
`musl-generated/` already has and for the same reason: these are configure
60+
products, and a package with no configure step commits them.
61+
62+
| | `generic` | `freestanding` |
63+
| --- | --- | --- |
64+
| threads | libc++ recognises the system and finds pthread itself | stated, because an unrecognised system makes libc++ stop with `No thread API` rather than guess |
65+
| filesystem | yes | no — and the sources that implement it are then not compiled, which is what libc++'s own build does with the same switch |
66+
| terminal | yes | no |
67+
68+
A target with no operating system also needs two switches a hosted one answers
69+
by recognising the system: the unwinder's use of `dladdr` (there is no dynamic
70+
loader, and the type its interface names does not exist), and `_GNU_SOURCE`
71+
(libc++abi reaches `syscall` for a thread identity, and musl declares that name
72+
only under it — LLVM's own runtimes build defines it for the same reason).
73+
74+
**Measured 2026-08-22**: `mcpp build --target riscv64-none-elf` produces 1431
75+
objects, `ELF 64-bit LSB relocatable, UCB RISC-V`, with `__cxa_throw` and
76+
`__cxa_begin_catch` defined.
77+
78+
⚠️ `import std;` on such a target is still refused, and not by this package:
79+
80+
```
81+
error: `import std;` is not available on 'riscv64-none-elf'
82+
--- a freestanding target has no hosted standard library.
83+
```
84+
85+
That message states a premise this package makes false. Lifting it is a change
86+
to the build tool, which asks whether the *target* is freestanding rather than
87+
whether a hosted standard library is *present*.
88+
5689
## What a program gets
5790

5891
`examples/cxx` asserts it, and every assertion is written so that it can fail:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"index_repos": [
3+
],
4+
"deps": ["xim:picolibc-riscv@1.8.12"],
5+
"lang": "en",
6+
"mirror": "auto"
7+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___ASSERTION_HANDLER
11+
#define _LIBCPP___ASSERTION_HANDLER
12+
13+
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
14+
# include <__cxx03/__config>
15+
# include <__cxx03/__verbose_abort>
16+
# include <__cxx03/__verbose_trap>
17+
#else
18+
# include <__config>
19+
# include <__log_hardening_failure>
20+
# include <__verbose_abort>
21+
# include <__verbose_trap>
22+
#endif
23+
24+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25+
# pragma GCC system_header
26+
#endif
27+
28+
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
29+
30+
// Keep the old implementation that doesn't support assertion semantics for backward compatibility with the frozen C++03
31+
// mode.
32+
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
33+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
34+
# else
35+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
36+
# endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
37+
38+
#else
39+
40+
# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
41+
# define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
42+
43+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
44+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
45+
46+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
47+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
48+
49+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
50+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
51+
52+
# else
53+
54+
# error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
55+
_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
56+
_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
57+
_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
58+
_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
59+
60+
# endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
61+
62+
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
63+
64+
#endif // _LIBCPP___ASSERTION_HANDLER
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___CONFIG_SITE
10+
#define _LIBCPP___CONFIG_SITE
11+
12+
#define _LIBCPP_ABI_VERSION 1
13+
#define _LIBCPP_ABI_NAMESPACE __1
14+
#define _LIBCPP_ABI_FORCE_ITANIUM 0
15+
#define _LIBCPP_ABI_FORCE_MICROSOFT 0
16+
#define _LIBCPP_HAS_THREADS 1
17+
#define _LIBCPP_HAS_MONOTONIC_CLOCK 1
18+
#define _LIBCPP_HAS_TERMINAL 0
19+
#define _LIBCPP_HAS_MUSL_LIBC 1
20+
#define _LIBCPP_HAS_THREAD_API_PTHREAD 1
21+
#define _LIBCPP_HAS_THREAD_API_EXTERNAL 0
22+
#define _LIBCPP_HAS_THREAD_API_WIN32 0
23+
#define _LIBCPP_HAS_THREAD_API_C11 0 // FIXME: Is this guarding dead code?
24+
/* #undef _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS */
25+
#define _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS 0
26+
/* #undef _LIBCPP_NO_VCRUNTIME */
27+
/* #undef _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION */
28+
#define _LIBCPP_HAS_FILESYSTEM 0
29+
#define _LIBCPP_HAS_RANDOM_DEVICE 0
30+
#define _LIBCPP_HAS_LOCALIZATION 1
31+
#define _LIBCPP_HAS_UNICODE 1
32+
#define _LIBCPP_HAS_WIDE_CHARACTERS 1
33+
#define _LIBCPP_HAS_TIME_ZONE_DATABASE 1
34+
#define _LIBCPP_INSTRUMENTED_WITH_ASAN 0
35+
36+
// PSTL backends
37+
/* #undef _LIBCPP_PSTL_BACKEND_SERIAL */
38+
#define _LIBCPP_PSTL_BACKEND_STD_THREAD
39+
/* #undef _LIBCPP_PSTL_BACKEND_LIBDISPATCH */
40+
41+
// Hardening.
42+
#define _LIBCPP_HARDENING_MODE_DEFAULT 2
43+
#define _LIBCPP_ASSERTION_SEMANTIC_DEFAULT 2
44+
45+
// C libraries
46+
#define _LIBCPP_LIBC_PICOLIBC 0
47+
#define _LIBCPP_LIBC_NEWLIB 0
48+
49+
// __USE_MINGW_ANSI_STDIO gets redefined on MinGW
50+
#ifdef __clang__
51+
# pragma clang diagnostic push
52+
# pragma clang diagnostic ignored "-Wmacro-redefined"
53+
#endif
54+
55+
56+
57+
58+
#ifdef __clang__
59+
# pragma clang diagnostic pop
60+
#endif
61+
62+
#endif // _LIBCPP___CONFIG_SITE
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___ASSERTION_HANDLER
11+
#define _LIBCPP___ASSERTION_HANDLER
12+
13+
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
14+
# include <__cxx03/__config>
15+
# include <__cxx03/__verbose_abort>
16+
# include <__cxx03/__verbose_trap>
17+
#else
18+
# include <__config>
19+
# include <__log_hardening_failure>
20+
# include <__verbose_abort>
21+
# include <__verbose_trap>
22+
#endif
23+
24+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25+
# pragma GCC system_header
26+
#endif
27+
28+
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
29+
30+
// Keep the old implementation that doesn't support assertion semantics for backward compatibility with the frozen C++03
31+
// mode.
32+
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
33+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
34+
# else
35+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
36+
# endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
37+
38+
#else
39+
40+
# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
41+
# define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
42+
43+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
44+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
45+
46+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
47+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
48+
49+
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
50+
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
51+
52+
# else
53+
54+
# error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
55+
_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
56+
_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
57+
_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
58+
_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
59+
60+
# endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
61+
62+
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
63+
64+
#endif // _LIBCPP___ASSERTION_HANDLER

mcpp.toml

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ sources = [
6464
# build if it reaches a <ctype.h> that is not its own. The generated
6565
# configuration precedes both, because every header of the library includes it.
6666
include_dirs = [
67-
"llvm-generated/generic",
6867
"llvm/libcxx/include",
6968
"llvm/libcxx/src",
7069
"llvm/libcxxabi/include",
@@ -81,6 +80,37 @@ include_dirs = [
8180
"llvm/libc",
8281
]
8382

83+
# ⚠️ THE CONFIGURATION IS PER TARGET, AND THAT IS WHAT `llvm-generated' IS FOR.
84+
#
85+
# A hosted target lets libc++ work out for itself that the thread API is
86+
# pthread's, because it recognises the system. A target with no operating system
87+
# is not recognised, and libc++ stops with `No thread API' rather than guessing
88+
# --- correctly, because guessing is what a configure step exists to avoid.
89+
#
90+
# So the freestanding configuration states it, and states the two facilities
91+
# that environment does not have. Nothing else differs: the C library beneath is
92+
# the same one.
93+
[target.'cfg(not(os = "none"))'.build]
94+
include_dirs = ["llvm-generated/generic"]
95+
96+
[target.'cfg(os = "none")'.build]
97+
include_dirs = ["llvm-generated/freestanding"]
98+
# Two switches that a hosted target answers by recognising the system and this
99+
# one cannot.
100+
#
101+
# the unwinder's use of dladdr --- there is no dynamic loader here, and the
102+
# type its interface names does not exist;
103+
# _GNU_SOURCE --- libc++abi reaches `syscall' for a thread identity, and musl
104+
# declares that name only under this macro. LLVM's own runtimes build defines
105+
# it for the same reason; a hosted build here gets it from elsewhere and the
106+
# difference was invisible until this target asked.
107+
cxxflags = ["-D_LIBUNWIND_USE_DLADDR=0", "-D_GNU_SOURCE"]
108+
# The configuration turns filesystem off, so the sources that implement it must
109+
# not be compiled --- the same rule as random.cpp above, and the same one libc++'s
110+
# own build applies with the same switch.
111+
sources = ["!llvm/libcxx/src/filesystem/*.cpp"]
112+
113+
[build]
84114
cxxflags = [
85115
"-nostdinc++",
86116
"-D_LIBCPP_BUILDING_LIBRARY",
@@ -98,8 +128,15 @@ cxxflags = [
98128
"-D_LIBCXXABI_USE_LLVM_UNWINDER=1",
99129
"-DLIBCXXABI_USE_LLVM_UNWINDER=1",
100130
"-DNDEBUG",
101-
# libc++abi implements exceptions; it cannot be compiled without them.
131+
# libc++abi IMPLEMENTS exceptions and run-time type information; it cannot be
132+
# compiled without them, and a freestanding target turns both off for the whole
133+
# graph (mcpp docs/13 records why: a BMI carries the setting, so a dependency
134+
# compiled with exceptions cannot be imported by a unit without them). The
135+
# target's flags precede a package's, so stating both here is what restores
136+
# them --- for this package, which is the one that supplies them, and for
137+
# nothing else.
102138
"-fexceptions",
139+
"-frtti",
103140
"-funwind-tables",
104141
"-w",
105142
# As with the C library beneath: a program links what it reaches and nothing

0 commit comments

Comments
 (0)