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:
| M | doc/DBG.md | | | 64 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
| M | driver/dbg.c | | | 85 | +++++++++++++++++++------------------------------------------------------------ |
| A | driver/inputs.c | | | 213 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | driver/inputs.h | | | 90 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | driver/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);