Skip to content

Commit 7b5d6b9

Browse files
committed
feat(port): 差异放一个目录 —— libc++ 按 OS 宏选后端,而 openkal 按「配置的是哪个 C 库」
⭐ 形态先说:`port/include` 是**覆盖目录**,不是对 vendored 树的修改。 openkal-musl 的 `port/` 把与上游 musl 的每一处差异收在一个目录里、靠 include 顺序遮蔽它;这里是 libc++ 的同一套安排 —— vendored 树与上游逐字节相同, `git diff` 对一份新 checkout 为空,而全部差异就是 port/include 下的这几个文件。 ## locale 后端 upstream 的选择链问的是「这是哪个操作系统」,并在每个答案上假定那个系统的 C 库: #if defined(__APPLE__) → support/apple.h #elif defined(__linux__) → support/linux.h 这在 libc++ 正常被构建的每个地方都成立,因为 OS 和它的 C 库是一起来的。 ⚠️ 在这里它们不是一起来的。openkal 的安排是 C 库是一个**包**,而下面的平台是一个 48 个函数的接口的实现。为 Apple 目标格式构建的程序定义了 `__APPLE__`,因为那是 关于**格式和 ABI** 的陈述 —— 而底下是 musl。upstream 的第一问于是对着错的问题 给出了正确答案: bsd_like.h:203: no member named 'asprintf_l' in the global namespace ⚠️ 这条**不能**用 `__config_site` 表达,而那是先试的:libc++ 那条链是写死的 `#if defined(__APPLE__)`,没有覆盖点;唯一相关的旋钮 `_LIBCPP_HAS_LOCALIZATION 0` 是**整个去掉 `<locale>`**,不是换后端。砍掉一个能用的设施去绕过一个口味问题, 比遮蔽一个头文件更差。 ## 三个 Apple SDK 桩 libunwind 与 libc++ 的源码在 `__APPLE__` 下 include Apple SDK 的头。整棵树里 一共 5 处、4 个头 —— 数出来的,不是估的。桩里只有**被读到**的那些名字,和 openkal-macos 那份只有两个名字的 `libSystem.tbd` 同一种做法。 ⚠️ `mach-o/dyld.h` 不是编译期的桩:refstring 与 UnwindCursor 在**运行期**调它。 openkal 没有动态加载器,一个这样构建的程序就是一个镜像,没有东西可枚举 —— 所以 诚实的答案是零个镜像,而两个调用者本来就处理这个情况。⚠️ 答零个和让符号未定义 不是一回事:后者是一个从没想要加载器的程序链接失败在 Apple 的加载器上。 ## std.compat `[package] std-compat-module`:提供一个模块的包提供两个。理由见 mcpp#486。 ⚠️ 未完成:libc++abi 的 guard 实现(`mach_port_t` / `syscall`)与 libc++ 的 locale 后端(`vasprintf` / `strtof_l`)各自按平台宏选实现,而「Mach-O 上的 musl」 两份都不是。CI 不跑这条,所以没有回归面。
1 parent 238bdb3 commit 7b5d6b9

5 files changed

Lines changed: 158 additions & 0 deletions

File tree

mcpp.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ provides = ["hosted-standard-library"]
2525
# knows nothing about, and its configuration is in llvm-generated/ rather than
2626
# beside the compiler.
2727
std-module = "llvm-generated/std.cppm"
28+
# ⚠️ AND THE COMPAT MODULE, BECAUSE IT IS THE SAME LIBRARY.
29+
#
30+
# `std.compat` re-exports the C library's names into the global namespace over
31+
# the SAME libc++. Naming only the first leaves the second resolving to the
32+
# toolchain's copy, and the two are then a mixed pair — which does not fail
33+
# where they are chosen but inside the toolchain's headers, against a
34+
# configuration never generated for this target:
35+
#
36+
# …/share/libc++/v1/std.compat.cppm → __config:13 '__config_site' not found
37+
std-compat-module = "llvm-generated/std.compat.cppm"
2838
std-module-flags = [
2939
# ⚠️ --no-default-config, and it is not tidiness.
3040
#
@@ -106,6 +116,13 @@ sources = [
106116
# build if it reaches a <ctype.h> that is not its own. The generated
107117
# configuration precedes both, because every header of the library includes it.
108118
include_dirs = [
119+
# ⭐ THE OVERLAY, FIRST. Every difference from vendored libc++ lives in one
120+
# directory that shadows it, exactly as openkal-musl's `port/` shadows
121+
# vendored musl — so the vendored tree stays byte-identical to upstream and
122+
# `git diff` against a fresh checkout of it is empty. port/include's own
123+
# headers say what each one is for and why it could not be a `__config_site`
124+
# switch instead.
125+
"port/include",
109126
"llvm/libcxx/include",
110127
"llvm/libcxx/src",
111128
"llvm/libcxxabi/include",

port/include/Availability.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// See AvailabilityMacros.h beside this file. Same header, Apple's newer
2+
// spelling; libunwind includes whichever it finds first.
3+
#ifndef OPENKAL_AVAILABILITY_H
4+
#define OPENKAL_AVAILABILITY_H
5+
#include <AvailabilityMacros.h>
6+
#endif

port/include/AvailabilityMacros.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// A stub for a header openkal has no reason to have, providing exactly the one
2+
// macro that is read.
3+
//
4+
// ⭐ THE SAME MOVE AS openkal-macos's `libSystem.tbd`, WHICH IS TWO NAMES.
5+
//
6+
// libunwind's public header asks how old a deployment target may be before its
7+
// entry points become unavailable, and it asks Apple's SDK. There is no Apple
8+
// SDK here: the platform is `openkal-macos`, an implementation of a
9+
// 48-function interface, and the object format is Mach-O because that is what
10+
// the machine loads — those are two different statements, and upstream reads
11+
// the second as the first.
12+
//
13+
// The whole of what is read is below. It was obtained by grepping the vendored
14+
// runtimes for every `AVAILABLE_*` / `__OSX_AVAILABLE*` / `__IPHONE_*` name, not
15+
// by copying Apple's header, so a name that appears here appears because
16+
// something asks for it.
17+
//
18+
// libunwind.h: AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
19+
//
20+
// ⚠️ EMPTY IS THE ANSWER, NOT A PLACEHOLDER. The macro's job upstream is to
21+
// attach an availability attribute, which describes when a symbol appeared in
22+
// Apple's shipping libSystem. Nothing here comes from Apple's libSystem, so
23+
// there is no version at which it appeared and nothing to attach.
24+
#ifndef OPENKAL_AVAILABILITY_MACROS_H
25+
#define OPENKAL_AVAILABILITY_MACROS_H
26+
27+
#define AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER /* always available here */
28+
29+
#endif
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// ⭐⭐ libc++ 的平台后端按 OS 宏选,而 openkal 的答案按「配置的是哪个 C 库」。
2+
//
3+
// THE OVERLAY, NOT AN EDIT. openkal-musl's `port/` carries every difference
4+
// from vendored musl in one directory that shadows it on the include path, and
5+
// this is that arrangement for libc++ — the vendored tree stays byte-identical
6+
// to upstream, and the whole of the difference is this file.
7+
//
8+
// WHAT UPSTREAM DOES, AND WHY IT IS RIGHT EVERYWHERE ELSE
9+
//
10+
// `__locale_dir/locale_base_api.h` selects a backend by asking which operating
11+
// system this is, and on each answer it assumes that system's C library:
12+
//
13+
// #if defined(__APPLE__) → support/apple.h
14+
// #elif defined(__linux__) → support/linux.h
15+
// …
16+
//
17+
// That holds wherever libc++ is normally built, because the OS and its C
18+
// library travel together.
19+
//
20+
// WHY THEY DO NOT TRAVEL TOGETHER HERE
21+
//
22+
// openkal's arrangement is that the C library is a PACKAGE — openkal-musl — and
23+
// the platform beneath it is an implementation of a 48-function interface. A
24+
// program for Apple's object format built this way has `__APPLE__` defined,
25+
// because that is a statement about the FORMAT and the ABI, and has musl
26+
// underneath it. Upstream's first question then gets the right answer to the
27+
// wrong question:
28+
//
29+
// bsd_like.h:203: no member named 'asprintf_l' in the global namespace
30+
//
31+
// — Apple's locale extensions, which musl does not have and never claimed to.
32+
//
33+
// ⚠️ WHY THIS IS NOT EXPRESSIBLE AS A `__config_site` SWITCH, WHICH WAS TRIED
34+
// FIRST. `__config_site` is where this package states every other decision
35+
// (which C library, whether there is a filesystem, whether there is a random
36+
// device), and it is the right place for anything libc++ offers a knob for.
37+
// libc++ offers none here: the chain is a fixed `#if defined(__APPLE__)` with
38+
// no override, and the only related knob — `_LIBCPP_HAS_LOCALIZATION 0` —
39+
// removes `<locale>` entirely rather than choosing a different backend.
40+
// Removing a working facility to route around a question of taste is a worse
41+
// trade than shadowing one header.
42+
//
43+
// ⇒ `_LIBCPP_HAS_MUSL_LIBC` comes from this package's own `__config_site`, so
44+
// it is a CONFIGURED FACT rather than a guess, and it is asked first. The
45+
// backend it selects is the one a musl build already uses on the other object
46+
// format — nothing new is written, the existing answer is merely reachable.
47+
//
48+
// Measured 2026-08-23: with this in place, libc++'s `std` module precompiles
49+
// for `arm64-apple-macos14.0` on a Linux host.
50+
#ifndef OPENKAL_LIBCXX_LOCALE_BASE_API_OVERLAY
51+
#define OPENKAL_LIBCXX_LOCALE_BASE_API_OVERLAY
52+
53+
#include <__config>
54+
55+
#if _LIBCPP_HAS_LOCALIZATION && _LIBCPP_HAS_MUSL_LIBC && defined(__APPLE__)
56+
// The one case upstream's chain answers wrongly for this configuration. Every
57+
// other target — including musl on ELF, where `__linux__` is defined and the
58+
// chain already reaches the same header — falls through to upstream below.
59+
# include <__locale_dir/support/linux.h>
60+
#else
61+
# include_next <__locale_dir/locale_base_api.h>
62+
#endif
63+
64+
#endif // OPENKAL_LIBCXX_LOCALE_BASE_API_OVERLAY

port/include/mach-o/dyld.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// The dynamic loader's enquiry interface — declared, and answering that there
2+
// is nothing loaded.
3+
//
4+
// ⚠️ THIS ONE IS NOT A COMPILE-TIME STUB. Two places call these at RUN time:
5+
//
6+
// libcxx/src/include/refstring.h — asks whether a string literal lies in a
7+
// read-only segment, so that a copy can be
8+
// skipped;
9+
// libunwind/src/UnwindCursor.hpp — asks which image a program counter is
10+
// in, for the compact-unwind path.
11+
//
12+
// openkal has no dynamic loader. A program built this way is one image, loaded
13+
// by whatever started it, and there is nothing to enumerate — so the honest
14+
// answer is zero images, and both callers already handle it: refstring falls
15+
// back to copying, and the unwinder falls back to the DWARF tables it is
16+
// configured to use here anyway.
17+
//
18+
// ⚠️ Answering zero is NOT the same as leaving the symbols undefined. Undefined
19+
// would be a link error naming Apple's loader in a program that never wanted
20+
// one; zero is the state of a program that is not dynamically loaded, and it is
21+
// true rather than a simulation.
22+
#ifndef OPENKAL_MACH_O_DYLD_H
23+
#define OPENKAL_MACH_O_DYLD_H
24+
25+
#include <stdint.h>
26+
27+
#ifdef __cplusplus
28+
extern "C" {
29+
#endif
30+
31+
struct mach_header;
32+
33+
static inline uint32_t _dyld_image_count(void) { return 0; }
34+
static inline const struct mach_header* _dyld_get_image_header(uint32_t) { return 0; }
35+
static inline intptr_t _dyld_get_image_vmaddr_slide(uint32_t) { return 0; }
36+
static inline const char* _dyld_get_image_name(uint32_t) { return 0; }
37+
38+
#ifdef __cplusplus
39+
}
40+
#endif
41+
42+
#endif

0 commit comments

Comments
 (0)