commit 610ddf0efcd7fce88d1e3a84771212e900392ae2
parent da199356cebfa4d53f2874216ea8b1dc84d0dc5b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 20 May 2026 14:48:14 -0700
rt: drop hosted libc shim, use system headers via sysroot
cfree's preprocessor now ingests system headers, so the rt/include/libc
overlay + per-OS cfree_hosted shim are unneeded. `-lc` now resolves
through the sysroot's real libc/libSystem; stdin/stdout/stderr/errno go
through native accessors (__errno_location, __stdinp, ...) resolved by
dlsym for the JIT path.
- driver/hosted.c: strip dead support_root scaffolding (~90 lines);
resolvers already only point at the sysroot.
- driver/env.c: drop __cfree_{errno_location,stdin,stdout,stderr}
resolver branches; native names go through dlsym(RTLD_DEFAULT).
- rt/include/libc/, rt/lib/cfree_hosted/, libcfree_hosted_{linux,macos}
Make targets: removed.
- test/driver: drop 5 hosted-shim cases (sysroot-required, missing-shim,
macos-libSystem, no-lc, hosted-errno-shim). test-musl/test-glibc cover
the end-to-end -lc path.
- test/libc: delete test-libc harness + cases; keep musl/ and glibc/
subdirs since test-musl/test-glibc still exercise sysroot linking.
- test/{rt,opt,test.mk}: drop redundant -isystem rt/include (driver
auto-prepends via driver/runtime.c).
- test/libc/musl/run.sh: pin docker.io/arm64v8/alpine:latest to avoid
the cached-wrong-arch trap that bare alpine:latest hits.
- test/libc/{musl,glibc}/Containerfile*: drop musl-dev/libc6-dev version
pins that created a maintenance treadmill on each upstream bump.
- test/libc/glibc/run.sh: keep -isystem rt/include (after sysroot) since
glibc doesn't ship compiler headers (musl-dev does, by coincidence).
- doc/HOSTED.md: deleted. STAGE2.md + BUGS.md: update repro recipes to
--sysroot=$SDK.
Diffstat:
31 files changed, 64 insertions(+), 1879 deletions(-)
diff --git a/doc/BUGS.md b/doc/BUGS.md
@@ -26,20 +26,21 @@ Known bugs caught by other harnesses
- [ ] Mach-O `link_macho: coalesce mismatch on __TEXT,__text (flags/zerofill)` when linking certain cfree-emitted relocatable objects. Reproduces with cfree-compiled `tmp/projects/stb_sprintf.h` (driver `tmp/refresh/use_stb_sprintf.c`) and `tmp/projects/cJSON/cJSON.c`; the trivial `int main(){return 0;}` + hosted shim still links, and `tmp/refresh/use_jsmn.c` links and runs end-to-end. The differentiator looks like a section-flag fan-out where an `__TEXT,__text` MSec gets emitted next to a `zerofill`-flagged MSec under the same `(segname, sectname)`, which trips the Phase A/B mismatch check in `src/link/link_macho.c`. No reduction yet:
```sh
+ SDK="$(xcrun --show-sdk-path)"
# Compile is fine:
- build/cfree cc -target aarch64-darwin -isystem rt/include/libc -isystem rt/include \
+ build/cfree cc -target aarch64-darwin --sysroot="$SDK" -isystem rt/include \
-c tmp/refresh/use_stb_sprintf.c -o /tmp/stb.o
# Link fails:
- SDK="$(xcrun --show-sdk-path)"
- build/cfree cc -target aarch64-darwin -e main -L "$SDK/usr/lib" \
- -o /tmp/stb.exe /tmp/stb.o build/cfree_hosted/macos.o -lSystem
+ build/cfree cc -target aarch64-darwin --sysroot="$SDK" -e main \
+ -o /tmp/stb.exe /tmp/stb.o -lc
# → fatal: link_macho: coalesce mismatch on __TEXT,__text (flags/zerofill)
```
- [ ] aarch64 call lowering rejects "INDIRECT arg storage kind 3". Reproduces compiling `cJSON_Utils.c:845`, which passes a sized aggregate by value to a function. The AAPCS64 classifier picks INDIRECT but the call emitter has no path for the source-storage shape it sees there. No minimal repro yet:
```sh
- build/cfree cc -target aarch64-darwin -isystem rt/include/libc -isystem rt/include \
+ SDK="$(xcrun --show-sdk-path)"
+ build/cfree cc -target aarch64-darwin --sysroot="$SDK" -isystem rt/include \
-c tmp/projects/cJSON/cJSON_Utils.c -o /tmp/u.o
# → fatal: aarch64 call: INDIRECT arg storage kind 3 unsupported
```
@@ -47,8 +48,9 @@ Known bugs caught by other harnesses
- [ ] silent SIGSEGV with no diagnostic when compiling much of lua-5.4.7. After B3 was fixed, 18 lua TUs now crash cfree (exit 139): `lapi, lcode, ldebug, ldo, ldump, lfunc, lgc, llex, lmem, lobject, lparser, lstate, lstring, ltable, ltm, luac, lundump, lvm, lzio`. The other 14 (`lauxlib, lbaselib, lcorolib, lctype, ldblib, linit, liolib, lmathlib, loadlib, lopcodes, loslib, ltablib, lua, lutf8lib`) compile cleanly. No minimal reduction yet, so no red test:
```sh
+ SDK="$(xcrun --show-sdk-path)"
build/cfree cc -target aarch64-darwin \
- -isystem rt/include/libc -isystem rt/include \
+ --sysroot="$SDK" -isystem rt/include \
-c tmp/projects/lua/src/lparser.c -o /tmp/lparser.o
# → Segmentation fault: 11 (exit 139, no diagnostic)
```
diff --git a/doc/HOSTED.md b/doc/HOSTED.md
@@ -1,278 +0,0 @@
-# Hosted Linking Plan
-
-This document describes the planned split between compiler support runtime
-linking and hosted C library linking in the `cfree cc` driver.
-
-## Goals
-
-- Keep freestanding links freestanding by default.
-- Always provide compiler runtime support needed by code generated by cfree.
-- Add hosted C runtime and libc inputs only when the user explicitly asks for
- libc, currently by passing `-lc`.
-- Keep platform-specific hosted knowledge isolated in a small, pluggable driver
- module. No platform paths should be scattered through `cc.c`, `ld.c`, or
- linker internals.
-- Make every platform path come from an explicit support layout or configured
- sysroot/SDK, not from implicit host filesystem probing.
-
-## Conceptual Split
-
-There are two distinct runtime classes.
-
-### Compiler Runtime
-
-`libcfree_rt.a` is compiler support code: integer helpers, soft-float helpers,
-memory helpers, atomics, coroutine/runtime helpers, and similar functions that
-generated code or compiler lowering may require.
-
-This is not hosted libc. It should be linked automatically by `cfree cc` during
-final link, independent of `-lc`.
-
-Rules:
-
-- `cfree cc` adds `libcfree_rt.a` for link actions.
-- `cfree cc -c`, `-E`, dependency-only, and token-dump actions do not add it.
-- `cfree ld` remains explicit and does not auto-add compiler runtime unless a
- future deliberate option such as `--compiler-rt=auto` is added.
-- `-nostdlib` and `-nodefaultlibs` suppress this automatic runtime input.
-- The runtime archive is placed late in the ordered link inputs, after user
- objects and libraries, so demand-loaded helpers satisfy unresolved references.
-
-### Hosted C Runtime And Libc
-
-Hosted linking means startup files, hosted ABI shim objects, libc, and dynamic
-loader settings.
-
-This is opt-in. Passing `-lc` requests hosted libc expansion. Without `-lc`,
-`cfree cc` must not add crt objects, hosted shims, libc, or platform dynamic
-loader policy.
-
-Rules:
-
-- `-lc` is recognized specially by the `cc` driver.
-- Other `-lfoo` inputs keep normal ordered library resolution.
-- `-nostdlib` and `-nodefaultlibs` should suppress hosted expansion. The exact
- policy can be either strict diagnostic or "treat `-lc` as a normal library";
- the preferred initial behavior is to avoid implicit hosted inputs.
-- Hosted inputs must preserve link order:
- startup objects before user objects, libc/runtime support after user objects,
- and trailing finalization objects last.
-
-## Distribution Layout
-
-The intended distribution model is a single directory with the `cfree` binary at
-the root and a sibling support directory:
-
-```text
-cfree
-support/
- rt/
- include/
- lib/
- cache/
-```
-
-The driver should be able to locate this support root from the installed binary
-path. A command-line override such as `--support-dir DIR` should be available
-for tests, development trees, and unusual packaging.
-
-The same resolver must also work from a development checkout. If the current
-tree contains `./rt/include/` and `./rt/lib/`, those directories are a valid
-support layout without an installed `support/` wrapper:
-
-```text
-./rt/
- include/
- lib/
- build/
-```
-
-Support path resolution order:
-
-1. An explicit `--support-dir DIR`.
-2. A development tree rooted at the current working directory when
- `./rt/include/` and `./rt/lib/` both exist.
-3. An installed support directory adjacent to the `cfree` executable.
-
-The resolved `rt/include/` directory is cfree's freestanding system include
-root. `cfree cc` should add it to preprocessing and compile actions as a system
-include directory before any configured SDK/sysroot. This applies to `-E`, `-M`,
-`-MM`, `-MD`, `-MMD`, `-c`, and compile-and-link actions. Hosted libc headers
-under `rt/include/libc/` are separate and should be added only by the hosted
-profile when libc is requested.
-
-## Runtime Build Cache
-
-`cfree cc` should compile and cache `libcfree_rt.a` for the target on demand.
-
-Proposed cache layout:
-
-```text
-support/cache/<target-triple>/
- libcfree_rt.a
- obj/
-```
-
-In a development checkout the cache path is:
-
-```text
-rt/build/<target-key>/
- libcfree_rt.a
- obj/
-```
-
-`<target-key>` should match the runtime Makefile variant names where possible,
-for example `aarch64-linux`, `x86_64-linux`, `riscv64-linux`,
-`aarch64-apple-darwin`, and `x86_64-apple-darwin`.
-
-The cache builder belongs in a dedicated driver module, for example
-`driver/runtime.c` with `driver/runtime.h`.
-
-Responsibilities:
-
-- Resolve the support directory.
-- Map a `CfreeTarget` to a stable target cache key/triple.
-- Check for an existing runtime archive in the resolved cache path.
-- Build missing or stale runtime objects from the resolved `rt/lib/` sources for
- the target.
-- Archive them into `libcfree_rt.a`.
-- Return a concrete path to the archive for insertion into ordered link inputs.
-
-Runtime source builds should use only support-tree inputs:
-
-- Freestanding headers: `-isystem <support-rt>/include`.
-- Runtime-private common includes:
- `-I<support-rt>/lib/include/common -I<support-rt>/lib/impl`.
-- ABI-specific includes such as `-I<support-rt>/lib/include/lp64_le`.
-- Target feature sources and flags selected from the target key, mirroring
- `rt/Makefile`.
-
-For normal `cfree cc` compile actions, only the freestanding header include path
-is injected. The runtime-private `rt/lib/...` include paths are private to the
-runtime builder.
-
-For link actions, the returned archive path is appended to the ordered link
-inputs after user objects, explicit user archives, and ordinary `-l` libraries,
-but before hosted finalization objects such as `crtn.o` when a hosted profile is
-active.
-
-Failure should produce diagnostics that name the target and the support path:
-
-```text
-cc: compiler runtime for aarch64-linux not found and could not be built
-cc: support dir: /path/to/support
-```
-
-## Hosted Resolver
-
-Hosted platform policy belongs in a separate driver module, for example
-`driver/hosted.c` with `driver/hosted.h`.
-
-`cc.c` should not know about `libSystem`, musl crt names, glibc loader names, or
-hosted shim paths. It should only pass target, configured roots, and the current
-ordered input list to the hosted resolver.
-
-The hosted resolver returns concrete ordered additions and link options.
-
-Conceptual profile shape:
-
-```c
-typedef struct HostedProfile {
- const char* name;
- CfreeOSKind os;
- CfreeObjFmt obj;
- const char* crt1;
- const char* crti;
- const char* crtn;
- const char* libc;
- const char* libc_nonshared;
- const char* hosted_shim;
- const char* interp;
- bool static_link;
- bool pie;
-} HostedProfile;
-```
-
-The actual implementation may use a richer structured representation, but the
-important property is locality: adding or changing hosted platform support
-should mean editing `driver/hosted.c`, not the compiler driver or linker.
-
-## Initial Hosted Profiles
-
-### macOS / libSystem
-
-When `-lc` is present for a Darwin/Mach-O target:
-
-- Add `libcfree_hosted_macos.a`.
-- Add the configured SDK/sysroot `libSystem` stub or dylib.
-- Do not add libSystem without `-lc`.
-
-The SDK path should come from explicit configuration, for example `--sysroot` or
-an equivalent support profile setting. The driver should not silently search
-arbitrary host SDK locations.
-
-### Linux / musl
-
-When `-lc` is present for a Linux/ELF target and the configured sysroot contains
-musl static libc:
-
-- Prefer static musl if `libc.a` is available.
-- Add startup and finalization objects in normal musl order:
-
-```text
-crt1.o crti.o user-inputs hosted-shim libc.a libcfree_rt.a crtn.o
-```
-
-For a future dynamic musl profile:
-
-```text
-Scrt1.o crti.o user-inputs hosted-shim libc.so libcfree_rt.a crtn.o
-```
-
-and set the musl interpreter from the profile.
-
-### Linux / glibc
-
-glibc hosted linking is dynamic-only initially.
-
-When `-lc` is present for a Linux/ELF target and the configured sysroot contains
-glibc:
-
-```text
-Scrt1.o crti.o user-inputs hosted-shim libc.so.6 libc_nonshared.a
-libcfree_rt.a crtn.o
-```
-
-The profile must set the dynamic loader explicitly, for example:
-
-```text
-/lib/ld-linux-aarch64.so.1
-/lib64/ld-linux-x86-64.so.2
-/lib/ld-linux-riscv64-lp64d.so.1
-```
-
-These values belong in the hosted profile table.
-
-## Linker Boundary
-
-The linker should consume explicit ordered inputs and explicit link options.
-It should not infer hosted policy.
-
-Existing linker defaults for platform loaders or system libraries should be
-treated as compatibility fallbacks only. The driver should pass explicit
-interpreter and DSO inputs when hosted linking is requested.
-
-## Test Strategy
-
-Start with focused driver tests before broad libc conformance runs.
-
-- No `-lc`: no hosted inputs are added.
-- Link mode through `cfree cc` adds `libcfree_rt.a` automatically.
-- `-lc` on macOS expands through the macOS hosted profile.
-- `-lc` on Linux/musl chooses static musl when `libc.a` exists.
-- `-lc` on Linux/glibc chooses dynamic glibc and sets the profile interpreter.
-- Missing sysroot/support files diagnose the missing concrete file and profile.
-- Ordered inputs preserve normal linker behavior around archives and libc.
-
-The existing `test/libc` sysroot harnesses are good end-to-end validation once
-the small resolver tests are green.
diff --git a/doc/STAGE2.md b/doc/STAGE2.md
@@ -19,11 +19,11 @@ link reaches the chained-fixup emit pass and trips D2 below.
Stage 2 currently invokes:
```
-cfree-stage1 cc -isystem rt/include -isystem rt/include/libc -Iinclude -Isrc
+cfree-stage1 cc --sysroot=$SDK -isystem rt/include -Iinclude -Isrc
```
-`-isystem rt/include/libc` is required so the hosted libc headers are
-visible (top-level `rt/include/` only ships the freestanding set).
+`--sysroot=$SDK` makes the host SDK's libc/POSIX headers visible.
+`rt/include/` ships the freestanding set on top.
`DEPFLAGS` is empty for stage 2 today; B0 has landed but the recipe has
not been switched back on.
@@ -38,11 +38,10 @@ not been switched back on.
- [ ] **A2.** System-header ingest. The driver pulls a POSIX/Mach surface
(`sys/stat.h`, `sys/mman.h`, `sys/syscall.h`, `fcntl.h`, `unistd.h`,
`signal.h`, `pthread.h`, `dlfcn.h`, `mach/mach.h`, `mach/mach_vm.h`,
- `mach/vm_map.h`) that today's `rt/include/libc/` doesn't ship.
- **Direction: ingest the real SDK headers** rather than growing
- `rt/include/libc/`. With `-isystem $SDK/usr/include` and the right host
- predefines, the SDK parses up to a small set of constructs cfree
- doesn't yet handle. Each sub-item below is the minimal feature needed.
+ `mach/vm_map.h`) from the host SDK. With `-isystem $SDK/usr/include`
+ and the right host predefines, the SDK parses up to a small set of
+ constructs cfree doesn't yet handle. Each sub-item below is the
+ minimal feature needed.
- [ ] **A2-S1.** Asm-label on function declarators:
`T fn(args) __asm__("name");`. GCC asm-label rename extension; what
@@ -73,8 +72,7 @@ not been switched back on.
don't need to hand-pass `-D`.
After S6+S7+S1+S2+S3+S4, both blocked driver files should ingest the
- SDK without any growth of `rt/include/libc/`. S5 only needed for
- signal.h/ucontext paths.
+ SDK directly. S5 only needed for signal.h/ucontext paths.
### Driver — dep emission
@@ -144,14 +142,6 @@ not been switched back on.
TLV section/segment added by the TLV ingest work isn't covered.
Blocks the standalone link probe past compile.
-### Hosted libc shim
-
-- [ ] **E1.** `libcfree_hosted_macos.a` is built but not threaded into
- the `$(BIN)` link. For a "self-host on rt libc" milestone (separate
- from "stage 2 builds at all"), the macOS `$(BIN)` rule should consume
- the hosted shim and route libc calls through it instead of clang's
- default `-lSystem` glue.
-
## Runtime — `rt/lib/*` ingest
Separate from stage-2 self-host: can cfree compile `libcfree_rt.a`?
@@ -239,8 +229,9 @@ Stage-2 audit (src + driver):
```sh
make && cp build/cfree build/cfree-stage1
BIN=$(pwd)/build/cfree-stage1
-FLAGS="-isystem rt/include -isystem rt/include/libc -Iinclude -Isrc"
-DFLAGS="-isystem rt/include -isystem rt/include/libc -Iinclude"
+SDK=$(xcrun --show-sdk-path)
+FLAGS="--sysroot=$SDK -isystem rt/include -Iinclude -Isrc"
+DFLAGS="--sysroot=$SDK -isystem rt/include -Iinclude"
for f in $(find src -name '*.c' | sort); do
$BIN cc $FLAGS -c "$f" -o /dev/null 2>&1 | head -1 | sed "s|^|$f: |"
done
diff --git a/driver/cc.c b/driver/cc.c
@@ -754,8 +754,6 @@ static int cc_apply_hosted_profile(CcOptions* o) {
req.tool = CC_TOOL;
req.target = o->target;
req.sysroot = o->sysroot;
- req.support_dir = o->support_dir;
- req.driver_path = o->driver_path;
req.static_link = o->static_link;
req.link_inputs = link_action;
if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1;
diff --git a/driver/env.c b/driver/env.c
@@ -1639,28 +1639,11 @@ out:
return ok;
}
-static int *driver_hosted_errno_location(void) { return &errno; }
-static FILE *driver_hosted_stdin(void) { return stdin; }
-static FILE *driver_hosted_stdout(void) { return stdout; }
-static FILE *driver_hosted_stderr(void) { return stderr; }
-
void *driver_dlsym_resolver(void *user, const char *name) {
void *p;
(void)user;
if (!name)
return NULL;
- if (strcmp(name, "__cfree_errno_location") == 0 ||
- (name[0] == '_' && strcmp(name + 1, "__cfree_errno_location") == 0))
- return (void *)&driver_hosted_errno_location;
- if (strcmp(name, "__cfree_stdin") == 0 ||
- (name[0] == '_' && strcmp(name + 1, "__cfree_stdin") == 0))
- return (void *)&driver_hosted_stdin;
- if (strcmp(name, "__cfree_stdout") == 0 ||
- (name[0] == '_' && strcmp(name + 1, "__cfree_stdout") == 0))
- return (void *)&driver_hosted_stdout;
- if (strcmp(name, "__cfree_stderr") == 0 ||
- (name[0] == '_' && strcmp(name + 1, "__cfree_stderr") == 0))
- return (void *)&driver_hosted_stderr;
p = dlsym(RTLD_DEFAULT, name);
/* On Mach-O hosts the linker hands us C names with a leading underscore
* (obj_format_c_mangle), but dlsym(RTLD_DEFAULT) expects the
diff --git a/driver/hosted.c b/driver/hosted.c
@@ -27,98 +27,6 @@ static char* hosted_join2(DriverEnv* env, const char* a, const char* b,
return out;
}
-static char* hosted_driver_dir(DriverEnv* env, const char* driver_path,
- size_t* out_size) {
- const char* slash = NULL;
- const char* p;
- size_t len;
- char* out;
- if (!driver_path || !driver_path[0]) driver_path = "build/cfree";
- for (p = driver_path; *p; ++p) {
- if (*p == '/') slash = p;
- }
- if (!slash) return hosted_join2(env, ".", "", out_size);
- len = (size_t)(slash - driver_path);
- if (len == 0) len = 1;
- out = driver_alloc(env, len + 1u);
- if (!out) return NULL;
- driver_memcpy(out, driver_path, len);
- out[len] = '\0';
- *out_size = len + 1u;
- return out;
-}
-
-static int hosted_dir_has_rt_layout(DriverEnv* env, const char* dir) {
- char* inc;
- char* lib;
- size_t inc_size = 0;
- size_t lib_size = 0;
- int ok;
- inc = hosted_join2(env, dir, "rt/include", &inc_size);
- lib = hosted_join2(env, dir, "rt/lib", &lib_size);
- if (!inc || !lib) {
- if (inc) driver_free(env, inc, inc_size);
- if (lib) driver_free(env, lib, lib_size);
- return 0;
- }
- ok = driver_path_exists(inc) && driver_path_exists(lib);
- driver_free(env, inc, inc_size);
- driver_free(env, lib, lib_size);
- return ok;
-}
-
-static char* hosted_resolve_support_root(const DriverHostedRequest* req,
- size_t* out_size) {
- DriverEnv* env = req->env;
- if (req->support_dir && req->support_dir[0]) {
- size_t n = driver_strlen(req->support_dir) + 1u;
- char* out = driver_alloc(env, n);
- if (!out) return NULL;
- driver_memcpy(out, req->support_dir, n);
- *out_size = n;
- return out;
- }
- if (hosted_dir_has_rt_layout(env, ".")) {
- char* out = driver_alloc(env, 2u);
- if (!out) return NULL;
- out[0] = '.';
- out[1] = '\0';
- *out_size = 2u;
- return out;
- }
- {
- size_t dir_size = 0;
- char* dir = hosted_driver_dir(env, req->driver_path, &dir_size);
- if (!dir) return NULL;
- if (hosted_dir_has_rt_layout(env, dir)) {
- *out_size = dir_size;
- return dir;
- }
- {
- size_t parent_size = 0;
- char* parent = hosted_join2(env, dir, "..", &parent_size);
- if (!parent) {
- driver_free(env, dir, dir_size);
- return NULL;
- }
- if (hosted_dir_has_rt_layout(env, parent)) {
- driver_free(env, dir, dir_size);
- *out_size = parent_size;
- return parent;
- }
- driver_free(env, parent, parent_size);
- }
- {
- size_t support_size = 0;
- char* support = hosted_join2(env, dir, "support", &support_size);
- driver_free(env, dir, dir_size);
- if (!support) return NULL;
- *out_size = support_size;
- return support;
- }
- }
-}
-
static int hosted_add_input(DriverHostedInput* items, uint32_t* n, uint32_t cap,
uint8_t kind, char* path, size_t path_size) {
DriverHostedInput* it;
@@ -247,11 +155,9 @@ static const char* hosted_musl_interp(CfreeArchKind arch) {
}
static int hosted_resolve_darwin(const DriverHostedRequest* req,
- DriverHostedPlan* plan,
- const char* support_root) {
+ DriverHostedPlan* plan) {
size_t size = 0;
char* libsystem = NULL;
- (void)support_root;
if (!req->sysroot || !req->sysroot[0]) {
driver_errf(req->tool,
"Darwin hosted profile requires --sysroot; try: --sysroot "
@@ -297,9 +203,7 @@ static int hosted_resolve_darwin(const DriverHostedRequest* req,
}
static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req,
- DriverHostedPlan* plan,
- const char* support_root) {
- (void)support_root;
+ DriverHostedPlan* plan) {
plan->profile_name = "linux-musl-static";
if (hosted_add_linux_defines(plan, 0) != 0 ||
hosted_add_existing_include(plan, req->env, req->sysroot, "include") !=
@@ -327,10 +231,8 @@ static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req,
}
static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req,
- DriverHostedPlan* plan,
- const char* support_root) {
+ DriverHostedPlan* plan) {
const char* interp = hosted_musl_interp(req->target.arch);
- (void)support_root;
if (!interp) {
driver_errf(req->tool, "no hosted musl profile for target architecture");
return 1;
@@ -363,10 +265,8 @@ static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req,
}
static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req,
- DriverHostedPlan* plan,
- const char* support_root) {
+ DriverHostedPlan* plan) {
const char* interp = hosted_glibc_interp(req->target.arch);
- (void)support_root;
if (!interp) {
driver_errf(req->tool, "no hosted glibc profile for target architecture");
return 1;
@@ -421,8 +321,7 @@ static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req,
}
static int hosted_resolve_linux(const DriverHostedRequest* req,
- DriverHostedPlan* plan,
- const char* support_root) {
+ DriverHostedPlan* plan) {
char* libc_a;
char* libc_so;
char* libc_so6;
@@ -461,11 +360,11 @@ static int hosted_resolve_linux(const DriverHostedRequest* req,
driver_free(req->env, libc_so6, ss6);
driver_free(req->env, libc_nonshared, sns);
if (!req->static_link && has_libc_so6 && has_glibc_nonshared)
- return hosted_resolve_linux_glibc_dynamic(req, plan, support_root);
+ return hosted_resolve_linux_glibc_dynamic(req, plan);
if (has_libc_a && !(has_libc_so6 && has_glibc_nonshared))
- return hosted_resolve_linux_musl_static(req, plan, support_root);
+ return hosted_resolve_linux_musl_static(req, plan);
if (!req->static_link && has_libc_so)
- return hosted_resolve_linux_musl_dynamic(req, plan, support_root);
+ return hosted_resolve_linux_musl_dynamic(req, plan);
driver_errf(req->tool,
"no supported Linux hosted libc found under sysroot: %s",
req->sysroot);
@@ -474,27 +373,19 @@ static int hosted_resolve_linux(const DriverHostedRequest* req,
int driver_hosted_resolve(const DriverHostedRequest* req,
DriverHostedPlan* out) {
- size_t support_size = 0;
- char* support_root;
DriverHostedPlan zero = {0};
int rc;
if (!req || !out || !req->env || !req->tool) return 1;
*out = zero;
- support_root = hosted_resolve_support_root(req, &support_size);
- if (!support_root) {
- driver_errf(req->tool, "out of memory");
- return 1;
- }
if (req->target.os == CFREE_OS_MACOS && req->target.obj == CFREE_OBJ_MACHO) {
- rc = hosted_resolve_darwin(req, out, support_root);
+ rc = hosted_resolve_darwin(req, out);
} else if (req->target.os == CFREE_OS_LINUX &&
req->target.obj == CFREE_OBJ_ELF) {
- rc = hosted_resolve_linux(req, out, support_root);
+ rc = hosted_resolve_linux(req, out);
} else {
driver_errf(req->tool, "no hosted libc profile for target");
rc = 1;
}
- driver_free(req->env, support_root, support_size);
if (rc != 0) driver_hosted_plan_fini(req->env, out);
return rc;
}
diff --git a/driver/hosted.h b/driver/hosted.h
@@ -49,8 +49,6 @@ typedef struct DriverHostedRequest {
const char* tool;
CfreeTarget target;
const char* sysroot;
- const char* support_dir;
- const char* driver_path;
int static_link;
int link_inputs;
} DriverHostedRequest;
diff --git a/rt/Makefile b/rt/Makefile
@@ -6,7 +6,7 @@ RT_AS ?= $(BIN) as
RT_AS_COMPILE_FLAGS ?=
RT_BUILD_DIR = rt/build
-RT_COMMON_CFLAGS = -isystem rt/include -isystem rt/include/libc -Werror
+RT_COMMON_CFLAGS = -Werror
RT_LIB_INCS = -Irt/lib/include/common -Irt/lib/impl
RT_VARIANTS = \
@@ -32,9 +32,7 @@ RT_DEFAULT_VARIANTS = \
aarch64-apple-darwin \
x86_64-apple-darwin
-.PHONY: rt rt-all-targets $(addprefix rt-,$(RT_VARIANTS)) \
- hosted-macos hosted-linux hosted-linux-aarch64 hosted-linux-x64 hosted-linux-rv64 \
- clean-rt
+.PHONY: rt rt-all-targets $(addprefix rt-,$(RT_VARIANTS)) clean-rt
rt: $(addprefix rt-,$(RT_DEFAULT_VARIANTS))
rt-all-targets: $(addprefix rt-,$(RT_VARIANTS))
@@ -224,59 +222,6 @@ endef
$(foreach variant,$(RT_VARIANTS),$(eval $(call RT_VARIANT_template,$(variant))))
-# libcfree_hosted: tiny per-OS shim that bridges the ABI-neutral names in
-# rt/include/libc/ to whatever the platform libc actually exports. macOS
-# variant is the only one wired today.
-#
-# Built by cfree itself. The shim reads libSystem-imported global
-# variables (__stdinp, __stdoutp, __stderrp), which require Mach-O
-# GOT_LOAD_PAGE21 / GOT_LO12_NC relocations so dyld can route the load
-# through a non-lazy pointer in __DATA,__got; the AArch64 backend picks
-# that sequence automatically for undefined externs on Mach-O targets
-# (see use_got_for_sym in src/arch/aarch64.c).
-RT_HOSTED_MACOS_AR = build/libcfree_hosted_macos.a
-RT_HOSTED_MACOS_SRC = rt/lib/cfree_hosted/macos.c
-RT_HOSTED_MACOS_OBJ = build/cfree_hosted/macos.o
-
-hosted-macos: $(RT_HOSTED_MACOS_AR)
-
-$(RT_HOSTED_MACOS_OBJ): $(RT_HOSTED_MACOS_SRC) $(BIN)
- @mkdir -p $(dir $@)
- $(BIN) cc -target aarch64-darwin -c $< -o $@
-
-$(RT_HOSTED_MACOS_AR): $(RT_HOSTED_MACOS_OBJ) $(BIN)
- @rm -f $@
- $(BIN) ar rcs $@ $(RT_HOSTED_MACOS_OBJ)
-
-# libcfree_hosted_linux: per-arch ELF shim object. Built by cfree-cc for
-# the cross-target so test-libc can link host-libc test cases against it
-# without needing clang's resource dir or a separate compile pass.
-# stdin/stdout/stderr are extern data globals on Linux; cfree-cc doesn't
-# yet emit GOT-loads for extern data on ELF (only Mach-O), so callers
-# must static-link the resulting exes - the addresses resolve at link
-# time when libc.a is in the input set.
-RT_HOSTED_LINUX_SRC = rt/lib/cfree_hosted/linux.c
-RT_HOSTED_LINUX_AARCH64_OBJ = build/cfree_hosted/linux-aarch64.o
-RT_HOSTED_LINUX_X64_OBJ = build/cfree_hosted/linux-x64.o
-RT_HOSTED_LINUX_RV64_OBJ = build/cfree_hosted/linux-rv64.o
-
-hosted-linux: hosted-linux-aarch64 hosted-linux-x64 hosted-linux-rv64
-hosted-linux-aarch64: $(RT_HOSTED_LINUX_AARCH64_OBJ)
-hosted-linux-x64: $(RT_HOSTED_LINUX_X64_OBJ)
-hosted-linux-rv64: $(RT_HOSTED_LINUX_RV64_OBJ)
-
-$(RT_HOSTED_LINUX_AARCH64_OBJ): $(RT_HOSTED_LINUX_SRC) $(BIN)
- @mkdir -p $(dir $@)
- $(BIN) cc -target aarch64-linux -fPIE -fpic -c $< -o $@
-
-$(RT_HOSTED_LINUX_X64_OBJ): $(RT_HOSTED_LINUX_SRC) $(BIN)
- @mkdir -p $(dir $@)
- $(BIN) cc -target x86_64-linux -fPIE -fpic -c $< -o $@
-
-$(RT_HOSTED_LINUX_RV64_OBJ): $(RT_HOSTED_LINUX_SRC) $(BIN)
- @mkdir -p $(dir $@)
- $(BIN) cc -target riscv64-linux -fPIE -fpic -c $< -o $@
-
clean: clean-rt
clean-rt:
diff --git a/rt/include/libc/ctype.h b/rt/include/libc/ctype.h
@@ -1,23 +0,0 @@
-/* ctype.h -- C11 7.4 -- Character classification and conversion
- *
- * Inputs are int and must be EOF (-1) or representable as unsigned char.
- * Behaviour outside that range is undefined per the standard. */
-#ifndef CFREE_LIBC_CTYPE_H
-#define CFREE_LIBC_CTYPE_H
-
-int isalnum(int c);
-int isalpha(int c);
-int isblank(int c);
-int iscntrl(int c);
-int isdigit(int c);
-int isgraph(int c);
-int islower(int c);
-int isprint(int c);
-int ispunct(int c);
-int isspace(int c);
-int isupper(int c);
-int isxdigit(int c);
-int tolower(int c);
-int toupper(int c);
-
-#endif
diff --git a/rt/include/libc/errno.h b/rt/include/libc/errno.h
@@ -1,55 +0,0 @@
-/* errno.h -- C11 7.5 -- Macros reporting error conditions
- *
- * C11 only mandates EDOM/EILSEQ/ERANGE. Other names are POSIX.
- *
- * `errno` is platform-divergent: macOS uses `int *__error(void)`, glibc /
- * musl use `int *__errno_location(void)`. We route through an
- * accessor the libcfree_hosted shim provides, keeping the header itself
- * ABI-neutral. */
-#ifndef CFREE_LIBC_ERRNO_H
-#define CFREE_LIBC_ERRNO_H
-
-extern int *__cfree_errno_location(void);
-#define errno (*__cfree_errno_location())
-
-/* C11 mandatory */
-#define EDOM 33
-#define EILSEQ 84
-#define ERANGE 34
-
-/* POSIX additions in common use. Values follow the Linux numbering;
- * Darwin and Linux mostly agree below 35, diverge above. Programs that
- * inspect numeric values across platforms should use strerror() instead. */
-#define EPERM 1
-#define ENOENT 2
-#define ESRCH 3
-#define EINTR 4
-#define EIO 5
-#define ENXIO 6
-#define E2BIG 7
-#define ENOEXEC 8
-#define EBADF 9
-#define ECHILD 10
-#define EAGAIN 11
-#define ENOMEM 12
-#define EACCES 13
-#define EFAULT 14
-#define EBUSY 16
-#define EEXIST 17
-#define EXDEV 18
-#define ENODEV 19
-#define ENOTDIR 20
-#define EISDIR 21
-#define EINVAL 22
-#define ENFILE 23
-#define EMFILE 24
-#define ENOTTY 25
-#define ETXTBSY 26
-#define EFBIG 27
-#define ENOSPC 28
-#define ESPIPE 29
-#define EROFS 30
-#define EMLINK 31
-#define EPIPE 32
-
-#endif
diff --git a/rt/include/libc/inttypes.h b/rt/include/libc/inttypes.h
@@ -1,88 +0,0 @@
-/* inttypes.h -- C11 7.8 -- Format conversion of integer types */
-#ifndef CFREE_LIBC_INTTYPES_H
-#define CFREE_LIBC_INTTYPES_H
-
-#include <stdint.h>
-
-typedef struct { intmax_t quot, rem; } imaxdiv_t;
-
-/* Width macros: cfree assumes int == 32-bit, long long == 64-bit, and
- * intmax_t == long long. On LP64 targets long is also 64-bit; we pick the
- * longer "ll" form for 64-bit conversions to keep the macros portable
- * across LP64 and LLP64. */
-
-#define PRId8 "d"
-#define PRId16 "d"
-#define PRId32 "d"
-#define PRId64 "lld"
-
-#define PRIi8 "i"
-#define PRIi16 "i"
-#define PRIi32 "i"
-#define PRIi64 "lli"
-
-#define PRIo8 "o"
-#define PRIo16 "o"
-#define PRIo32 "o"
-#define PRIo64 "llo"
-
-#define PRIu8 "u"
-#define PRIu16 "u"
-#define PRIu32 "u"
-#define PRIu64 "llu"
-
-#define PRIx8 "x"
-#define PRIx16 "x"
-#define PRIx32 "x"
-#define PRIx64 "llx"
-
-#define PRIX8 "X"
-#define PRIX16 "X"
-#define PRIX32 "X"
-#define PRIX64 "llX"
-
-#define PRIdMAX "lld"
-#define PRIiMAX "lli"
-#define PRIoMAX "llo"
-#define PRIuMAX "llu"
-#define PRIxMAX "llx"
-#define PRIXMAX "llX"
-
-#define PRIdPTR "ld"
-#define PRIiPTR "li"
-#define PRIoPTR "lo"
-#define PRIuPTR "lu"
-#define PRIxPTR "lx"
-#define PRIXPTR "lX"
-
-#define SCNd8 "hhd"
-#define SCNd16 "hd"
-#define SCNd32 "d"
-#define SCNd64 "lld"
-
-#define SCNi8 "hhi"
-#define SCNi16 "hi"
-#define SCNi32 "i"
-#define SCNi64 "lli"
-
-#define SCNo8 "hho"
-#define SCNo16 "ho"
-#define SCNo32 "o"
-#define SCNo64 "llo"
-
-#define SCNu8 "hhu"
-#define SCNu16 "hu"
-#define SCNu32 "u"
-#define SCNu64 "llu"
-
-#define SCNx8 "hhx"
-#define SCNx16 "hx"
-#define SCNx32 "x"
-#define SCNx64 "llx"
-
-intmax_t imaxabs(intmax_t j);
-imaxdiv_t imaxdiv(intmax_t numer, intmax_t denom);
-intmax_t strtoimax(const char *nptr, char **endptr, int base);
-uintmax_t strtoumax(const char *nptr, char **endptr, int base);
-
-#endif
diff --git a/rt/include/libc/locale.h b/rt/include/libc/locale.h
@@ -1,42 +0,0 @@
-/* locale.h -- C11 7.11 -- Localization */
-#ifndef CFREE_LIBC_LOCALE_H
-#define CFREE_LIBC_LOCALE_H
-
-#define LC_ALL 0
-#define LC_COLLATE 1
-#define LC_CTYPE 2
-#define LC_MONETARY 3
-#define LC_NUMERIC 4
-#define LC_TIME 5
-
-struct lconv {
- char *decimal_point;
- char *thousands_sep;
- char *grouping;
- char *mon_decimal_point;
- char *mon_thousands_sep;
- char *mon_grouping;
- char *positive_sign;
- char *negative_sign;
- char *currency_symbol;
- char frac_digits;
- char p_cs_precedes;
- char n_cs_precedes;
- char p_sep_by_space;
- char n_sep_by_space;
- char p_sign_posn;
- char n_sign_posn;
- char *int_curr_symbol;
- char int_frac_digits;
- char int_p_cs_precedes;
- char int_n_cs_precedes;
- char int_p_sep_by_space;
- char int_n_sep_by_space;
- char int_p_sign_posn;
- char int_n_sign_posn;
-};
-
-char *setlocale(int category, const char *locale);
-struct lconv *localeconv(void);
-
-#endif
diff --git a/rt/include/libc/math.h b/rt/include/libc/math.h
@@ -1,132 +0,0 @@
-/* math.h -- C11 7.12 -- Mathematics
- *
- * INFINITY/NAN are defined as quotient expressions; the standard requires
- * them to be representable as float and to evaluate to +inf / NaN under
- * IEC 60559, which both forms do. They are not strict constant expressions
- * in pedantic C11, but every conforming runtime accepts them. */
-#ifndef CFREE_LIBC_MATH_H
-#define CFREE_LIBC_MATH_H
-
-typedef float float_t;
-typedef double double_t;
-
-#define HUGE_VAL (1.0 / 0.0)
-#define HUGE_VALF (1.0f / 0.0f)
-#define HUGE_VALL (1.0L / 0.0L)
-#define INFINITY (1.0f / 0.0f)
-#define NAN (0.0f / 0.0f)
-
-#define FP_INFINITE 1
-#define FP_NAN 2
-#define FP_NORMAL 4
-#define FP_SUBNORMAL 3
-#define FP_ZERO 0
-
-#define MATH_ERRNO 1
-#define MATH_ERREXCEPT 2
-
-#define M_E 2.71828182845904523536
-#define M_LOG2E 1.44269504088896340736
-#define M_LOG10E 0.43429448190325182765
-#define M_LN2 0.69314718055994530942
-#define M_LN10 2.30258509299404568402
-#define M_PI 3.14159265358979323846
-#define M_PI_2 1.57079632679489661923
-#define M_PI_4 0.78539816339744830962
-#define M_SQRT2 1.41421356237309504880
-#define M_SQRT1_2 0.70710678118654752440
-
-/* Trigonometric */
-double acos(double x);
-double asin(double x);
-double atan(double x);
-double atan2(double y, double x);
-double cos(double x);
-double sin(double x);
-double tan(double x);
-
-float acosf(float x);
-float asinf(float x);
-float atanf(float x);
-float atan2f(float y, float x);
-float cosf(float x);
-float sinf(float x);
-float tanf(float x);
-
-/* Hyperbolic */
-double acosh(double x);
-double asinh(double x);
-double atanh(double x);
-double cosh(double x);
-double sinh(double x);
-double tanh(double x);
-
-/* Exponential and logarithmic */
-double exp(double x);
-double exp2(double x);
-double expm1(double x);
-double log(double x);
-double log10(double x);
-double log1p(double x);
-double log2(double x);
-double frexp(double x, int *exp);
-double ldexp(double x, int exp);
-double modf(double x, double *iptr);
-double scalbn(double x, int n);
-
-float expf(float x);
-float logf(float x);
-float log2f(float x);
-float log10f(float x);
-
-/* Power and absolute value */
-double cbrt(double x);
-double fabs(double x);
-double hypot(double x, double y);
-double pow(double x, double y);
-double sqrt(double x);
-
-float fabsf(float x);
-float powf(float x, float y);
-float sqrtf(float x);
-
-/* Error and gamma */
-double erf(double x);
-double erfc(double x);
-double lgamma(double x);
-double tgamma(double x);
-
-/* Nearest integer */
-double ceil(double x);
-double floor(double x);
-double nearbyint(double x);
-double rint(double x);
-double round(double x);
-double trunc(double x);
-long lrint(double x);
-long lround(double x);
-long long llrint(double x);
-long long llround(double x);
-
-float ceilf(float x);
-float floorf(float x);
-float roundf(float x);
-float truncf(float x);
-
-/* Remainder */
-double fmod(double x, double y);
-double remainder(double x, double y);
-
-/* Manipulation / classification */
-double copysign(double x, double y);
-double nan(const char *tagp);
-double nextafter(double x, double y);
-int fpclassify(double x);
-
-/* Maximum, minimum, positive difference, fused multiply-add */
-double fdim(double x, double y);
-double fmax(double x, double y);
-double fmin(double x, double y);
-double fma(double x, double y, double z);
-
-#endif
diff --git a/rt/include/libc/signal.h b/rt/include/libc/signal.h
@@ -1,38 +0,0 @@
-/* signal.h -- C11 7.14 -- Signal handling
- *
- * Signal numbers below follow the historical Unix layout (1..15 + SIGTERM)
- * which both Darwin and Linux honour for the C11-mandated names. */
-#ifndef CFREE_LIBC_SIGNAL_H
-#define CFREE_LIBC_SIGNAL_H
-
-typedef int sig_atomic_t;
-
-typedef void (*__sighandler_t)(int);
-
-#define SIG_DFL ((__sighandler_t)0)
-#define SIG_ERR ((__sighandler_t)-1)
-#define SIG_IGN ((__sighandler_t)1)
-
-/* C11-mandated signal names */
-#define SIGABRT 6
-#define SIGFPE 8
-#define SIGILL 4
-#define SIGINT 2
-#define SIGSEGV 11
-#define SIGTERM 15
-
-/* Common POSIX additions */
-#define SIGHUP 1
-#define SIGQUIT 3
-#define SIGTRAP 5
-#define SIGKILL 9
-#define SIGBUS 10
-#define SIGPIPE 13
-#define SIGALRM 14
-#define SIGUSR1 30
-#define SIGUSR2 31
-
-int raise(int sig);
-__sighandler_t signal(int sig, __sighandler_t func);
-
-#endif
diff --git a/rt/include/libc/stdio.h b/rt/include/libc/stdio.h
@@ -1,107 +0,0 @@
-/* stdio.h -- C11 7.21 -- Input/output
- *
- * Declarations only. Bodies are provided by the platform's libc at link
- * time (e.g. libSystem.B.dylib on macOS, libc.so on Linux). FILE is opaque:
- * programs may only hold FILE* and pass it back to the runtime. */
-#ifndef CFREE_LIBC_STDIO_H
-#define CFREE_LIBC_STDIO_H
-
-#include <stdarg.h>
-#include <stddef.h>
-
-typedef struct FILE FILE;
-typedef long fpos_t; /* opaque enough for ftell-equivalents */
-
-#define EOF (-1)
-#define BUFSIZ 1024
-
-#define SEEK_SET 0
-#define SEEK_CUR 1
-#define SEEK_END 2
-
-#define FILENAME_MAX 4096
-#define FOPEN_MAX 16
-#define TMP_MAX 32
-#define L_tmpnam 32
-
-#define _IOFBF 0
-#define _IOLBF 1
-#define _IONBF 2
-
-/* stdin / stdout / stderr are platform-divergent under the hood: macOS's
- * libSystem exports `__stdinp` / `__stdoutp` / `__stderrp`, while glibc /
- * musl export `stdin` / `stdout` / `stderr` as data globals (frequently
- * TLS). The libcfree_hosted shim per platform provides these accessors;
- * the platform-specific aliasing stays out of the header so this file
- * remains ABI-neutral. */
-extern FILE *__cfree_stdin(void);
-extern FILE *__cfree_stdout(void);
-extern FILE *__cfree_stderr(void);
-#define stdin (__cfree_stdin())
-#define stdout (__cfree_stdout())
-#define stderr (__cfree_stderr())
-
-/* Operations on files */
-int remove(const char *filename);
-int rename(const char *old_, const char *new_);
-FILE *tmpfile(void);
-char *tmpnam(char *s);
-
-/* File access */
-int fclose(FILE *stream);
-int fflush(FILE *stream);
-FILE *fopen(const char *filename, const char *mode);
-FILE *freopen(const char *filename, const char *mode,
- FILE *stream);
-void setbuf(FILE *stream, char *buf);
-int setvbuf(FILE *stream, char *buf, int mode, size_t size);
-
-/* Formatted input/output */
-int fprintf(FILE *stream, const char *fmt, ...);
-int fscanf(FILE *stream, const char *fmt, ...);
-int printf(const char *fmt, ...);
-int scanf(const char *fmt, ...);
-int snprintf(char *s, size_t n, const char *fmt, ...);
-int sprintf(char *s, const char *fmt, ...);
-int sscanf(const char *s, const char *fmt, ...);
-int vfprintf(FILE *stream, const char *fmt, va_list ap);
-int vfscanf(FILE *stream, const char *fmt, va_list ap);
-int vprintf(const char *fmt, va_list ap);
-int vscanf(const char *fmt, va_list ap);
-int vsnprintf(char *s, size_t n, const char *fmt,
- va_list ap);
-int vsprintf(char *s, const char *fmt, va_list ap);
-int vsscanf(const char *s, const char *fmt, va_list ap);
-
-/* Character input/output */
-int fgetc(FILE *stream);
-char *fgets(char *s, int n, FILE *stream);
-int fputc(int c, FILE *stream);
-int fputs(const char *s, FILE *stream);
-int getc(FILE *stream);
-int getchar(void);
-int putc(int c, FILE *stream);
-int putchar(int c);
-int puts(const char *s);
-int ungetc(int c, FILE *stream);
-
-/* Direct input/output */
-size_t fread(void *ptr, size_t size, size_t nmemb,
- FILE *stream);
-size_t fwrite(const void *ptr, size_t size, size_t nmemb,
- FILE *stream);
-
-/* File positioning */
-int fgetpos(FILE *stream, fpos_t *pos);
-int fseek(FILE *stream, long offset, int whence);
-int fsetpos(FILE *stream, const fpos_t *pos);
-long ftell(FILE *stream);
-void rewind(FILE *stream);
-
-/* Error-handling */
-void clearerr(FILE *stream);
-int feof(FILE *stream);
-int ferror(FILE *stream);
-void perror(const char *s);
-
-#endif
diff --git a/rt/include/libc/stdlib.h b/rt/include/libc/stdlib.h
@@ -1,73 +0,0 @@
-/* stdlib.h -- C11 7.22 -- General utilities */
-#ifndef CFREE_LIBC_STDLIB_H
-#define CFREE_LIBC_STDLIB_H
-
-#include <stddef.h>
-
-#define EXIT_SUCCESS 0
-#define EXIT_FAILURE 1
-#define RAND_MAX 0x7fffffff
-#define MB_CUR_MAX 4
-
-typedef struct { int quot, rem; } div_t;
-typedef struct { long quot, rem; } ldiv_t;
-typedef struct { long long quot, rem; } lldiv_t;
-
-/* Numeric conversion */
-double atof(const char *nptr);
-int atoi(const char *nptr);
-long atol(const char *nptr);
-long long atoll(const char *nptr);
-double strtod(const char *nptr, char **endptr);
-float strtof(const char *nptr, char **endptr);
-long double strtold(const char *nptr, char **endptr);
-long strtol(const char *nptr, char **endptr, int base);
-long long strtoll(const char *nptr, char **endptr, int base);
-unsigned long strtoul(const char *nptr, char **endptr,
- int base);
-unsigned long long strtoull(const char *nptr, char **endptr,
- int base);
-
-/* Pseudo-random */
-int rand(void);
-void srand(unsigned seed);
-
-/* Memory management */
-void *aligned_alloc(size_t alignment, size_t size);
-void *calloc(size_t nmemb, size_t size);
-void free(void *ptr);
-void *malloc(size_t size);
-void *realloc(void *ptr, size_t size);
-
-/* Program environment */
-void abort(void);
-int atexit(void (*func)(void));
-int at_quick_exit(void (*func)(void));
-void exit(int status);
-void _Exit(int status);
-void quick_exit(int status);
-char *getenv(const char *name);
-int system(const char *command);
-
-/* Searching and sorting */
-void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *));
-void qsort(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *));
-
-/* Integer arithmetic */
-int abs(int j);
-long labs(long j);
-long long llabs(long long j);
-div_t div(int numer, int denom);
-ldiv_t ldiv(long numer, long denom);
-lldiv_t lldiv(long long numer, long long denom);
-
-/* Multibyte (declarations only; cfree treats wchar_t as platform width) */
-int mblen(const char *s, size_t n);
-int mbtowc(wchar_t *pwc, const char *s, size_t n);
-int wctomb(char *s, wchar_t wc);
-size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n);
-size_t wcstombs(char *s, const wchar_t *pwcs, size_t n);
-
-#endif
diff --git a/rt/include/libc/string.h b/rt/include/libc/string.h
@@ -1,39 +0,0 @@
-/* string.h -- C11 7.24 -- String handling */
-#ifndef CFREE_LIBC_STRING_H
-#define CFREE_LIBC_STRING_H
-
-#include <stddef.h>
-
-/* Copying */
-void *memcpy(void *dest, const void *src, size_t n);
-void *memmove(void *dest, const void *src, size_t n);
-char *strcpy(char *dest, const char *src);
-char *strncpy(char *dest, const char *src, size_t n);
-
-/* Concatenation */
-char *strcat(char *dest, const char *src);
-char *strncat(char *dest, const char *src, size_t n);
-
-/* Comparison */
-int memcmp(const void *s1, const void *s2, size_t n);
-int strcmp(const char *s1, const char *s2);
-int strcoll(const char *s1, const char *s2);
-int strncmp(const char *s1, const char *s2, size_t n);
-size_t strxfrm(char *dest, const char *src, size_t n);
-
-/* Search */
-void *memchr(const void *s, int c, size_t n);
-char *strchr(const char *s, int c);
-size_t strcspn(const char *s, const char *reject);
-char *strpbrk(const char *s, const char *accept);
-char *strrchr(const char *s, int c);
-size_t strspn(const char *s, const char *accept);
-char *strstr(const char *haystack, const char *needle);
-char *strtok(char *s, const char *delim);
-
-/* Miscellaneous */
-void *memset(void *s, int c, size_t n);
-char *strerror(int errnum);
-size_t strlen(const char *s);
-
-#endif
diff --git a/rt/include/libc/time.h b/rt/include/libc/time.h
@@ -1,46 +0,0 @@
-/* time.h -- C11 7.27 -- Date and time
- *
- * time_t and clock_t are integer types of unspecified width. We choose
- * long for both, matching every modern POSIX/Darwin platform. */
-#ifndef CFREE_LIBC_TIME_H
-#define CFREE_LIBC_TIME_H
-
-#include <stddef.h>
-
-typedef long time_t;
-typedef long clock_t;
-
-struct timespec {
- time_t tv_sec;
- long tv_nsec;
-};
-
-struct tm {
- int tm_sec;
- int tm_min;
- int tm_hour;
- int tm_mday;
- int tm_mon;
- int tm_year;
- int tm_wday;
- int tm_yday;
- int tm_isdst;
-};
-
-#define CLOCKS_PER_SEC 1000000L
-#define TIME_UTC 1
-
-clock_t clock(void);
-double difftime(time_t end, time_t beginning);
-time_t mktime(struct tm *tp);
-time_t time(time_t *arg);
-int timespec_get(struct timespec *ts, int base);
-
-char *asctime(const struct tm *tp);
-char *ctime(const time_t *timer);
-struct tm *gmtime(const time_t *timer);
-struct tm *localtime(const time_t *timer);
-size_t strftime(char *s, size_t maxsize, const char *format,
- const struct tm *tp);
-
-#endif
diff --git a/rt/lib/cfree_hosted/linux.c b/rt/lib/cfree_hosted/linux.c
@@ -1,33 +0,0 @@
-/* libcfree_hosted -- Linux shim (glibc + musl)
- *
- * Bridges the ABI-neutral names declared in rt/include/libc/ to the
- * actual symbols glibc / musl export. Compiled by cfree per Linux
- * arch into build/cfree_hosted/linux-<arch>.o; programs link it in
- * alongside -lc (and crt files) when they include any of the libc/
- * headers that name a platform-divergent symbol.
- *
- * On Linux both common libcs agree on these names: stdin/stdout/stderr
- * are FILE* data globals (TLS-backed in glibc, plain data in musl),
- * and errno is reached via __errno_location(). The single source file
- * covers both. */
-
-/* FILE is opaque in libc/stdio.h. Use the same incomplete-struct form
- * here so the function signatures line up; we never dereference. */
-struct FILE;
-
-/* glibc/musl-exported globals: declared as data, not functions. */
-extern struct FILE *stdin;
-extern struct FILE *stdout;
-extern struct FILE *stderr;
-
-struct FILE *__cfree_stdin(void) { return stdin; }
-struct FILE *__cfree_stdout(void) { return stdout; }
-struct FILE *__cfree_stderr(void) { return stderr; }
-
-/* glibc and musl both expose `int *__errno_location(void)` returning a
- * thread-local int*. Darwin uses `__error()` with the same shape;
- * the rt/include/libc/errno.h header routes through the cfree-named
- * accessor so platform divergence stays in this file. */
-extern int *__errno_location(void);
-
-int *__cfree_errno_location(void) { return __errno_location(); }
diff --git a/rt/lib/cfree_hosted/macos.c b/rt/lib/cfree_hosted/macos.c
@@ -1,34 +0,0 @@
-/* libcfree_hosted -- macOS shim
- *
- * Bridges the ABI-neutral names declared in rt/include/libc/ to the
- * actual symbols libSystem.B.dylib exports on Darwin. cfree compiles
- * this file; the resulting object goes into libcfree_hosted_macos.a,
- * which programs link in alongside -lSystem when they include any of
- * the libc/ headers that name a platform-divergent symbol.
- *
- * The set is intentionally tiny -- only the symbols whose ABI names
- * differ between Darwin and the Linux libcs. Functions like printf,
- * malloc, memcpy share names everywhere and need no shim. */
-
-/* FILE is opaque in libc/stdio.h. Use the same incomplete-struct form
- * here so the function signatures line up; we never dereference. */
-struct FILE;
-
-/* libSystem-exported globals on Darwin. Apple's <stdio.h> wraps these
- * behind `#define stdin __stdinp` macros for the same reason this shim
- * exists -- the on-disk symbol names diverged from the C-source names
- * and never converged back. */
-extern struct FILE *__stdinp;
-extern struct FILE *__stdoutp;
-extern struct FILE *__stderrp;
-
-struct FILE *__cfree_stdin(void) { return __stdinp; }
-struct FILE *__cfree_stdout(void) { return __stdoutp; }
-struct FILE *__cfree_stderr(void) { return __stderrp; }
-
-/* On Darwin, errno is reached via __error() returning a thread-local int*.
- * Linux/glibc/musl use __errno_location() with the same shape; the shim
- * keeps the accessor name uniform. */
-extern int *__error(void);
-
-int *__cfree_errno_location(void) { return __error(); }
diff --git a/test/driver/run.sh b/test/driver/run.sh
@@ -237,101 +237,6 @@ else
fail=$((fail + 1))
fi
-if "$CFREE" cc -target x86_64-linux --sysroot "$work/no-such-sysroot" \
- "$work/main.c" -o "$work/no-lc-sysroot" \
- > "$work/no-lc-sysroot.out" 2> "$work/no-lc-sysroot.err"; then
- printf 'PASS %s\n' "cc-no-lc-does-not-expand-hosted"
- pass=$((pass + 1))
-else
- printf 'FAIL %s (cfree cc failed)\n' "cc-no-lc-does-not-expand-hosted"
- sed 's/^/ | /' "$work/no-lc-sysroot.err"
- fail=$((fail + 1))
-fi
-
-if ! "$CFREE" cc -target x86_64-linux "$work/main.c" -lc \
- -o "$work/hosted-no-sysroot" \
- > "$work/hosted-no-sysroot.out" 2> "$work/hosted-no-sysroot.err" &&
- grep -q "Linux hosted profile requires --sysroot" \
- "$work/hosted-no-sysroot.err"; then
- printf 'PASS %s\n' "cc-lc-requires-explicit-sysroot"
- pass=$((pass + 1))
-else
- printf 'FAIL %s (missing hosted sysroot diagnostic)\n' \
- "cc-lc-requires-explicit-sysroot"
- sed 's/^/ | /' "$work/hosted-no-sysroot.err"
- fail=$((fail + 1))
-fi
-
-mkdir -p "$work/musl-sysroot/lib" "$work/support/rt/include/libc" \
- "$work/support/rt/lib"
-: > "$work/musl-sysroot/lib/crt1.o"
-: > "$work/musl-sysroot/lib/crti.o"
-: > "$work/musl-sysroot/lib/crtn.o"
-: > "$work/musl-sysroot/lib/libc.a"
-if ! "$CFREE" cc -target x86_64-linux --sysroot "$work/musl-sysroot" \
- --support-dir "$work/support" "$work/main.c" -lc \
- -o "$work/hosted-missing-shim" \
- > "$work/hosted-missing-shim.out" 2> "$work/hosted-missing-shim.err" &&
- grep -q "hosted profile missing required file: .*linux-x64.o" \
- "$work/hosted-missing-shim.err"; then
- printf 'PASS %s\n' "cc-lc-reports-missing-hosted-shim"
- pass=$((pass + 1))
-else
- printf 'FAIL %s (missing hosted shim diagnostic)\n' \
- "cc-lc-reports-missing-hosted-shim"
- sed 's/^/ | /' "$work/hosted-missing-shim.err"
- fail=$((fail + 1))
-fi
-
-macos_sdk=""
-if [ -n "${SDKROOT:-}" ] && [ -f "$SDKROOT/usr/lib/libSystem.tbd" ]; then
- macos_sdk=$SDKROOT
-elif command -v xcrun >/dev/null 2>&1; then
- sdk=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || true)
- if [ -n "$sdk" ] && [ -f "$sdk/usr/lib/libSystem.tbd" ]; then
- macos_sdk=$sdk
- fi
-fi
-if [ -n "$macos_sdk" ] && [ -f "$repo_root/build/libcfree_hosted_macos.a" ]; then
- cat > "$work/hosted-macos.c" <<'SRC'
-#include <stdio.h>
-int main(void) { return stdin == 0 || fflush(0) != 0; }
-SRC
- if "$CFREE" cc -target aarch64-darwin --sysroot "$macos_sdk" \
- --support-dir "$repo_root" "$work/hosted-macos.c" -lc \
- -o "$work/hosted-macos" \
- > "$work/hosted-macos.out" 2> "$work/hosted-macos.err"; then
- printf 'PASS %s\n' "cc-lc-macos-requires-libSystem"
- pass=$((pass + 1))
- else
- printf 'FAIL %s (cfree cc failed)\n' \
- "cc-lc-macos-requires-libSystem"
- sed 's/^/ | /' "$work/hosted-macos.err"
- fail=$((fail + 1))
- fi
-fi
-
-cat > "$work/run-errno.c" <<'SRC'
-#include <errno.h>
-int main(void) {
- errno = 42;
- return errno;
-}
-SRC
-
-"$CFREE" run -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
- "$work/run-errno.c" > "$work/run-errno.out" 2> "$work/run-errno.err"
-run_errno_status=$?
-if [ "$run_errno_status" -eq 42 ]; then
- printf 'PASS %s\n' "run-hosted-errno-shim"
- pass=$((pass + 1))
-else
- printf 'FAIL %s (status %s, want 42)\n' \
- "run-hosted-errno-shim" "$run_errno_status"
- sed 's/^/ | /' "$work/run-errno.err"
- fail=$((fail + 1))
-fi
-
cat > "$work/run-main.c" <<'SRC'
int add42(int);
int main(void) { return add42(0); }
@@ -340,12 +245,12 @@ cat > "$work/run-lib.c" <<'SRC'
int add42(int x) { return x + 42; }
SRC
-if "$CFREE" cc -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
+if "$CFREE" cc -I"$repo_root/rt/include" \
-c "$work/run-lib.c" -o "$work/run-lib.o" \
> "$work/run-lib.out" 2> "$work/run-lib.err" &&
"$CFREE" ar rcs "$work/librun.a" "$work/run-lib.o" \
> "$work/run-ar.out" 2> "$work/run-ar.err"; then
- "$CFREE" run -I"$repo_root/rt/include" -I"$repo_root/rt/include/libc" \
+ "$CFREE" run -I"$repo_root/rt/include" \
"$work/run-main.c" "$work/librun.a" \
> "$work/run-archive.out" 2> "$work/run-archive.err"
run_status=$?
diff --git a/test/libc/glibc/Containerfile b/test/libc/glibc/Containerfile
@@ -39,7 +39,7 @@ FROM docker.io/arm64v8/debian:bookworm-slim
# / TF / 128-bit-int helpers come from our own rt/ build.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
- libc6-dev=2.36-9+deb12u13 \
+ libc6-dev \
linux-libc-dev \
&& rm -rf /var/lib/apt/lists/*
diff --git a/test/libc/glibc/Containerfile.x64 b/test/libc/glibc/Containerfile.x64
@@ -11,7 +11,7 @@ FROM docker.io/amd64/debian:bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
- libc6-dev=2.36-9+deb12u13 \
+ libc6-dev \
linux-libc-dev \
&& rm -rf /var/lib/apt/lists/*
diff --git a/test/libc/glibc/run.sh b/test/libc/glibc/run.sh
@@ -139,28 +139,24 @@ run_case() {
# ---- compile ----
# Three -isystem layers, in order of precedence:
- # rt/include/ — cfree's own freestanding
- # headers (stddef.h,
- # stdarg.h, stdint.h, ...).
- # Compiler-defined; libc
- # does not ship these.
- # glibc's unistd.h /
- # stdio.h #include <stddef.h>
- # for size_t, so the
- # freestanding set must be
- # reachable.
# sysroot/include/ — glibc + linux-libc-dev
# headers (top-level uapi).
# sysroot/include/aarch64-linux-gnu — glibc multi-arch (bits/*,
# gnu/stubs-lp64.h, ...);
# <features.h> reaches in.
- # -nostdinc strips the host's default include path so clang's
- # resource dir is not consulted.
+ # rt/include/ — cfree's freestanding overlay
+ # (stddef.h, stdarg.h, stdint.h).
+ # glibc's stdio.h #include
+ # <stddef.h> for size_t; glibc
+ # doesn't ship compiler headers
+ # so rt/include must be reachable.
+ # -nostdinc strips clang's default include path so cross targets
+ # don't accidentally pick up the host's compiler headers.
local cc_flags=(--target=aarch64-linux-gnu --sysroot="$SYSROOT"
-nostdinc
- -isystem "$ROOT/rt/include"
-isystem "$SYSROOT/include"
-isystem "$SYSROOT/include/aarch64-linux-gnu"
+ -isystem "$ROOT/rt/include"
-fPIE -fpic -O0)
local obj="$work/${name}.o"
diff --git a/test/libc/musl/Containerfile b/test/libc/musl/Containerfile
@@ -17,7 +17,7 @@ FROM docker.io/arm64v8/alpine:3.20.10
# Note: we deliberately do NOT pull clang's compiler-rt or libgcc — the
# soft-float / TF / 128-bit-int helpers (__extenddftf2 etc.) come from
# our own rt/ build (rt/build/aarch64-linux/libcfree_rt.a).
-RUN apk add --no-cache musl-dev=1.2.5-r3 linux-headers
+RUN apk add --no-cache musl-dev linux-headers
# Stage the artifacts the linker needs into one tree so the host extract
# is a single tar pipe.
diff --git a/test/libc/musl/Containerfile.x64 b/test/libc/musl/Containerfile.x64
@@ -9,7 +9,7 @@
# libc.so so cfree ld can link against it as a shared input).
FROM docker.io/amd64/alpine:3.20.10
-RUN apk add --no-cache musl-dev=1.2.5-r3 linux-headers
+RUN apk add --no-cache musl-dev linux-headers
RUN mkdir -p /sysroot/lib /sysroot/include \
&& cp /usr/lib/crt1.o /sysroot/lib/ \
diff --git a/test/libc/musl/run.sh b/test/libc/musl/run.sh
@@ -84,11 +84,19 @@ run_aarch64() {
"$QEMU_BIN" "$exe" >"$out" 2>"$err"; RUN_RC=$?; return
fi
if [ $have_podman -eq 1 ]; then
- local dir base platform_flag=()
+ local dir base
dir="$(cd "$(dirname "$exe")" && pwd)"; base="$(basename "$exe")"
- [ $is_aarch64 -eq 0 ] && platform_flag=(--platform linux/arm64)
- podman run --rm "${platform_flag[@]}" --net=none \
- -v "$dir":/work:Z -w /work alpine:latest "./$base" >"$out" 2>"$err"
+ # Pin the image name to the arm64-specific repo
+ # (docker.io/arm64v8/...) instead of the multi-arch alpine:latest.
+ # Avoids the cached-wrong-arch-manifest trap that bare alpine:latest
+ # hits when an unrelated pull cached a different arch; also avoids
+ # --platform, which would force a registry manifest lookup on every
+ # run. arm64v8/alpine ships the musl loader at /lib/ld-musl-aarch64.so.1
+ # so the dynamic variant resolves PT_INTERP without extra mounts.
+ podman run --rm --net=none \
+ -v "$dir":/work:Z -w /work \
+ docker.io/arm64v8/alpine:latest "./$base" \
+ >"$out" 2>"$err"
RUN_RC=$?; return
fi
RUN_RC=127
@@ -113,15 +121,11 @@ run_case() {
fi
# ---- compile ----
- # Two -isystem layers, in order of precedence:
- # rt/include/ — cfree's own freestanding headers
- # (stddef.h, stdarg.h, stdint.h, ...).
- # sysroot/include/ — musl + linux-headers tree.
- # -nostdinc strips the host's default include path so clang's
- # resource dir is not consulted.
+ # -nostdinc strips clang's default include path (resource dir +
+ # /usr/include) so the sysroot's musl + linux-headers tree is the
+ # sole source. -isystem $SYSROOT/include picks it up.
local cc_flags=(--target=aarch64-linux-musl --sysroot="$SYSROOT"
-nostdinc
- -isystem "$ROOT/rt/include"
-isystem "$SYSROOT/include"
-O0)
case "$variant" in
diff --git a/test/libc/run.sh b/test/libc/run.sh
@@ -1,518 +0,0 @@
-#!/usr/bin/env bash
-# test/libc/run.sh -- exercise cfree's rt/include/libc headers + the
-# per-OS libcfree_hosted shim. Each case is compiled by cfree-cc against
-# the libc header set, linked against the platform's C library plus
-# the hosted shim, and executed.
-#
-# Cells:
-# Darwin host -> one cell (darwin), runs natively on the host.
-# Linux -> 9 cells = {x64, aarch64, rv64}
-# x {musl-static, musl-dynamic, glibc-dynamic}.
-# Each cell cross-compiles + cross-links with cfree on
-# the host against the matching sysroot under build/,
-# then execs the produced ELF inside podman (or under
-# qemu-user when available) in alpine (musl) or
-# debian (glibc).
-#
-# musl gets both static and dynamic because musl is happy in either
-# shape and the two link paths exercise different cfree-ld surfaces.
-# glibc is dynamic-only — static-glibc is officially discouraged and
-# small-program-only, and isn't a real-world deployment shape.
-#
-# Each case file may carry:
-# <name>.expected -- numeric exit code, default 0
-# <name>.stdout -- exact-substring match against captured stdout
-#
-# Cases that aren't part of the host-libc surface (raw-syscall,
-# unistd.h) are filtered out by name. Run with CFREE_LIBC_KEEP=1 to
-# leave intermediates in build/libc/<cell>/<case>/.
-#
-# Environment knobs:
-# CFREE_LIBC_KEEP=1 keep build dir after run
-# CFREE_LIBC_CELLS=... comma-separated cell allowlist (default: all
-# cells for the detected host).
-
-set -u
-
-ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
-CASES_DIR="$ROOT/test/libc/cases"
-BUILD_ROOT="$ROOT/build/libc"
-CFREE="$ROOT/build/cfree"
-
-color_red() { printf '\033[31m%s\033[0m' "$1"; }
-color_grn() { printf '\033[32m%s\033[0m' "$1"; }
-color_yel() { printf '\033[33m%s\033[0m' "$1"; }
-
-note_skip() { printf ' %s %s -- %s\n' "$(color_yel SKIP)" "$1" "$2"; }
-note_fail() { printf ' %s %s\n' "$(color_red FAIL)" "$1"; }
-note_pass() { printf ' %s %s\n' "$(color_grn PASS)" "$1"; }
-
-if [ ! -x "$CFREE" ]; then
- printf 'cfree driver missing at %s -- run `make` first\n' "$CFREE" >&2
- exit 2
-fi
-
-# Cases shared with test-musl/test-glibc; filter to the subset that
-# exercises rt/include/libc surface (we don't ship raw-syscall or
-# unistd.h headers, so 01/02 are out).
-case_supported() {
- case "$1" in
- 03_printf_hello|10_*|11_*|12_*|13_*|14_*|15_*) return 0 ;;
- *) return 1 ;;
- esac
-}
-
-# ----- per-host cell selection ----------------------------------------------
-#
-# A cell is identified by a tag like "darwin" or "aarch64-musl-dynamic".
-# Each cell is configured by configure_cell into a set of shell globals
-# (TRIPLE, SYSROOT, HOSTED_OBJ, CFREE_RT, LINK_VARIANT, EXEC_TAG, ...)
-# consumed by run_one_case below.
-
-uname_s="$(uname -s 2>/dev/null || echo unknown)"
-case "$uname_s" in
- Darwin)
- HOST_CELLS=(darwin)
- ;;
- Linux)
- HOST_CELLS=(
- x64-musl-static x64-musl-dynamic x64-glibc-dynamic
- aarch64-musl-static aarch64-musl-dynamic aarch64-glibc-dynamic
- rv64-musl-static rv64-musl-dynamic rv64-glibc-dynamic
- )
- ;;
- *)
- note_skip "all" "test-libc not supported on $uname_s"
- exit 0
- ;;
-esac
-
-if [ -n "${CFREE_LIBC_CELLS:-}" ]; then
- IFS=',' read -r -a HOST_CELLS <<<"$CFREE_LIBC_CELLS"
-fi
-
-# ----- runner detection (shared by linux cells) -----------------------------
-#
-# Each cell that runs Linux ELFs needs *some* runner: matching-arch
-# native exec, qemu-user for the target, or podman with the right
-# platform. configure_cell records which is usable, in that priority
-# order.
-
-arch_raw="$(uname -m 2>/dev/null || true)"
-# host_is_linux_arch returns 0 only when the host kernel is Linux AND
-# its native arch matches. Darwin/arm64 cannot exec a Linux aarch64
-# ELF, so native exec is gated on both axes.
-host_is_linux_arch() {
- [ "$uname_s" = "Linux" ] || return 1
- case "$arch_raw" in
- aarch64|arm64) [ "$1" = "aarch64" ] ;;
- x86_64|amd64) [ "$1" = "x64" ] ;;
- riscv64) [ "$1" = "rv64" ] ;;
- *) return 1 ;;
- esac
-}
-
-have_podman=0; command -v podman >/dev/null 2>&1 && have_podman=1
-
-qemu_for_arch() {
- case "$1" in
- aarch64)
- command -v qemu-aarch64-static 2>/dev/null \
- || command -v qemu-aarch64 2>/dev/null \
- || true
- ;;
- x64)
- command -v qemu-x86_64-static 2>/dev/null \
- || command -v qemu-x86_64 2>/dev/null \
- || true
- ;;
- rv64)
- command -v qemu-riscv64-static 2>/dev/null \
- || command -v qemu-riscv64 2>/dev/null \
- || true
- ;;
- esac
-}
-
-# Cell config writes into these globals.
-CELL=""
-LABEL=""
-TRIPLE=""
-SYSROOT=""
-HOSTED_OBJ=""
-CFREE_RT=""
-LINK_VARIANT="" # macos | musl-static | musl-dynamic | glibc-dynamic
-LOADER="" # -dynamic-linker arg, or empty
-EXEC_KIND="" # native | qemu | podman | host-darwin
-QEMU_BIN=""
-PODMAN_PLATFORM=""
-PODMAN_IMAGE=""
-
-# Returns 0 (configured) or 1 (skip + reason in CELL_SKIP_REASON).
-CELL_SKIP_REASON=""
-configure_cell() {
- CELL="$1"
- LABEL="$CELL"
- CELL_SKIP_REASON=""
- TRIPLE=""; SYSROOT=""; HOSTED_OBJ=""; CFREE_RT=""
- LINK_VARIANT=""; LOADER=""; EXEC_KIND=""; QEMU_BIN=""
- PODMAN_PLATFORM=""; PODMAN_IMAGE=""
-
- if [ "$CELL" = "darwin" ]; then
- case "$arch_raw" in
- arm64|aarch64) TRIPLE="aarch64-darwin" ;;
- x86_64) TRIPLE="x86_64-darwin" ;;
- *) CELL_SKIP_REASON="unknown Darwin arch: $arch_raw"; return 1 ;;
- esac
- if ! command -v xcrun >/dev/null 2>&1; then
- CELL_SKIP_REASON="xcrun missing (Xcode CLT required)"; return 1
- fi
- SDK="$(xcrun --show-sdk-path 2>/dev/null || true)"
- if [ -z "$SDK" ] || [ ! -d "$SDK" ]; then
- CELL_SKIP_REASON="xcrun --show-sdk-path failed"; return 1
- fi
- HOSTED_OBJ="$ROOT/build/cfree_hosted/macos.o"
- if [ ! -f "$HOSTED_OBJ" ]; then
- CELL_SKIP_REASON="hosted-macos shim missing at $HOSTED_OBJ (run 'make hosted-macos')"
- return 1
- fi
- LINK_VARIANT="macos"
- EXEC_KIND="host-darwin"
- DARWIN_LDFLAGS=(-L "$SDK/usr/lib")
- DARWIN_LDLIBS=(-lSystem)
- return 0
- fi
-
- # Linux cells: <arch>-<libc>-<variant>
- local arch libc variant
- arch="${CELL%%-*}"
- local rest="${CELL#*-}"
- libc="${rest%%-*}"
- variant="${rest#*-}"
-
- case "$arch" in
- x64) TRIPLE="x86_64-linux" ; PODMAN_PLATFORM="linux/amd64" ;;
- aarch64) TRIPLE="aarch64-linux" ; PODMAN_PLATFORM="linux/arm64" ;;
- rv64) TRIPLE="riscv64-linux" ; PODMAN_PLATFORM="linux/riscv64" ;;
- *) CELL_SKIP_REASON="unknown arch in cell: $arch"; return 1 ;;
- esac
-
- # Sysroot path. aarch64 reuses the bare build/{musl,glibc}-sysroot/
- # tree shared with test-musl/test-glibc; x64/rv64 live under the
- # arch-suffixed dirs that test/libc/{musl,glibc}/extract.sh -a writes.
- case "$libc:$arch" in
- musl:aarch64) SYSROOT="$ROOT/build/musl-sysroot" ;;
- musl:x64) SYSROOT="$ROOT/build/musl-sysroot-x64" ;;
- musl:rv64) SYSROOT="$ROOT/build/musl-sysroot-rv64" ;;
- glibc:aarch64) SYSROOT="$ROOT/build/glibc-sysroot" ;;
- glibc:x64) SYSROOT="$ROOT/build/glibc-sysroot-x64" ;;
- glibc:rv64) SYSROOT="$ROOT/build/glibc-sysroot-rv64" ;;
- *) CELL_SKIP_REASON="unknown libc in cell: $libc"; return 1 ;;
- esac
- if [ ! -f "$SYSROOT/PROVENANCE" ]; then
- CELL_SKIP_REASON="sysroot missing at $SYSROOT (run 'make test-libc' or extract.sh -a $arch)"
- return 1
- fi
-
- # Hosted shim object for this arch (cfree-built ELF .o).
- case "$arch" in
- x64) HOSTED_OBJ="$ROOT/build/cfree_hosted/linux-x64.o" ;;
- aarch64) HOSTED_OBJ="$ROOT/build/cfree_hosted/linux-aarch64.o" ;;
- rv64) HOSTED_OBJ="$ROOT/build/cfree_hosted/linux-rv64.o" ;;
- esac
- if [ ! -f "$HOSTED_OBJ" ]; then
- CELL_SKIP_REASON="hosted-linux shim missing at $HOSTED_OBJ (run 'make hosted-linux')"
- return 1
- fi
-
- # libcfree_rt — soft-float / TF / 128-bit-int helpers. cfree-cc may
- # emit calls into these from long-double printf paths and similar.
- case "$arch" in
- x64) CFREE_RT="$ROOT/rt/build/x86_64-linux/libcfree_rt.a" ;;
- aarch64) CFREE_RT="$ROOT/rt/build/aarch64-linux/libcfree_rt.a" ;;
- rv64) CFREE_RT="$ROOT/rt/build/riscv64-linux/libcfree_rt.a" ;;
- esac
- if [ ! -f "$CFREE_RT" ]; then
- CELL_SKIP_REASON="cfree rt missing at $CFREE_RT (run 'make rt-${TRIPLE%-*}-linux')"
- return 1
- fi
-
- # Link variant + dynamic-linker path for cfree ld.
- case "$libc-$variant" in
- musl-static)
- LINK_VARIANT="musl-static"
- ;;
- musl-dynamic)
- LINK_VARIANT="musl-dynamic"
- case "$arch" in
- x64) LOADER="/lib/ld-musl-x86_64.so.1" ;;
- aarch64) LOADER="/lib/ld-musl-aarch64.so.1" ;;
- rv64) LOADER="/lib/ld-musl-riscv64.so.1" ;;
- esac
- ;;
- glibc-dynamic)
- LINK_VARIANT="glibc-dynamic"
- case "$arch" in
- x64) LOADER="/lib64/ld-linux-x86-64.so.2" ;;
- aarch64) LOADER="/lib/ld-linux-aarch64.so.1" ;;
- rv64) LOADER="/lib/ld-linux-riscv64-lp64d.so.1";;
- esac
- ;;
- *) CELL_SKIP_REASON="unknown libc-variant: $libc-$variant"; return 1 ;;
- esac
-
- # Runner: prefer native exec, then qemu-user, then podman.
- if host_is_linux_arch "$arch"; then
- EXEC_KIND="native"
- else
- QEMU_BIN="$(qemu_for_arch "$arch")"
- if [ -n "$QEMU_BIN" ]; then
- EXEC_KIND="qemu"
- elif [ "$have_podman" -eq 1 ]; then
- EXEC_KIND="podman"
- else
- CELL_SKIP_REASON="no runner for $arch (need native, qemu-$arch, or podman)"
- return 1
- fi
- fi
-
- # Podman image per (arch, libc). Pinning the arch-specific repo
- # (arm64v8/, amd64/, riscv64/) avoids the manifest-lookup detour
- # that --platform triggers on hosts whose podman cache is mixed.
- # Tags are pinned to the same versions the sysroot extract.sh /
- # Containerfile pulls — so the image is guaranteed cached, and
- # --pull=never on `podman run` (below) skips a docker.io round-trip.
- case "$libc:$arch" in
- musl:aarch64) PODMAN_IMAGE="docker.io/arm64v8/alpine:3.20.10" ;;
- musl:x64) PODMAN_IMAGE="docker.io/amd64/alpine:3.20.10" ;;
- musl:rv64) PODMAN_IMAGE="docker.io/riscv64/alpine:edge" ;;
- glibc:aarch64) PODMAN_IMAGE="docker.io/arm64v8/debian:bookworm-slim" ;;
- glibc:x64) PODMAN_IMAGE="docker.io/amd64/debian:bookworm-slim" ;;
- glibc:rv64) PODMAN_IMAGE="docker.io/riscv64/debian:trixie-slim" ;;
- esac
-
- return 0
-}
-
-# ----- link command builder --------------------------------------------------
-
-# Produces the cfree-ld command in the LINK_CMD array given the per-case
-# .o path + output exe path. Pulls from configure_cell globals.
-build_link_cmd() {
- local obj="$1" exe="$2"
- case "$LINK_VARIANT" in
- musl-static)
- # crt1.o crti.o obj hosted_obj libc.a libcfree_rt.a crtn.o
- LINK_CMD=(
- "$CFREE" ld -static -o "$exe"
- "$SYSROOT/lib/crt1.o" "$SYSROOT/lib/crti.o"
- "$obj" "$HOSTED_OBJ"
- "$SYSROOT/lib/libc.a" "$CFREE_RT"
- "$SYSROOT/lib/crtn.o"
- )
- ;;
- musl-dynamic)
- # Scrt1.o crti.o obj hosted_obj libc.so libcfree_rt.a crtn.o
- LINK_CMD=(
- "$CFREE" ld -pie -dynamic-linker "$LOADER" -o "$exe"
- "$SYSROOT/lib/Scrt1.o" "$SYSROOT/lib/crti.o"
- "$obj" "$HOSTED_OBJ"
- "$SYSROOT/lib/libc.so" "$CFREE_RT"
- "$SYSROOT/lib/crtn.o"
- )
- ;;
- glibc-dynamic)
- # Scrt1.o crti.o obj hosted_obj libc.so.6 libc_nonshared.a libcfree_rt.a crtn.o
- LINK_CMD=(
- "$CFREE" ld -pie -dynamic-linker "$LOADER" -o "$exe"
- "$SYSROOT/lib/Scrt1.o" "$SYSROOT/lib/crti.o"
- "$obj" "$HOSTED_OBJ"
- "$SYSROOT/lib/libc.so.6" "$SYSROOT/lib/libc_nonshared.a" "$CFREE_RT"
- "$SYSROOT/lib/crtn.o"
- )
- ;;
- esac
-}
-
-# ----- exec dispatch ---------------------------------------------------------
-
-# Sets RUN_RC and writes captured stdout/stderr to <out>/<err>.
-exec_one() {
- local exe="$1" out="$2" err="$3"
- case "$EXEC_KIND" in
- host-darwin|native)
- "$exe" >"$out" 2>"$err"
- RUN_RC=$?
- ;;
- qemu)
- "$QEMU_BIN" "$exe" >"$out" 2>"$err"
- RUN_RC=$?
- ;;
- podman)
- local dir base
- dir="$(cd "$(dirname "$exe")" && pwd)"
- base="$(basename "$exe")"
- # --pull=never skips a docker.io manifest lookup that otherwise
- # adds ~30 s per invocation even when the image is cached
- # locally (per-cell images are pre-pulled by extract.sh).
- podman run --rm --pull=never --platform "$PODMAN_PLATFORM" \
- --net=none \
- -v "$dir":/work:Z -w /work \
- "$PODMAN_IMAGE" "./$base" \
- >"$out" 2>"$err"
- RUN_RC=$?
- ;;
- *)
- RUN_RC=127
- ;;
- esac
-}
-
-# ----- per-case driver -------------------------------------------------------
-
-PASS_TOTAL=0
-FAIL_TOTAL=0
-SKIP_TOTAL=0
-FAIL_NAMES=()
-
-run_one_case() {
- local src="$1"
- local name="$(basename "$src" .c)"
- local label="$LABEL/$name"
- local work="$BUILD_ROOT/$LABEL/$name"
- mkdir -p "$work"
-
- local expected=0
- [ -f "$CASES_DIR/${name}.expected" ] && \
- expected="$(tr -d '[:space:]' < "$CASES_DIR/${name}.expected")"
-
- local expect_stdout=""
- [ -f "$CASES_DIR/${name}.stdout" ] && \
- expect_stdout="$(cat "$CASES_DIR/${name}.stdout")"
-
- # ---- compile to .o ----
- local cc_flags=(cc -target "$TRIPLE"
- -isystem "$ROOT/rt/include/libc"
- -isystem "$ROOT/rt/include")
- if [ "$LINK_VARIANT" = "macos" ]; then
- # Darwin: compile + link in one cfree-cc invocation against the
- # SDK, matching the original host-libc flow.
- local exe="$work/${name}.exe"
- if ! "$CFREE" "${cc_flags[@]}" -e main \
- "${DARWIN_LDFLAGS[@]}" -o "$exe" "$src" "$HOSTED_OBJ" \
- "${DARWIN_LDLIBS[@]}" \
- >"$work/build.out" 2>"$work/build.err"; then
- note_fail "$label (build)"
- sed 's/^/ | /' "$work/build.err" | head -10
- FAIL_TOTAL=$((FAIL_TOTAL+1))
- FAIL_NAMES+=("$label (build)")
- return
- fi
- chmod +x "$exe" 2>/dev/null
- else
- # Linux: compile-only with cfree cc, then cfree ld with the cell's
- # link variant. -fPIC/-fPIE for dynamic variants so cfree-cc emits
- # PC-relative addressing usable in PIE output.
- case "$LINK_VARIANT" in
- *-dynamic) cc_flags+=(-fPIE -fpic) ;;
- esac
- cc_flags+=(-isystem "$SYSROOT/include")
- # glibc multi-arch headers live under include/<triple-ish>/; add
- # the matching subdir when the sysroot ships one.
- case "$LINK_VARIANT-$TRIPLE" in
- glibc-dynamic-aarch64-linux)
- cc_flags+=(-isystem "$SYSROOT/include/aarch64-linux-gnu") ;;
- glibc-dynamic-x86_64-linux)
- cc_flags+=(-isystem "$SYSROOT/include/x86_64-linux-gnu") ;;
- glibc-dynamic-riscv64-linux)
- cc_flags+=(-isystem "$SYSROOT/include/riscv64-linux-gnu") ;;
- esac
-
- local obj="$work/${name}.o"
- if ! "$CFREE" "${cc_flags[@]}" -c "$src" -o "$obj" \
- >"$work/cc.out" 2>"$work/cc.err"; then
- note_fail "$label (cc)"
- sed 's/^/ cc| /' "$work/cc.err" | head -10
- FAIL_TOTAL=$((FAIL_TOTAL+1))
- FAIL_NAMES+=("$label (cc)")
- return
- fi
-
- local exe="$work/${name}.exe"
- build_link_cmd "$obj" "$exe"
- if ! "${LINK_CMD[@]}" >"$work/ld.out" 2>"$work/ld.err"; then
- note_fail "$label (ld)"
- sed 's/^/ ld| /' "$work/ld.err" | head -10
- FAIL_TOTAL=$((FAIL_TOTAL+1))
- FAIL_NAMES+=("$label (ld)")
- return
- fi
- chmod +x "$exe" 2>/dev/null
- fi
-
- # ---- exec ----
- exec_one "$work/${name}.exe" "$work/run.out" "$work/run.err"
- if [ "$RUN_RC" -ne "$expected" ]; then
- note_fail "$label (rc=$RUN_RC, want $expected)"
- [ -s "$work/run.err" ] && sed 's/^/ err| /' "$work/run.err" | head -5
- [ -s "$work/run.out" ] && sed 's/^/ out| /' "$work/run.out" | head -5
- FAIL_TOTAL=$((FAIL_TOTAL+1))
- FAIL_NAMES+=("$label (rc)")
- return
- fi
-
- if [ -n "$expect_stdout" ]; then
- if ! grep -qF -- "$expect_stdout" "$work/run.out"; then
- note_fail "$label (stdout mismatch)"
- printf ' expected substring:\n'
- printf '%s\n' "$expect_stdout" | sed 's/^/ | /'
- printf ' got:\n'
- sed 's/^/ | /' "$work/run.out" | head -10
- FAIL_TOTAL=$((FAIL_TOTAL+1))
- FAIL_NAMES+=("$label (stdout)")
- return
- fi
- fi
-
- note_pass "$label"
- PASS_TOTAL=$((PASS_TOTAL+1))
-}
-
-# ----- top-level loop --------------------------------------------------------
-
-shopt -s nullglob
-
-mkdir -p "$BUILD_ROOT"
-
-for cell in "${HOST_CELLS[@]}"; do
- if ! configure_cell "$cell"; then
- note_skip "$cell/all" "$CELL_SKIP_REASON"
- SKIP_TOTAL=$((SKIP_TOTAL+1))
- continue
- fi
- printf 'Running cell %s (%s)...\n' "$cell" "$TRIPLE"
- for src in "$CASES_DIR"/*.c; do
- name="$(basename "$src" .c)"
- if ! case_supported "$name"; then
- # Quiet skip — these are out-of-surface by design, not a config
- # gap. Reported once at the end as "out-of-surface" rather than
- # noisily per cell.
- continue
- fi
- run_one_case "$src"
- done
-done
-
-if [ "$FAIL_TOTAL" -gt 0 ]; then
- printf '\nFailed:\n'
- for n in "${FAIL_NAMES[@]}"; do printf ' %s\n' "$n"; done
-fi
-
-printf '\nResults: %s pass, %s fail, %s cell-skip\n' \
- "$PASS_TOTAL" "$FAIL_TOTAL" "$SKIP_TOTAL"
-
-if [ -z "${CFREE_LIBC_KEEP:-}" ]; then
- rm -rf "$BUILD_ROOT"
-fi
-
-exit "$FAIL_TOTAL"
diff --git a/test/opt/run.sh b/test/opt/run.sh
@@ -6,6 +6,5 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
mkdir -p "$ROOT/build/test/opt"
"$ROOT/build/cfree" cc -target aarch64-linux-gnu \
- -isystem "$ROOT/rt/include" -isystem "$ROOT/rt/include/libc" \
-O2 -c "$ROOT/test/opt/o2_many_values.c" \
-o "$ROOT/build/test/opt/o2_many_values.o"
diff --git a/test/rt/run.sh b/test/rt/run.sh
@@ -114,8 +114,7 @@ run_arch() {
err="$case_dir/run.err"
mkdir -p "$case_dir"
- if ! "$CFREE" cc -target "$triple" -isystem "$ROOT/rt/include" \
- -isystem "$ROOT/rt/include/libc" -Werror -c "$src" -o "$obj" \
+ if ! "$CFREE" cc -target "$triple" -Werror -c "$src" -o "$obj" \
>"$case_dir/cc.out" 2>"$case_dir/cc.err"; then
note_fail "$arch/$name compile (see $case_dir/cc.err)"
continue
diff --git a/test/test.mk b/test/test.mk
@@ -27,7 +27,7 @@
# asm_parse / cfree_disasm_iter_* are still stubs; the harness builds
# and runs end-to-end so the wiring stays exercised. See doc/ASM.md.
-.PHONY: test test-driver test-lex test-pp test-pp-err test-elf test-ar test-ar-driver test-strip-driver test-objcopy-driver test-link test-cg-api test-toy test-opt test-dwarf test-debug test-parse test-parse-err test-asm test-wasm-front test-isa test-aa64-inline test-rt-headers test-rt-runtime test-libc test-musl test-glibc test-lib-deps test-smoke-x64 test-smoke-rv64 test-cbackend
+.PHONY: test test-driver test-lex test-pp test-pp-err test-elf test-ar test-ar-driver test-strip-driver test-objcopy-driver test-link test-cg-api test-toy test-opt test-dwarf test-debug test-parse test-parse-err test-asm test-wasm-front test-isa test-aa64-inline test-rt-headers test-rt-runtime test-musl test-glibc test-lib-deps test-smoke-x64 test-smoke-rv64 test-cbackend
test: test-driver test-lex test-pp test-pp-err test-elf test-ar test-ar-driver test-strip-driver test-objcopy-driver test-link test-toy test-dwarf test-debug test-parse test-parse-err test-asm test-isa test-aa64-inline test-rt-headers test-lib-deps
# `test-cbackend` is intentionally not in the default `test` target: the
@@ -167,8 +167,7 @@ test-rt-headers: bin
for target in $(RT_HEADER_TEST_TARGETS); do \
out="build/test/rt-headers/$$target/smoke.o"; \
mkdir -p "$$(dirname "$$out")"; \
- $(BIN) cc -target "$$target" -isystem rt/include -isystem rt/include/libc \
- -Werror -c test/smoke.c -o "$$out"; \
+ $(BIN) cc -target "$$target" -Werror -c test/smoke.c -o "$$out"; \
done
test-rt-runtime: bin rt $(LINK_EXE_RUNNER)
@@ -314,25 +313,6 @@ test-musl: bin rt-aarch64-linux $(MUSL_SYSROOT_MARKER)
test-glibc: bin rt-aarch64-linux $(GLIBC_SYSROOT_MARKER)
@bash test/libc/glibc/run.sh
-# test-libc: end-to-end host-libc tests. Compiles each test/libc/cases/
-# case with cfree-cc against rt/include/libc, links it against the
-# target's C library (libc.a — static, since cfree-cc doesn't yet emit
-# GOT-loads for extern data on ELF) plus the libcfree_hosted shim, and
-# executes on the host or inside a podman container per target.
-#
-# macOS host: uses the system SDK + hosted-macos shim natively.
-# Linux: per (arch, libc) ∈ {x64, aarch64, rv64} × {musl, glibc}, links
-# against the matching sysroot + per-arch hosted-linux shim and runs
-# the result in alpine (musl) or debian (glibc) under podman.
-# Skips arch+libc combos whose sysroot extraction isn't available.
-# Excluded from the default `test` target because Linux paths need
-# podman + cross sysroots.
-test-libc: bin hosted-macos hosted-linux \
- rt-aarch64-linux rt-x86_64-linux rt-riscv64-linux \
- $(MUSL_SYSROOT_MARKER) $(MUSL_SYSROOT_X64_MARKER) $(MUSL_SYSROOT_RV64_MARKER) \
- $(GLIBC_SYSROOT_MARKER) $(GLIBC_SYSROOT_X64_MARKER) $(GLIBC_SYSROOT_RV64_MARKER)
- @bash test/libc/run.sh
-
# Fail if libcfree.a depends on any external symbol not in the allowlist.
# Drift in either direction (new dep, or stale entry) is a failure.
LIB_DEPS_ACTUAL = build/libcfree.deps.txt