kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Mdoc/BUGS.md | 14++++++++------
Ddoc/HOSTED.md | 278-------------------------------------------------------------------------------
Mdoc/STAGE2.md | 31+++++++++++--------------------
Mdriver/cc.c | 2--
Mdriver/env.c | 17-----------------
Mdriver/hosted.c | 129+++++++------------------------------------------------------------------------
Mdriver/hosted.h | 2--
Mrt/Makefile | 59++---------------------------------------------------------
Drt/include/libc/ctype.h | 23-----------------------
Drt/include/libc/errno.h | 55-------------------------------------------------------
Drt/include/libc/inttypes.h | 88-------------------------------------------------------------------------------
Drt/include/libc/locale.h | 42------------------------------------------
Drt/include/libc/math.h | 132-------------------------------------------------------------------------------
Drt/include/libc/signal.h | 38--------------------------------------
Drt/include/libc/stdio.h | 107-------------------------------------------------------------------------------
Drt/include/libc/stdlib.h | 73-------------------------------------------------------------------------
Drt/include/libc/string.h | 39---------------------------------------
Drt/include/libc/time.h | 46----------------------------------------------
Drt/lib/cfree_hosted/linux.c | 33---------------------------------
Drt/lib/cfree_hosted/macos.c | 34----------------------------------
Mtest/driver/run.sh | 99++-----------------------------------------------------------------------------
Mtest/libc/glibc/Containerfile | 2+-
Mtest/libc/glibc/Containerfile.x64 | 2+-
Mtest/libc/glibc/run.sh | 22+++++++++-------------
Mtest/libc/musl/Containerfile | 2+-
Mtest/libc/musl/Containerfile.x64 | 2+-
Mtest/libc/musl/run.sh | 26+++++++++++++++-----------
Dtest/libc/run.sh | 518-------------------------------------------------------------------------------
Mtest/opt/run.sh | 1-
Mtest/rt/run.sh | 3+--
Mtest/test.mk | 24++----------------------
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