kit

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

commit 8fa98299090ab9d859b5b53d7cf0f2a046a8a44d
parent fd83f1e6567d8dc8866ec4be7512b51625564627
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 19 May 2026 19:46:02 -0700

Add hosted libc and runtime driver support

Diffstat:
Mdoc/HOSTED.md | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdriver/cc.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mdriver/cflags.c | 2+-
Mdriver/driver.h | 7+++++++
Mdriver/env.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adriver/hosted.c | 621+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adriver/hosted.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/main.c | 9+++++++--
Adriver/runtime.c | 652+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adriver/runtime.h | 31+++++++++++++++++++++++++++++++
Mtest/driver/run.sh | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 1831 insertions(+), 15 deletions(-)

diff --git a/doc/HOSTED.md b/doc/HOSTED.md @@ -34,6 +34,7 @@ Rules: - `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. @@ -68,12 +69,38 @@ 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. @@ -86,6 +113,18 @@ support/cache/<target-triple>/ 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`. @@ -93,11 +132,30 @@ Responsibilities: - Resolve the support directory. - Map a `CfreeTarget` to a stable target cache key/triple. -- Check for an existing runtime archive. -- Build missing or stale runtime objects for the target. +- 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 diff --git a/driver/cc.c b/driver/cc.c @@ -7,7 +7,9 @@ #include "cflags.h" #include "driver.h" +#include "hosted.h" #include "lib_resolve.h" +#include "runtime.h" /* `cfree cc` — C compiler driver. With -c produces a single object; * without -c compiles all C sources, links any .o/.a inputs alongside, and @@ -90,6 +92,7 @@ typedef struct CcPendingLib { typedef struct CcOptions { DriverEnv* env; + const char* driver_path; size_t argv_bound; /* upper bound on per-array list size */ int compile_only; /* -c */ @@ -107,6 +110,8 @@ typedef struct CcOptions { size_t owned_output_path_size; const char* entry; /* -e */ const char* linker_script; /* -T path */ + const char* sysroot; /* --sysroot / -isysroot */ + const char* support_dir; /* --support-dir */ /* -ffile-prefix-map=old=new entries; old is heap-owned (split out), new * aliases argv. */ @@ -173,6 +178,11 @@ typedef struct CcOptions { int pie; int gc_sections; const char* interp_path; + int no_stdlib; + int no_defaultlibs; + int no_startfiles; + int wants_hosted_libc; + DriverHostedPlan hosted; } CcOptions; static void cc_usage(void) { @@ -197,12 +207,14 @@ void driver_help_cc(void) { " cfree cc -shared [options] inputs... link a shared library\n" " cfree cc -M|-MM [options] input.c print header deps; no " "compile\n" + " --sysroot DIR / -isysroot DIR hosted SDK/sysroot\n" + " --support-dir DIR cfree support root\n" "\n" "(see source for the full GCC-subset flag reference)\n"); } static int cc_alloc_arrays(CcOptions* o, int argc) { - size_t bound = (size_t)argc; + size_t bound = (size_t)argc + 16u; o->argv_bound = bound; o->source_files = driver_alloc_zeroed(o->env, bound * sizeof(*o->source_files)); @@ -235,7 +247,7 @@ static int cc_alloc_arrays(CcOptions* o, int argc) { } o->new_dtags = 1; o->cur_link_mode = CFREE_LM_DEFAULT; - if (driver_cflags_init(&o->cf, o->env, argc) != 0) { + if (driver_cflags_init(&o->cf, o->env, (int)bound) != 0) { driver_errf(CC_TOOL, "out of memory"); return 1; } @@ -267,6 +279,7 @@ static void cc_options_release(CcOptions* o) { driver_free(o->env, o->build_id_bytes, o->build_id_len); if (o->owned_output_path) driver_free(o->env, o->owned_output_path, o->owned_output_path_size); + driver_hosted_plan_fini(o->env, &o->hosted); driver_cflags_fini(&o->cf, o->env); driver_free(o->env, o->source_files, bound * sizeof(*o->source_files)); driver_free(o->env, o->source_langs, bound * sizeof(*o->source_langs)); @@ -296,6 +309,7 @@ static char* cc_dup_span(DriverEnv* env, const char* s, size_t n) { } static int cc_record_build_id(CcOptions* o, const char* val); +static int cc_apply_hosted_profile(CcOptions* o); /* Parse a single GCC-style -Wl,X[,Y...] pass-through argument. */ static int cc_record_wl(CcOptions* o, const char* arg) { @@ -428,6 +442,68 @@ static void cc_push_link_item(CcOptions* o, uint8_t kind, uint32_t index) { it->index = index; } +static void cc_insert_link_item(CcOptions* o, uint32_t pos, uint8_t kind, + uint32_t index) { + uint32_t i; + if (pos > o->nlink_items) pos = o->nlink_items; + for (i = o->nlink_items; i > pos; --i) { + o->link_items[i] = o->link_items[i - 1u]; + } + o->link_items[pos].kind = kind; + o->link_items[pos].index = index; + o->nlink_items++; +} + +static int cc_append_hosted_input(CcOptions* o, const DriverHostedInput* in, + uint32_t insert_pos, int insert) { + uint32_t index; + uint8_t kind; + switch ((DriverHostedInputKind)in->kind) { + case DRIVER_HOSTED_INPUT_OBJECT: + index = o->nobject_files++; + o->object_files[index] = in->path; + kind = CC_LINK_OBJECT; + break; + case DRIVER_HOSTED_INPUT_ARCHIVE: { + CcArchiveInput* ar = &o->archives[o->narchives++]; + ar->path = in->path; + ar->whole_archive = 0; + ar->link_mode = CFREE_LM_DEFAULT; + ar->group_id = 0; + index = o->narchives - 1u; + kind = CC_LINK_ARCHIVE; + break; + } + case DRIVER_HOSTED_INPUT_DSO: { + CcDsoInput* d = &o->dsos[o->ndsos++]; + d->path = in->path; + index = o->ndsos - 1u; + kind = CC_LINK_DSO; + break; + } + default: + driver_errf(CC_TOOL, "internal error: unknown hosted input kind"); + return 1; + } + if (insert) + cc_insert_link_item(o, insert_pos, kind, index); + else + cc_push_link_item(o, kind, index); + return 0; +} + +static void cc_insert_owned_archive(CcOptions* o, char* path, size_t path_size, + uint32_t insert_pos) { + CcArchiveInput* ar = &o->archives[o->narchives++]; + ar->path = path; + ar->owned = 1; + ar->owned_size = path_size; + ar->whole_archive = 0; + ar->link_mode = CFREE_LM_STATIC; + ar->group_id = 0; + cc_insert_link_item(o, insert_pos, CC_LINK_ARCHIVE, o->narchives - 1u); +} + static int cc_parse_u64(const char* s, uint64_t* out) { uint64_t v = 0; int any = 0; @@ -654,6 +730,53 @@ static int cc_resolve_pending_libs(CcOptions* o) { return 0; } +static int cc_apply_hosted_profile(CcOptions* o) { + DriverHostedRequest req; + uint32_t i; + uint32_t insert_pos = 0; + int link_action = !o->compile_only && !o->preprocess_only && + !o->dump_tokens && o->dep_mode != CC_DEP_M && + o->dep_mode != CC_DEP_MM; + if (!link_action || !o->wants_hosted_libc || o->shared) return 0; + if (o->no_stdlib || o->no_defaultlibs) { + driver_errf(CC_TOOL, + "-lc hosted expansion is disabled by -nostdlib/-nodefaultlibs"); + return 1; + } + { + DriverHostedRequest z = {0}; + req = z; + } + req.env = o->env; + 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; + if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1; + for (i = 0; i < o->hosted.nsystem_includes; ++i) { + o->cf.system_include_dirs[o->cf.nsystem_include_dirs++] = + o->hosted.system_includes[i]; + } + for (i = 0; i < o->hosted.nbefore; ++i) { + if (o->no_startfiles) break; + if (cc_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0) + return 1; + insert_pos++; + } + for (i = 0; i < o->hosted.nafter; ++i) { + if (cc_append_hosted_input(o, &o->hosted.after[i], 0, 0) != 0) return 1; + } + for (i = 0; i < o->hosted.nfinal; ++i) { + if (o->no_startfiles) break; + if (cc_append_hosted_input(o, &o->hosted.final[i], 0, 0) != 0) return 1; + } + if (!o->interp_path && o->hosted.interp_path) + o->interp_path = o->hosted.interp_path; + return 0; +} + static int cc_apply_env(CcOptions* o) { const char* sde = driver_getenv("SOURCE_DATE_EPOCH"); if (sde && cc_parse_u64(sde, &o->epoch) != 0) { @@ -755,23 +878,55 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { } if (driver_streq(a, "-ffreestanding") || driver_streq(a, "-fhosted") || driver_streq(a, "-fno-builtin") || driver_streq(a, "-pipe") || - driver_streq(a, "-pthread") || driver_streq(a, "-nostdlib") || - driver_streq(a, "-nodefaultlibs") || driver_streq(a, "-nostartfiles") || - driver_streq(a, "-nostdinc")) { + driver_streq(a, "-pthread") || driver_streq(a, "-nostdinc")) { cc_log_ignored(a); continue; } - if (driver_streq(a, "-isysroot") || driver_streq(a, "--sysroot") || - driver_streq(a, "-include") || driver_streq(a, "-iquote") || - driver_streq(a, "-idirafter")) { + if (driver_streq(a, "-nostdlib")) { + o->no_stdlib = 1; + cc_log_ignored(a); + continue; + } + if (driver_streq(a, "-nodefaultlibs")) { + o->no_defaultlibs = 1; + cc_log_ignored(a); + continue; + } + if (driver_streq(a, "-nostartfiles")) { + o->no_startfiles = 1; + cc_log_ignored(a); + continue; + } + if (driver_streq(a, "-isysroot") || driver_streq(a, "--sysroot")) { if (++i >= argc) { driver_errf(CC_TOOL, "%s requires an argument", a); return 1; } - cc_log_ignored(a); + o->sysroot = argv[i]; continue; } if (driver_strneq(a, "--sysroot=", 10)) { + o->sysroot = a + 10; + continue; + } + if (driver_streq(a, "--support-dir")) { + if (++i >= argc) { + driver_errf(CC_TOOL, "--support-dir requires an argument"); + return 1; + } + o->support_dir = argv[i]; + continue; + } + if (driver_strneq(a, "--support-dir=", 14)) { + o->support_dir = a + 14; + continue; + } + if (driver_streq(a, "-include") || driver_streq(a, "-iquote") || + driver_streq(a, "-idirafter")) { + if (++i >= argc) { + driver_errf(CC_TOOL, "%s requires an argument", a); + return 1; + } cc_log_ignored(a); continue; } @@ -975,6 +1130,10 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { driver_errf(CC_TOOL, "-l requires an argument"); return 1; } + if (driver_streq(name, "c") && !o->no_stdlib && !o->no_defaultlibs) { + o->wants_hosted_libc = 1; + continue; + } { CcPendingLib* pl = &o->pending_libs[o->npending_libs++]; pl->name = name; @@ -1140,6 +1299,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { } } } + if (cc_apply_hosted_profile(o) != 0) return 1; return 0; } @@ -1696,7 +1856,8 @@ static int cc_run_compile_objs(DriverEnv* env, const CcOptions* o, if (o->output_path) return cc_run_compile_obj(env, o, pp); for (i = 0; i < o->nsource_files; ++i) { size_t out_size = 0; - char* out = cc_default_obj_path_for_name(env, o->source_files[i], &out_size); + char* out = + cc_default_obj_path_for_name(env, o->source_files[i], &out_size); int rc; if (!out) { driver_errf(CC_TOOL, "out of memory"); @@ -2000,8 +2161,11 @@ out: int driver_cc(int argc, char** argv) { DriverEnv env; CcOptions co = {0}; + DriverRuntimeSupport runtime = {0}; CfreePreprocessOptions pp; int rc; + int runtime_resolved = 0; + int link_action; if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { driver_help_cc(); @@ -2010,6 +2174,7 @@ int driver_cc(int argc, char** argv) { driver_env_init(&env); co.env = &env; + co.driver_path = argv[0]; if (cc_parse(argc, argv, &co) != 0) { cc_options_release(&co); @@ -2017,6 +2182,50 @@ int driver_cc(int argc, char** argv) { return 2; } + link_action = !co.compile_only && !co.preprocess_only && !co.dump_tokens && + co.dep_mode != CC_DEP_M && co.dep_mode != CC_DEP_MM; + if (driver_runtime_resolve(&env, co.support_dir, co.driver_path, &runtime) == + 0) { + runtime_resolved = 1; + if (co.nsource_files || co.nsource_memory) { + if (driver_runtime_add_freestanding_headers(&runtime, &co.cf) != 0) { + driver_errf(CC_TOOL, "failed to add freestanding headers"); + driver_runtime_support_fini(&env, &runtime); + cc_options_release(&co); + driver_env_fini(&env); + return 1; + } + } + } else if (co.support_dir || link_action || co.nsource_files || + co.nsource_memory) { + driver_errf(CC_TOOL, "support dir not found"); + cc_options_release(&co); + driver_env_fini(&env); + return 1; + } + + if (link_action && !co.no_stdlib && !co.no_defaultlibs) { + char* rt_path = NULL; + size_t rt_path_size = 0; + uint32_t insert_pos; + if (!runtime_resolved) { + driver_errf(CC_TOOL, "support dir not found"); + cc_options_release(&co); + driver_env_fini(&env); + return 1; + } + if (driver_runtime_ensure_archive(&env, &runtime, co.target, co.epoch, + &rt_path, &rt_path_size) != 0) { + driver_runtime_support_fini(&env, &runtime); + cc_options_release(&co); + driver_env_fini(&env); + return 1; + } + insert_pos = co.nlink_items; + if (co.hosted.nfinal <= insert_pos) insert_pos -= co.hosted.nfinal; + cc_insert_owned_archive(&co, rt_path, rt_path_size, insert_pos); + } + driver_cflags_fill_pp(&co.cf, &pp); if (co.preprocess_only) { @@ -2032,6 +2241,7 @@ int driver_cc(int argc, char** argv) { } cc_options_release(&co); + if (runtime_resolved) driver_runtime_support_fini(&env, &runtime); driver_env_fini(&env); return rc; } diff --git a/driver/cflags.c b/driver/cflags.c @@ -4,7 +4,7 @@ #include <stdint.h> int driver_cflags_init(DriverCflags* cf, DriverEnv* env, int argc_bound) { - size_t bound = argc_bound > 0 ? (size_t)argc_bound : 1; + size_t bound = argc_bound > 0 ? (size_t)argc_bound + 4u : 4u; cf->_cap = bound; cf->include_dirs = driver_alloc_zeroed(env, bound * sizeof(*cf->include_dirs)); diff --git a/driver/driver.h b/driver/driver.h @@ -170,6 +170,13 @@ CfreeWriter *driver_stdout_writer(DriverEnv *); * loops don't slurp file contents on a hit. */ int driver_path_exists(const char *path); +/* Read a path's last modification time in nanoseconds since the Unix epoch. + * Returns 0 on success, nonzero on stat failure. */ +int driver_path_mtime_ns(const char *path, int64_t *out); + +/* Create a directory and any missing parents. Returns 0 on success. */ +int driver_mkdir_p(DriverEnv *, const char *path); + /* Set a linked binary output's final mode according to the active umask. * Returns 0 on success, nonzero on chmod failure. */ int driver_mark_executable_output(const char *path); diff --git a/driver/env.c b/driver/env.c @@ -1275,6 +1275,67 @@ int driver_path_exists(const char *path) { return stat(path, &sb) == 0; } +int driver_path_mtime_ns(const char *path, int64_t *out) { + struct stat sb; + int64_t sec; + int64_t nsec; + + if (!path || !out) + return 1; + if (stat(path, &sb) != 0) + return 1; +#if defined(__APPLE__) + sec = (int64_t)sb.st_mtimespec.tv_sec; + nsec = (int64_t)sb.st_mtimespec.tv_nsec; +#elif defined(__linux__) + sec = (int64_t)sb.st_mtim.tv_sec; + nsec = (int64_t)sb.st_mtim.tv_nsec; +#else + sec = (int64_t)sb.st_mtime; + nsec = 0; +#endif + *out = sec * 1000000000LL + nsec; + return 0; +} + +int driver_mkdir_p(DriverEnv *env, const char *path) { + size_t len; + char *buf; + size_t i; + struct stat sb; + + if (!path || !path[0]) + return 1; + len = strlen(path); + buf = (char *)driver_alloc(env, len + 1); + if (!buf) + return 1; + memcpy(buf, path, len + 1); + + for (i = 1; i <= len; ++i) { + int at_end = (i == len); + if (!at_end && buf[i] != '/') + continue; + if (!at_end) + buf[i] = '\0'; + if (buf[0] != '\0' && strcmp(buf, ".") != 0) { + if (mkdir(buf, 0755) != 0 && errno != EEXIST) { + driver_free(env, buf, len + 1); + return 1; + } + if (stat(buf, &sb) != 0 || !S_ISDIR(sb.st_mode)) { + driver_free(env, buf, len + 1); + return 1; + } + } + if (!at_end) + buf[i] = '/'; + } + + driver_free(env, buf, len + 1); + return 0; +} + int driver_mark_executable_output(const char *path) { mode_t mask; mode_t mode; diff --git a/driver/hosted.c b/driver/hosted.c @@ -0,0 +1,621 @@ +#include "hosted.h" + +#include <stddef.h> +#include <stdint.h> + +static char* hosted_join2(DriverEnv* env, const char* a, const char* b, + size_t* out_size) { + size_t alen = driver_strlen(a); + size_t blen = driver_strlen(b); + size_t slash = (alen > 0 && a[alen - 1] != '/') ? 1u : 0u; + size_t bytes = alen + slash + blen + 1u; + char* out = driver_alloc(env, bytes); + size_t off = 0; + if (!out) return NULL; + if (alen) { + driver_memcpy(out + off, a, alen); + off += alen; + } + if (slash) out[off++] = '/'; + if (blen) { + driver_memcpy(out + off, b, blen); + off += blen; + } + out[off] = '\0'; + if (out_size) *out_size = bytes; + 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; + if (*n >= cap) return 1; + it = &items[*n]; + it->kind = kind; + it->path = path; + it->owned_path = path; + it->owned_size = path_size; + (*n)++; + return 0; +} + +static int hosted_add_required(DriverHostedInput* items, uint32_t* n, + uint32_t cap, const DriverHostedRequest* req, + const char* base, const char* rel, + uint8_t kind) { + size_t size = 0; + char* path = hosted_join2(req->env, base, rel, &size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (!driver_path_exists(path)) { + driver_errf(req->tool, "hosted profile missing required file: %s", path); + driver_free(req->env, path, size); + return 1; + } + if (hosted_add_input(items, n, cap, kind, path, size) != 0) { + driver_errf(req->tool, "too many hosted inputs"); + driver_free(req->env, path, size); + return 1; + } + return 0; +} + +static int hosted_add_existing_include(DriverHostedPlan* plan, DriverEnv* env, + const char* base, const char* rel) { + size_t size = 0; + char* path; + if (plan->nsystem_includes >= DRIVER_HOSTED_MAX_INCLUDES) return 1; + path = hosted_join2(env, base, rel, &size); + if (!path) return 1; + if (!driver_path_exists(path)) { + driver_free(env, path, size); + return 0; + } + plan->system_includes[plan->nsystem_includes] = path; + plan->owned_system_includes[plan->nsystem_includes] = path; + plan->owned_system_include_sizes[plan->nsystem_includes] = size; + plan->nsystem_includes++; + return 0; +} + +static const char* hosted_linux_arch_suffix(CfreeArchKind arch) { + switch (arch) { + case CFREE_ARCH_ARM_64: + return "aarch64"; + case CFREE_ARCH_X86_64: + return "x64"; + case CFREE_ARCH_RV64: + return "rv64"; + default: + return NULL; + } +} + +static const char* hosted_glibc_interp(CfreeArchKind arch) { + switch (arch) { + case CFREE_ARCH_ARM_64: + return "/lib/ld-linux-aarch64.so.1"; + case CFREE_ARCH_X86_64: + return "/lib64/ld-linux-x86-64.so.2"; + case CFREE_ARCH_RV64: + return "/lib/ld-linux-riscv64-lp64d.so.1"; + default: + return NULL; + } +} + +static const char* hosted_musl_interp(CfreeArchKind arch) { + switch (arch) { + case CFREE_ARCH_ARM_64: + return "/lib/ld-musl-aarch64.so.1"; + case CFREE_ARCH_X86_64: + return "/lib/ld-musl-x86_64.so.1"; + case CFREE_ARCH_RV64: + return "/lib/ld-musl-riscv64.so.1"; + default: + return NULL; + } +} + +static int hosted_add_linux_shim(DriverHostedPlan* plan, + const DriverHostedRequest* req, + const char* support_root) { + const char* suffix = hosted_linux_arch_suffix(req->target.arch); + char rel[64]; + char devrel[64]; + if (!suffix) { + driver_errf(req->tool, "no hosted Linux profile for target architecture"); + return 1; + } + rel[0] = '\0'; + devrel[0] = '\0'; + { + const char prefix[] = "rt/lib/cfree_hosted/linux-"; + const char obj[] = ".o"; + size_t off = 0; + size_t n = driver_strlen(prefix); + size_t s = driver_strlen(suffix); + driver_memcpy(rel + off, prefix, n); + off += n; + driver_memcpy(rel + off, suffix, s); + off += s; + driver_memcpy(rel + off, obj, sizeof(obj)); + } + { + const char prefix[] = "build/cfree_hosted/linux-"; + const char obj[] = ".o"; + size_t off = 0; + size_t n = driver_strlen(prefix); + size_t s = driver_strlen(suffix); + driver_memcpy(devrel + off, prefix, n); + off += n; + driver_memcpy(devrel + off, suffix, s); + off += s; + driver_memcpy(devrel + off, obj, sizeof(obj)); + } + { + size_t size = 0; + char* path = hosted_join2(req->env, support_root, rel, &size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (driver_path_exists(path)) { + return hosted_add_input(plan->after, &plan->nafter, + DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_OBJECT, path, size); + } + driver_free(req->env, path, size); + } + { + size_t size = 0; + char* path = hosted_join2(req->env, support_root, devrel, &size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (driver_path_exists(path)) { + return hosted_add_input(plan->after, &plan->nafter, + DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_OBJECT, path, size); + } + driver_errf(req->tool, "hosted profile missing required file: %s", path); + driver_free(req->env, path, size); + } + return 1; +} + +static int hosted_add_macos_shim(DriverHostedPlan* plan, + const DriverHostedRequest* req, + const char* support_root) { + { + size_t size = 0; + char* path = + hosted_join2(req->env, support_root, + "rt/lib/cfree_hosted/libcfree_hosted_macos.a", &size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (driver_path_exists(path)) { + return hosted_add_input(plan->after, &plan->nafter, + DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_ARCHIVE, path, size); + } + driver_free(req->env, path, size); + } + { + size_t size = 0; + char* path = hosted_join2(req->env, support_root, + "build/libcfree_hosted_macos.a", &size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (driver_path_exists(path)) { + return hosted_add_input(plan->after, &plan->nafter, + DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_ARCHIVE, path, size); + } + driver_free(req->env, path, size); + } + { + size_t dir_size = 0; + size_t size = 0; + char* dir = hosted_driver_dir(req->env, req->driver_path, &dir_size); + char* path; + if (!dir) { + driver_errf(req->tool, "out of memory"); + return 1; + } + path = hosted_join2(req->env, dir, "libcfree_hosted_macos.a", &size); + driver_free(req->env, dir, dir_size); + if (!path) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (driver_path_exists(path)) { + return hosted_add_input(plan->after, &plan->nafter, + DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_ARCHIVE, path, size); + } + driver_errf(req->tool, "hosted profile missing required file: %s", path); + driver_free(req->env, path, size); + } + return 1; +} + +static int hosted_resolve_darwin(const DriverHostedRequest* req, + DriverHostedPlan* plan, + const char* support_root) { + size_t size = 0; + char* libsystem = NULL; + if (!req->sysroot || !req->sysroot[0]) { + driver_errf(req->tool, + "Darwin hosted profile requires --sysroot; try: --sysroot " + "\"$(xcrun --sdk macosx --show-sdk-path)\""); + return 1; + } + plan->profile_name = "macos-libSystem"; + if (hosted_add_existing_include(plan, req->env, support_root, + "rt/include/libc") != 0 || + hosted_add_existing_include(plan, req->env, req->sysroot, + "usr/include") != 0) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (hosted_add_macos_shim(plan, req, support_root) != 0) { + return 1; + } + libsystem = + hosted_join2(req->env, req->sysroot, "usr/lib/libSystem.tbd", &size); + if (!libsystem) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (!driver_path_exists(libsystem)) { + driver_free(req->env, libsystem, size); + libsystem = + hosted_join2(req->env, req->sysroot, "usr/lib/libSystem.dylib", &size); + if (!libsystem) { + driver_errf(req->tool, "out of memory"); + return 1; + } + } + if (!driver_path_exists(libsystem)) { + driver_errf(req->tool, "hosted profile missing required file: %s", + libsystem); + driver_free(req->env, libsystem, size); + return 1; + } + if (hosted_add_input(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, + DRIVER_HOSTED_INPUT_DSO, libsystem, size) != 0) { + driver_free(req->env, libsystem, size); + driver_errf(req->tool, "too many hosted inputs"); + return 1; + } + return 0; +} + +static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req, + DriverHostedPlan* plan, + const char* support_root) { + plan->profile_name = "linux-musl-static"; + if (hosted_add_existing_include(plan, req->env, support_root, + "rt/include/libc") != 0 || + hosted_add_existing_include(plan, req->env, req->sysroot, "include") != + 0) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/crt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || + hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; + if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, + req, req->sysroot, "lib/libc.a", + DRIVER_HOSTED_INPUT_ARCHIVE) != 0) + return 1; + if (hosted_add_required(plan->final, &plan->nfinal, DRIVER_HOSTED_MAX_FINAL, + req, req->sysroot, "lib/crtn.o", + DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + return 0; +} + +static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req, + DriverHostedPlan* plan, + const char* support_root) { + const char* interp = hosted_musl_interp(req->target.arch); + if (!interp) { + driver_errf(req->tool, "no hosted musl profile for target architecture"); + return 1; + } + plan->profile_name = "linux-musl-dynamic"; + plan->interp_path = interp; + if (hosted_add_existing_include(plan, req->env, support_root, + "rt/include/libc") != 0 || + hosted_add_existing_include(plan, req->env, req->sysroot, "include") != + 0) { + driver_errf(req->tool, "out of memory"); + return 1; + } + if (hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/Scrt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || + hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; + if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, + req, req->sysroot, "lib/libc.so", + DRIVER_HOSTED_INPUT_DSO) != 0) + return 1; + if (hosted_add_required(plan->final, &plan->nfinal, DRIVER_HOSTED_MAX_FINAL, + req, req->sysroot, "lib/crtn.o", + DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + return 0; +} + +static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, + DriverHostedPlan* plan, + const char* support_root) { + const char* interp = hosted_glibc_interp(req->target.arch); + if (!interp) { + driver_errf(req->tool, "no hosted glibc profile for target architecture"); + return 1; + } + plan->profile_name = "linux-glibc-dynamic"; + plan->interp_path = interp; + if (hosted_add_existing_include(plan, req->env, support_root, + "rt/include/libc") != 0 || + hosted_add_existing_include(plan, req->env, req->sysroot, "include") != + 0) { + driver_errf(req->tool, "out of memory"); + return 1; + } + switch (req->target.arch) { + case CFREE_ARCH_ARM_64: + if (hosted_add_existing_include(plan, req->env, req->sysroot, + "include/aarch64-linux-gnu") != 0) + return 1; + break; + case CFREE_ARCH_X86_64: + if (hosted_add_existing_include(plan, req->env, req->sysroot, + "include/x86_64-linux-gnu") != 0) + return 1; + break; + case CFREE_ARCH_RV64: + if (hosted_add_existing_include(plan, req->env, req->sysroot, + "include/riscv64-linux-gnu") != 0) + return 1; + break; + default: + break; + } + if (hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/Scrt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || + hosted_add_required(plan->before, &plan->nbefore, + DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, + "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; + if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, + req, req->sysroot, "lib/libc.so.6", + DRIVER_HOSTED_INPUT_DSO) != 0 || + hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, + req, req->sysroot, "lib/libc_nonshared.a", + DRIVER_HOSTED_INPUT_ARCHIVE) != 0) + return 1; + if (hosted_add_required(plan->final, &plan->nfinal, DRIVER_HOSTED_MAX_FINAL, + req, req->sysroot, "lib/crtn.o", + DRIVER_HOSTED_INPUT_OBJECT) != 0) + return 1; + return 0; +} + +static int hosted_resolve_linux(const DriverHostedRequest* req, + DriverHostedPlan* plan, + const char* support_root) { + char* libc_a; + char* libc_so; + char* libc_so6; + char* libc_nonshared; + size_t sa = 0; + size_t ss = 0; + size_t ss6 = 0; + size_t sns = 0; + int has_libc_a; + int has_libc_so; + int has_libc_so6; + int has_glibc_nonshared; + if (!req->sysroot || !req->sysroot[0]) { + driver_errf(req->tool, "Linux hosted profile requires --sysroot"); + return 1; + } + libc_a = hosted_join2(req->env, req->sysroot, "lib/libc.a", &sa); + libc_so = hosted_join2(req->env, req->sysroot, "lib/libc.so", &ss); + libc_so6 = hosted_join2(req->env, req->sysroot, "lib/libc.so.6", &ss6); + libc_nonshared = + hosted_join2(req->env, req->sysroot, "lib/libc_nonshared.a", &sns); + if (!libc_a || !libc_so || !libc_so6 || !libc_nonshared) { + if (libc_a) driver_free(req->env, libc_a, sa); + if (libc_so) driver_free(req->env, libc_so, ss); + if (libc_so6) driver_free(req->env, libc_so6, ss6); + if (libc_nonshared) driver_free(req->env, libc_nonshared, sns); + driver_errf(req->tool, "out of memory"); + return 1; + } + has_libc_a = driver_path_exists(libc_a); + has_libc_so = driver_path_exists(libc_so); + has_libc_so6 = driver_path_exists(libc_so6); + has_glibc_nonshared = driver_path_exists(libc_nonshared); + driver_free(req->env, libc_a, sa); + driver_free(req->env, libc_so, ss); + 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); + if (has_libc_a && !(has_libc_so6 && has_glibc_nonshared)) + return hosted_resolve_linux_musl_static(req, plan, support_root); + if (!req->static_link && has_libc_so) + return hosted_resolve_linux_musl_dynamic(req, plan, support_root); + driver_errf(req->tool, + "no supported Linux hosted libc found under sysroot: %s", + req->sysroot); + return 1; +} + +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); + } else if (req->target.os == CFREE_OS_LINUX && + req->target.obj == CFREE_OBJ_ELF) { + rc = hosted_resolve_linux(req, out, support_root); + } 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; +} + +void driver_hosted_plan_fini(DriverEnv* env, DriverHostedPlan* plan) { + uint32_t i; + if (!env || !plan) return; + for (i = 0; i < plan->nbefore; ++i) { + if (plan->before[i].owned_path) + driver_free(env, plan->before[i].owned_path, plan->before[i].owned_size); + } + for (i = 0; i < plan->nafter; ++i) { + if (plan->after[i].owned_path) + driver_free(env, plan->after[i].owned_path, plan->after[i].owned_size); + } + for (i = 0; i < plan->nfinal; ++i) { + if (plan->final[i].owned_path) + driver_free(env, plan->final[i].owned_path, plan->final[i].owned_size); + } + for (i = 0; i < plan->nsystem_includes; ++i) { + if (plan->owned_system_includes[i]) + driver_free(env, plan->owned_system_includes[i], + plan->owned_system_include_sizes[i]); + } + { + DriverHostedPlan zero = {0}; + *plan = zero; + } +} diff --git a/driver/hosted.h b/driver/hosted.h @@ -0,0 +1,56 @@ +#ifndef CFREE_DRIVER_HOSTED_H +#define CFREE_DRIVER_HOSTED_H + +#include <stdint.h> + +#include "driver.h" + +typedef enum DriverHostedInputKind { + DRIVER_HOSTED_INPUT_OBJECT, + DRIVER_HOSTED_INPUT_ARCHIVE, + DRIVER_HOSTED_INPUT_DSO, +} DriverHostedInputKind; + +typedef struct DriverHostedInput { + uint8_t kind; /* DriverHostedInputKind */ + uint8_t pad[3]; + const char* path; + char* owned_path; + size_t owned_size; +} DriverHostedInput; + +#define DRIVER_HOSTED_MAX_BEFORE 4 +#define DRIVER_HOSTED_MAX_AFTER 6 +#define DRIVER_HOSTED_MAX_FINAL 2 +#define DRIVER_HOSTED_MAX_INCLUDES 4 + +typedef struct DriverHostedPlan { + const char* profile_name; + const char* interp_path; + DriverHostedInput before[DRIVER_HOSTED_MAX_BEFORE]; + uint32_t nbefore; + DriverHostedInput after[DRIVER_HOSTED_MAX_AFTER]; + uint32_t nafter; + DriverHostedInput final[DRIVER_HOSTED_MAX_FINAL]; + uint32_t nfinal; + const char* system_includes[DRIVER_HOSTED_MAX_INCLUDES]; + char* owned_system_includes[DRIVER_HOSTED_MAX_INCLUDES]; + size_t owned_system_include_sizes[DRIVER_HOSTED_MAX_INCLUDES]; + uint32_t nsystem_includes; +} DriverHostedPlan; + +typedef struct DriverHostedRequest { + DriverEnv* env; + const char* tool; + CfreeTarget target; + const char* sysroot; + const char* support_dir; + const char* driver_path; + int static_link; +} DriverHostedRequest; + +int driver_hosted_resolve(const DriverHostedRequest* req, + DriverHostedPlan* out); +void driver_hosted_plan_fini(DriverEnv* env, DriverHostedPlan* plan); + +#endif diff --git a/driver/main.c b/driver/main.c @@ -157,8 +157,13 @@ int driver_main(int argc, char** argv) { return 2; } - rc = dispatch(argv[1], argc - 1, argv + 1); - if (rc != -1) return rc; + { + char* tool_name = argv[1]; + argv[1] = argv[0]; + rc = dispatch(tool_name, argc - 1, argv + 1); + argv[1] = tool_name; + if (rc != -1) return rc; + } driver_errf("cfree", "no such tool: %s", argv[1]); driver_help_top(); diff --git a/driver/runtime.c b/driver/runtime.c @@ -0,0 +1,652 @@ +#include "runtime.h" + +#include <cfree/archive.h> +#include <cfree/compile.h> +#include <cfree/core.h> +#include <stdint.h> + +#include "lang/c/c.h" + +#define RT_TOOL "cc" + +typedef struct RuntimeVariant { + const char* key; + CfreeArchKind arch; + CfreeOSKind os; + CfreeObjFmt obj; + uint8_t ptr_size; + uint8_t ptr_align; + const char* abi_include; + uint8_t has_int128; + uint8_t ldbl128; + const char* const* sources; + uint32_t nsources; +} RuntimeVariant; + +static const char* const kRtSrcX64[] = { + "int/int.c", "fp/fp.c", + "mem/mem.c", "atomic/atomic_freestanding.c", + "cfree/ifunc_init.c", "int64/int64.c", + "coro/x86_64.c", "coro/coro.c", +}; + +static const char* const kRtSrcAarch64Linux[] = { + "int/int.c", "fp/fp.c", + "mem/mem.c", "atomic/atomic_freestanding.c", + "cfree/ifunc_init.c", "int64/int64.c", + "coro/aarch64.c", "coro/coro.c", + "fp_tf/fp_tf.c", "fp_ti/fp_ti.c", + "coro/aarch64_elf.s", +}; + +static const char* const kRtSrcAarch64Darwin[] = { + "int/int.c", + "fp/fp.c", + "mem/mem.c", + "atomic/atomic_freestanding.c", + "cfree/ifunc_init.c", + "int64/int64.c", + "coro/aarch64.c", + "coro/coro.c", + "coro/aarch64_macho.s", +}; + +static const char* const kRtSrcRv64Linux[] = { + "int/int.c", "fp/fp.c", + "mem/mem.c", "atomic/atomic_freestanding.c", + "cfree/ifunc_init.c", "int64/int64.c", + "coro/riscv64.c", "coro/coro.c", + "fp_tf/fp_tf.c", "fp_ti/fp_ti.c", +}; + +static const char* const kRtSrcRv64Elf[] = { + "int/int.c", "fp/fp.c", + "mem/mem.c", "atomic/atomic_freestanding.c", + "cfree/ifunc_init.c", "int64/int64.c", + "coro/riscv64.c", "coro/coro.c", +}; + +static const RuntimeVariant kRtVariants[] = { + {"x86_64-linux", CFREE_ARCH_X86_64, CFREE_OS_LINUX, CFREE_OBJ_ELF, 8, 8, + "lib/include/lp64_le", 1, 0, kRtSrcX64, + (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0]))}, + {"x86_64-apple-darwin", CFREE_ARCH_X86_64, CFREE_OS_MACOS, CFREE_OBJ_MACHO, + 8, 8, "lib/include/lp64_le", 1, 0, kRtSrcX64, + (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0]))}, + {"aarch64-linux", CFREE_ARCH_ARM_64, CFREE_OS_LINUX, CFREE_OBJ_ELF, 8, 8, + "lib/include/lp64_le", 1, 1, kRtSrcAarch64Linux, + (uint32_t)(sizeof(kRtSrcAarch64Linux) / sizeof(kRtSrcAarch64Linux[0]))}, + {"aarch64-apple-darwin", CFREE_ARCH_ARM_64, CFREE_OS_MACOS, CFREE_OBJ_MACHO, + 8, 8, "lib/include/lp64_le", 1, 0, kRtSrcAarch64Darwin, + (uint32_t)(sizeof(kRtSrcAarch64Darwin) / sizeof(kRtSrcAarch64Darwin[0]))}, + {"riscv64-linux", CFREE_ARCH_RV64, CFREE_OS_LINUX, CFREE_OBJ_ELF, 8, 8, + "lib/include/lp64_le", 1, 1, kRtSrcRv64Linux, + (uint32_t)(sizeof(kRtSrcRv64Linux) / sizeof(kRtSrcRv64Linux[0]))}, + {"riscv64-elf", CFREE_ARCH_RV64, CFREE_OS_FREESTANDING, CFREE_OBJ_ELF, 8, 8, + "lib/include/lp64_le", 1, 0, kRtSrcRv64Elf, + (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0]))}, +}; + +static char* rt_dup(DriverEnv* env, const char* s, size_t* out_size) { + size_t len = driver_strlen(s); + char* p = (char*)driver_alloc(env, len + 1u); + if (!p) return NULL; + driver_memcpy(p, s, len + 1u); + if (out_size) *out_size = len + 1u; + return p; +} + +static char* rt_join(DriverEnv* env, const char* a, const char* b, + size_t* out_size) { + size_t la = driver_strlen(a); + size_t lb = driver_strlen(b); + int slash = la > 0 && a[la - 1u] != '/'; + size_t n = la + (slash ? 1u : 0u) + lb + 1u; + char* p = (char*)driver_alloc(env, n); + if (!p) return NULL; + driver_memcpy(p, a, la); + if (slash) p[la++] = '/'; + driver_memcpy(p + la, b, lb); + p[la + lb] = '\0'; + if (out_size) *out_size = n; + return p; +} + +static void rt_free_str(DriverEnv* env, char** p, size_t* n) { + if (*p) driver_free(env, *p, *n); + *p = NULL; + *n = 0; +} + +static int rt_has_layout(const char* rt_root) { + char inc[4096]; + char lib[4096]; + size_t n = driver_strlen(rt_root); + if (n + 13u >= sizeof(inc)) return 0; + driver_memcpy(inc, rt_root, n); + inc[n] = '/'; + driver_memcpy(inc + n + 1u, "include", 8u); + driver_memcpy(lib, rt_root, n); + lib[n] = '/'; + driver_memcpy(lib + n + 1u, "lib", 4u); + return driver_path_exists(inc) && driver_path_exists(lib); +} + +static int rt_set_layout(DriverEnv* env, DriverRuntimeSupport* out, + const char* support_root, const char* rt_root, + const char* cache_root) { + out->support_root = rt_dup(env, support_root, &out->support_root_size); + out->rt_root = rt_dup(env, rt_root, &out->rt_root_size); + out->include_dir = rt_join(env, rt_root, "include", &out->include_dir_size); + out->cache_root = rt_dup(env, cache_root, &out->cache_root_size); + if (!out->support_root || !out->rt_root || !out->include_dir || + !out->cache_root) { + driver_runtime_support_fini(env, out); + return 1; + } + return 0; +} + +static int rt_try_support_root(DriverEnv* env, const char* root, + DriverRuntimeSupport* out) { + char* rt_root; + char* cache_root; + size_t rt_root_size = 0; + size_t cache_root_size = 0; + int ok = 1; + + if (rt_has_layout(root)) { + cache_root = rt_join(env, root, "build", &cache_root_size); + if (!cache_root) return 1; + ok = rt_set_layout(env, out, root, root, cache_root); + driver_free(env, cache_root, cache_root_size); + return ok; + } + + rt_root = rt_join(env, root, "rt", &rt_root_size); + cache_root = rt_join(env, root, "cache", &cache_root_size); + if (!rt_root || !cache_root) ok = 0; + if (ok && rt_has_layout(rt_root)) + ok = rt_set_layout(env, out, root, rt_root, cache_root) == 0; + else + ok = 0; + if (rt_root) driver_free(env, rt_root, rt_root_size); + if (cache_root) driver_free(env, cache_root, cache_root_size); + return ok ? 0 : 1; +} + +static int rt_try_argv0_support(DriverEnv* env, const char* argv0, + DriverRuntimeSupport* out) { + const char* slash; + size_t dir_len; + char* dir; + char* support; + size_t dir_size; + size_t support_size; + int rc; + + if (!argv0) return 1; + slash = argv0 + driver_strlen(argv0); + while (slash > argv0 && slash[-1] != '/') --slash; + if (slash == argv0) return 1; + dir_len = (size_t)(slash - argv0); + if (dir_len == 0) return 1; + + dir_size = dir_len + 1u; + dir = (char*)driver_alloc(env, dir_size); + if (!dir) return 1; + driver_memcpy(dir, argv0, dir_len); + dir[dir_len] = '\0'; + + support = rt_join(env, dir, "support", &support_size); + driver_free(env, dir, dir_size); + if (!support) return 1; + rc = rt_try_support_root(env, support, out); + driver_free(env, support, support_size); + return rc; +} + +static int rt_try_argv0_checkout_root(DriverEnv* env, const char* argv0, + DriverRuntimeSupport* out) { + const char* slash; + size_t dir_len; + char* dir; + char* parent; + size_t dir_size; + size_t parent_size; + int rc; + + if (!argv0) return 1; + slash = argv0 + driver_strlen(argv0); + while (slash > argv0 && slash[-1] != '/') --slash; + if (slash == argv0) return 1; + dir_len = (size_t)(slash - argv0); + if (dir_len == 0) return 1; + + dir_size = dir_len + 1u; + dir = (char*)driver_alloc(env, dir_size); + if (!dir) return 1; + driver_memcpy(dir, argv0, dir_len); + dir[dir_len] = '\0'; + + parent = rt_join(env, dir, "..", &parent_size); + driver_free(env, dir, dir_size); + if (!parent) return 1; + rc = rt_try_support_root(env, parent, out); + driver_free(env, parent, parent_size); + return rc; +} + +int driver_runtime_resolve(DriverEnv* env, const char* explicit_support_dir, + const char* argv0, DriverRuntimeSupport* out) { + int rc; + DriverRuntimeSupport zero = {0}; + *out = zero; + + if (explicit_support_dir) { + rc = rt_try_support_root(env, explicit_support_dir, out); + } else if (rt_try_support_root(env, "rt", out) == 0) { + rc = 0; + } else if (rt_try_argv0_checkout_root(env, argv0, out) == 0) { + rc = 0; + } else { + rc = rt_try_argv0_support(env, argv0, out); + } + if (rc == 0) out->tool_path = argv0; + return rc; +} + +void driver_runtime_support_fini(DriverEnv* env, DriverRuntimeSupport* s) { + rt_free_str(env, &s->cache_root, &s->cache_root_size); + rt_free_str(env, &s->include_dir, &s->include_dir_size); + rt_free_str(env, &s->rt_root, &s->rt_root_size); + rt_free_str(env, &s->support_root, &s->support_root_size); +} + +int driver_runtime_add_freestanding_headers(const DriverRuntimeSupport* s, + DriverCflags* cf) { + uint32_t i; + if (!s || !s->include_dir || !cf) return 1; + for (i = cf->nsystem_include_dirs; i > 0; --i) + cf->system_include_dirs[i] = cf->system_include_dirs[i - 1u]; + cf->system_include_dirs[0] = s->include_dir; + cf->nsystem_include_dirs++; + return 0; +} + +static const RuntimeVariant* rt_variant_for_target(CfreeTarget target) { + uint32_t i; + for (i = 0; i < (uint32_t)(sizeof(kRtVariants) / sizeof(kRtVariants[0])); + ++i) { + const RuntimeVariant* v = &kRtVariants[i]; + if (target.arch == v->arch && target.os == v->os && target.obj == v->obj && + target.ptr_size == v->ptr_size && target.ptr_align == v->ptr_align) + return v; + } + return NULL; +} + +static int rt_prepare_pp(DriverEnv* env, const DriverRuntimeSupport* support, + const RuntimeVariant* variant, int assembler, + CfreePreprocessOptions* pp, char*** owned_dirs, + size_t** owned_sizes, uint32_t* owned_count) { + static const char* const def_int128_1 = "1"; + static const char* const def_int128_0 = "0"; + char** dirs = NULL; + size_t* sizes = NULL; + const char** include_dirs; + const char** system_dirs; + CfreeDefine* defs; + uint32_t ninc = variant->ldbl128 ? 4u : 3u; + uint32_t nsys = 2u; + uint32_t ndefs = 1u + (variant->ldbl128 ? 1u : 0u) + (assembler ? 1u : 0u); + uint32_t i = 0; + uint32_t d = 0; + CfreePreprocessOptions z = {0}; + + *pp = z; + *owned_dirs = NULL; + *owned_sizes = NULL; + *owned_count = 0; + + include_dirs = (const char**)driver_alloc_zeroed( + env, (size_t)ninc * sizeof(*include_dirs)); + system_dirs = (const char**)driver_alloc_zeroed( + env, (size_t)nsys * sizeof(*system_dirs)); + defs = (CfreeDefine*)driver_alloc_zeroed(env, (size_t)ndefs * sizeof(*defs)); + dirs = + (char**)driver_alloc_zeroed(env, (size_t)(ninc + nsys) * sizeof(*dirs)); + sizes = + (size_t*)driver_alloc_zeroed(env, (size_t)(ninc + nsys) * sizeof(*sizes)); + if (!include_dirs || !system_dirs || !defs || !dirs || !sizes) { + if (include_dirs) + driver_free(env, (void*)include_dirs, + (size_t)ninc * sizeof(*include_dirs)); + if (system_dirs) + driver_free(env, (void*)system_dirs, (size_t)nsys * sizeof(*system_dirs)); + if (defs) driver_free(env, defs, (size_t)ndefs * sizeof(*defs)); + if (dirs) driver_free(env, dirs, (size_t)(ninc + nsys) * sizeof(*dirs)); + if (sizes) driver_free(env, sizes, (size_t)(ninc + nsys) * sizeof(*sizes)); + return 1; + } + + dirs[d] = rt_join(env, support->rt_root, "lib/include/common", &sizes[d]); + include_dirs[i++] = dirs[d++]; + dirs[d] = rt_join(env, support->rt_root, "lib/impl", &sizes[d]); + include_dirs[i++] = dirs[d++]; + dirs[d] = rt_join(env, support->rt_root, variant->abi_include, &sizes[d]); + include_dirs[i++] = dirs[d++]; + if (variant->ldbl128) { + dirs[d] = rt_join(env, support->rt_root, "lib/include/lp64_le_ldbl128", + &sizes[d]); + include_dirs[i++] = dirs[d++]; + } + dirs[d] = rt_join(env, support->rt_root, "include", &sizes[d]); + system_dirs[0] = dirs[d++]; + dirs[d] = rt_join(env, support->rt_root, "include/libc", &sizes[d]); + system_dirs[1] = dirs[d++]; + for (i = 0; i < d; ++i) { + if (!dirs[i]) { + uint32_t j; + for (j = 0; j < d; ++j) + if (dirs[j]) driver_free(env, dirs[j], sizes[j]); + driver_free(env, (void*)include_dirs, + (size_t)ninc * sizeof(*include_dirs)); + driver_free(env, (void*)system_dirs, (size_t)nsys * sizeof(*system_dirs)); + driver_free(env, defs, (size_t)ndefs * sizeof(*defs)); + driver_free(env, dirs, (size_t)(ninc + nsys) * sizeof(*dirs)); + driver_free(env, sizes, (size_t)(ninc + nsys) * sizeof(*sizes)); + return 1; + } + } + + defs[0].name = "HAS_INT128"; + defs[0].body = variant->has_int128 ? def_int128_1 : def_int128_0; + i = 1; + if (variant->ldbl128) { + defs[i].name = "CFREERT_LDBL128"; + defs[i].body = "1"; + ++i; + } + if (assembler) { + defs[i].name = "__ASSEMBLER__"; + defs[i].body = "1"; + } + + pp->include_dirs = include_dirs; + pp->ninclude_dirs = ninc; + pp->system_include_dirs = system_dirs; + pp->nsystem_include_dirs = nsys; + pp->defines = defs; + pp->ndefines = ndefs; + *owned_dirs = dirs; + *owned_sizes = sizes; + *owned_count = d; + return 0; +} + +static void rt_free_pp(DriverEnv* env, CfreePreprocessOptions* pp, + char** owned_dirs, size_t* owned_sizes, + uint32_t owned_count) { + uint32_t i; + for (i = 0; i < owned_count; ++i) + if (owned_dirs[i]) driver_free(env, owned_dirs[i], owned_sizes[i]); + if (owned_dirs) + driver_free(env, owned_dirs, (size_t)owned_count * sizeof(*owned_dirs)); + if (owned_sizes) + driver_free(env, owned_sizes, (size_t)owned_count * sizeof(*owned_sizes)); + if (pp->include_dirs) + driver_free(env, (void*)pp->include_dirs, + (size_t)pp->ninclude_dirs * sizeof(*pp->include_dirs)); + if (pp->system_include_dirs) + driver_free( + env, (void*)pp->system_include_dirs, + (size_t)pp->nsystem_include_dirs * sizeof(*pp->system_include_dirs)); + if (pp->defines) + driver_free(env, (void*)pp->defines, + (size_t)pp->ndefines * sizeof(*pp->defines)); +} + +static int rt_compile_source(DriverEnv* env, + const DriverRuntimeSupport* support, + const RuntimeVariant* variant, + CfreeCompiler* compiler, const char* rel, + CfreeBytes* member, CfreeWriter** obj_writer) { + CfreeContext ctx = driver_env_to_context(env); + char* lib_path = NULL; + char* src_path = NULL; + size_t lib_path_size = 0; + size_t src_path_size = 0; + DriverLoad src_load = {0}; + CfreeBytes input = {0}; + CfreeWriter* writer = NULL; + CfreeWriter* pp_writer = NULL; + const uint8_t* obj_data; + size_t obj_len = 0; + CfreeStatus st; + int is_asm; + int preprocess_asm; + int rc = 1; + + *obj_writer = NULL; + lib_path = rt_join(env, support->rt_root, "lib", &lib_path_size); + if (!lib_path) goto out; + src_path = rt_join(env, lib_path, rel, &src_path_size); + if (!src_path) goto out; + if (driver_load_bytes(ctx.file_io, RT_TOOL, src_path, &src_load, &input) != 0) + goto out; + if (cfree_writer_mem(ctx.heap, &writer) != CFREE_OK) goto out; + + is_asm = driver_has_suffix(rel, ".s") || driver_has_suffix(rel, ".S"); + preprocess_asm = driver_has_suffix(rel, ".S"); + if (is_asm) { + CfreeAsmCompileOptions aopts = {0}; + CfreeBytes asm_input = input; + if (preprocess_asm) { + CfreePreprocessOptions pp; + char** owned_dirs = NULL; + size_t* owned_sizes = NULL; + uint32_t owned_count = 0; + if (rt_prepare_pp(env, support, variant, 1, &pp, &owned_dirs, + &owned_sizes, &owned_count) != 0) + goto out; + if (cfree_writer_mem(ctx.heap, &pp_writer) != CFREE_OK) { + rt_free_pp(env, &pp, owned_dirs, owned_sizes, owned_count); + goto out; + } + st = cfree_c_preprocess(compiler, &pp, &input, pp_writer); + rt_free_pp(env, &pp, owned_dirs, owned_sizes, owned_count); + if (st != CFREE_OK || cfree_writer_status(pp_writer) != CFREE_OK) + goto out; + asm_input.data = cfree_writer_mem_bytes(pp_writer, &asm_input.len); + } + st = cfree_compile_asm_obj_emit(compiler, &aopts, &asm_input, writer); + } else { + CfreeCCompileOptions copts = {0}; + char** owned_dirs = NULL; + size_t* owned_sizes = NULL; + uint32_t owned_count = 0; + if (rt_prepare_pp(env, support, variant, 0, &copts.preprocess, &owned_dirs, + &owned_sizes, &owned_count) != 0) + goto out; + st = cfree_compile_c_obj_emit(compiler, &copts, &input, writer); + rt_free_pp(env, &copts.preprocess, owned_dirs, owned_sizes, owned_count); + } + if (st != CFREE_OK || cfree_writer_status(writer) != CFREE_OK) { + driver_errf(RT_TOOL, "failed to build runtime source: %s", src_path); + goto out; + } + + obj_data = cfree_writer_mem_bytes(writer, &obj_len); + member->name = driver_basename(rel); + member->data = obj_data; + member->len = obj_len; + *obj_writer = writer; + writer = NULL; + rc = 0; + +out: + if (pp_writer) cfree_writer_close(pp_writer); + if (writer) cfree_writer_close(writer); + driver_release_bytes(ctx.file_io, &src_load); + if (src_path) driver_free(env, src_path, src_path_size); + if (lib_path) driver_free(env, lib_path, lib_path_size); + return rc; +} + +static int rt_build_archive(DriverEnv* env, const DriverRuntimeSupport* support, + const RuntimeVariant* variant, uint64_t epoch, + const char* archive_path) { + CfreeContext ctx = driver_env_to_context(env); + CfreeCompiler* compiler = NULL; + CfreeWriter* archive_writer = NULL; + CfreeBytes* members = NULL; + CfreeWriter** obj_writers = NULL; + CfreeArWriteOptions ar_opts = {0}; + CfreeTarget target; + uint32_t i; + int rc = 1; + + members = (CfreeBytes*)driver_alloc_zeroed( + env, (size_t)variant->nsources * sizeof(*members)); + obj_writers = (CfreeWriter**)driver_alloc_zeroed( + env, (size_t)variant->nsources * sizeof(*obj_writers)); + if (!members || !obj_writers) { + driver_errf(RT_TOOL, "out of memory"); + goto out; + } + + target.arch = variant->arch; + target.os = variant->os; + target.obj = variant->obj; + target.ptr_size = variant->ptr_size; + target.ptr_align = variant->ptr_align; + target.big_endian = 0; + target.pic = CFREE_PIC_NONE; + target.code_model = CFREE_CM_DEFAULT; + + if (driver_compiler_new(target, &ctx, &compiler) != CFREE_OK) { + driver_errf(RT_TOOL, "failed to initialize compiler for runtime"); + goto out; + } + + for (i = 0; i < variant->nsources; ++i) { + if (rt_compile_source(env, support, variant, compiler, variant->sources[i], + &members[i], &obj_writers[i]) != 0) + goto out; + } + + if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, + &archive_writer) != CFREE_OK) { + driver_errf(RT_TOOL, "failed to open runtime archive: %s", archive_path); + goto out; + } + + ar_opts.epoch = epoch; + ar_opts.long_names = 1; + if (cfree_ar_write(archive_writer, members, variant->nsources, &ar_opts) != + CFREE_OK) { + driver_errf(RT_TOOL, "failed to write runtime archive: %s", archive_path); + goto out; + } + rc = 0; + +out: + if (archive_writer) cfree_writer_close(archive_writer); + if (compiler) driver_compiler_free(compiler); + if (obj_writers) { + for (i = 0; i < variant->nsources; ++i) + if (obj_writers[i]) cfree_writer_close(obj_writers[i]); + driver_free(env, obj_writers, + (size_t)variant->nsources * sizeof(*obj_writers)); + } + if (members) + driver_free(env, members, (size_t)variant->nsources * sizeof(*members)); + return rc; +} + +static int rt_is_archive_stale(DriverEnv* env, + const DriverRuntimeSupport* support, + const RuntimeVariant* variant, + const char* archive_path) { + int64_t archive_mtime; + int64_t tool_mtime; + uint32_t i; + if (driver_path_mtime_ns(archive_path, &archive_mtime) != 0) return 1; + if (support->tool_path && + driver_path_mtime_ns(support->tool_path, &tool_mtime) == 0 && + tool_mtime > archive_mtime) + return 1; + for (i = 0; i < variant->nsources; ++i) { + char* lib_path; + char* src_path; + size_t lib_path_size = 0; + size_t src_path_size = 0; + int64_t src_mtime; + int stale = 0; + lib_path = rt_join(env, support->rt_root, "lib", &lib_path_size); + if (!lib_path) return 1; + src_path = rt_join(env, lib_path, variant->sources[i], &src_path_size); + driver_free(env, lib_path, lib_path_size); + if (!src_path) return 1; + if (driver_path_mtime_ns(src_path, &src_mtime) != 0 || + src_mtime > archive_mtime) + stale = 1; + driver_free(env, src_path, src_path_size); + if (stale) return 1; + } + return 0; +} + +int driver_runtime_ensure_archive(DriverEnv* env, + const DriverRuntimeSupport* support, + CfreeTarget target, uint64_t epoch, + char** out_path, size_t* out_path_size) { + const RuntimeVariant* variant = rt_variant_for_target(target); + char* cache_dir = NULL; + char* archive_path = NULL; + size_t cache_dir_size = 0; + size_t archive_path_size = 0; + + *out_path = NULL; + *out_path_size = 0; + + if (!variant) { + driver_errf(RT_TOOL, "compiler runtime is not available for this target"); + return 1; + } + cache_dir = rt_join(env, support->cache_root, variant->key, &cache_dir_size); + if (!cache_dir) { + driver_errf(RT_TOOL, "out of memory"); + return 1; + } + archive_path = rt_join(env, cache_dir, "libcfree_rt.a", &archive_path_size); + if (!archive_path) { + driver_free(env, cache_dir, cache_dir_size); + driver_errf(RT_TOOL, "out of memory"); + return 1; + } + + if (!driver_path_exists(archive_path) || + rt_is_archive_stale(env, support, variant, archive_path)) { + if (driver_mkdir_p(env, cache_dir) != 0) { + driver_errf(RT_TOOL, "failed to create runtime cache: %s", cache_dir); + driver_free(env, archive_path, archive_path_size); + driver_free(env, cache_dir, cache_dir_size); + return 1; + } + if (rt_build_archive(env, support, variant, epoch, archive_path) != 0) { + driver_errf(RT_TOOL, "compiler runtime for %s could not be built", + variant->key); + driver_errf(RT_TOOL, "support dir: %s", support->support_root); + driver_free(env, archive_path, archive_path_size); + driver_free(env, cache_dir, cache_dir_size); + return 1; + } + } + + driver_free(env, cache_dir, cache_dir_size); + *out_path = archive_path; + *out_path_size = archive_path_size; + return 0; +} diff --git a/driver/runtime.h b/driver/runtime.h @@ -0,0 +1,31 @@ +#ifndef CFREE_DRIVER_RUNTIME_H +#define CFREE_DRIVER_RUNTIME_H + +#include "cflags.h" +#include "driver.h" + +typedef struct DriverRuntimeSupport { + char* support_root; + size_t support_root_size; + char* rt_root; + size_t rt_root_size; + char* include_dir; + size_t include_dir_size; + char* cache_root; + size_t cache_root_size; + const char* tool_path; +} DriverRuntimeSupport; + +int driver_runtime_resolve(DriverEnv* env, const char* explicit_support_dir, + const char* argv0, DriverRuntimeSupport* out); +void driver_runtime_support_fini(DriverEnv* env, DriverRuntimeSupport* s); + +int driver_runtime_add_freestanding_headers(const DriverRuntimeSupport* s, + DriverCflags* cf); + +int driver_runtime_ensure_archive(DriverEnv* env, + const DriverRuntimeSupport* support, + CfreeTarget target, uint64_t epoch, + char** out_path, size_t* out_path_size); + +#endif diff --git a/test/driver/run.sh b/test/driver/run.sh @@ -155,6 +155,121 @@ else fail=$((fail + 1)) fi +cat > "$work/implicit-header.c" <<'SRC' +#include <stddef.h> +#include <stdint.h> +int f(void) { return (int)sizeof(size_t) + (int)UINT8_MAX; } +SRC +if "$CFREE" cc -target aarch64-linux -c "$work/implicit-header.c" \ + -o "$work/implicit-header.o" \ + > "$work/implicit-header.out" 2> "$work/implicit-header.err"; then + printf 'PASS %s\n' "cc-implicit-freestanding-headers" + pass=$((pass + 1)) +else + printf 'FAIL %s (cfree cc failed)\n' "cc-implicit-freestanding-headers" + sed 's/^/ | /' "$work/implicit-header.err" + fail=$((fail + 1)) +fi + +mkdir -p "$work/rt-support/rt" +cp -R "$repo_root/rt/include" "$work/rt-support/rt/include" +cp -R "$repo_root/rt/lib" "$work/rt-support/rt/lib" +cat > "$work/rt-div.c" <<'SRC' +#include <stdint.h> +typedef unsigned __int128 u128; +u128 div128(u128 a, u128 b) { return a / b; } +void _start(void) { + volatile u128 x = div128((u128)9, (u128)3); + (void)x; + for (;;) {} +} +SRC +if "$CFREE" cc --support-dir "$work/rt-support" -target aarch64-linux \ + -e _start "$work/rt-div.c" -o "$work/rt-div" \ + > "$work/rt-div.out" 2> "$work/rt-div.err" && + [ -f "$work/rt-support/cache/aarch64-linux/libcfree_rt.a" ]; then + printf 'PASS %s\n' "cc-auto-builds-and-links-libcfree-rt" + pass=$((pass + 1)) +else + printf 'FAIL %s (cfree cc failed)\n' "cc-auto-builds-and-links-libcfree-rt" + sed 's/^/ | /' "$work/rt-div.err" + 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) {