kit

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

commit e384489fd26ddedf0845e0ceb688cfa9b1f7e26f
parent 47228f1c901f89406c2bfbf0b8e59eba7f4f0284
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 11 May 2026 11:44:46 -0700

driver: share .c/.o/.a/stdin input handling between run and dbg

Factor the input-classification + load + compile + JIT-link path that
`cfree run` already had into a new driver/inputs.{c,h} module
(DriverInputs). `cfree dbg` now uses the same path, so it accepts the
full input shape — .c / .cc / .cpp sources, .o / .obj objects, .a
archives, and `-` for stdin — instead of only .c sources.

Knock-on doc updates in DBG.md §10, §11, and §12: note the new
DriverInputs lane, mark the driver-side multi-input work complete, and
flag the three remaining DWARF-side concerns the broader input set
introduces (cfree_jit_view spanning prebuilt .o/.a debug info,
basename disambiguation in cfree_dwarf_line_to_addr, graceful "no
debug info" returns for blackbox PC ranges).

Diffstat:
Mdoc/DBG.md | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mdriver/dbg.c | 85+++++++++++++++++++------------------------------------------------------------
Adriver/inputs.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adriver/inputs.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/run.c | 202+++++++------------------------------------------------------------------------
5 files changed, 402 insertions(+), 252 deletions(-)

diff --git a/doc/DBG.md b/doc/DBG.md @@ -316,9 +316,33 @@ typed `p name`, `info locals/args`, `b file:line`) is therefore offline even though the lib-side wiring is complete. Fix is independent and self-contained; see §12. +**Multi-input fallout.** The driver now accepts `.o` / `.a` inputs +alongside pipeline-compiled sources (§11), which introduces three new +DWARF concerns the consumer must handle: + +1. `cfree_jit_view` must span every input in the JIT image — including + debug sections from prebuilt `.o` / `.a` files — not just the + pipeline's compile outputs. Without this, PCs inside externally + built code resolve to no source location even when those inputs + were compiled with `-g`. +2. `cfree_dwarf_line_to_addr` keys on a filename string; two inputs + that share a basename (`util.c` in both `a/` and `b/`) need a + disambiguation rule. Options: require a unique path suffix, or + error on collision and surface the candidate list. +3. PC ranges that land inside an input compiled without `-g` (the + "blackbox region" — typical for `.o` / `.a` from a prebuilt SDK) + should degrade gracefully: `bt` shows raw PC + symbol only, `p + NAME` reports "no debug info for this frame", `b file:line` errors + if the file isn't covered. The existing `dbg_pc_rt_to_img` + pass-through fallback at `dbg.c:360` already handles the runtime + side; the DWARF lookups themselves need parallel "no data" + returns rather than treating absence as a hard failure. + ## 11. Driver-side changes -Only `driver/env.c` and `driver/dbg.c` change in the driver: +Three TUs in the driver carry dbg-specific code: `driver/env.c`, +`driver/dbg.c`, and the shared `driver/inputs.{c,h}` module that backs +both `cfree run` and `cfree dbg`. - `driver/env.c` carries the `g_dbg_os_posix` singleton populated by `driver_env_init` and exposed through `driver_env_to_cfree`. Sections: @@ -339,6 +363,13 @@ Only `driver/env.c` and `driver/dbg.c` change in the driver: resume modes light up the moment `cfree_jit_view` returns non-NULL. The degraded-mode warning at `dbg.c:1862-1868` is left in place; it now only fires on non-aarch64 hosts. +- `driver/inputs.{c,h}` owns the `DriverInputs` struct + classify / + load / compile-and-jit pipeline, shared with `cfree run`. Recognized + positionals: `.c` / `.cc` / `.cpp` sources, `.o` / `.obj` objects, + `.a` archives, and `-` for stdin (single source). The dbg-specific + consequence is that DWARF coverage is now uneven: pipeline-compiled + sources get `-g` forced on, but `.o` / `.a` inputs carry only the + debug info they were built with — see §10. ## 12. Checklist @@ -384,7 +415,10 @@ the box. `CfreeObjBuilder`(s) on `CfreeJit` with post-link debug-section relocations applied, or emit a fresh `CfreeBytesInput` after linking and re-open. PC translation from image-relative to - runtime addresses needed on the DWARF side too. + runtime addresses needed on the DWARF side too. Must span + every input in the link — pipeline-compiled sources *and* + prebuilt `.o` / `.a` debug sections — not just the pipeline's + outputs (see §10). ### Public arch-register API @@ -404,12 +438,36 @@ the box. - [ ] Windows host: vectored exception handlers + `SetThreadContext` instead of POSIX signals -### Driver — `driver/dbg.c` +### Driver — `driver/dbg.c` and `driver/inputs.{c,h}` - [x] `cfree_jit_session_attach_dwarf` call right after `cfree_dwarf_open` - [x] Degraded-mode warning still present but only fires on non-aarch64 +- [x] Accept the full `cfree run` input shape (`.c` / `.cc` / `.cpp`, + `.o` / `.obj`, `.a`, stdin via `-`) through the shared + `DriverInputs` module in `driver/inputs.{c,h}` - [ ] Remove the warning entirely once x64 / rv64 sessions are real +### DWARF / multi-input (lands once `cfree_jit_view` is real) + +The driver now feeds the linker a mixed input set (§11), but the DWARF +consumer still assumes one debug-info-bearing compile unit per lookup. +The three items below land after `cfree_jit_view` so they can be tested +against real debug info; see §10 for the failure modes each addresses. + +- [ ] `cfree_dwarf_line_to_addr`: define and implement a basename + disambiguation rule when two inputs share a filename. Either + require a unique path suffix or surface the candidate list as an + error; current behavior is "first match wins" by walking + compile-unit order. +- [ ] DWARF lookups return "no data" (not failure) when a PC lands in + an input compiled without `-g`. The REPL formats `bt` / + `info locals` / `p NAME` accordingly; `b file:line` errors with a + file-not-covered message instead of a generic miss. +- [ ] `driver/dbg.c` source listing (`list file:line`) reads from disk + via `env.file_io`; when the input is from a `.o` / `.a` debug + section whose source file isn't accessible, show the DWARF line + number alone and omit the source snippet. + ### Tests (none landed yet — all verification to date is by-hand REPL) - [ ] `test/smoke/dbg_hello`: scripted REPL against a JIT'd C source, diff --git a/driver/dbg.c b/driver/dbg.c @@ -3,6 +3,7 @@ #include "cflags.h" #include "driver.h" +#include "inputs.h" /* `cfree dbg` — interactive JIT debugger. * @@ -46,9 +47,7 @@ typedef struct DbgOpts { const char* entry; DriverCflags cf; - - const char** sources; - uint32_t nsources; + DriverInputs inputs; char** prog_argv; uint32_t prog_argc; @@ -129,12 +128,12 @@ void driver_help_dbg(void) { static int dbg_alloc_arrays(DbgOpts* o, int argc) { size_t bound = (size_t)argc; o->argv_bound = bound; - o->sources = driver_alloc_zeroed(o->env, bound * sizeof(*o->sources)); o->prog_argv = driver_alloc_zeroed(o->env, bound * sizeof(*o->prog_argv)); - if (!o->sources || !o->prog_argv) { + if (!o->prog_argv) { driver_errf(DBG_TOOL, "out of memory"); return 1; } + if (driver_inputs_init(&o->inputs, o->env, DBG_TOOL, argc) != 0) return 1; if (driver_cflags_init(&o->cf, o->env, argc) != 0) { driver_errf(DBG_TOOL, "out of memory"); return 1; @@ -197,10 +196,18 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) { return 1; } - o->sources[o->nsources++] = a; + { + int r = driver_inputs_classify(&o->inputs, a); + if (r < 0) return 1; + if (r == 0) { + driver_errf(DBG_TOOL, "input does not have a recognized suffix: %s", + a); + return 1; + } + } } - if (o->nsources == 0) { + if (driver_inputs_count(&o->inputs) == 0) { driver_errf(DBG_TOOL, "no input files"); dbg_usage(); return 1; @@ -218,45 +225,17 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) { static void dbg_options_release(DbgOpts* o) { size_t bound = o->argv_bound; + driver_inputs_release(&o->inputs); driver_cflags_fini(&o->cf, o->env); - driver_free(o->env, o->sources, bound * sizeof(*o->sources)); driver_free(o->env, o->prog_argv, bound * sizeof(*o->prog_argv)); } /* Compile every C source through a pipeline owned by the caller and JIT-link * the result. Pipeline ownership stays with the caller so DWARF lookups * during the REPL session can run against the live pipeline compiler. */ -static int dbg_compile_and_jit(DriverEnv* env, const DbgOpts* o, - CfreePipeline* pipe, CfreeJit** out_jit) { - CfreeEnv cenv = driver_env_to_cfree(env); - const CfreeFileIO* io = cenv.file_io; - DriverLoad* src_lf = NULL; - CfreeBytesInput* src_in = NULL; - CfreeObjBuilder** objs = NULL; +static int dbg_compile_and_jit(DbgOpts* o, CfreePipeline* pipe, + CfreeJit** out_jit) { CfreeCompileOptions copts; - CfreeLinkOptions link_opts; - uint32_t i; - int rc = 1; - - if (!io || !io->read_all) { - driver_errf(DBG_TOOL, "host file I/O unavailable"); - return 1; - } - - src_in = driver_alloc_zeroed(env, o->nsources * sizeof(*src_in)); - src_lf = driver_alloc_zeroed(env, o->nsources * sizeof(*src_lf)); - objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs)); - if (!src_in || !src_lf || !objs) { - driver_errf(DBG_TOOL, "out of memory"); - goto out; - } - - for (i = 0; i < o->nsources; ++i) { - if (driver_load_bytes(io, DBG_TOOL, o->sources[i], &src_lf[i], - &src_in[i]) != 0) - goto out; - } - { CfreeCompileOptions z = {0}; copts = z; @@ -264,32 +243,8 @@ static int dbg_compile_and_jit(DriverEnv* env, const DbgOpts* o, copts.opt_level = o->opt_level; copts.debug_info = o->debug_info; driver_cflags_fill_pp(&o->cf, &copts.pp); - - for (i = 0; i < o->nsources; ++i) { - if (cfree_pipeline_compile_obj(pipe, &copts, &src_in[i], &objs[i]) != 0) - goto out; - } - - { - CfreeLinkOptions z = {0}; - link_opts = z; - } - link_opts.inputs.objs = objs; - link_opts.inputs.nobjs = o->nsources; - link_opts.inputs.entry = o->entry; - link_opts.inputs.extern_resolver = driver_dlsym_resolver; - link_opts.inputs.extern_resolver_user = NULL; - - rc = cfree_pipeline_link_jit(pipe, &link_opts, out_jit); - -out: - if (src_lf) { - for (i = 0; i < o->nsources; ++i) driver_release_bytes(io, &src_lf[i]); - driver_free(env, src_lf, o->nsources * sizeof(*src_lf)); - } - if (objs) driver_free(env, objs, o->nsources * sizeof(*objs)); - if (src_in) driver_free(env, src_in, o->nsources * sizeof(*src_in)); - return rc; + return driver_inputs_compile_and_jit(&o->inputs, pipe, &copts, o->entry, + driver_dlsym_resolver, NULL, out_jit); } /* ============================================================ @@ -1895,7 +1850,7 @@ int driver_dbg(int argc, char** argv) { return 1; } - rc = dbg_compile_and_jit(&env, &o, pipe, &jit); + rc = dbg_compile_and_jit(&o, pipe, &jit); if (rc != 0) { driver_pipeline_free(pipe); dbg_options_release(&o); diff --git a/driver/inputs.c b/driver/inputs.c @@ -0,0 +1,213 @@ +#include "inputs.h" + +#include <stddef.h> +#include <stdint.h> + +int driver_inputs_init(DriverInputs* in, DriverEnv* env, const char* tool, + int argc) { + size_t bound = (size_t)argc; + in->env = env; + in->tool = tool; + in->bound = bound; + in->sources = driver_alloc_zeroed(env, bound * sizeof(*in->sources)); + in->source_memory = + driver_alloc_zeroed(env, bound * sizeof(*in->source_memory)); + in->object_files = + driver_alloc_zeroed(env, bound * sizeof(*in->object_files)); + in->archives = driver_alloc_zeroed(env, bound * sizeof(*in->archives)); + if (!in->sources || !in->source_memory || !in->object_files || + !in->archives) { + driver_errf(tool, "out of memory"); + return 1; + } + return 0; +} + +void driver_inputs_release(DriverInputs* in) { + size_t bound = in->bound; + if (in->stdin_buf) driver_free(in->env, in->stdin_buf, in->stdin_size); + if (in->sources) driver_free(in->env, in->sources, bound * sizeof(*in->sources)); + if (in->source_memory) + driver_free(in->env, in->source_memory, bound * sizeof(*in->source_memory)); + if (in->object_files) + driver_free(in->env, in->object_files, bound * sizeof(*in->object_files)); + if (in->archives) + driver_free(in->env, in->archives, bound * sizeof(*in->archives)); +} + +/* Slurp stdin into the single source_memory entry. Guarded against + * duplicates so a user typo (`cfree run - -`) is surfaced rather than + * silently leaking the first slurp. */ +static int inputs_record_stdin(DriverInputs* in) { + CfreeBytesInput* slot; + if (in->stdin_buf) { + driver_errf(in->tool, "'-' (stdin) may appear at most once"); + return -1; + } + if (!driver_read_stdin(in->env, &in->stdin_buf, &in->stdin_size)) { + driver_errf(in->tool, "failed to read stdin"); + return -1; + } + slot = &in->source_memory[in->nsource_memory++]; + slot->name = "<stdin>"; + slot->data = in->stdin_buf; + slot->len = in->stdin_size; + return 1; +} + +int driver_inputs_classify(DriverInputs* in, const char* arg) { + if (driver_streq(arg, "-")) return inputs_record_stdin(in); + if (driver_has_suffix(arg, ".c") || driver_has_suffix(arg, ".cc") || + driver_has_suffix(arg, ".cpp")) { + in->sources[in->nsources++] = arg; + return 1; + } + if (driver_has_suffix(arg, ".o") || driver_has_suffix(arg, ".obj")) { + in->object_files[in->nobject_files++] = arg; + return 1; + } + if (driver_has_suffix(arg, ".a")) { + in->archives[in->narchives++] = arg; + return 1; + } + return 0; +} + +uint32_t driver_inputs_count(const DriverInputs* in) { + return in->nsources + in->nsource_memory + in->nobject_files + in->narchives; +} + +const char* driver_inputs_first_name(const DriverInputs* in) { + if (in->nsources) return in->sources[0]; + if (in->nsource_memory) return in->source_memory[0].name; + if (in->nobject_files) return in->object_files[0]; + if (in->narchives) return in->archives[0]; + return NULL; +} + +/* Load every input through env.file_io, compile sources via `pipe` with + * `copts`, and JIT-link. The pipeline must outlive the JIT — its + * Compiler backs jit->c, which cfree_jit_lookup dereferences. */ +int driver_inputs_compile_and_jit(DriverInputs* in, CfreePipeline* pipe, + const CfreeCompileOptions* copts, + const char* entry, + void* (*extern_resolver)(void*, const char*), + void* extern_resolver_user, + CfreeJit** out_jit) { + DriverEnv* env = in->env; + const char* tool = in->tool; + CfreeEnv cenv = driver_env_to_cfree(env); + const CfreeFileIO* io = cenv.file_io; + DriverLoad* src_lf = NULL; + DriverLoad* obj_lf = NULL; + DriverLoad* arch_lf = NULL; + CfreeBytesInput* src_in = NULL; + CfreeBytesInput* obj_in = NULL; + CfreeBytesInputArchive* arch_in = NULL; + CfreeObjBuilder** objs = NULL; + CfreeLinkOptions link_opts; + uint32_t nsrc = in->nsources + in->nsource_memory; + uint32_t i; + int rc = 1; + + if (!io || !io->read_all) { + driver_errf(tool, "host file I/O unavailable"); + return 1; + } + + if (nsrc) { + src_in = driver_alloc_zeroed(env, nsrc * sizeof(*src_in)); + objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs)); + if (!src_in || !objs) { + driver_errf(tool, "out of memory"); + goto out; + } + } + if (in->nsources) { + src_lf = driver_alloc_zeroed(env, in->nsources * sizeof(*src_lf)); + if (!src_lf) { + driver_errf(tool, "out of memory"); + goto out; + } + } + if (in->nobject_files) { + obj_lf = driver_alloc_zeroed(env, in->nobject_files * sizeof(*obj_lf)); + obj_in = driver_alloc_zeroed(env, in->nobject_files * sizeof(*obj_in)); + if (!obj_lf || !obj_in) { + driver_errf(tool, "out of memory"); + goto out; + } + } + if (in->narchives) { + arch_lf = driver_alloc_zeroed(env, in->narchives * sizeof(*arch_lf)); + arch_in = driver_alloc_zeroed(env, in->narchives * sizeof(*arch_in)); + if (!arch_lf || !arch_in) { + driver_errf(tool, "out of memory"); + goto out; + } + } + + for (i = 0; i < in->nsources; ++i) { + if (driver_load_bytes(io, tool, in->sources[i], &src_lf[i], &src_in[i]) != + 0) + goto out; + } + for (i = 0; i < in->nsource_memory; ++i) { + src_in[in->nsources + i] = in->source_memory[i]; + } + + for (i = 0; i < in->nobject_files; ++i) { + if (driver_load_bytes(io, tool, in->object_files[i], &obj_lf[i], + &obj_in[i]) != 0) + goto out; + } + for (i = 0; i < in->narchives; ++i) { + if (driver_load_bytes(io, tool, in->archives[i], &arch_lf[i], + &arch_in[i].input) != 0) + goto out; + arch_in[i].link_mode = CFREE_LM_DEFAULT; + arch_in[i].whole_archive = 0; + arch_in[i].group_id = 0; + } + + for (i = 0; i < nsrc; ++i) { + if (cfree_pipeline_compile_obj(pipe, copts, &src_in[i], &objs[i]) != 0) + goto out; + } + + { + CfreeLinkOptions z = {0}; + link_opts = z; + } + link_opts.inputs.objs = objs; + link_opts.inputs.nobjs = nsrc; + link_opts.inputs.obj_bytes = obj_in; + link_opts.inputs.nobj_bytes = in->nobject_files; + link_opts.inputs.archives = arch_in; + link_opts.inputs.narchives = in->narchives; + link_opts.inputs.entry = entry; + link_opts.inputs.extern_resolver = extern_resolver; + link_opts.inputs.extern_resolver_user = extern_resolver_user; + + rc = cfree_pipeline_link_jit(pipe, &link_opts, out_jit); + +out: + if (arch_lf) { + for (i = 0; i < in->narchives; ++i) driver_release_bytes(io, &arch_lf[i]); + } + if (obj_lf) { + for (i = 0; i < in->nobject_files; ++i) + driver_release_bytes(io, &obj_lf[i]); + } + if (src_lf) { + for (i = 0; i < in->nsources; ++i) driver_release_bytes(io, &src_lf[i]); + } + if (arch_in) driver_free(env, arch_in, in->narchives * sizeof(*arch_in)); + if (arch_lf) driver_free(env, arch_lf, in->narchives * sizeof(*arch_lf)); + if (obj_in) driver_free(env, obj_in, in->nobject_files * sizeof(*obj_in)); + if (obj_lf) driver_free(env, obj_lf, in->nobject_files * sizeof(*obj_lf)); + if (src_lf) driver_free(env, src_lf, in->nsources * sizeof(*src_lf)); + if (objs) driver_free(env, objs, nsrc * sizeof(*objs)); + if (src_in) driver_free(env, src_in, nsrc * sizeof(*src_in)); + return rc; +} diff --git a/driver/inputs.h b/driver/inputs.h @@ -0,0 +1,90 @@ +#ifndef CFREE_DRIVER_INPUTS_H +#define CFREE_DRIVER_INPUTS_H + +#include "driver.h" + +/* Shared input handling for tools that take a mixed list of C sources, + * stdin source, object files, and static archives — `cfree run` and + * `cfree dbg` today. Owns parallel arrays sized to the worst-case argv + * slot count plus an optional one-shot stdin slurp. + * + * Usage: + * DriverInputs in = {0}; + * if (driver_inputs_init(&in, env, TOOL, argc) != 0) return 1; + * for (i = 1; i < argc; ++i) { + * int r = driver_inputs_classify(&in, argv[i]); + * if (r < 0) { driver_inputs_release(&in); return 1; } + * if (r) continue; + * ... handle other flags / positionals ... + * } + * if (driver_inputs_count(&in) == 0) { ... no inputs ... } + * rc = driver_inputs_compile_and_jit(&in, pipe, &copts, entry, + * driver_dlsym_resolver, NULL, &jit); + * driver_inputs_release(&in); + * + * Path strings (`.c`, `.o`, `.a`) are borrowed from argv. The stdin + * buffer is heap-owned and freed by driver_inputs_release. */ +typedef struct DriverInputs { + DriverEnv* env; + const char* tool; + size_t bound; + + const char** sources; /* .c .cc .cpp paths (borrowed) */ + uint32_t nsources; + CfreeBytesInput* source_memory; /* "-" stdin slurp; at most one entry */ + uint32_t nsource_memory; + uint8_t* stdin_buf; /* owning storage for the one stdin slurp */ + size_t stdin_size; + const char** object_files; /* .o .obj paths (borrowed) */ + uint32_t nobject_files; + const char** archives; /* .a paths (borrowed) */ + uint32_t narchives; +} DriverInputs; + +/* Allocate the parallel arrays. `argc` is the worst-case input count + * (typically the program's argc). `tool` is the short name used in + * driver_errf messages. Returns 0 on success, 1 on allocation failure + * (already reported). */ +int driver_inputs_init(DriverInputs*, DriverEnv*, const char* tool, int argc); + +/* Release every allocation held by *in, including the stdin buffer. + * Safe to call even after a failed init. */ +void driver_inputs_release(DriverInputs*); + +/* Try to consume one positional input at `arg`. Recognized: + * "-" read C source from stdin (at most once) + * *.c *.cc *.cpp C source path + * *.o *.obj object-file path + * *.a static-archive path + * + * Returns: + * 1 arg matched and was recorded. + * 0 arg is not a recognized input shape (caller decides whether to + * treat it as an unknown flag, etc.). + * -1 arg matched a recognized shape but recording failed (duplicate + * stdin, stdin read failure, allocation failure); the error has + * already been reported via driver_errf. */ +int driver_inputs_classify(DriverInputs*, const char* arg); + +/* Total recorded inputs across every kind. */ +uint32_t driver_inputs_count(const DriverInputs*); + +/* Display name of the first recorded input (sources, then source_memory, + * then object_files, then archives — matches the order used by the + * link/compile loop). Used by callers to synthesize argv[0] for the + * JITed program. Returns NULL when no inputs are recorded. */ +const char* driver_inputs_first_name(const DriverInputs*); + +/* Load every input through env.file_io, compile sources via `pipe` with + * `copts`, and JIT-link the result. `entry`, `extern_resolver`, and + * `extern_resolver_user` populate the matching fields of the internal + * CfreeLinkOptions. On success *out_jit owns the JIT image; on failure + * an error has already been reported. Returns 0 on success, 1 otherwise. */ +int driver_inputs_compile_and_jit(DriverInputs*, CfreePipeline*, + const CfreeCompileOptions* copts, + const char* entry, + void* (*extern_resolver)(void*, const char*), + void* extern_resolver_user, + CfreeJit** out_jit); + +#endif diff --git a/driver/run.c b/driver/run.c @@ -2,6 +2,7 @@ #include "cflags.h" #include "driver.h" +#include "inputs.h" /* `cfree run` — JIT-compile one or more inputs and invoke the entry symbol * (default `main`) in-process. Args after `--` are passed to the JITed @@ -28,18 +29,7 @@ typedef struct RunOptions { CfreeTarget target; /* -target / host */ DriverCflags cf; - - /* Inputs split by suffix, like cc/ld. */ - const char** sources; /* .c paths */ - uint32_t nsources; - CfreeBytesInput* source_memory; /* "-" stdin slurp */ - uint32_t nsource_memory; - uint8_t* stdin_buf; /* owning storage for the one stdin */ - size_t stdin_size; - const char** object_files; /* .o/.obj paths */ - uint32_t nobject_files; - const char** archives; /* .a paths */ - uint32_t narchives; + DriverInputs inputs; char** prog_argv; /* args after `--` */ uint32_t prog_argc; @@ -120,18 +110,12 @@ void driver_help_run(void) { static int run_alloc_arrays(RunOptions* o, int argc) { size_t bound = (size_t)argc; o->argv_bound = bound; - o->sources = driver_alloc_zeroed(o->env, bound * sizeof(*o->sources)); - o->source_memory = - driver_alloc_zeroed(o->env, bound * sizeof(*o->source_memory)); - o->object_files = - driver_alloc_zeroed(o->env, bound * sizeof(*o->object_files)); - o->archives = driver_alloc_zeroed(o->env, bound * sizeof(*o->archives)); o->prog_argv = driver_alloc_zeroed(o->env, bound * sizeof(*o->prog_argv)); - if (!o->sources || !o->source_memory || !o->object_files || !o->archives || - !o->prog_argv) { + if (!o->prog_argv) { driver_errf(RUN_TOOL, "out of memory"); return 1; } + if (driver_inputs_init(&o->inputs, o->env, RUN_TOOL, argc) != 0) return 1; if (driver_cflags_init(&o->cf, o->env, argc) != 0) { driver_errf(RUN_TOOL, "out of memory"); return 1; @@ -175,45 +159,16 @@ static int run_record_mcmodel(RunOptions* o, const char* val) { return 1; } -/* Slurp stdin into a single source_memory entry. Guarded against duplicates - * so a user typo (`cfree run - -`) is surfaced rather than silently leaking - * the first slurp. */ -static int run_record_stdin(RunOptions* o) { - CfreeBytesInput* in; - if (o->stdin_buf) { - driver_errf(RUN_TOOL, "'-' (stdin) may appear at most once"); - return 1; - } - if (!driver_read_stdin(o->env, &o->stdin_buf, &o->stdin_size)) { - driver_errf(RUN_TOOL, "failed to read stdin"); +static int run_classify_positional(RunOptions* o, const char* a) { + int r = driver_inputs_classify(&o->inputs, a); + if (r < 0) return 1; + if (r == 0) { + driver_errf(RUN_TOOL, "input does not have a recognized suffix: %s", a); return 1; } - in = &o->source_memory[o->nsource_memory++]; - in->name = "<stdin>"; - in->data = o->stdin_buf; - in->len = o->stdin_size; return 0; } -static int run_classify_positional(RunOptions* o, const char* a) { - if (driver_streq(a, "-")) return run_record_stdin(o); - if (driver_has_suffix(a, ".c") || driver_has_suffix(a, ".cc") || - driver_has_suffix(a, ".cpp")) { - o->sources[o->nsources++] = a; - return 0; - } - if (driver_has_suffix(a, ".o") || driver_has_suffix(a, ".obj")) { - o->object_files[o->nobject_files++] = a; - return 0; - } - if (driver_has_suffix(a, ".a")) { - o->archives[o->narchives++] = a; - return 0; - } - driver_errf(RUN_TOOL, "input does not have a recognized suffix: %s", a); - return 1; -} - static int run_parse(int argc, char** argv, RunOptions* o) { int i; int after_dash_dash = 0; @@ -329,7 +284,7 @@ static int run_parse(int argc, char** argv, RunOptions* o) { if (run_classify_positional(o, a) != 0) return 1; } - if (o->nsources + o->nsource_memory + o->nobject_files + o->narchives == 0) { + if (driver_inputs_count(&o->inputs) == 0) { driver_errf(RUN_TOOL, "no input files"); run_usage(); return 1; @@ -338,25 +293,15 @@ static int run_parse(int argc, char** argv, RunOptions* o) { /* Synthetic argv[0]. Hosted programs conventionally read argv[0] as * the program name; under `cfree run` there is no executable path, so - * use the first input's display name. Preference matches the input - * scan in run_compile_and_jit (sources before memory before objects - * before archives) so it lines up with whatever shows up first in - * diagnostics. */ - if (o->nsources) o->prog_argv[0] = (char*)o->sources[0]; - else if (o->nsource_memory) o->prog_argv[0] = (char*)o->source_memory[0].name; - else if (o->nobject_files) o->prog_argv[0] = (char*)o->object_files[0]; - else o->prog_argv[0] = (char*)o->archives[0]; + * use the first input's display name. */ + o->prog_argv[0] = (char*)driver_inputs_first_name(&o->inputs); return 0; } static void run_options_release(RunOptions* o) { size_t bound = o->argv_bound; - if (o->stdin_buf) driver_free(o->env, o->stdin_buf, o->stdin_size); + driver_inputs_release(&o->inputs); driver_cflags_fini(&o->cf, o->env); - driver_free(o->env, o->sources, bound * sizeof(*o->sources)); - driver_free(o->env, o->source_memory, bound * sizeof(*o->source_memory)); - driver_free(o->env, o->object_files, bound * sizeof(*o->object_files)); - driver_free(o->env, o->archives, bound * sizeof(*o->archives)); driver_free(o->env, o->prog_argv, bound * sizeof(*o->prog_argv)); } @@ -376,123 +321,12 @@ static void run_fill_compile_opts(const RunOptions* o, * releases via cfree_jit_free. The pipeline must outlive the JIT — its * Compiler backs jit->c, which cfree_jit_lookup dereferences. */ -static int run_compile_and_jit(DriverEnv* env, const RunOptions* o, - CfreePipeline* pipe, CfreeJit** out_jit) { - CfreeEnv cenv = driver_env_to_cfree(env); - const CfreeFileIO* io = cenv.file_io; - DriverLoad* src_lf = NULL; - DriverLoad* obj_lf = NULL; - DriverLoad* arch_lf = NULL; - CfreeBytesInput* src_in = NULL; - CfreeBytesInput* obj_in = NULL; - CfreeBytesInputArchive* arch_in = NULL; - CfreeObjBuilder** objs = NULL; +static int run_compile_and_jit(RunOptions* o, CfreePipeline* pipe, + CfreeJit** out_jit) { CfreeCompileOptions copts; - CfreeLinkOptions link_opts; - uint32_t nsrc = o->nsources + o->nsource_memory; - uint32_t i; - int rc = 1; - - if (!io || !io->read_all) { - driver_errf(RUN_TOOL, "host file I/O unavailable"); - return 1; - } - - if (nsrc) { - src_in = driver_alloc_zeroed(env, nsrc * sizeof(*src_in)); - objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs)); - if (!src_in || !objs) { - driver_errf(RUN_TOOL, "out of memory"); - goto out; - } - } - if (o->nsources) { - src_lf = driver_alloc_zeroed(env, o->nsources * sizeof(*src_lf)); - if (!src_lf) { - driver_errf(RUN_TOOL, "out of memory"); - goto out; - } - } - if (o->nobject_files) { - obj_lf = driver_alloc_zeroed(env, o->nobject_files * sizeof(*obj_lf)); - obj_in = driver_alloc_zeroed(env, o->nobject_files * sizeof(*obj_in)); - if (!obj_lf || !obj_in) { - driver_errf(RUN_TOOL, "out of memory"); - goto out; - } - } - if (o->narchives) { - arch_lf = driver_alloc_zeroed(env, o->narchives * sizeof(*arch_lf)); - arch_in = driver_alloc_zeroed(env, o->narchives * sizeof(*arch_in)); - if (!arch_lf || !arch_in) { - driver_errf(RUN_TOOL, "out of memory"); - goto out; - } - } - - for (i = 0; i < o->nsources; ++i) { - if (driver_load_bytes(io, RUN_TOOL, o->sources[i], &src_lf[i], - &src_in[i]) != 0) - goto out; - } - for (i = 0; i < o->nsource_memory; ++i) { - src_in[o->nsources + i] = o->source_memory[i]; - } - - for (i = 0; i < o->nobject_files; ++i) { - if (driver_load_bytes(io, RUN_TOOL, o->object_files[i], &obj_lf[i], - &obj_in[i]) != 0) - goto out; - } - for (i = 0; i < o->narchives; ++i) { - if (driver_load_bytes(io, RUN_TOOL, o->archives[i], &arch_lf[i], - &arch_in[i].input) != 0) - goto out; - arch_in[i].link_mode = CFREE_LM_DEFAULT; - arch_in[i].whole_archive = 0; - arch_in[i].group_id = 0; - } - run_fill_compile_opts(o, &copts); - for (i = 0; i < nsrc; ++i) { - if (cfree_pipeline_compile_obj(pipe, &copts, &src_in[i], &objs[i]) != 0) - goto out; - } - - { - CfreeLinkOptions z = {0}; - link_opts = z; - } - link_opts.inputs.objs = objs; - link_opts.inputs.nobjs = nsrc; - link_opts.inputs.obj_bytes = obj_in; - link_opts.inputs.nobj_bytes = o->nobject_files; - link_opts.inputs.archives = arch_in; - link_opts.inputs.narchives = o->narchives; - link_opts.inputs.entry = o->entry; - link_opts.inputs.extern_resolver = driver_dlsym_resolver; - link_opts.inputs.extern_resolver_user = NULL; - - rc = cfree_pipeline_link_jit(pipe, &link_opts, out_jit); - -out: - if (arch_lf) { - for (i = 0; i < o->narchives; ++i) driver_release_bytes(io, &arch_lf[i]); - } - if (obj_lf) { - for (i = 0; i < o->nobject_files; ++i) driver_release_bytes(io, &obj_lf[i]); - } - if (src_lf) { - for (i = 0; i < o->nsources; ++i) driver_release_bytes(io, &src_lf[i]); - } - if (arch_in) driver_free(env, arch_in, o->narchives * sizeof(*arch_in)); - if (arch_lf) driver_free(env, arch_lf, o->narchives * sizeof(*arch_lf)); - if (obj_in) driver_free(env, obj_in, o->nobject_files * sizeof(*obj_in)); - if (obj_lf) driver_free(env, obj_lf, o->nobject_files * sizeof(*obj_lf)); - if (src_lf) driver_free(env, src_lf, o->nsources * sizeof(*src_lf)); - if (objs) driver_free(env, objs, nsrc * sizeof(*objs)); - if (src_in) driver_free(env, src_in, nsrc * sizeof(*src_in)); - return rc; + return driver_inputs_compile_and_jit(&o->inputs, pipe, &copts, o->entry, + driver_dlsym_resolver, NULL, out_jit); } typedef int (*MainFn)(int, char**); @@ -533,7 +367,7 @@ int driver_run(int argc, char** argv) { return 1; } - rc = run_compile_and_jit(&env, &ro, pipe, &jit); + rc = run_compile_and_jit(&ro, pipe, &jit); if (rc != 0) { driver_pipeline_free(pipe); run_options_release(&ro);