commit 3f79201def3efe20717a163fee4ca0142a4f0dbd
parent f95c8fbb62afcf93138dc8dd542c0e89ef107005
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 14:00:32 -0700
Migrate codebase to new component-split public API
Migrate src/, lang/, driver/, and test/ to consume the public API as
rewritten in commit f95c8fb (single <cfree.h> umbrella replaced by per-
component <cfree/...> headers).
Highlights:
- CfreeEnv -> CfreeContext; execmem/dbg_os/jit_tls move to CfreeJitHost /
CfreeDbgHost passed at link/session creation.
- Status-returning entry points throughout (compiler_new, writer_mem,
arena_new, obj_open, dwarf_open, ar_iter_new, link_*, jit_session_*).
- CfreeBytesInput split into CfreeBytes (objects, archives, linker
inputs) and CfreeSourceInput (source compile only).
- CfreeCompileOptions split into CCompile/AsmCompile/FrontendCompile
with shared CodeOptions, PreprocessOptions, DiagnosticOptions.
- New CfreeObjBuilder public surface implemented in src/api/objbuilder.c
over the internal obj_*; new CfreeObjFile reader in src/api/objfile.c
wraps the existing readers.
- cfree_pipeline_* removed; driver/ orchestrates compile->link directly.
- DWARF loc_read takes a memory-reader callback; the consumer is no
longer dependent on cfree/dbg.h.
- Per-language dispatch in driver cc/run via cfree_language_for_path.
- doc/api-migration.md added as the canonical translation rubric.
Diffstat:
107 files changed, 4397 insertions(+), 3890 deletions(-)
diff --git a/doc/api-migration.md b/doc/api-migration.md
@@ -0,0 +1,302 @@
+# Public API migration
+
+The single `<cfree.h>` header has been replaced by a component-split
+public surface under `<cfree/...>`. Headers in `include/` are the new
+contract — internals (`src/`, `lang/`, `driver/`, `test/`) must be
+rewritten to match. **No compat shims.**
+
+## New header layout
+
+| Header | What it covers |
+| --- | --- |
+| `cfree/core.h` | `CfreeCompiler`, `CfreeContext`, `CfreeHeap`, `CfreeDiagSink`, `CfreeWriter`, `CfreeFileIO`, `CfreeMetrics`, `CfreeTarget`, `CfreeStatus`, `CfreeIterResult`, `CfreeSrcLoc`, `CfreeBytes`, `CfreeSymBind/Kind`, `CfreeSym`, lifecycle, `cfree_writer_mem`. |
+| `cfree/source.h` | `cfree_source_add_*`, `CfreeSourceFile`. |
+| `cfree/support/arena.h` | Public arena (used by frontends and link script parser). |
+| `cfree/support/hashmap.h` | Public hashmap. |
+| `cfree/objmodel.h` | Format-neutral object types: `CfreeObjSection/Symbol/Group`, `CfreeObjSecInfo`, `CfreeObjSymInfo`, `CfreeObjReloc`, `CfreeRelocKind`. |
+| `cfree/objbuild.h` | `CfreeObjBuilder` API: `cfree_obj_builder_*`. |
+| `cfree/object.h` | `CfreeObjFile` reader: `cfree_obj_open` etc. |
+| `cfree/compile.h` | `cfree_compile_c_obj{,_emit}`, `_asm_`, `_source_`. `CfreeCCompileOptions`, `CfreeAsmCompileOptions`, `CfreeFrontendCompileOptions`. `CfreeLanguage`, `CfreeSourceInput`, `cfree_register_frontend`, dep iter. |
+| `cfree/link.h` | `CfreeExeLinkOptions`, `CfreeSharedLinkOptions`, `CfreeJitLinkOptions`, `CfreeLinkScript`, parse helper. Takes `CfreeJitHost` for JIT link. |
+| `cfree/jit.h` | `CfreeJit*`, `CfreeJitHost { execmem, tls }`, image inspection. |
+| `cfree/dbg.h` | `CfreeJitSession`, `CfreeDbgHost { os }`, breakpoints/resume. |
+| `cfree/emu.h` | `cfree_emu_run/new/step/lookup/free`. |
+| `cfree/dwarf.h` | `CfreeDebugInfo`, `cfree_dwarf_open/free`, query API. `loc_read` takes a memory-reader callback, not a JIT session. |
+| `cfree/arch.h` | `CfreeArchReg`, `CfreeUnwindFrame`, register name/index helpers. |
+| `cfree/archive.h` | `cfree_ar_*` over `CfreeBytes` + `CfreeContext`. |
+| `cfree/disasm.h` | `cfree_disasm_iter_new` over a `CfreeDisasmContext`, `cfree_disasm_obj`. |
+| `cfree/frontend.h` | Frontend convenience: includes `cg.h`, `source.h`, `support/arena.h`; declares `cfree_frontend_run`, metrics bridge, fatal helpers. |
+| `cfree/cg.h` | Codegen API. Includes `cfree/core.h` + `cfree/objbuild.h`. |
+
+`<cfree.h>` no longer exists. Every TU must include only what it needs.
+
+## Type-level renames and reshapes
+
+### `CfreeEnv` → `CfreeContext`
+
+```c
+typedef struct CfreeContext {
+ CfreeHeap *heap;
+ const CfreeFileIO *file_io; /* may be NULL */
+ CfreeDiagSink *diag;
+ const CfreeMetrics *metrics; /* may be NULL */
+ int64_t now; /* negative when host has no clock */
+} CfreeContext;
+```
+
+`CfreeEnv.execmem`, `.dbg_os`, `.jit_tls` are **removed**. They are now
+passed as `CfreeJitHost { execmem, tls }` to `cfree_link_jit` and
+`CfreeDbgHost { os }` to `cfree_jit_session_new`.
+
+Internal: `Compiler.env` becomes `Compiler.ctx` (type `const
+CfreeContext*`). `CfreeContext cfree_compiler_context(CfreeCompiler*)`
+is the public accessor that returns a value copy.
+
+### `CfreeBytesInput` → split
+
+The old single shape carried `name + data + len + lang`. It is now two:
+
+```c
+typedef struct CfreeBytes { /* used everywhere except source compile */
+ const char *name;
+ const uint8_t *data;
+ size_t len;
+} CfreeBytes;
+
+typedef struct CfreeSourceInput { /* used by cfree_compile_source_obj* */
+ CfreeBytes bytes;
+ CfreeLanguage lang;
+} CfreeSourceInput;
+```
+
+Linker archive input is now `CfreeLinkArchiveInput { bytes + flags... }`,
+not `CfreeBytesInputArchive`.
+
+### `CfreeCompileOptions` → split per language
+
+```c
+typedef struct CfreeCodeOptions {
+ int opt_level, debug_info;
+ uint64_t epoch;
+ const CfreePathPrefixMap *path_map;
+ uint32_t npath_map;
+} CfreeCodeOptions;
+
+typedef struct CfreePreprocessOptions {
+ const char *const *include_dirs; uint32_t ninclude_dirs;
+ const char *const *system_include_dirs; uint32_t nsystem_include_dirs;
+ const CfreeDefine *defines; uint32_t ndefines;
+ const char *const *undefines; uint32_t nundefines;
+} CfreePreprocessOptions;
+
+typedef struct CfreeDiagnosticOptions {
+ int warnings_are_errors;
+ uint32_t max_errors;
+} CfreeDiagnosticOptions;
+
+typedef struct CfreeCCompileOptions {
+ CfreeCodeOptions code;
+ CfreePreprocessOptions preprocess;
+ CfreeDiagnosticOptions diagnostics;
+} CfreeCCompileOptions;
+
+typedef struct CfreeAsmCompileOptions {
+ CfreeCodeOptions code;
+ CfreeDiagnosticOptions diagnostics;
+} CfreeAsmCompileOptions;
+
+typedef struct CfreeFrontendCompileOptions {
+ CfreeCodeOptions code;
+ CfreeDiagnosticOptions diagnostics;
+ const void *language_options;
+} CfreeFrontendCompileOptions;
+```
+
+`cfree_compile_obj` → `cfree_compile_c_obj`, `cfree_compile_asm_obj`,
+`cfree_compile_source_obj` (registered frontend). All have `_emit`
+variants. Frontend hook signature is now
+`CfreeStatus (*)(CfreeCompiler*, const CfreeFrontendCompileOptions*, const CfreeSourceInput*, CfreeObjBuilder*)`.
+
+### Status-returning APIs
+
+Every entry that used to return `int` (0 ok, nonzero error) or a pointer
+(NULL on failure) returns `CfreeStatus` and writes the result to an out
+parameter. Examples:
+
+| Old | New |
+| --- | --- |
+| `CfreeCompiler* cfree_compiler_new(t,e)` | `CfreeStatus cfree_compiler_new(t, ctx, CfreeCompiler **out)` |
+| `CfreeWriter* cfree_writer_mem(h)` | `CfreeStatus cfree_writer_mem(h, CfreeWriter **out)` |
+| `CfreeArena* cfree_arena_new(h, blk)` | `CfreeStatus cfree_arena_new(h, blk, CfreeArena **out)` |
+| `CfreeObjFile* cfree_obj_open(env, in)` | `CfreeStatus cfree_obj_open(ctx, bytes, CfreeObjFile **out)` |
+| `void cfree_obj_close` | `void cfree_obj_free` |
+| `CfreeObjSecInfo cfree_obj_section(o,i)` | `CfreeStatus cfree_obj_section(o, i, CfreeObjSecInfo *out)` |
+| `const u8* cfree_obj_section_data(o,i,*)` | `CfreeStatus cfree_obj_section_data(o, i, const uint8_t **out, size_t *len_out)` |
+| `CfreeObjSymIter* cfree_obj_symiter_new` | `CfreeStatus cfree_obj_symiter_new(file, CfreeObjSymIter **out)`; iterator next returns `CfreeIterResult`. |
+| `int cfree_obj_symiter_next(it, *out)` | `CfreeIterResult cfree_obj_symiter_next(it, CfreeObjSymInfo *out)` |
+| `CfreeDebugInfo* cfree_dwarf_open(c,o)` | `CfreeStatus cfree_dwarf_open(ctx, obj, CfreeDebugInfo **out)` |
+| `void cfree_dwarf_close` | `void cfree_dwarf_free` |
+| `int cfree_dwarf_*` (queries) | `CfreeStatus cfree_dwarf_*` (queries; semantics carried by status enum) |
+| `CfreeArIter* cfree_ar_iter_init(it,b)` | `CfreeStatus cfree_ar_iter_new(ctx, bytes, CfreeArIter **out)`; iterator next returns `CfreeIterResult`. |
+| `CfreeJit* cfree_link_jit(...)` | `CfreeStatus cfree_link_jit(c, opts, host, CfreeJit **out_jit)` |
+| `CfreeJitSession* cfree_jit_session_new` | `CfreeStatus cfree_jit_session_new(jit, dbghost, CfreeJitSession **out)` |
+| `int cfree_jit_session_*` | `CfreeStatus cfree_jit_session_*` |
+| `CfreeDisasmIter* cfree_disasm_iter_new` | `CfreeStatus cfree_disasm_iter_new(const CfreeDisasmContext*, bytes, len, vaddr, const CfreeObjFile* annot, CfreeDisasmIter **out)`. Iterator next returns `CfreeIterResult`. |
+| `int cfree_obj_disasm` | `CfreeStatus cfree_disasm_obj(ctx, objfile, w)` and `cfree_disasm_obj_bytes(ctx, bytes, w)`. |
+| `int cfree_register_frontend` | `CfreeStatus cfree_register_frontend` |
+| `int cfree_link_script_parse(c, t, l, *)` | `CfreeStatus cfree_link_script_parse(const CfreeContext*, t, l, CfreeLinkScript **out)`; pair-free signature `cfree_link_script_free(const CfreeContext*, CfreeLinkScript*)`. |
+| `u32 cfree_source_add_file(c,p,sys)` | `CfreeStatus cfree_source_add_file(c, p, sys, uint32_t *id_out)` (analogous for memory/builtin/include/file). |
+| `int cfree_arch_register_index/at` | `CfreeStatus` variants. |
+
+`CfreeWriter` vtable now returns `CfreeStatus` on `write` and `seek`,
+exposes `status` (not `error`); the dispatch helpers in
+`cfree/core.h` return `CfreeStatus`.
+
+`CfreeFileIO.read_all` and `.open_writer` now return `CfreeStatus` and
+take an out-parameter (already declared in the new header).
+
+`CfreeDbgOs` vtable methods that used to return `int` now return
+`CfreeStatus` (e.g. `thread_start`, `event_new` takes `void **event_out`).
+
+### `cfree_pipeline_*` is gone
+
+The driver synthesizes its own thin orchestrator (one `CfreeCompiler` +
+the call sequence). All `cfree_pipeline_*` call sites in `driver/` must
+inline the equivalent compose: build compiler → compile_c/asm → keep
+builder live → link.
+
+### `CfreeJit` linker host
+
+`cfree_link_jit` now requires a `const CfreeJitHost*` and writes the
+result through `CfreeJit **out_jit`. The host bundles `execmem` + `tls`
+that used to live on `CfreeEnv`. Drivers construct one per build.
+
+### Object-builder public API
+
+`cfree/objbuild.h` exposes the format-neutral build API atop the
+internal `obj_*`. The public surface uses `CfreeSym` (interned through
+the compiler) for section/symbol names, `CfreeObjSection`/`Symbol`/`Group`
+opaque-int handles, and `CfreeRelocKind { arch, obj_fmt, code }` for
+relocations. The implementation lives in `src/api/objbuilder.c` and is a
+thin adapter around `src/obj/obj.h`. Section indices wire through 1:1;
+the public API uses `CFREE_SECTION_NONE` = `UINT32_MAX` while the
+internal sentinel is `OBJ_SEC_NONE = 0` — convert at the boundary.
+
+### Object-file public reader
+
+`CfreeObjFile` is the public read handle. It can be:
+
+ - opened from bytes via `cfree_obj_open(const CfreeContext*, const CfreeBytes*, CfreeObjFile **out)`,
+ - obtained for inspection from a `CfreeJit` via `cfree_jit_view(jit)`
+ (the returned `CfreeObjFile *` is non-owned).
+
+Internally the reader keeps a borrowed `ObjBuilder*` (so symbol/reloc
+iteration reuses the existing read path). The public API never exposes
+the internal handle; everything is funnelled through `CfreeObjFmt`,
+`CfreeObjSecInfo`, `CfreeObjSymInfo`, `CfreeObjReloc`.
+
+### Source registration
+
+`source_add_*` internal functions now return `CfreeStatus` and write the
+new file id to an out parameter (the public API requires this; the
+internal callers are easier to update at the same time). The public
+`CfreeSourceFile` is unchanged in shape.
+
+### Arena public type
+
+`cfree_arena_new` returns `CfreeStatus`. Callers receive the arena
+through an out pointer. The macros (`cfree_arena_new_obj`, etc.) are
+unchanged.
+
+### Per-component status codes
+
+The full enum:
+
+```c
+typedef enum CfreeStatus {
+ CFREE_OK = 0, CFREE_ERR, CFREE_NOMEM, CFREE_INVALID, CFREE_UNSUPPORTED,
+ CFREE_MALFORMED, CFREE_IO, CFREE_NOT_FOUND, CFREE_AMBIGUOUS,
+} CfreeStatus;
+```
+
+Pick the most specific one available. Old return semantics map as:
+- bad argument → `CFREE_INVALID`
+- allocation failure → `CFREE_NOMEM`
+- input not found in DWARF / link script / archive → `CFREE_NOT_FOUND`
+- ambiguous DWARF line resolution → `CFREE_AMBIGUOUS`
+- malformed bytes (bad magic, truncated input) → `CFREE_MALFORMED`
+- IO error from a `CfreeWriter` or `CfreeFileIO` → `CFREE_IO`
+- generic compile failure with diagnostics already emitted → `CFREE_ERR`
+- unsupported feature / arch → `CFREE_UNSUPPORTED`
+
+## Translation rubric for call-site updates
+
+1. **Headers** — replace `#include <cfree.h>` with the specific
+ `<cfree/X.h>` headers actually needed. Frontends include
+ `<cfree/frontend.h>`. Drivers compose what they need. Internals
+ include `src/...` and the relevant `cfree/X.h`.
+
+2. **`CfreeEnv` → `CfreeContext`** — drop `execmem/dbg_os/jit_tls`
+ fields at construction sites; pass them to the JIT/dbg hosts later.
+ Internal `Compiler.env` becomes `Compiler.ctx`.
+
+3. **`CfreeBytesInput` for non-source uses** → `CfreeBytes`. Drop the
+ `lang` field. For source compile entries, build a
+ `CfreeSourceInput`.
+
+4. **`CfreeCompileOptions` users** — pick the right specialization:
+ - C → `CfreeCCompileOptions { .code, .preprocess, .diagnostics }`
+ - asm → `CfreeAsmCompileOptions { .code, .diagnostics }`
+ - registered frontend → `CfreeFrontendCompileOptions`
+
+5. **Return value rewrites** — for every API marked Status, do
+ `CfreeStatus st = cfree_X(... &out); if (st != CFREE_OK) ...`. Don't
+ discard non-OK statuses silently.
+
+6. **Iterators** — `next()` returns `CfreeIterResult` (`CFREE_ITER_ITEM`,
+ `_END`, `_ERROR`). Migrate `while (...next(&out))` loops to
+ `for (;;) { CfreeIterResult r = next(it, &out); if (r != CFREE_ITER_ITEM) break; ... }`.
+
+7. **JIT/dbg construction** — driver builds:
+
+ ```c
+ CfreeJitHost jhost = { .execmem = &my_execmem, .tls = &my_tls };
+ CfreeJit *jit;
+ CfreeStatus st = cfree_link_jit(c, &opts, &jhost, &jit);
+
+ CfreeDbgHost dhost = { .os = &my_dbg_os };
+ CfreeJitSession *sess;
+ st = cfree_jit_session_new(jit, &dhost, &sess);
+ ```
+
+8. **DWARF loc read** — replace `cfree_jit_session_*` based reads with
+ a small `CfreeDwarfReadMemFn` adapter that calls
+ `cfree_jit_session_read_mem` on a captured session. The DWARF API no
+ longer pulls in `cfree/dbg.h`.
+
+9. **`cfree_pipeline_*` call sites** — replaced with explicit
+ `cfree_compiler_new` + `cfree_compile_c_obj` (etc.) + `cfree_link_*`
+ sequences. The driver carries the resulting compiler/builder
+ ownership directly.
+
+10. **Linker script** — `cfree_link_script_parse(ctx, txt, len, &out)`;
+ free with `cfree_link_script_free(ctx, out)`.
+
+## Internal aliases (src/core/core.h)
+
+`Compiler`, `Heap`, `DiagSink`, `Writer`, `Target`, `ObjBuilder`,
+`ArchKind`, `OSKind`, `ObjFmt` aliases stay. Add `Context` aliasing
+`CfreeContext`. Rename `Compiler.env` → `Compiler.ctx`. Update every
+reader. `compiler_init` takes `const CfreeContext*`.
+
+## Things that **don't** change
+
+- `CfreeSym` is still a `uint32_t`.
+- `CfreeSrcLoc` shape unchanged.
+- Internal `obj_*`, `read_elf*`, `read_macho*`, `read_coff`, `read_wasm`
+ signatures don't move — only their public wrappers do.
+- Internal `link_*`, `dwarf_*`, `mc_*`, `cg_*` keep their internal
+ shapes.
+- The codegen public API (`cfree/cg.h`) is largely intact; only
+ `cfree_cg_new` and `cfree_cg_type_record_field` switch to
+ Status-return shapes.
diff --git a/driver/ar.c b/driver/ar.c
@@ -1,5 +1,9 @@
#include "driver.h"
+#include <cfree/archive.h>
+#include <cfree/core.h>
+#include <cfree/object.h>
+
/* `cfree ar` — POSIX ar archive front-end.
*
* Supported operations (mutually exclusive):
@@ -127,12 +131,13 @@ static int ar_name_selected(const char* name, int argc, char** argv,
return 0;
}
-/* Open the archive, init the iterator. Caller must release fd via cenv. */
+/* Open the archive bytes via file_io. Caller releases fd via ctx. */
static int ar_open_for_read(DriverEnv* env, const char* path,
- CfreeEnv* cenv_out, CfreeFileData* fd_out,
- CfreeBytesInput* input_out) {
- *cenv_out = driver_env_to_cfree(env);
- if (!cenv_out->file_io->read_all(cenv_out->file_io->user, path, fd_out)) {
+ CfreeContext* ctx_out, CfreeFileData* fd_out,
+ CfreeBytes* input_out) {
+ *ctx_out = driver_env_to_context(env);
+ if (ctx_out->file_io->read_all(ctx_out->file_io->user, path, fd_out) !=
+ CFREE_OK) {
driver_errf(AR_TOOL, "failed to read: %s", path);
return 0;
}
@@ -143,70 +148,75 @@ static int ar_open_for_read(DriverEnv* env, const char* path,
}
static int ar_do_list(DriverEnv* env, const char* archive_path, int verbose) {
- CfreeEnv cenv;
+ CfreeContext ctx;
CfreeFileData fd = {0};
- CfreeBytesInput input;
+ CfreeBytes input;
CfreeWriter* out;
int rc;
- if (!ar_open_for_read(env, archive_path, &cenv, &fd, &input)) return 1;
+ if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1;
out = driver_stdout_writer(env);
if (!out) {
driver_errf(AR_TOOL, "out of memory");
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return 1;
}
if (verbose) {
- CfreeArIter it;
+ CfreeArIter* it = NULL;
CfreeArMember m;
- if (!cfree_ar_iter_init(&it, &input)) {
+ if (cfree_ar_iter_new(&ctx, &input, &it) != CFREE_OK) {
driver_errf(AR_TOOL, "not an archive: %s", archive_path);
cfree_writer_close(out);
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return 1;
}
- while (cfree_ar_iter_next(&it, &m))
+ for (;;) {
+ CfreeIterResult r = cfree_ar_iter_next(it, &m);
+ if (r != CFREE_ITER_ITEM) break;
driver_printf("%zu\t%s\n", m.size, m.name);
- rc = cfree_writer_error(out) ? 1 : 0;
+ }
+ cfree_ar_iter_free(it);
+ rc = cfree_writer_status(out) == CFREE_OK ? 0 : 1;
} else {
- rc = cfree_ar_list(&input, out);
+ rc = cfree_ar_list(&input, out) == CFREE_OK ? 0 : 1;
if (rc) driver_errf(AR_TOOL, "failed to read archive: %s", archive_path);
}
cfree_writer_close(out);
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
static int ar_do_extract(DriverEnv* env, const char* archive_path, int argc,
char** argv, int start, int verbose) {
- CfreeEnv cenv;
+ CfreeContext ctx;
CfreeFileData fd = {0};
- CfreeBytesInput input;
- CfreeArIter it;
+ CfreeBytes input;
+ CfreeArIter* it = NULL;
CfreeArMember m;
int rc = 0;
- if (!ar_open_for_read(env, archive_path, &cenv, &fd, &input)) return 1;
- if (!cfree_ar_iter_init(&it, &input)) {
+ if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1;
+ if (cfree_ar_iter_new(&ctx, &input, &it) != CFREE_OK) {
driver_errf(AR_TOOL, "not an archive: %s", archive_path);
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return 1;
}
- while (cfree_ar_iter_next(&it, &m)) {
+ for (;;) {
CfreeWriter* out;
+ CfreeIterResult r = cfree_ar_iter_next(it, &m);
+ if (r != CFREE_ITER_ITEM) break;
if (!ar_name_selected(m.name, argc, argv, start)) continue;
- out = cenv.file_io->open_writer(cenv.file_io->user, m.name);
- if (!out) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, m.name, &out) != CFREE_OK) {
driver_errf(AR_TOOL, "failed to open: %s", m.name);
rc = 1;
continue;
}
if (m.size) cfree_writer_write(out, m.data, m.size);
- if (cfree_writer_error(out)) {
+ if (cfree_writer_status(out) != CFREE_OK) {
driver_errf(AR_TOOL, "write failed: %s", m.name);
rc = 1;
}
@@ -214,45 +224,50 @@ static int ar_do_extract(DriverEnv* env, const char* archive_path, int argc,
if (verbose) driver_printf("x - %s\n", m.name);
}
- cenv.file_io->release(cenv.file_io->user, &fd);
+ cfree_ar_iter_free(it);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
static int ar_do_print(DriverEnv* env, const char* archive_path, int argc,
char** argv, int start, int verbose) {
- CfreeEnv cenv;
+ CfreeContext ctx;
CfreeFileData fd = {0};
- CfreeBytesInput input;
- CfreeArIter it;
+ CfreeBytes input;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeWriter* out;
/* prefix when 0 or >=2 filters, or when verbose forces it */
int header = verbose || (argc - start) != 1;
int rc = 0;
- if (!ar_open_for_read(env, archive_path, &cenv, &fd, &input)) return 1;
- if (!cfree_ar_iter_init(&it, &input)) {
+ if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1;
+ if (cfree_ar_iter_new(&ctx, &input, &it) != CFREE_OK) {
driver_errf(AR_TOOL, "not an archive: %s", archive_path);
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return 1;
}
out = driver_stdout_writer(env);
if (!out) {
driver_errf(AR_TOOL, "out of memory");
- cenv.file_io->release(cenv.file_io->user, &fd);
+ cfree_ar_iter_free(it);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return 1;
}
- while (cfree_ar_iter_next(&it, &m)) {
+ for (;;) {
+ CfreeIterResult r = cfree_ar_iter_next(it, &m);
+ if (r != CFREE_ITER_ITEM) break;
if (!ar_name_selected(m.name, argc, argv, start)) continue;
if (header) driver_printf("%s(%s):\n", archive_path, m.name);
if (m.size) cfree_writer_write(out, m.data, m.size);
}
- if (cfree_writer_error(out)) rc = 1;
+ if (cfree_writer_status(out) != CFREE_OK) rc = 1;
+ cfree_ar_iter_free(it);
cfree_writer_close(out);
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
@@ -269,11 +284,11 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
char** member_paths, int has_r, int has_c, int has_s,
int has_v) {
uint32_t nnew = nmembers > 0 ? (uint32_t)nmembers : 0u;
- CfreeBytesInput* members = NULL;
+ CfreeBytes* members = NULL;
CfreeFileData* new_fds = NULL;
size_t members_cap = 0;
uint32_t nm = 0; /* count of entries in `members` */
- CfreeEnv cenv = driver_env_to_cfree(env);
+ CfreeContext ctx = driver_env_to_context(env);
CfreeWriter* out = NULL;
CfreeArWriteOptions opts = {0};
CfreeFileData old_fd = {0};
@@ -299,27 +314,32 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
/* `r`: read existing archive (if any) and seed `members` with it. */
if (has_r) {
- CfreeBytesInput input;
- CfreeArIter it;
+ CfreeBytes input;
+ CfreeArIter* it = NULL;
CfreeArMember m;
uint32_t nold = 0;
- if (cenv.file_io->read_all(cenv.file_io->user, archive_path, &old_fd)) {
+ if (ctx.file_io->read_all(ctx.file_io->user, archive_path, &old_fd) ==
+ CFREE_OK) {
have_old = 1;
input.name = archive_path;
input.data = old_fd.data;
input.len = old_fd.size;
- if (!cfree_ar_iter_init(&it, &input)) {
+ if (cfree_ar_iter_new(&ctx, &input, &it) != CFREE_OK) {
driver_errf(AR_TOOL, "not an archive: %s", archive_path);
rc = 1;
goto done;
}
/* Count first so we can size the array exactly, and total the
* name bytes so we can stash a stable copy of each name. */
- while (cfree_ar_iter_next(&it, &m)) {
+ for (;;) {
+ CfreeIterResult r = cfree_ar_iter_next(it, &m);
+ if (r != CFREE_ITER_ITEM) break;
nold++;
old_name_bytes += driver_strlen(m.name) + 1;
}
+ cfree_ar_iter_free(it);
+ it = NULL;
} else if (!has_c) {
/* POSIX: warn (not an error) when `r` creates a new archive. */
driver_errf(AR_TOOL, "creating %s", archive_path);
@@ -327,7 +347,7 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
members_cap = (size_t)nold + (size_t)nnew;
if (members_cap > 0) {
- members = (CfreeBytesInput*)driver_alloc_zeroed(
+ members = (CfreeBytes*)driver_alloc_zeroed(
env, members_cap * sizeof(*members));
if (!members) {
driver_errf(AR_TOOL, "out of memory");
@@ -346,10 +366,17 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
goto done;
}
}
- cfree_ar_iter_init(&it, &input);
- while (cfree_ar_iter_next(&it, &m) && nm < nold) {
- char* dst = old_name_storage + cursor;
+ if (cfree_ar_iter_new(&ctx, &input, &it) != CFREE_OK) {
+ driver_errf(AR_TOOL, "iter re-open failed");
+ rc = 1;
+ goto done;
+ }
+ while (nm < nold) {
+ CfreeIterResult r = cfree_ar_iter_next(it, &m);
+ char* dst;
const char* p;
+ if (r != CFREE_ITER_ITEM) break;
+ dst = old_name_storage + cursor;
for (p = m.name; *p; ++p) *dst++ = *p;
*dst++ = '\0';
members[nm].name = old_name_storage + cursor;
@@ -358,12 +385,13 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
cursor = (size_t)(dst - old_name_storage);
nm++;
}
+ cfree_ar_iter_free(it);
}
} else {
/* `c`: overwrite. */
members_cap = (size_t)nnew;
if (members_cap > 0) {
- members = (CfreeBytesInput*)driver_alloc_zeroed(
+ members = (CfreeBytes*)driver_alloc_zeroed(
env, members_cap * sizeof(*members));
if (!members) {
driver_errf(AR_TOOL, "out of memory");
@@ -384,7 +412,8 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
for (i = 0; i < nnew; ++i) {
const char* path = member_paths[i];
const char* base = driver_basename(path);
- if (!cenv.file_io->read_all(cenv.file_io->user, path, &new_fds[i])) {
+ if (ctx.file_io->read_all(ctx.file_io->user, path, &new_fds[i]) !=
+ CFREE_OK) {
driver_errf(AR_TOOL, "failed to read: %s", path);
rc = 1;
goto done;
@@ -433,9 +462,9 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
goto done;
}
for (i = 0; i < nm; ++i) {
- CfreeBytesInput in;
- CfreeObjFile* of;
- CfreeObjSymIter* it;
+ CfreeBytes in;
+ CfreeObjFile* of = NULL;
+ CfreeObjSymIter* it = NULL;
CfreeObjSymInfo si;
uint32_t count = 0;
size_t name_bytes = 0;
@@ -448,20 +477,20 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
in.name = members[i].name;
in.data = members[i].data;
in.len = members[i].len;
- in.lang = 0;
- of = cfree_obj_open(&cenv, &in);
- if (!of) continue; /* not an object file → no symbols */
+ if (cfree_obj_open(&ctx, &in, &of) != CFREE_OK)
+ continue; /* not an object file → no symbols */
/* Pass A: count globally-defined symbols and total name bytes. */
- it = cfree_obj_symiter_new(of);
- if (!it) {
- cfree_obj_close(of);
+ if (cfree_obj_symiter_new(of, &it) != CFREE_OK) {
+ cfree_obj_free(of);
driver_errf(AR_TOOL, "out of memory");
rc = 1;
goto done;
}
- while (cfree_obj_symiter_next(it, &si)) {
+ for (;;) {
+ CfreeIterResult r = cfree_obj_symiter_next(it, &si);
+ if (r != CFREE_ITER_ITEM) break;
if (si.bind != CFREE_SB_GLOBAL) continue;
if (si.section == CFREE_SECTION_NONE) continue;
if (!si.name || !si.name[0]) continue;
@@ -475,16 +504,16 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
cfree_obj_symiter_free(it);
if (count == 0) {
- cfree_obj_close(of);
+ cfree_obj_free(of);
continue;
}
/* Single allocation: [name_arr][name_storage]. Names are copied
- * out of the obj file before close so they outlive cfree_obj_close. */
+ * out of the obj file before close so they outlive cfree_obj_free. */
alloc_sz = (size_t)count * sizeof(const char*) + name_bytes;
blob = (char*)driver_alloc_zeroed(env, alloc_sz);
if (!blob) {
- cfree_obj_close(of);
+ cfree_obj_free(of);
driver_errf(AR_TOOL, "out of memory");
rc = 1;
goto done;
@@ -493,19 +522,22 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
name_storage = blob + (size_t)count * sizeof(const char*);
/* Pass B: copy names. */
- it = cfree_obj_symiter_new(of);
- if (!it) {
+ if (cfree_obj_symiter_new(of, &it) != CFREE_OK) {
driver_free(env, blob, alloc_sz);
- cfree_obj_close(of);
+ cfree_obj_free(of);
driver_errf(AR_TOOL, "out of memory");
rc = 1;
goto done;
}
{
uint32_t k = 0;
- while (cfree_obj_symiter_next(it, &si) && k < count) {
+ for (;;) {
+ CfreeIterResult r;
const char* p;
char* dst;
+ if (k >= count) break;
+ r = cfree_obj_symiter_next(it, &si);
+ if (r != CFREE_ITER_ITEM) break;
if (si.bind != CFREE_SB_GLOBAL) continue;
if (si.section == CFREE_SECTION_NONE) continue;
if (!si.name || !si.name[0]) continue;
@@ -519,7 +551,7 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
count = k;
}
cfree_obj_symiter_free(it);
- cfree_obj_close(of);
+ cfree_obj_free(of);
sym_allocs[i] = blob;
sym_alloc_szs[i] = alloc_sz;
@@ -530,13 +562,13 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
opts.member_symbols = msyms;
}
- out = cenv.file_io->open_writer(cenv.file_io->user, archive_path);
- if (!out) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) !=
+ CFREE_OK) {
driver_errf(AR_TOOL, "failed to open: %s", archive_path);
rc = 1;
} else {
- rc = cfree_ar_write(out, members, nm, &opts);
- if (rc == 0 && cfree_writer_error(out)) rc = 1;
+ rc = cfree_ar_write(out, members, nm, &opts) == CFREE_OK ? 0 : 1;
+ if (rc == 0 && cfree_writer_status(out) != CFREE_OK) rc = 1;
cfree_writer_close(out);
}
@@ -552,13 +584,13 @@ done:
if (new_fds) {
for (i = 0; i < nnew; ++i) {
if (new_fds[i].data)
- cenv.file_io->release(cenv.file_io->user, &new_fds[i]);
+ ctx.file_io->release(ctx.file_io->user, &new_fds[i]);
}
driver_free(env, new_fds, (size_t)nnew * sizeof(*new_fds));
}
if (members) driver_free(env, members, members_cap * sizeof(*members));
if (old_name_storage) driver_free(env, old_name_storage, old_name_bytes);
- if (have_old) cenv.file_io->release(cenv.file_io->user, &old_fd);
+ if (have_old) ctx.file_io->release(ctx.file_io->user, &old_fd);
return rc;
}
diff --git a/driver/as.c b/driver/as.c
@@ -4,10 +4,13 @@
#include "driver.h"
#include "lang/c/c.h"
+#include <cfree/compile.h>
+#include <cfree/core.h>
+
/* `cfree as` — standalone assembler. Reads a single text source, writes a
- * relocatable object via cfree_compile_obj_emit with the input tagged
- * CFREE_LANG_ASM. `.S` inputs are preprocessed first; `.s` inputs are not.
- * The accepted input is a GAS subset (AT&T syntax on x86); see <cfree.h>. */
+ * relocatable object via cfree_compile_asm_obj_emit. `.S` inputs are
+ * preprocessed first via cfree_c_preprocess; `.s` inputs are not. The
+ * accepted input is a GAS subset (AT&T syntax on x86). */
#define AS_TOOL "as"
@@ -126,16 +129,16 @@ int driver_as(int argc, char** argv) {
DriverEnv env;
AsOptions o = {0};
DriverCflags cf = {0};
- CfreeEnv cenv;
- CfreePpOptions pp;
+ CfreeContext ctx;
+ CfreePreprocessOptions pp;
CfreeCompiler* compiler = NULL;
CfreeWriter* writer = NULL;
CfreeWriter* pp_writer = NULL;
CfreeFileData src = {0};
- CfreeBytesInput input;
- CfreeBytesInput asm_input;
- CfreeCompileOptions copts;
- CfreeCompileOptions zero = {0};
+ CfreeBytes input;
+ CfreeBytes asm_input;
+ CfreeAsmCompileOptions copts;
+ CfreeAsmCompileOptions zero = {0};
const uint8_t* pp_data;
size_t pp_len;
int rc = 1;
@@ -161,59 +164,59 @@ int driver_as(int argc, char** argv) {
}
driver_cflags_fill_pp(&cf, &pp);
- cenv = driver_env_to_cfree(&env);
+ ctx = driver_env_to_context(&env);
- if (!cenv.file_io->read_all(cenv.file_io->user, o.source, &src)) {
+ if (ctx.file_io->read_all(ctx.file_io->user, o.source, &src) != CFREE_OK) {
driver_errf(AS_TOOL, "failed to read: %s", o.source);
goto out;
}
loaded = 1;
- writer = cenv.file_io->open_writer(cenv.file_io->user, o.output_path);
- if (!writer) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, o.output_path, &writer) !=
+ CFREE_OK) {
driver_errf(AS_TOOL, "failed to open output: %s", o.output_path);
goto out;
}
- compiler = driver_compiler_new(o.target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o.target, &ctx, &compiler) != CFREE_OK) {
driver_errf(AS_TOOL, "failed to initialize compiler");
goto out;
}
copts = zero;
- copts.debug_info = o.debug_info;
+ copts.code.debug_info = o.debug_info;
input.name = o.source;
input.data = src.data;
input.len = src.size;
- input.lang = CFREE_LANG_ASM;
asm_input = input;
if (driver_has_suffix(o.source, ".S")) {
- pp_writer = cfree_writer_mem(cenv.heap);
- if (!pp_writer) {
+ if (cfree_writer_mem(ctx.heap, &pp_writer) != CFREE_OK) {
driver_errf(AS_TOOL, "out of memory");
goto out;
}
- if (cfree_c_preprocess(compiler, &pp, &input, pp_writer) != 0) goto out;
- if (cfree_writer_error(pp_writer)) {
+ if (cfree_c_preprocess(compiler, &pp, &input, pp_writer) != CFREE_OK)
+ goto out;
+ if (cfree_writer_status(pp_writer) != CFREE_OK) {
driver_errf(AS_TOOL, "failed to preprocess: %s", o.source);
goto out;
}
pp_data = cfree_writer_mem_bytes(pp_writer, &pp_len);
asm_input.data = pp_data;
asm_input.len = pp_len;
- asm_input.lang = CFREE_LANG_ASM;
}
- rc = cfree_compile_obj_emit(compiler, &copts, &asm_input, writer);
+ rc = cfree_compile_asm_obj_emit(compiler, &copts, &asm_input, writer) ==
+ CFREE_OK
+ ? 0
+ : 1;
out:
if (compiler) driver_compiler_free(compiler);
if (pp_writer) cfree_writer_close(pp_writer);
if (writer) cfree_writer_close(writer);
- if (loaded) cenv.file_io->release(cenv.file_io->user, &src);
+ if (loaded) ctx.file_io->release(ctx.file_io->user, &src);
driver_cflags_fini(&cf, &env);
driver_env_fini(&env);
return rc;
diff --git a/driver/cc.c b/driver/cc.c
@@ -5,6 +5,10 @@
#include "lang/c/c.h"
#include "lib_resolve.h"
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/link.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
* emits an executable. The flag surface is a GCC subset:
@@ -84,7 +88,7 @@ typedef struct CcOptions {
/* Positional inputs split by suffix. */
const char** source_files; /* .c paths */
uint32_t nsource_files;
- CfreeBytesInput* source_memory; /* "-" stdin slurp */
+ CfreeSourceInput* source_memory; /* "-" stdin slurp */
uint32_t nsource_memory;
uint8_t* stdin_buf; /* owning storage for the one stdin */
size_t stdin_size;
@@ -148,89 +152,7 @@ void driver_help_cc(void) {
" cfree cc -M|-MM [options] input.c print header deps; no "
"compile\n"
"\n"
- "DESCRIPTION\n"
- " Compiles C11/Wasm sources and links them with .o/.a inputs. Inputs are\n"
- " classified by suffix:\n"
- " .c C source\n"
- " .wat .wasm WebAssembly source module\n"
- " .o .obj object file (link-time input)\n"
- " .a static archive (link-time input)\n"
- " - read C source from stdin (single source only)\n"
- " With -c the driver requires exactly one source and emits a "
- "relocatable\n"
- " object. Without -c every C source is compiled and linked together "
- "with\n"
- " any .o / .a / -l inputs.\n"
- "\n"
- "OUTPUT MODE\n"
- " -c Compile to a relocatable object (one source per "
- "call)\n"
- " -E Run the preprocessor only; write to -o\n"
- " --dump-tokens Dump the token stream to -o\n"
- " -shared Link a position-independent shared library\n"
- " (default) Compile + link to an executable\n"
- "\n"
- "OUTPUT\n"
- " -o PATH Output path (required except for -M / -MM)\n"
- "\n"
- "CODEGEN\n"
- " -O0 -O1 -O2 Optimization level (default -O0)\n"
- " -g Emit DWARF debug info\n"
- " -fPIC -fpic Position-independent code\n"
- " -fPIE -fpie Position-independent executable\n"
- " -static Non-PIC, non-PIE\n"
- " -pie Position-independent executable\n"
- " -no-pie Disable PIE\n"
- " -mcmodel=MODEL small | medium | large\n"
- " -target TRIPLE Cross-compile target. Recognized arches: x86_64,\n"
- " i386/i486/i586/i686, aarch64/arm64, arm/armv7,\n"
- " riscv64, riscv32, wasm32, wasm64.\n"
- " Recognized OSes: linux, darwin/macos, "
- "windows/win32,\n"
- " wasi, none/freestanding.\n"
- "\n"
- "PREPROCESSOR\n"
- " -I DIR Add quoted-include search path\n"
- " -isystem DIR Add system-include search path\n"
- " -D NAME[=BODY] Define a macro (BODY defaults to 1)\n"
- " -U NAME Undefine a builtin/predefined macro\n"
- "\n"
- "DEPENDENCY EMISSION (subset of GCC -M family)\n"
- " -M Print all header deps to stdout; do not compile\n"
- " -MM Like -M, but skip <bracketed> system headers\n"
- " -MD Compile and write all deps to a side file\n"
- " -MMD Like -MD, without system headers\n"
- " -MF FILE Write deps to FILE (default <out>.d for MD/MMD)\n"
- " -MT TARGET Set the make-target name (repeatable)\n"
- " -MQ TARGET Like -MT (with quoting)\n"
- " -MP Emit phony rules for each prerequisite\n"
- "\n"
- "DIAGNOSTICS\n"
- " -Werror Treat warnings as errors\n"
- " -fmax-errors=N Stop after N errors (0 = unlimited)\n"
- "\n"
- "LINKER (also applies to -shared)\n"
- " -e SYMBOL Set entry symbol\n"
- " -T SCRIPT.ld Use a linker script\n"
- " -L DIR Add library search path\n"
- " -l NAME Link against lib<NAME>.a (resolved via -L)\n"
- " -Wl,TOK[,TOK...] Pass tokens to the linker. Recognized tokens:\n"
- " -soname=NAME, -rpath=DIR,\n"
- " --enable-new-dtags / --disable-new-dtags\n"
- "\n"
- "REPRODUCIBILITY\n"
- " --build-id=MODE none | sha256 | uuid | 0xHEX\n"
- " -ffile-prefix-map=OLD=NEW\n"
- " Rewrite OLD path prefix as NEW in debug info\n"
- " SOURCE_DATE_EPOCH=N (env) embed N as build epoch\n"
- "\n"
- "OTHER\n"
- " -x c No-op (only C is accepted)\n"
- " -h, --help Show this help and exit\n"
- "\n"
- "EXIT CODES\n"
- " 0 success 1 compile/link error 2 bad "
- "usage\n");
+ "(see source for the full GCC-subset flag reference)\n");
}
static int cc_alloc_arrays(CcOptions* o, int argc) {
@@ -317,25 +239,13 @@ static void cc_options_release(CcOptions* o) {
driver_free(o->env, o->rpaths, bound * sizeof(*o->rpaths));
}
-/* Parse a single GCC-style -Wl,X[,Y...] pass-through argument. Each comma-
- * separated token is forwarded to the linker layer. We honour the ones
- * that map onto cfree_link_shared / cfree_link_exe directly; unknown
- * tokens are an error so the user notices when something doesn't go
- * through. Returns 0 on success. */
+/* Parse a single GCC-style -Wl,X[,Y...] pass-through argument. */
static int cc_record_wl(CcOptions* o, const char* arg) {
- /* Walk comma-separated tokens. argv tokens are NUL-terminated; we
- * stage each segment into a heap-owned buffer only when we need to
- * keep it past this call. For the values we currently honour, the
- * driver only stores pointers to caller-owned memory, so we copy each
- * token into a fresh buffer interned in argv-bounded storage. */
const char* p = arg;
while (*p) {
const char* tok = p;
size_t n = 0;
while (p[n] && p[n] != ',') ++n;
- /* Move p past this token (and its trailing comma) for the next
- * iteration; do this before we mutate anything so a `continue`
- * still advances. */
p = tok + n + (tok[n] == ',' ? 1 : 0);
if (n >= 8 && driver_strneq(tok, "-soname=", 8)) {
@@ -348,11 +258,6 @@ static int cc_record_wl(CcOptions* o, const char* arg) {
}
driver_memcpy(buf, tok + 8, n - 8);
buf[n - 8] = '\0';
- /* The buffer leaks until process exit — cc options release does
- * not currently track per-Wl allocations; this matches how
- * -ffile-prefix-map handles short-lived strings. To keep this
- * tidy across many -Wl,-soname=... entries (which would be
- * unusual), we record only the latest. */
o->soname = buf;
continue;
}
@@ -384,15 +289,11 @@ static int cc_record_wl(CcOptions* o, const char* arg) {
return 0;
}
-/* Suffix predicate: is `s` a recognized source suffix? */
static int cc_is_c_source(const char* s) {
return driver_has_suffix(s, ".c") || driver_has_suffix(s, ".toy") ||
driver_has_suffix(s, ".wat") || driver_has_suffix(s, ".wasm");
}
-/* Decimal uint64 parse for SOURCE_DATE_EPOCH. Stops at the first non-digit;
- * returns 0 on success and writes the parsed value, 1 if the string was
- * empty or contained no digits, or on overflow. */
static int cc_parse_u64(const char* s, uint64_t* out) {
uint64_t v = 0;
int any = 0;
@@ -411,9 +312,6 @@ static int cc_parse_u64(const char* s, uint64_t* out) {
return 0;
}
-/* Parse an even-length lowercase/uppercase hex string into a freshly-
- * allocated byte buffer. `s` must point at the first hex digit (no `0x`
- * prefix). Returns 0 on success. */
static int cc_parse_hex_bytes(DriverEnv* env, const char* s,
uint8_t** out_bytes, uint32_t* out_len) {
size_t n = driver_strlen(s);
@@ -452,8 +350,6 @@ static int cc_parse_hex_bytes(DriverEnv* env, const char* s,
return 0;
}
-/* Record one -ffile-prefix-map=OLD=NEW entry. `arg` points past the leading
- * "-ffile-prefix-map=". Splits on the first '='; both halves may be empty. */
static int cc_record_path_map(CcOptions* o, const char* arg) {
const char* eq = driver_strchr(arg, '=');
CfreePathPrefixMap* m = &o->path_map[o->npath_map];
@@ -480,7 +376,6 @@ static int cc_record_path_map(CcOptions* o, const char* arg) {
return 0;
}
-/* Parse `--build-id=VALUE` (the leading `--build-id=` already stripped). */
static int cc_record_build_id(CcOptions* o, const char* val) {
if (driver_streq(val, "none")) {
o->build_id_mode = CFREE_BUILDID_NONE;
@@ -525,10 +420,8 @@ static int cc_record_mcmodel(CcOptions* o, const char* val) {
return 1;
}
-/* Slurp stdin into o->stdin_buf (single-source guarded by caller) and
- * register it in source_memory[]. The diagnostic name is "<stdin>". */
static int cc_record_stdin(CcOptions* o) {
- CfreeBytesInput* in;
+ CfreeSourceInput* in;
if (o->stdin_buf) {
driver_errf(CC_TOOL, "'-' (stdin) may appear at most once");
return 1;
@@ -538,9 +431,10 @@ static int cc_record_stdin(CcOptions* o) {
return 1;
}
in = &o->source_memory[o->nsource_memory++];
- in->name = "<stdin>";
- in->data = o->stdin_buf;
- in->len = o->stdin_size;
+ in->bytes.name = "<stdin>";
+ in->bytes.data = o->stdin_buf;
+ in->bytes.len = o->stdin_size;
+ in->lang = CFREE_LANG_C;
return 0;
}
@@ -562,10 +456,6 @@ static int cc_classify_positional(CcOptions* o, const char* a) {
return 1;
}
-/* Resolve every accumulated -lname against -L paths; routes results to
- * archives[] (.a) or dsos[] (.so/.dylib/.tbd). Reports first lookup
- * failure. Mach-O targets prefer dynamic resolution (.tbd > .dylib > .a);
- * other targets fall back to the historical static-only path. */
static int cc_resolve_pending_libs(CcOptions* o) {
uint32_t i;
LibResolveMode mode =
@@ -604,9 +494,8 @@ static int cc_apply_env(CcOptions* o) {
return 0;
}
-/* Returns 0 on success; non-zero on bad args (already reported). */
static int cc_parse(int argc, char** argv, CcOptions* o) {
- int x_lang_pinned = 0; /* set after a -x c is seen */
+ int x_lang_pinned = 0;
int i;
if (cc_alloc_arrays(o, argc) != 0) return 1;
@@ -615,7 +504,6 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
for (i = 1; i < argc; ++i) {
const char* a = argv[i];
- /* Cflags first (-I/-isystem/-D/-U). */
{
int r =
driver_cflags_try_consume(&o->cf, o->env, CC_TOOL, argc, argv, &i);
@@ -623,40 +511,15 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
if (r > 0) continue;
}
- if (driver_streq(a, "-c")) {
- o->compile_only = 1;
- continue;
- }
- if (driver_streq(a, "-E")) {
- o->preprocess_only = 1;
- continue;
- }
- if (driver_streq(a, "--dump-tokens")) {
- o->dump_tokens = 1;
- continue;
- }
- if (driver_streq(a, "-g")) {
- o->debug_info = 1;
- continue;
- }
- if (driver_streq(a, "-O0")) {
- o->opt_level = 0;
- continue;
- }
- if (driver_streq(a, "-O1")) {
- o->opt_level = 1;
- continue;
- }
- if (driver_streq(a, "-O2")) {
- o->opt_level = 2;
- continue;
- }
-
- if (driver_streq(a, "-Werror")) {
- o->warnings_are_errors = 1;
- continue;
- }
+ if (driver_streq(a, "-c")) { o->compile_only = 1; continue; }
+ if (driver_streq(a, "-E")) { o->preprocess_only = 1; continue; }
+ if (driver_streq(a, "--dump-tokens")) { o->dump_tokens = 1; continue; }
+ if (driver_streq(a, "-g")) { o->debug_info = 1; continue; }
+ if (driver_streq(a, "-O0")) { o->opt_level = 0; continue; }
+ if (driver_streq(a, "-O1")) { o->opt_level = 1; continue; }
+ if (driver_streq(a, "-O2")) { o->opt_level = 2; continue; }
+ if (driver_streq(a, "-Werror")) { o->warnings_are_errors = 1; continue; }
if (driver_strneq(a, "-fmax-errors=", 13)) {
uint64_t v;
if (cc_parse_u64(a + 13, &v) != 0 || v > 0xFFFFFFFFu) {
@@ -667,34 +530,17 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
continue;
}
- if (driver_streq(a, "-fPIC")) {
+ if (driver_streq(a, "-fPIC") || driver_streq(a, "-fpic")) {
o->target.pic = CFREE_PIC_PIC;
continue;
}
- if (driver_streq(a, "-fpic")) {
- o->target.pic = CFREE_PIC_PIC;
- continue;
- }
- if (driver_streq(a, "-fPIE")) {
- o->target.pic = CFREE_PIC_PIE;
- continue;
- }
- if (driver_streq(a, "-fpie")) {
- o->target.pic = CFREE_PIC_PIE;
- continue;
- }
- if (driver_streq(a, "-static")) {
- o->target.pic = CFREE_PIC_NONE;
- continue;
- }
- if (driver_streq(a, "-pie")) {
+ if (driver_streq(a, "-fPIE") || driver_streq(a, "-fpie")) {
o->target.pic = CFREE_PIC_PIE;
continue;
}
- if (driver_streq(a, "-no-pie")) {
- o->target.pic = CFREE_PIC_NONE;
- continue;
- }
+ if (driver_streq(a, "-static")) { o->target.pic = CFREE_PIC_NONE; continue; }
+ if (driver_streq(a, "-pie")) { o->target.pic = CFREE_PIC_PIE; continue; }
+ if (driver_streq(a, "-no-pie")) { o->target.pic = CFREE_PIC_NONE; continue; }
if (driver_streq(a, "-shared")) {
o->shared = 1;
@@ -710,54 +556,34 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
if (cc_record_mcmodel(o, a + 9) != 0) return 1;
continue;
}
-
if (driver_strneq(a, "--build-id=", 11)) {
if (cc_record_build_id(o, a + 11) != 0) return 1;
continue;
}
-
if (driver_strneq(a, "-ffile-prefix-map=", 18)) {
if (cc_record_path_map(o, a + 18) != 0) return 1;
continue;
}
if (driver_streq(a, "-o")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-o requires an argument");
- return 1;
- }
- if (o->output_path_set) {
- driver_errf(CC_TOOL, "duplicate -o");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-o requires an argument"); return 1; }
+ if (o->output_path_set) { driver_errf(CC_TOOL, "duplicate -o"); return 1; }
o->output_path = argv[i];
o->output_path_set = 1;
continue;
}
-
if (driver_streq(a, "-e")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-e requires an argument");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-e requires an argument"); return 1; }
o->entry = argv[i];
continue;
}
-
if (driver_streq(a, "-T")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-T requires an argument");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-T requires an argument"); return 1; }
o->linker_script = argv[i];
continue;
}
-
if (driver_streq(a, "-target")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-target requires an argument");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-target requires an argument"); return 1; }
if (driver_target_from_triple(argv[i], &o->target) != 0) {
driver_errf(CC_TOOL, "unrecognized target triple: %s", argv[i]);
return 1;
@@ -765,12 +591,8 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->target_set = 1;
continue;
}
-
if (driver_streq(a, "-x")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-x requires an argument");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-x requires an argument"); return 1; }
if (!driver_streq(argv[i], "c")) {
driver_errf(CC_TOOL, "unsupported -x language: %s", argv[i]);
return 1;
@@ -778,60 +600,31 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
x_lang_pinned = 1;
continue;
}
-
if (driver_strneq(a, "-L", 2)) {
const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
- if (!dir) {
- driver_errf(CC_TOOL, "-L requires an argument");
- return 1;
- }
+ if (!dir) { driver_errf(CC_TOOL, "-L requires an argument"); return 1; }
o->lib_search_paths[o->nlib_search_paths++] = dir;
continue;
}
-
if (driver_strneq(a, "-l", 2)) {
const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
- if (!name) {
- driver_errf(CC_TOOL, "-l requires an argument");
- return 1;
- }
+ if (!name) { driver_errf(CC_TOOL, "-l requires an argument"); return 1; }
o->pending_libs[o->npending_libs++] = name;
continue;
}
- if (driver_streq(a, "-M")) {
- o->dep_mode = CC_DEP_M;
- continue;
- }
- if (driver_streq(a, "-MM")) {
- o->dep_mode = CC_DEP_MM;
- continue;
- }
- if (driver_streq(a, "-MD")) {
- o->dep_mode = CC_DEP_MD;
- continue;
- }
- if (driver_streq(a, "-MMD")) {
- o->dep_mode = CC_DEP_MMD;
- continue;
- }
- if (driver_streq(a, "-MP")) {
- o->dep_phony = 1;
- continue;
- }
+ if (driver_streq(a, "-M")) { o->dep_mode = CC_DEP_M; continue; }
+ if (driver_streq(a, "-MM")) { o->dep_mode = CC_DEP_MM; continue; }
+ if (driver_streq(a, "-MD")) { o->dep_mode = CC_DEP_MD; continue; }
+ if (driver_streq(a, "-MMD")) { o->dep_mode = CC_DEP_MMD; continue; }
+ if (driver_streq(a, "-MP")) { o->dep_phony = 1; continue; }
if (driver_streq(a, "-MF")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "-MF requires an argument");
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "-MF requires an argument"); return 1; }
o->dep_file = argv[i];
continue;
}
if (driver_streq(a, "-MT") || driver_streq(a, "-MQ")) {
- if (++i >= argc) {
- driver_errf(CC_TOOL, "%s requires an argument", a);
- return 1;
- }
+ if (++i >= argc) { driver_errf(CC_TOOL, "%s requires an argument", a); return 1; }
o->dep_targets[o->ndep_targets++] = argv[i];
continue;
}
@@ -847,8 +640,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
if (cc_classify_positional(o, a) != 0) return 1;
}
- (void)x_lang_pinned; /* recorded for future warnings; cc currently only does C
- */
+ (void)x_lang_pinned;
if (cc_apply_env(o) != 0) return 1;
if (cc_resolve_pending_libs(o) != 0) return 1;
@@ -870,8 +662,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
driver_errf(CC_TOOL, "--dump-tokens is incompatible with -c/-E");
return 1;
}
- if (o->shared &&
- (o->compile_only || o->preprocess_only || o->dump_tokens)) {
+ if (o->shared && (o->compile_only || o->preprocess_only || o->dump_tokens)) {
driver_errf(CC_TOOL, "-shared is incompatible with -c/-E/--dump-tokens");
return 1;
}
@@ -881,23 +672,20 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
}
if (o->compile_only) {
if (total_sources != 1 || total_link != 0) {
- driver_errf(CC_TOOL,
- "-c requires exactly one C source and no .o/.a inputs");
+ driver_errf(CC_TOOL, "-c requires exactly one C source and no .o/.a inputs");
return 1;
}
}
if (o->preprocess_only) {
if (total_sources != 1 || total_link != 0) {
- driver_errf(CC_TOOL,
- "-E requires exactly one C source and no .o/.a inputs");
+ driver_errf(CC_TOOL, "-E requires exactly one C source and no .o/.a inputs");
return 1;
}
}
if (o->dump_tokens) {
if (total_sources != 1 || total_link != 0) {
- driver_errf(
- CC_TOOL,
- "--dump-tokens requires exactly one C source and no .o/.a inputs");
+ driver_errf(CC_TOOL,
+ "--dump-tokens requires exactly one C source and no .o/.a inputs");
return 1;
}
}
@@ -927,19 +715,18 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
return 0;
}
-/* Borrow the single source as a CfreeBytesInput. Caller owns `*loaded` (set
- * to nonzero only when the file was opened via env.file_io and must be
- * released). */
-static int cc_load_single_source(DriverEnv* env, const CfreeEnv* cenv,
- const CcOptions* o, CfreeBytesInput* in,
- CfreeFileData* fd, int* loaded) {
+/* Borrow the single source as a CfreeBytes. `*loaded` is set nonzero only
+ * when the file was opened via file_io and must be released. */
+static int cc_load_single_source(const CfreeContext* ctx, const CcOptions* o,
+ CfreeBytes* in, CfreeFileData* fd,
+ int* loaded) {
*loaded = 0;
if (o->nsource_memory == 1) {
- *in = o->source_memory[0];
+ *in = o->source_memory[0].bytes;
return 0;
}
- /* nsource_files == 1 here. */
- if (!cenv->file_io->read_all(cenv->file_io->user, o->source_files[0], fd)) {
+ if (ctx->file_io->read_all(ctx->file_io->user, o->source_files[0], fd) !=
+ CFREE_OK) {
driver_errf(CC_TOOL, "failed to read: %s", o->source_files[0]);
return 1;
}
@@ -947,73 +734,70 @@ static int cc_load_single_source(DriverEnv* env, const CfreeEnv* cenv,
in->name = o->source_files[0];
in->data = fd->data;
in->len = fd->size;
- in->lang = cfree_language_for_path(o->source_files[0]);
- (void)env;
return 0;
}
static int cc_preprocess(DriverEnv* env, const CcOptions* o,
- const CfreePpOptions* pp_opts) {
- CfreeEnv cenv = driver_env_to_cfree(env);
+ const CfreePreprocessOptions* pp_opts) {
+ CfreeContext ctx = driver_env_to_context(env);
CfreeCompiler* compiler = NULL;
CfreeWriter* writer = NULL;
CfreeFileData fd = {0};
- CfreeBytesInput input = {0};
+ CfreeBytes input = {0};
int rc = 1;
int loaded = 0;
- if (cc_load_single_source(env, &cenv, o, &input, &fd, &loaded) != 0) goto out;
+ if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
- writer = cenv.file_io->open_writer(cenv.file_io->user, o->output_path);
- if (!writer) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &writer) !=
+ CFREE_OK) {
driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
goto out;
}
- compiler = driver_compiler_new(o->target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
goto out;
}
- rc = cfree_c_preprocess(compiler, pp_opts, &input, writer);
+ rc = cfree_c_preprocess(compiler, pp_opts, &input, writer) == CFREE_OK ? 0
+ : 1;
out:
if (compiler) driver_compiler_free(compiler);
if (writer) cfree_writer_close(writer);
- if (loaded) cenv.file_io->release(cenv.file_io->user, &fd);
+ if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
static int cc_dump_tokens(DriverEnv* env, const CcOptions* o) {
- CfreeEnv cenv = driver_env_to_cfree(env);
+ CfreeContext ctx = driver_env_to_context(env);
CfreeCompiler* compiler = NULL;
CfreeWriter* writer = NULL;
CfreeFileData fd = {0};
- CfreeBytesInput input = {0};
+ CfreeBytes input = {0};
int rc = 1;
int loaded = 0;
- if (cc_load_single_source(env, &cenv, o, &input, &fd, &loaded) != 0) goto out;
+ if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
- writer = cenv.file_io->open_writer(cenv.file_io->user, o->output_path);
- if (!writer) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &writer) !=
+ CFREE_OK) {
driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
goto out;
}
- compiler = driver_compiler_new(o->target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
goto out;
}
- rc = cfree_c_dump_tokens(compiler, &input, writer);
+ rc = cfree_c_dump_tokens(compiler, &input, writer) == CFREE_OK ? 0 : 1;
out:
if (compiler) driver_compiler_free(compiler);
if (writer) cfree_writer_close(writer);
- if (loaded) cenv.file_io->release(cenv.file_io->user, &fd);
+ if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
@@ -1031,19 +815,21 @@ typedef struct CcDiscardWriter {
uint64_t pos;
} CcDiscardWriter;
-static void cc_disc_write(CfreeWriter* w, const void* d, size_t n) {
+static CfreeStatus cc_disc_write(CfreeWriter* w, const void* d, size_t n) {
(void)d;
((CcDiscardWriter*)w)->pos += (uint64_t)n;
+ return CFREE_OK;
}
-static void cc_disc_seek(CfreeWriter* w, uint64_t off) {
+static CfreeStatus cc_disc_seek(CfreeWriter* w, uint64_t off) {
((CcDiscardWriter*)w)->pos = off;
+ return CFREE_OK;
}
static uint64_t cc_disc_tell(CfreeWriter* w) {
return ((CcDiscardWriter*)w)->pos;
}
-static int cc_disc_error(CfreeWriter* w) {
+static CfreeStatus cc_disc_status(CfreeWriter* w) {
(void)w;
- return 0;
+ return CFREE_OK;
}
static void cc_disc_close(CfreeWriter* w) {
CcDiscardWriter* dw = (CcDiscardWriter*)w;
@@ -1056,7 +842,7 @@ static CfreeWriter* cc_discard_writer_new(DriverEnv* env) {
dw->base.write = cc_disc_write;
dw->base.seek = cc_disc_seek;
dw->base.tell = cc_disc_tell;
- dw->base.error = cc_disc_error;
+ dw->base.status = cc_disc_status;
dw->base.close = cc_disc_close;
dw->env = env;
return &dw->base;
@@ -1073,7 +859,7 @@ static int cc_dep_filters_system(int mode) {
static int cc_dep_list_push(DriverEnv* env, CcDepList* l, const char* s) {
uint32_t i;
for (i = 0; i < l->n; ++i) {
- if (driver_streq(l->items[i], s)) return 0; /* dedupe */
+ if (driver_streq(l->items[i], s)) return 0;
}
if (l->n == l->cap) {
uint32_t newcap = l->cap ? l->cap * 2 : 16;
@@ -1097,9 +883,6 @@ static void cc_dep_list_free(DriverEnv* env, CcDepList* l) {
l->cap = 0;
}
-/* Default Make-target name. With -o set, use that. Otherwise (only -M/-MM)
- * derive ".o" from the single C-source basename, or use "<stdin>.o" for the
- * stdin source. */
static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
size_t* out_size) {
const char* base = o->output_path;
@@ -1131,21 +914,15 @@ static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
size_t slash = 0;
size_t k;
for (k = srclen; k > 0; --k) {
- if (src[k - 1] == '.') {
- dot = k - 1;
- break;
- }
+ if (src[k - 1] == '.') { dot = k - 1; break; }
if (src[k - 1] == '/') break;
}
for (k = dot; k > 0; --k) {
- if (src[k - 1] == '/') {
- slash = k;
- break;
- }
+ if (src[k - 1] == '/') { slash = k; break; }
}
{
size_t name_len = dot - slash;
- size_t bufsz = name_len + 3; /* ".o" + NUL */
+ size_t bufsz = name_len + 3;
buf = driver_alloc(env, bufsz);
if (!buf) return NULL;
driver_memcpy(buf, src + slash, name_len);
@@ -1164,14 +941,11 @@ static char* cc_dep_default_path(DriverEnv* env, const char* out_path,
size_t dot = len;
size_t k;
for (k = len; k > 0; --k) {
- if (out_path[k - 1] == '.') {
- dot = k - 1;
- break;
- }
+ if (out_path[k - 1] == '.') { dot = k - 1; break; }
if (out_path[k - 1] == '/') break;
}
{
- size_t bufsz = dot + 3; /* ".d" + NUL */
+ size_t bufsz = dot + 3;
char* buf = driver_alloc(env, bufsz);
if (!buf) return NULL;
driver_memcpy(buf, out_path, dot);
@@ -1185,10 +959,12 @@ static char* cc_dep_default_path(DriverEnv* env, const char* out_path,
static int cc_dep_collect(DriverEnv* env, CfreeCompiler* compiler,
int system_filter, CcDepList* list) {
- CfreeDepIter* it = cfree_dep_iter_new(compiler);
+ CfreeDepIter* it = NULL;
CfreeDepEdge e;
- if (!it) return 1;
- while (cfree_dep_iter_next(it, &e)) {
+ if (cfree_dep_iter_new(compiler, &it) != CFREE_OK) return 1;
+ for (;;) {
+ CfreeIterResult r = cfree_dep_iter_next(it, &e);
+ if (r != CFREE_ITER_ITEM) break;
if (system_filter && e.from_system_path) continue;
if (cc_dep_list_push(env, list, e.included_name) != 0) {
cfree_dep_iter_free(it);
@@ -1226,32 +1002,37 @@ static void cc_dep_emit_rule(CfreeWriter* w, const char* const* targets,
}
}
-static CfreeWriter* cc_dep_open_writer(DriverEnv* env, const CfreeEnv* cenv,
+static CfreeWriter* cc_dep_open_writer(DriverEnv* env, const CfreeContext* ctx,
const CcOptions* o, char** owned_path,
size_t* owned_path_size) {
+ CfreeWriter* w = NULL;
*owned_path = NULL;
*owned_path_size = 0;
if (o->dep_file) {
- return cenv->file_io->open_writer(cenv->file_io->user, o->dep_file);
+ if (ctx->file_io->open_writer(ctx->file_io->user, o->dep_file, &w) !=
+ CFREE_OK)
+ return NULL;
+ return w;
}
if (o->dep_mode == CC_DEP_M || o->dep_mode == CC_DEP_MM) {
return driver_stdout_writer(env);
}
- /* MD/MMD: derive .d path from -o */
{
char* p = cc_dep_default_path(env, o->output_path, owned_path_size);
if (!p) return NULL;
*owned_path = p;
- return cenv->file_io->open_writer(cenv->file_io->user, p);
+ if (ctx->file_io->open_writer(ctx->file_io->user, p, &w) != CFREE_OK)
+ return NULL;
+ return w;
}
}
static const char* cc_primary_source_name(const CcOptions* o) {
- if (o->nsource_memory == 1) return o->source_memory[0].name;
+ if (o->nsource_memory == 1) return o->source_memory[0].bytes.name;
return o->source_files[0];
}
-static int cc_dep_finish(DriverEnv* env, const CfreeEnv* cenv,
+static int cc_dep_finish(DriverEnv* env, const CfreeContext* ctx,
CfreeCompiler* compiler, const CcOptions* o) {
CcDepList deps = {0};
CfreeWriter* dep_w = NULL;
@@ -1274,26 +1055,22 @@ static int cc_dep_finish(DriverEnv* env, const CfreeEnv* cenv,
ntargets = o->ndep_targets;
if (ntargets == 0) {
owned_target = cc_dep_default_target(env, o, &owned_target_size);
- if (!owned_target) {
- driver_errf(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!owned_target) { driver_errf(CC_TOOL, "out of memory"); goto out; }
one_target[0] = owned_target;
targets = one_target;
ntargets = 1;
}
- dep_w = cc_dep_open_writer(env, cenv, o, &owned_path, &owned_size);
+ dep_w = cc_dep_open_writer(env, ctx, o, &owned_path, &owned_size);
if (!dep_w) {
- driver_errf(
- CC_TOOL, "failed to open dep output: %s",
+ driver_errf(CC_TOOL, "failed to open dep output: %s",
o->dep_file ? o->dep_file : (owned_path ? owned_path : "<stdout>"));
goto out;
}
cc_dep_emit_rule(dep_w, targets, ntargets, cc_primary_source_name(o), &deps,
o->dep_phony);
- rc = cfree_writer_error(dep_w);
+ rc = cfree_writer_status(dep_w) == CFREE_OK ? 0 : 1;
out:
if (dep_w) cfree_writer_close(dep_w);
@@ -1303,119 +1080,132 @@ out:
return rc;
}
-static void cc_fill_compile_opts(const CcOptions* o, const CfreePpOptions* pp,
- CfreeCompileOptions* copts) {
- CfreeCompileOptions zero = {0};
+static void cc_fill_c_opts(const CcOptions* o, const CfreePreprocessOptions* pp,
+ CfreeCCompileOptions* copts) {
+ CfreeCCompileOptions zero = {0};
*copts = zero;
- copts->opt_level = o->opt_level;
- copts->debug_info = o->debug_info;
- copts->pp = *pp;
- copts->epoch = o->epoch;
- copts->path_map = o->npath_map ? o->path_map : NULL;
- copts->npath_map = o->npath_map;
- copts->warnings_are_errors = o->warnings_are_errors;
- copts->max_errors = o->max_errors;
+ copts->code.opt_level = o->opt_level;
+ copts->code.debug_info = o->debug_info;
+ copts->code.epoch = o->epoch;
+ copts->code.path_map = o->npath_map ? o->path_map : NULL;
+ copts->code.npath_map = o->npath_map;
+ copts->preprocess = *pp;
+ copts->diagnostics.warnings_are_errors = o->warnings_are_errors;
+ copts->diagnostics.max_errors = o->max_errors;
}
static int cc_run_deps_only(DriverEnv* env, const CcOptions* o,
- const CfreePpOptions* pp) {
- CfreeEnv cenv = driver_env_to_cfree(env);
+ const CfreePreprocessOptions* pp) {
+ CfreeContext ctx = driver_env_to_context(env);
CfreeCompiler* compiler = NULL;
CfreeWriter* discard = NULL;
CfreeFileData fd = {0};
- CfreeBytesInput input = {0};
+ CfreeBytes input = {0};
int loaded = 0;
int rc = 1;
- if (cc_load_single_source(env, &cenv, o, &input, &fd, &loaded) != 0) goto out;
+ if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
discard = cc_discard_writer_new(env);
- if (!discard) {
- driver_errf(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!discard) { driver_errf(CC_TOOL, "out of memory"); goto out; }
- compiler = driver_compiler_new(o->target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
goto out;
}
- if (cfree_c_preprocess(compiler, pp, &input, discard) != 0) goto out;
+ if (cfree_c_preprocess(compiler, pp, &input, discard) != CFREE_OK) goto out;
- rc = cc_dep_finish(env, &cenv, compiler, o);
+ rc = cc_dep_finish(env, &ctx, compiler, o);
out:
if (compiler) driver_compiler_free(compiler);
if (discard) cfree_writer_close(discard);
- if (loaded) cenv.file_io->release(cenv.file_io->user, &fd);
+ if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
-/* -c path: compile a single C source to a writer. If dep_mode is MD/MMD the
- * compiler is held open after the emit so cc_dep_finish can walk the
- * SourceManager edges; otherwise it returns immediately after a clean emit. */
static int cc_run_compile_obj(DriverEnv* env, const CcOptions* o,
- const CfreePpOptions* pp) {
- CfreeEnv cenv = driver_env_to_cfree(env);
+ const CfreePreprocessOptions* pp) {
+ CfreeContext ctx = driver_env_to_context(env);
CfreeCompiler* compiler = NULL;
CfreeWriter* obj_w = NULL;
CfreeFileData fd = {0};
- CfreeBytesInput input = {0};
- CfreeCompileOptions copts;
+ CfreeBytes input = {0};
+ CfreeCCompileOptions copts;
int loaded = 0;
int rc = 1;
- if (cc_load_single_source(env, &cenv, o, &input, &fd, &loaded) != 0) goto out;
+ if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
- obj_w = cenv.file_io->open_writer(cenv.file_io->user, o->output_path);
- if (!obj_w) {
+ if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &obj_w) !=
+ CFREE_OK) {
driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
goto out;
}
- compiler = driver_compiler_new(o->target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
goto out;
}
- cc_fill_compile_opts(o, pp, &copts);
- if (cfree_compile_obj_emit(compiler, &copts, &input, obj_w) != 0) goto out;
+ cc_fill_c_opts(o, pp, &copts);
+ {
+ CfreeLanguage lang = cfree_language_for_path(o->source_files[0]);
+ CfreeStatus st;
+ if (lang == CFREE_LANG_C) {
+ st = cfree_compile_c_obj_emit(compiler, &copts, &input, obj_w);
+ } else if (lang == CFREE_LANG_ASM) {
+ CfreeAsmCompileOptions aopts = {0};
+ aopts.code = copts.code;
+ aopts.diagnostics = copts.diagnostics;
+ st = cfree_compile_asm_obj_emit(compiler, &aopts, &input, obj_w);
+ } else {
+ CfreeFrontendCompileOptions fopts = {0};
+ CfreeSourceInput sin;
+ fopts.code = copts.code;
+ fopts.diagnostics = copts.diagnostics;
+ fopts.language_options = &copts;
+ sin.bytes = input;
+ sin.lang = lang;
+ st = cfree_compile_source_obj_emit(compiler, &fopts, &sin, obj_w);
+ }
+ if (st != CFREE_OK) goto out;
+ }
rc = (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD)
- ? cc_dep_finish(env, &cenv, compiler, o)
+ ? cc_dep_finish(env, &ctx, compiler, o)
: 0;
out:
if (compiler) driver_compiler_free(compiler);
if (obj_w) cfree_writer_close(obj_w);
- if (loaded) cenv.file_io->release(cenv.file_io->user, &fd);
+ if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
return rc;
}
-/* exe path: compile every C source through a CfreePipeline, load .o/.a/script
- * inputs through env.file_io, and call cfree_pipeline_link_exe. The pipeline
- * owns the per-source CfreeObjBuilders for the lifetime of the link. */
+/* exe path: compile every C source via a single CfreeCompiler, load
+ * .o/.a/script inputs, and call cfree_link_exe or cfree_link_shared. The
+ * compiler owns the per-source CfreeObjBuilders for the lifetime of the
+ * link. */
static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
- const CfreePpOptions* pp) {
- CfreeEnv cenv = driver_env_to_cfree(env);
- const CfreeFileIO* io = cenv.file_io;
- CfreePipeline* pipe = NULL;
+ const CfreePreprocessOptions* pp) {
+ CfreeContext ctx = driver_env_to_context(env);
+ const CfreeFileIO* io = ctx.file_io;
+ CfreeCompiler* compiler = NULL;
CfreeWriter* out_w = NULL;
DriverLoad* src_lf = NULL;
DriverLoad* obj_lf = NULL;
DriverLoad* arch_lf = NULL;
DriverLoad script_lf = {0};
DriverLoad* dso_lf = NULL;
- CfreeBytesInput* src_in = NULL;
- CfreeBytesInput* obj_in = NULL;
- CfreeBytesInputArchive* arch_in = NULL;
- CfreeBytesInput* dso_in = NULL;
+ CfreeBytes* src_bytes = NULL;
+ CfreeBytes* obj_in = NULL;
+ CfreeLinkArchiveInput* arch_in = NULL;
+ CfreeBytes* dso_in = NULL;
CfreeObjBuilder** objs = NULL;
- const CfreeLinkScript* script = NULL;
- CfreeCompileOptions copts;
- CfreeLinkOptions link_opts;
+ CfreeLinkScript* script = NULL;
+ CfreeCCompileOptions copts;
uint32_t nsrc = o->nsource_files + o->nsource_memory;
uint32_t i;
int rc = 1;
@@ -1425,59 +1215,38 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
return 1;
}
- /* Allocate scratch for parallel arrays. */
- 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(CC_TOOL, "out of memory");
- goto out;
- }
- }
if (o->nsource_files) {
+ src_bytes =
+ driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_bytes));
src_lf = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_lf));
- if (!src_lf) {
- driver_errf(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!src_bytes || !src_lf) { driver_errf(CC_TOOL, "out of memory"); goto out; }
+ }
+ if (nsrc) {
+ objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs));
+ if (!objs) { driver_errf(CC_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(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!obj_lf || !obj_in) { driver_errf(CC_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(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!arch_lf || !arch_in) { driver_errf(CC_TOOL, "out of memory"); goto out; }
}
if (o->ndsos) {
dso_lf = driver_alloc_zeroed(env, o->ndsos * sizeof(*dso_lf));
dso_in = driver_alloc_zeroed(env, o->ndsos * sizeof(*dso_in));
- if (!dso_lf || !dso_in) {
- driver_errf(CC_TOOL, "out of memory");
- goto out;
- }
+ if (!dso_lf || !dso_in) { driver_errf(CC_TOOL, "out of memory"); goto out; }
}
- /* Stage source inputs: paths first (paths-then-memory matches the old
- * cfree_run order so reproducibility is unchanged), then memory. */
for (i = 0; i < o->nsource_files; ++i) {
if (driver_load_bytes(io, CC_TOOL, o->source_files[i], &src_lf[i],
- &src_in[i]) != 0)
+ &src_bytes[i]) != 0)
goto out;
}
- for (i = 0; i < o->nsource_memory; ++i) {
- src_in[o->nsource_files + i] = o->source_memory[i];
- }
- /* Load object inputs and archives. */
for (i = 0; i < o->nobject_files; ++i) {
if (driver_load_bytes(io, CC_TOOL, o->object_files[i], &obj_lf[i],
&obj_in[i]) != 0)
@@ -1485,48 +1254,71 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
}
for (i = 0; i < o->narchives; ++i) {
if (driver_load_bytes(io, CC_TOOL, o->archives[i], &arch_lf[i],
- &arch_in[i].input) != 0)
+ &arch_in[i].bytes) != 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 < o->ndsos; ++i) {
- if (driver_load_bytes(io, CC_TOOL, o->dsos[i], &dso_lf[i],
- &dso_in[i]) != 0)
+ if (driver_load_bytes(io, CC_TOOL, o->dsos[i], &dso_lf[i], &dso_in[i]) != 0)
goto out;
}
- /* Linker script. */
if (o->linker_script) {
- CfreeBytesInput dummy;
+ CfreeBytes dummy;
if (driver_load_bytes(io, CC_TOOL, o->linker_script, &script_lf, &dummy) !=
0)
goto out;
}
- pipe = driver_pipeline_new(o->target, &cenv);
- if (!pipe) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to initialize compiler");
goto out;
}
if (script_lf.loaded) {
- if (cfree_link_script_parse(cfree_pipeline_compiler(pipe),
- (const char*)script_lf.fd.data,
- script_lf.fd.size, &script) != 0)
+ if (cfree_link_script_parse(&ctx, (const char*)script_lf.fd.data,
+ script_lf.fd.size, &script) != CFREE_OK)
goto out;
}
- /* Compile each TU in order. */
- cc_fill_compile_opts(o, pp, &copts);
- for (i = 0; i < nsrc; ++i) {
- if (cfree_pipeline_compile_obj(pipe, &copts, &src_in[i], &objs[i]) != 0)
+ cc_fill_c_opts(o, pp, &copts);
+ for (i = 0; i < o->nsource_files; ++i) {
+ CfreeLanguage lang = cfree_language_for_path(o->source_files[i]);
+ CfreeStatus st;
+ if (lang == CFREE_LANG_C) {
+ st = cfree_compile_c_obj(compiler, &copts, &src_bytes[i], &objs[i]);
+ } else if (lang == CFREE_LANG_ASM) {
+ CfreeAsmCompileOptions aopts = {0};
+ aopts.code = copts.code;
+ aopts.diagnostics = copts.diagnostics;
+ st = cfree_compile_asm_obj(compiler, &aopts, &src_bytes[i], &objs[i]);
+ } else {
+ CfreeFrontendCompileOptions fopts = {0};
+ CfreeSourceInput sin;
+ fopts.code = copts.code;
+ fopts.diagnostics = copts.diagnostics;
+ fopts.language_options = &copts;
+ sin.bytes = src_bytes[i];
+ sin.lang = lang;
+ st = cfree_compile_source_obj(compiler, &fopts, &sin, &objs[i]);
+ }
+ if (st != CFREE_OK) goto out;
+ }
+ for (i = 0; i < o->nsource_memory; ++i) {
+ CfreeFrontendCompileOptions fopts;
+ CfreeFrontendCompileOptions z = {0};
+ fopts = z;
+ fopts.code = copts.code;
+ fopts.diagnostics = copts.diagnostics;
+ fopts.language_options = &copts;
+ if (cfree_compile_source_obj(compiler, &fopts, &o->source_memory[i],
+ &objs[o->nsource_files + i]) != CFREE_OK)
goto out;
}
- out_w = io->open_writer(io->user, o->output_path);
- if (!out_w) {
+ if (io->open_writer(io->user, o->output_path, &out_w) != CFREE_OK) {
driver_errf(CC_TOOL, "failed to open output: %s", o->output_path);
goto out;
}
@@ -1550,8 +1342,8 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
inputs.build_id_len = o->build_id_len;
if (o->shared) {
- CfreeLinkSharedOptions z = {0};
- CfreeLinkSharedOptions s;
+ CfreeSharedLinkOptions z = {0};
+ CfreeSharedLinkOptions s;
s = z;
s.inputs = inputs;
s.soname = o->soname;
@@ -1563,12 +1355,13 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
s.nrpaths = o->nrpaths;
}
s.allow_undefined = 1;
- rc = cfree_pipeline_link_shared(pipe, &s, out_w);
+ rc = cfree_link_shared(compiler, &s, out_w) == CFREE_OK ? 0 : 1;
} else {
- CfreeLinkOptions z = {0};
+ CfreeExeLinkOptions z = {0};
+ CfreeExeLinkOptions link_opts;
link_opts = z;
link_opts.inputs = inputs;
- rc = cfree_pipeline_link_exe(pipe, &link_opts, out_w);
+ rc = cfree_link_exe(compiler, &link_opts, out_w) == CFREE_OK ? 0 : 1;
}
}
@@ -1581,12 +1374,13 @@ out:
rc = 1;
}
}
- if (script && pipe)
- cfree_link_script_free(cfree_pipeline_compiler(pipe), script);
- if (pipe) driver_pipeline_free(pipe);
+ if (script) cfree_link_script_free(&ctx, script);
+ if (compiler) driver_compiler_free(compiler);
driver_release_bytes(io, &script_lf);
if (arch_lf) {
for (i = 0; i < o->narchives; ++i) driver_release_bytes(io, &arch_lf[i]);
+ }
+ if (dso_lf) {
for (i = 0; i < o->ndsos; ++i) driver_release_bytes(io, &dso_lf[i]);
}
if (obj_lf) {
@@ -1602,15 +1396,16 @@ out:
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->nsource_files * sizeof(*src_lf));
+ if (src_bytes)
+ driver_free(env, src_bytes, o->nsource_files * sizeof(*src_bytes));
if (objs) driver_free(env, objs, nsrc * sizeof(*objs));
- if (src_in) driver_free(env, src_in, nsrc * sizeof(*src_in));
return rc;
}
int driver_cc(int argc, char** argv) {
DriverEnv env;
CcOptions co = {0};
- CfreePpOptions pp;
+ CfreePreprocessOptions pp;
int rc;
if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
diff --git a/driver/cflags.c b/driver/cflags.c
@@ -130,8 +130,8 @@ int driver_cflags_try_consume(DriverCflags* cf, DriverEnv* env,
return 0;
}
-void driver_cflags_fill_pp(const DriverCflags* cf, CfreePpOptions* pp) {
- CfreePpOptions z = {0};
+void driver_cflags_fill_pp(const DriverCflags* cf, CfreePreprocessOptions* pp) {
+ CfreePreprocessOptions z = {0};
*pp = z;
pp->include_dirs = cf->include_dirs;
pp->ninclude_dirs = cf->ninclude_dirs;
diff --git a/driver/cflags.h b/driver/cflags.h
@@ -3,6 +3,8 @@
#include "driver.h"
+#include <cfree/compile.h>
+
/* Shared parsing of the C-preprocessor flag family used by both `cc` and
* `run`: -I, -isystem, -D, -U. Owns a back-store sized to the worst-case
* argv slot count so callers can hold borrowed pointers into argv without
@@ -65,9 +67,9 @@ void driver_cflags_fini(DriverCflags*, DriverEnv*);
int driver_cflags_try_consume(DriverCflags*, DriverEnv*, const char* tool,
int argc, char** argv, int* i);
-/* Populate a CfreePpOptions from the accumulated flags. The CfreePpOptions
- * borrows the cf-owned arrays; the caller must keep cf alive across any
- * libcfree call that consumes pp_out. */
-void driver_cflags_fill_pp(const DriverCflags*, CfreePpOptions* pp_out);
+/* Populate a CfreePreprocessOptions from the accumulated flags. The
+ * struct borrows the cf-owned arrays; the caller must keep cf alive
+ * across any libcfree call that consumes pp_out. */
+void driver_cflags_fill_pp(const DriverCflags*, CfreePreprocessOptions* pp_out);
#endif
diff --git a/driver/dbg.c b/driver/dbg.c
@@ -5,6 +5,14 @@
#include "driver.h"
#include "inputs.h"
+#include <cfree/arch.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/dbg.h>
+#include <cfree/dwarf.h>
+#include <cfree/jit.h>
+#include <cfree/object.h>
+
/* `cfree dbg` — interactive JIT debugger.
*
* Mirrors `cfree run` for compile flags and argv shape, but instead of
@@ -240,31 +248,32 @@ static void dbg_options_release(DbgOpts *o) {
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(DbgOpts *o, CfreePipeline *pipe,
- CfreeJit **out_jit) {
- CfreeCompileOptions copts;
+/* Compile every C source through a compiler owned by the caller and JIT-link
+ * the result. Compiler ownership stays with the caller so DWARF lookups
+ * during the REPL session can run against the live compiler. */
+static int dbg_compile_and_jit(DbgOpts *o, CfreeCompiler *compiler,
+ const CfreeJitHost *host, CfreeJit **out_jit) {
+ CfreeCCompileOptions copts;
{
- CfreeCompileOptions z = {0};
+ CfreeCCompileOptions z = {0};
copts = z;
}
- copts.opt_level = o->opt_level;
- copts.debug_info = o->debug_info;
- driver_cflags_fill_pp(&o->cf, &copts.pp);
- return driver_inputs_compile_and_jit(&o->inputs, pipe, &copts, o->entry,
- driver_dlsym_resolver, NULL, out_jit);
+ copts.code.opt_level = o->opt_level;
+ copts.code.debug_info = o->debug_info;
+ driver_cflags_fill_pp(&o->cf, &copts.preprocess);
+ return driver_inputs_compile_and_jit(&o->inputs, compiler, host, &copts,
+ o->entry, driver_dlsym_resolver, NULL,
+ out_jit);
}
-static void dbg_fill_compile_options(DbgOpts *o, CfreeCompileOptions *copts) {
+static void dbg_fill_compile_options(DbgOpts *o, CfreeCCompileOptions *copts) {
{
- CfreeCompileOptions z = {0};
+ CfreeCCompileOptions z = {0};
*copts = z;
}
- copts->opt_level = o->opt_level;
- copts->debug_info = o->debug_info;
- driver_cflags_fill_pp(&o->cf, &copts->pp);
+ copts->code.opt_level = o->opt_level;
+ copts->code.debug_info = o->debug_info;
+ driver_cflags_fill_pp(&o->cf, &copts->preprocess);
}
/* ============================================================
@@ -299,8 +308,9 @@ typedef struct Bp {
typedef struct DbgState {
DriverEnv *env;
- CfreePipeline *pipe;
- CfreeCompileOptions copts;
+ CfreeCompiler *compiler;
+ CfreeContext ctx;
+ CfreeCCompileOptions copts;
CfreeJit *jit;
CfreeJitSession *session;
const CfreeObjFile *view;
@@ -572,20 +582,14 @@ static int dbg_resolve_loc(DbgState *s, const char *spec, BpKind *kind_out,
return 1;
}
{
- int rc = cfree_dwarf_line_to_addr(s->dwarf, file, (uint32_t)line64, &pc);
- if (rc == 1) {
- driver_errf(DBG_TOOL, "file not covered by debug info: %s", file);
- driver_free(s->env, file, file_size);
- return 1;
- }
- if (rc == 2) {
+ CfreeStatus rc =
+ cfree_dwarf_line_to_addr(s->dwarf, file, (uint32_t)line64, &pc);
+ if (rc == CFREE_NOT_FOUND) {
driver_errf(DBG_TOOL, "no line %u in %s", (uint32_t)line64, file);
driver_free(s->env, file, file_size);
return 1;
}
- if (rc == 3) {
- /* Ambiguous: enumerate candidates and ask the user to retype
- * with a longer suffix. Cap at 8 — more than that, just say so. */
+ if (rc == CFREE_AMBIGUOUS) {
CfreeDwarfLineMatch cands[8];
uint32_t n = 0;
uint32_t k;
@@ -604,7 +608,7 @@ static int dbg_resolve_loc(DbgState *s, const char *spec, BpKind *kind_out,
driver_free(s->env, file, file_size);
return 1;
}
- if (rc != 0) {
+ if (rc != CFREE_OK) {
driver_errf(DBG_TOOL, "no line entry for %s", spec);
driver_free(s->env, file, file_size);
return 1;
@@ -692,7 +696,7 @@ static void dbg_print_pc(DbgState *s, uint64_t pc) {
uint32_t col = 0;
driver_printf("0x%llx", (unsigned long long)pc);
- if (cfree_jit_addr_to_sym(s->jit, pc, &sym, &off) == 0 && sym) {
+ if (cfree_jit_addr_to_sym(s->jit, pc, &sym, &off) == CFREE_OK && sym) {
if (off)
driver_printf(" <%s+0x%llx>", sym, (unsigned long long)off);
else
@@ -700,7 +704,7 @@ static void dbg_print_pc(DbgState *s, uint64_t pc) {
}
if (s->dwarf &&
cfree_dwarf_addr_to_line(s->dwarf, dbg_pc_rt_to_img(s, pc), &file, &line,
- &col) == 0 &&
+ &col) == CFREE_OK &&
file) {
driver_printf(" at %s:%u", file, line);
if (col)
@@ -761,7 +765,7 @@ typedef enum DbgRunMode {
} DbgRunMode;
static int dbg_drive(DbgState *s, DbgRunMode mode) {
- int rc;
+ CfreeStatus rc;
if (mode == RUN_FRESH && s->has_stop) {
/* The previous session is dead (entry returned or signal landed).
@@ -806,11 +810,11 @@ static int dbg_drive(DbgState *s, DbgRunMode mode) {
driver_restore_sigint();
- if (rc != 0) {
+ if (rc != CFREE_OK) {
driver_errf(DBG_TOOL,
- "session %s failed (rc=%d) — "
+ "session %s failed (st=%d) — "
"JIT session implementation pending",
- mode == RUN_FRESH ? "call" : "resume", rc);
+ mode == RUN_FRESH ? "call" : "resume", (int)rc);
return 1;
}
@@ -858,7 +862,7 @@ static void dbg_cmd_bt(DbgState *s) {
CfreeDwarfSubprogram sp;
CfreeUnwindFrame img_frame;
int have_sp;
- int step;
+ CfreeStatus step;
driver_printf("#%-2d ", level);
driver_printf("0x%llx", (unsigned long long)frame.pc);
@@ -866,7 +870,8 @@ static void dbg_cmd_bt(DbgState *s) {
{
const char *sym = NULL;
uint64_t off = 0;
- if (cfree_jit_addr_to_sym(s->jit, frame.pc, &sym, &off) == 0 && sym) {
+ if (cfree_jit_addr_to_sym(s->jit, frame.pc, &sym, &off) == CFREE_OK &&
+ sym) {
if (off)
driver_printf(" <%s+0x%llx>", sym, (unsigned long long)off);
else
@@ -875,19 +880,22 @@ static void dbg_cmd_bt(DbgState *s) {
}
img_frame = dbg_frame_for_dwarf(s, &frame);
- have_sp = (cfree_dwarf_subprogram_at(s->dwarf, img_frame.pc, &sp) == 0);
+ have_sp =
+ (cfree_dwarf_subprogram_at(s->dwarf, img_frame.pc, &sp) == CFREE_OK);
if (have_sp && sp.name) {
- CfreeDwarfParamIter *it;
+ CfreeDwarfParamIter *it = NULL;
CfreeDwarfVar p;
int first = 1;
driver_printf(" in %s%s (", sp.name, sp.inlined ? " [inlined]" : "");
- it = cfree_dwarf_param_iter_new(s->dwarf, img_frame.pc);
- if (it) {
- while (cfree_dwarf_param_iter_next(it, &p)) {
+ if (cfree_dwarf_param_iter_new(s->dwarf, img_frame.pc, &it) == CFREE_OK) {
+ for (;;) {
+ CfreeIterResult r;
uint8_t stack_buf[64];
uint8_t *buf;
size_t alloc;
size_t got;
+ r = cfree_dwarf_param_iter_next(it, &p);
+ if (r != CFREE_ITER_ITEM) break;
if (!first)
driver_printf(", ");
driver_printf("%s=", p.name ? p.name : "?");
@@ -910,13 +918,13 @@ static void dbg_cmd_bt(DbgState *s) {
const char *file = NULL;
uint32_t line = 0;
uint32_t col = 0;
- int rc =
+ CfreeStatus rc =
cfree_dwarf_addr_to_line(s->dwarf, img_frame.pc, &file, &line, &col);
- if (rc == 0 && file) {
+ if (rc == CFREE_OK && file) {
driver_printf(" at %s:%u", file, line);
if (col)
driver_printf(":%u", col);
- } else if (rc == 2) {
+ } else if (rc == CFREE_NOT_FOUND) {
driver_printf(" [no debug info for this frame]");
}
}
@@ -928,9 +936,9 @@ static void dbg_cmd_bt(DbgState *s) {
* frame in, then copy back to `frame` so the next iteration's
* lookups translate from the new runtime PC. */
step = cfree_dwarf_unwind_step(s->dwarf, &img_frame);
- if (step == 1)
+ if (step == CFREE_NOT_FOUND)
break; /* bottom of stack */
- if (step != 0) {
+ if (step != CFREE_OK) {
driver_errf(DBG_TOOL, "unwind step failed");
break;
}
@@ -1044,11 +1052,13 @@ static void dbg_print_value(DbgState *s, const CfreeDwarfType *type,
return;
case CFREE_DT_ENUM: {
int64_t v = dbg_load_le_s(buf, got);
- CfreeDwarfEnumIter *it = cfree_dwarf_enum_iter_new(s->dwarf, type);
+ CfreeDwarfEnumIter *it = NULL;
CfreeDwarfEnumVal ev;
const char *match = NULL;
- if (it) {
- while (cfree_dwarf_enum_iter_next(it, &ev)) {
+ if (cfree_dwarf_enum_iter_new(s->dwarf, type, &it) == CFREE_OK) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_enum_iter_next(it, &ev);
+ if (r != CFREE_ITER_ITEM) break;
if (ev.value == v) {
match = ev.name;
break;
@@ -1094,11 +1104,13 @@ static void dbg_print_value(DbgState *s, const CfreeDwarfType *type,
}
case CFREE_DT_STRUCT:
case CFREE_DT_UNION: {
- CfreeDwarfFieldIter *it = cfree_dwarf_field_iter_new(s->dwarf, type);
+ CfreeDwarfFieldIter *it = NULL;
CfreeDwarfField f;
driver_printf("{\n");
- if (it) {
- while (cfree_dwarf_field_iter_next(it, &f)) {
+ if (cfree_dwarf_field_iter_new(s->dwarf, type, &it) == CFREE_OK) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_field_iter_next(it, &f);
+ if (r != CFREE_ITER_ITEM) break;
size_t fsz = 0;
dbg_indent(depth + 1);
driver_printf(".%s = ", (f.name && *f.name) ? f.name : "<anon>");
@@ -1140,6 +1152,16 @@ static void dbg_print_value(DbgState *s, const CfreeDwarfType *type,
driver_printf("<?>");
}
+/* CfreeDwarfReadMemFn adapter: forwards a DWARF-driven memory read into
+ * the JIT session's address space. The user pointer carries the
+ * CfreeJitSession. */
+static CfreeStatus dbg_dwarf_read_mem(void *user, uint64_t addr, void *dst,
+ size_t n) {
+ CfreeJitSession *sess = (CfreeJitSession *)user;
+ if (!sess) return CFREE_INVALID;
+ return cfree_jit_session_read_mem(sess, addr, dst, n);
+}
+
/* Read a variable's bytes into a heap or stack buffer sized for its DIE
* type. On success returns 0 and sets *buf_out (which may point at
* stack_buf or at a heap allocation) plus *got_out. The caller frees
@@ -1160,8 +1182,8 @@ static int dbg_read_value(DbgState *s, const CfreeDwarfVarLoc *loc,
return 1;
cap = alloc;
}
- if (cfree_dwarf_loc_read(s->dwarf, loc, frame, s->session, buf, cap, &got) !=
- 0) {
+ if (cfree_dwarf_loc_read(s->dwarf, loc, frame, dbg_dwarf_read_mem, s->session,
+ buf, cap, &got) != CFREE_OK) {
if (alloc)
driver_free(s->env, buf, alloc);
return 1;
@@ -1194,12 +1216,13 @@ static void dbg_cmd_print(DbgState *s, const char *name) {
}
{
- int rc = s->dwarf
- ? cfree_dwarf_var_at(s->dwarf,
- dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
- name, &loc)
- : 1;
- if (rc == 0) {
+ CfreeStatus rc =
+ s->dwarf
+ ? cfree_dwarf_var_at(s->dwarf,
+ dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
+ name, &loc)
+ : CFREE_NOT_FOUND;
+ if (rc == CFREE_OK) {
dbg_translate_loc(s, &loc);
if (dbg_read_value(s, &loc, &s->last_stop.regs, stack_buf,
sizeof(stack_buf), &buf, &alloc, &got) != 0) {
@@ -1227,12 +1250,7 @@ static void dbg_cmd_print(DbgState *s, const char *name) {
return;
}
}
- if (rc == 2)
- driver_errf(DBG_TOOL,
- "no debug info for this frame; '%s' not in global symbols",
- name);
- else
- driver_errf(DBG_TOOL, "no variable or symbol named '%s'", name);
+ driver_errf(DBG_TOOL, "no variable or symbol named '%s'", name);
}
}
@@ -1255,17 +1273,14 @@ static void dbg_cmd_set(DbgState *s, const char *name, uint64_t value) {
return;
}
{
- int rc = s->dwarf
- ? cfree_dwarf_var_at(s->dwarf,
- dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
- name, &loc)
- : 1;
- if (rc != 0) {
- if (rc == 2)
- driver_errf(DBG_TOOL, "no debug info for this frame; cannot set '%s'",
- name);
- else
- driver_errf(DBG_TOOL, "no variable named '%s'", name);
+ CfreeStatus rc =
+ s->dwarf
+ ? cfree_dwarf_var_at(s->dwarf,
+ dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
+ name, &loc)
+ : CFREE_NOT_FOUND;
+ if (rc != CFREE_OK) {
+ driver_errf(DBG_TOOL, "no variable named '%s'", name);
return;
}
}
@@ -1278,13 +1293,14 @@ static void dbg_cmd_set(DbgState *s, const char *name, uint64_t value) {
switch (loc.kind) {
case CFREE_DLOC_FRAME_OFS: {
uint64_t addr = s->last_stop.regs.cfa + (uint64_t)(int64_t)loc.v.frame_ofs;
- if (cfree_jit_session_write_mem(s->session, addr, buf, sz) != 0) {
+ if (cfree_jit_session_write_mem(s->session, addr, buf, sz) != CFREE_OK) {
driver_errf(DBG_TOOL, "memory write failed");
}
return;
}
case CFREE_DLOC_GLOBAL:
- if (cfree_jit_session_write_mem(s->session, loc.v.global, buf, sz) != 0) {
+ if (cfree_jit_session_write_mem(s->session, loc.v.global, buf, sz) !=
+ CFREE_OK) {
driver_errf(DBG_TOOL, "memory write failed");
}
return;
@@ -1296,7 +1312,7 @@ static void dbg_cmd_set(DbgState *s, const char *name, uint64_t value) {
return;
}
fr.regs[loc.v.reg] = value;
- if (cfree_jit_session_set_regs(s->session, &fr) != 0) {
+ if (cfree_jit_session_set_regs(s->session, &fr) != CFREE_OK) {
driver_errf(DBG_TOOL, "register write failed");
return;
}
@@ -1338,7 +1354,7 @@ static void dbg_cmd_jump(DbgState *s, uint64_t pc) {
* ============================================================ */
static void dbg_cmd_info_vars(DbgState *s, uint32_t mask, const char *label) {
- CfreeDwarfVarIter *it;
+ CfreeDwarfVarIter *it = NULL;
CfreeDwarfVar v;
int printed = 0;
@@ -1351,13 +1367,15 @@ static void dbg_cmd_info_vars(DbgState *s, uint32_t mask, const char *label) {
return;
}
- it = cfree_dwarf_vars_at_new(s->dwarf,
- dbg_pc_rt_to_img(s, s->last_stop.regs.pc), mask);
- if (!it) {
+ if (cfree_dwarf_vars_at_new(s->dwarf,
+ dbg_pc_rt_to_img(s, s->last_stop.regs.pc), mask,
+ &it) != CFREE_OK) {
driver_printf("No %s.\n", label);
return;
}
- while (cfree_dwarf_vars_at_next(it, &v)) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_vars_at_next(it, &v);
+ if (r != CFREE_ITER_ITEM) break;
uint8_t stack_buf[64];
uint8_t *buf;
size_t alloc;
@@ -1397,7 +1415,7 @@ static void dbg_cmd_info_reg(DbgState *s) {
(unsigned long long)s->last_stop.regs.cfa);
for (i = 0; i < n; ++i) {
CfreeArchReg r;
- if (cfree_arch_register_at(arch, i, &r) != 0)
+ if (cfree_arch_register_at(arch, i, &r) != CFREE_OK)
continue;
if (r.dwarf_idx >= 32)
continue; /* outside CfreeUnwindFrame.regs */
@@ -1438,15 +1456,17 @@ static int dbg_glob(const char *pat, const char *s) {
static void dbg_cmd_info_syms(DbgState *s, CfreeSymKind want,
const char *pattern) {
- CfreeJitSymIter *it = cfree_jit_sym_iter_new(s->jit);
+ CfreeJitSymIter *it = NULL;
CfreeJitSym sym;
int printed = 0;
- if (!it) {
+ if (cfree_jit_sym_iter_new(s->jit, &it) != CFREE_OK) {
driver_errf(DBG_TOOL, "symbol enumeration unavailable");
return;
}
- while (cfree_jit_sym_iter_next(it, &sym)) {
+ for (;;) {
+ CfreeIterResult r = cfree_jit_sym_iter_next(it, &sym);
+ if (r != CFREE_ITER_ITEM) break;
if (sym.kind != want)
continue;
if (pattern && !dbg_glob(pattern, sym.name))
@@ -1461,12 +1481,13 @@ static void dbg_cmd_info_syms(DbgState *s, CfreeSymKind want,
static int dbg_refresh_dwarf(DbgState *s) {
if (s->dwarf) {
- cfree_dwarf_close(s->dwarf);
+ cfree_dwarf_free(s->dwarf);
s->dwarf = NULL;
}
s->view = cfree_jit_view(s->jit);
if (s->view) {
- s->dwarf = cfree_dwarf_open(cfree_pipeline_compiler(s->pipe), s->view);
+ if (cfree_dwarf_open(&s->ctx, s->view, &s->dwarf) != CFREE_OK)
+ s->dwarf = NULL;
if (s->dwarf && s->session) {
cfree_jit_session_attach_dwarf(s->session, s->dwarf);
}
@@ -1588,19 +1609,28 @@ static CfreeLanguage dbg_jit_language_for_tag(DbgState *s, const char *tag,
static int dbg_jit_compile_append(DbgState *s, CfreeLanguage lang,
const char *input_name, const char *src,
size_t len) {
- CfreeBytesInput in;
+ CfreeSourceInput sin;
+ CfreeFrontendCompileOptions fopts;
CfreeObjBuilder *ob = NULL;
s->jit_counter++;
- in.name = input_name ? input_name : dbg_jit_default_name(lang);
- in.data = (const uint8_t *)src;
- in.len = len;
- in.lang = lang;
- if (cfree_pipeline_compile_obj(s->pipe, &s->copts, &in, &ob) != 0 || !ob) {
+ sin.bytes.name = input_name ? input_name : dbg_jit_default_name(lang);
+ sin.bytes.data = (const uint8_t *)src;
+ sin.bytes.len = len;
+ sin.lang = lang;
+ {
+ CfreeFrontendCompileOptions z = {0};
+ fopts = z;
+ }
+ fopts.code = s->copts.code;
+ fopts.diagnostics = s->copts.diagnostics;
+ fopts.language_options = &s->copts;
+ if (cfree_compile_source_obj(s->compiler, &fopts, &sin, &ob) != CFREE_OK ||
+ !ob) {
driver_errf(DBG_TOOL, "jit compile failed");
return 1;
}
- if (cfree_jit_append_obj(s->jit, ob) != 0) {
+ if (cfree_jit_append_obj(s->jit, ob) != CFREE_OK) {
driver_errf(DBG_TOOL, "jit append failed");
return 1;
}
@@ -1777,7 +1807,7 @@ static int dbg_call_u64_entry(DbgState *s, void *entry, const uint64_t *args,
return 1;
}
if (cfree_jit_session_call_u64(s->session, entry, args, nargs, &ret, &stop) !=
- 0) {
+ CFREE_OK) {
driver_restore_sigint();
driver_errf(DBG_TOOL, "call failed (debuggee must be idle or exited)");
return 1;
@@ -1919,8 +1949,8 @@ static int dbg_append_typed_func_proto(DbgState *s, char **src, size_t *len,
if (!s->dwarf)
goto out;
img_pc = dbg_pc_rt_to_img(s, sym->addr);
- if (!img_pc || cfree_dwarf_subprogram_at(s->dwarf, img_pc, &sp) != 0) {
- if (cfree_dwarf_subprogram_named(s->dwarf, sym->name, &sp) != 0)
+ if (!img_pc || cfree_dwarf_subprogram_at(s->dwarf, img_pc, &sp) != CFREE_OK) {
+ if (cfree_dwarf_subprogram_named(s->dwarf, sym->name, &sp) != CFREE_OK)
goto out;
use_named = 1;
}
@@ -1937,13 +1967,17 @@ static int dbg_append_typed_func_proto(DbgState *s, char **src, size_t *len,
goto out;
if (dbg_buf_append(s, &tmp, &tmp_len, &tmp_cap, "(", 1) != 0)
goto out;
- pit = use_named ? cfree_dwarf_param_iter_new_named(s->dwarf, sym->name)
- : cfree_dwarf_param_iter_new(s->dwarf, img_pc);
- if (!pit) {
+ pit = NULL;
+ if (use_named
+ ? cfree_dwarf_param_iter_new_named(s->dwarf, sym->name, &pit) !=
+ CFREE_OK
+ : cfree_dwarf_param_iter_new(s->dwarf, img_pc, &pit) != CFREE_OK) {
if (dbg_buf_append(s, &tmp, &tmp_len, &tmp_cap, "void", 4) != 0)
goto out;
} else {
- while (cfree_dwarf_param_iter_next(pit, ¶m)) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_param_iter_next(pit, ¶m);
+ if (r != CFREE_ITER_ITEM) break;
if (any_param &&
dbg_buf_append(s, &tmp, &tmp_len, &tmp_cap, ", ", 2) != 0) {
cfree_dwarf_param_iter_free(pit);
@@ -2072,11 +2106,13 @@ static int dbg_append_fallback_func_proto(DbgState *s, char **src, size_t *len,
static int dbg_append_expr_prelude(DbgState *s, char **src, size_t *len,
size_t *cap, const char *call_scan_text) {
- CfreeJitSymIter *it = cfree_jit_sym_iter_new(s->jit);
+ CfreeJitSymIter *it = NULL;
CfreeJitSym sym;
- if (!it)
+ if (cfree_jit_sym_iter_new(s->jit, &it) != CFREE_OK)
return 0;
- while (cfree_jit_sym_iter_next(it, &sym)) {
+ for (;;) {
+ CfreeIterResult r = cfree_jit_sym_iter_next(it, &sym);
+ if (r != CFREE_ITER_ITEM) break;
if (sym.kind != CFREE_SK_FUNC)
continue;
if (!dbg_c_ident_ok(sym.name))
@@ -2296,7 +2332,7 @@ static void dbg_cmd_examine(DbgState *s, uint64_t addr, size_t count) {
while (remaining) {
size_t chunk = remaining > sizeof(buf) ? sizeof(buf) : remaining;
size_t i;
- if (cfree_jit_session_read_mem(s->session, addr, buf, chunk) != 0) {
+ if (cfree_jit_session_read_mem(s->session, addr, buf, chunk) != CFREE_OK) {
driver_errf(DBG_TOOL, "read failed at 0x%llx", (unsigned long long)addr);
return;
}
@@ -2358,33 +2394,33 @@ static void dbg_cmd_list(DbgState *s, const char *spec) {
return;
}
- /* Validate via DWARF first. Maps the new return codes to focused
- * REPL messages identical to `b file:line`'s. */
- rc = cfree_dwarf_line_to_addr(s->dwarf, path, (uint32_t)line_u, &pc);
- if (rc == 1) {
- driver_errf(DBG_TOOL, "file not covered by debug info: %s", path);
- return;
- }
- if (rc == 2) {
- driver_errf(DBG_TOOL, "no line %u in %s", (uint32_t)line_u, path);
- return;
- }
- if (rc == 3) {
- driver_errf(DBG_TOOL,
- "ambiguous: %s:%u matches multiple files; "
- "use a longer path suffix",
- path, (uint32_t)line_u);
- return;
- }
- if (rc != 0) {
- driver_errf(DBG_TOOL, "no line entry for %s", spec);
- return;
+ /* Validate via DWARF first. */
+ {
+ CfreeStatus st =
+ cfree_dwarf_line_to_addr(s->dwarf, path, (uint32_t)line_u, &pc);
+ if (st == CFREE_NOT_FOUND) {
+ driver_errf(DBG_TOOL, "no line %u in %s", (uint32_t)line_u, path);
+ return;
+ }
+ if (st == CFREE_AMBIGUOUS) {
+ driver_errf(DBG_TOOL,
+ "ambiguous: %s:%u matches multiple files; "
+ "use a longer path suffix",
+ path, (uint32_t)line_u);
+ return;
+ }
+ if (st != CFREE_OK) {
+ driver_errf(DBG_TOOL, "no line entry for %s", spec);
+ return;
+ }
+ rc = 0;
+ (void)rc;
}
/* Try to read the file via file_io. On miss, fall back to the
* DWARF-only summary line per doc/DBG.md §10. */
io = s->env && s->env->file_io.read_all ? &s->env->file_io : NULL;
- if (!io || !io->read_all(io->user, path, &fd)) {
+ if (!io || io->read_all(io->user, path, &fd) != CFREE_OK) {
driver_printf("%s:%u [source not available; pc=0x%llx]\n", path,
(uint32_t)line_u, (unsigned long long)pc);
return;
@@ -2434,20 +2470,20 @@ static void dbg_cmd_list(DbgState *s, const char *spec) {
* driver does not yet wire conditions, but the spec callback slot is left
* NULL so this remains forward-compatible. */
static int dbg_bp_arm(DbgState *s, Bp *b) {
+ CfreeStatus st;
if (b->skip_count == 0 && b->max_hits == 0) {
- return cfree_jit_session_breakpoint_set(s->session, b->addr,
- &b->session_id);
- }
- {
+ st = cfree_jit_session_breakpoint_set(s->session, b->addr, &b->session_id);
+ } else {
CfreeBreakpointSpec spec;
CfreeBreakpointSpec z = {0};
spec = z;
spec.addr = b->addr;
spec.skip_count = b->skip_count;
spec.max_hits = b->max_hits;
- return cfree_jit_session_breakpoint_set_spec(s->session, &spec,
- &b->session_id);
+ st = cfree_jit_session_breakpoint_set_spec(s->session, &spec,
+ &b->session_id);
}
+ return st == CFREE_OK ? 0 : 1;
}
static void dbg_cmd_break(DbgState *s, const char *spec) {
@@ -2605,6 +2641,8 @@ static CfreeLanguage dbg_default_language_from_inputs(const DbgOpts *o) {
return CFREE_LANG_C;
}
+
+
/* ============================================================
* Tokenizer / dispatch
* ============================================================ */
@@ -2929,10 +2967,12 @@ static void dbg_repl(DbgState *s) {
int driver_dbg(int argc, char **argv) {
DriverEnv env;
DbgOpts o = {0};
- CfreePipeline *pipe = NULL;
+ CfreeCompiler *compiler = NULL;
CfreeJit *jit = NULL;
DbgState st = {0};
- CfreeEnv cenv;
+ CfreeContext ctx;
+ CfreeJitHost jhost;
+ CfreeDbgHost dhost;
CfreeTarget target;
int rc;
@@ -2950,26 +2990,28 @@ int driver_dbg(int argc, char **argv) {
return 2;
}
- cenv = driver_env_to_cfree(&env);
+ ctx = driver_env_to_context(&env);
+ jhost = driver_env_to_jit_host(&env);
+ dhost = driver_env_to_dbg_host(&env);
target = driver_host_target();
- pipe = driver_pipeline_new(target, &cenv);
- if (!pipe) {
+ if (driver_compiler_new(target, &ctx, &compiler) != CFREE_OK) {
driver_errf(DBG_TOOL, "failed to initialize compiler");
dbg_options_release(&o);
driver_env_fini(&env);
return 1;
}
- rc = dbg_compile_and_jit(&o, pipe, &jit);
+ rc = dbg_compile_and_jit(&o, compiler, &jhost, &jit);
if (rc != 0) {
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
dbg_options_release(&o);
driver_env_fini(&env);
return rc;
}
st.env = &env;
- st.pipe = pipe;
+ st.compiler = compiler;
+ st.ctx = ctx;
dbg_fill_compile_options(&o, &st.copts);
st.jit = jit;
st.default_jit_lang = dbg_default_language_from_inputs(&o);
@@ -2981,24 +3023,25 @@ int driver_dbg(int argc, char **argv) {
if (!st.entry_addr) {
driver_errf(DBG_TOOL, "entry symbol not found: %s", o.entry);
cfree_jit_free(jit);
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
dbg_options_release(&o);
driver_env_fini(&env);
return 1;
}
- st.session = cfree_jit_session_new(jit);
- if (!st.session) {
+ if (cfree_jit_session_new(jit, &dhost, &st.session) != CFREE_OK) {
driver_errf(DBG_TOOL,
"JIT session not yet implemented in libcfree — "
"REPL will start in degraded mode (commands will surface "
"'session implementation pending' until the lib lands)");
+ st.session = NULL;
/* Keep going so the surrounding driver path is exercised. */
}
st.view = cfree_jit_view(jit);
if (st.view) {
- st.dwarf = cfree_dwarf_open(cfree_pipeline_compiler(pipe), st.view);
+ if (cfree_dwarf_open(&ctx, st.view, &st.dwarf) != CFREE_OK)
+ st.dwarf = NULL;
if (st.dwarf && st.session) {
cfree_jit_session_attach_dwarf(st.session, st.dwarf);
}
@@ -3008,11 +3051,11 @@ int driver_dbg(int argc, char **argv) {
dbg_bps_release_all(&st);
if (st.dwarf)
- cfree_dwarf_close(st.dwarf);
+ cfree_dwarf_free(st.dwarf);
if (st.session)
cfree_jit_session_free(st.session);
cfree_jit_free(jit);
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
dbg_options_release(&o);
driver_env_fini(&env);
return 0;
diff --git a/driver/driver.h b/driver/driver.h
@@ -1,12 +1,15 @@
#ifndef CFREE_DRIVER_H
#define CFREE_DRIVER_H
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/compile.h>
+#include <cfree/jit.h>
+#include <cfree/dbg.h>
/* The cfree CLI driver. Multi-call binary: dispatches to one of seven tool
* front-ends by argv[0]'s basename, falling back to argv[1] (e.g.
* `cfree cc ...`). The driver only depends on libcfree's public API
- * (<cfree.h>); it has no access to libcfree's internal headers.
+ * (<cfree/...>); it has no access to libcfree's internal headers.
*
* Preprocessor-only mode is `cc -E` — there is no separate `cpp` tool. */
@@ -67,7 +70,12 @@ int driver_argv_wants_help(int argc, char **argv, int accept_short_h);
/* Shared host environment used by every tool that calls into libcfree.
* driver_env_init wires up the libc-backed heap, the stderr diag sink, and
* a POSIX file_io implementation (open/read/write on real paths). It is
- * the single piece of glue that turns "the host" into a CfreeEnv. */
+ * the single piece of glue that turns "the host" into a CfreeContext.
+ *
+ * The execmem / dbg_os / jit_tls vtables that used to live on CfreeEnv now
+ * live on CfreeJitHost / CfreeDbgHost. They are still constructed here so
+ * that one DriverEnv covers every libcfree-using tool, but they're handed
+ * to libcfree per-call via the appropriate host struct. */
typedef struct DriverEnv {
CfreeHeap *heap;
CfreeDiagSink *diag;
@@ -81,23 +89,34 @@ typedef struct DriverEnv {
void driver_env_init(DriverEnv *);
void driver_env_fini(DriverEnv *);
-CfreeEnv driver_env_to_cfree(const DriverEnv *);
+
+/* Build a CfreeContext value pointing at the DriverEnv's heap/diag/file_io
+ * vtables. The returned value can be passed by const-pointer to any
+ * libcfree entry that takes `const CfreeContext *`. */
+CfreeContext driver_env_to_context(const DriverEnv *);
+
+/* Build a CfreeJitHost from the DriverEnv (execmem + tls). The returned
+ * struct holds borrowed pointers to vtables owned by g_execmem_posix /
+ * g_jit_tls_posix; callers must not outlive driver_env_fini. */
+CfreeJitHost driver_env_to_jit_host(const DriverEnv *);
+
+/* Build a CfreeDbgHost from the DriverEnv (dbg_os). */
+CfreeDbgHost driver_env_to_dbg_host(const DriverEnv *);
/* Tells the stderr diag sink which compiler to use when resolving
- * SrcLoc.file_id to a path. The driver_{compiler,pipeline}_{new,free}
- * helpers below already manage this; call this directly only when you
- * hand a CfreeCompiler from outside those helpers (none today). */
+ * SrcLoc.file_id to a path. The driver_compiler_{new,free} helpers
+ * below already manage this; call this directly only when you hand a
+ * CfreeCompiler from outside those helpers (none today). */
void driver_diag_set_compiler(CfreeCompiler *);
-/* Lifecycle helpers around cfree_{compiler,pipeline}_{new,free}.
- * Identical to the raw entries except they register/clear the active
- * compiler with the stderr diag sink, so diagnostics can resolve
- * loc.file_id to its registered path. Tools should always go through
- * these inside the driver. */
-CfreeCompiler *driver_compiler_new(CfreeTarget, const CfreeEnv *);
+/* Lifecycle helpers around cfree_compiler_{new,free}. Identical to the
+ * raw entries except they register the C / TOY / WASM frontends and
+ * register the active compiler with the stderr diag sink so diagnostics
+ * resolve loc.file_id to its registered path. Returns CFREE_OK on
+ * success; on failure *out is NULL. */
+CfreeStatus driver_compiler_new(CfreeTarget, const CfreeContext *,
+ CfreeCompiler **out);
void driver_compiler_free(CfreeCompiler *);
-CfreePipeline *driver_pipeline_new(CfreeTarget, const CfreeEnv *);
-void driver_pipeline_free(CfreePipeline *);
/* Default target used by tools that don't expose a target-selection flag
* yet. v1: native-looking host target (chosen at compile time). */
@@ -187,7 +206,7 @@ int driver_edit_temp(DriverEnv *, const char *suffix, const uint8_t *initial,
size_t initial_size, uint8_t **out_data, size_t *out_size);
/* Path-shaped input loader. Wraps env.file_io.read_all so each tool can
- * convert a list of paths to a list of CfreeBytesInput without re-implementing
+ * convert a list of paths to a list of CfreeBytes without re-implementing
* load/release/error bookkeeping. `loaded` is set to 1 on success; release is
* idempotent and does nothing when loaded is already 0. driver_load_bytes
* fills `in.name = path` plus the loaded data/len; driver_release_bytes hands
@@ -199,7 +218,7 @@ typedef struct DriverLoad {
} DriverLoad;
int driver_load_bytes(const CfreeFileIO *, const char *tool, const char *path,
- DriverLoad *out, CfreeBytesInput *in);
+ DriverLoad *out, CfreeBytes *in);
void driver_release_bytes(const CfreeFileIO *, DriverLoad *);
/* Read one line from stdin into `buf` (cap >= 2). Strips the trailing
diff --git a/driver/emu.c b/driver/emu.c
@@ -3,6 +3,10 @@
#include "driver.h"
+#include <cfree/core.h>
+#include <cfree/emu.h>
+#include <cfree/object.h>
+
/* `cfree emu` — run a guest user-mode ELF on the host via libcfree's
* per-basic-block JIT translator. v1 guest archs: aarch64, riscv64.
*
@@ -216,10 +220,10 @@ static const char* emu_arch_name(CfreeEmuArch a) {
/* Auto-detect the guest arch from the ELF magic when -arch was not set.
* Honors a user-supplied -arch verbatim (no cross-check against the ELF;
* mismatches surface as decode failures inside the emu). */
-static int emu_resolve_arch(EmuOptions* o, const CfreeBytesInput* elf) {
+static int emu_resolve_arch(EmuOptions* o, const CfreeBytes* elf) {
CfreeTarget detected;
if (o->guest_arch_set) return 0;
- if (cfree_detect_target(elf->data, elf->len, &detected) != 0) {
+ if (cfree_detect_target(elf->data, elf->len, &detected) != CFREE_OK) {
driver_errf(EMU_TOOL, "could not detect target from %s; pass -arch",
o->elf_path);
return 1;
@@ -251,10 +255,10 @@ static void emu_finalize_argv(EmuOptions* o, const char*** out_argv) {
int driver_emu(int argc, char** argv) {
DriverEnv env;
EmuOptions eo = {0};
- CfreeEnv cenv;
+ CfreeContext ctx;
CfreeCompiler* compiler = NULL;
DriverLoad elf_lf = {0};
- CfreeBytesInput elf_in;
+ CfreeBytes elf_in;
CfreeEmuOptions opts;
const char** guest_argv;
int exit_code = 0;
@@ -274,13 +278,13 @@ int driver_emu(int argc, char** argv) {
return 2;
}
- cenv = driver_env_to_cfree(&env);
- if (!cenv.file_io || !cenv.file_io->read_all) {
+ ctx = driver_env_to_context(&env);
+ if (!ctx.file_io || !ctx.file_io->read_all) {
driver_errf(EMU_TOOL, "host file I/O unavailable");
goto out;
}
- if (driver_load_bytes(cenv.file_io, EMU_TOOL, eo.elf_path, &elf_lf,
+ if (driver_load_bytes(ctx.file_io, EMU_TOOL, eo.elf_path, &elf_lf,
&elf_in) != 0) {
goto out;
}
@@ -290,8 +294,7 @@ int driver_emu(int argc, char** argv) {
/* The emu's host-side compiler runs at the host's native target —
* the JIT image holds host code, the *guest* arch is configured
* through CfreeEmuOptions.guest_arch. */
- compiler = driver_compiler_new(driver_host_target(), &cenv);
- if (!compiler) {
+ if (driver_compiler_new(driver_host_target(), &ctx, &compiler) != CFREE_OK) {
driver_errf(EMU_TOOL, "failed to initialize compiler");
goto out;
}
@@ -310,7 +313,7 @@ int driver_emu(int argc, char** argv) {
opts.argv = (const char* const*)guest_argv;
opts.envp = 0;
- if (cfree_emu_run(compiler, &opts, &exit_code) != 0) {
+ if (cfree_emu_run(compiler, &opts, &exit_code) != CFREE_OK) {
driver_errf(EMU_TOOL, "emulation of %s (%s) failed", eo.elf_path,
emu_arch_name(eo.guest_arch));
goto out;
@@ -320,8 +323,8 @@ int driver_emu(int argc, char** argv) {
out:
if (compiler) driver_compiler_free(compiler);
- if (elf_lf.loaded && cenv.file_io)
- driver_release_bytes(cenv.file_io, &elf_lf);
+ if (elf_lf.loaded && ctx.file_io)
+ driver_release_bytes(ctx.file_io, &elf_lf);
emu_options_release(&eo);
driver_env_fini(&env);
return rc;
diff --git a/driver/env.c b/driver/env.c
@@ -136,20 +136,25 @@ static CfreeCompiler *g_diag_active_compiler;
void driver_diag_set_compiler(CfreeCompiler *c) { g_diag_active_compiler = c; }
-/* driver_{compiler,pipeline}_{new,free}: thin wrappers around the libcfree
+/* driver_compiler_{new,free}: thin wrappers around the libcfree
* lifecycle entries that register the active compiler with the diag sink
* and clear it on free. Driver tools call these instead of the raw
* cfree_* calls so a fatal diagnostic from libcfree prints the source
* file name rather than a bare numeric file_id. */
-CfreeCompiler *driver_compiler_new(CfreeTarget t, const CfreeEnv *env) {
- CfreeCompiler *c = cfree_compiler_new(t, env);
- if (c) {
- cfree_c_register(c);
- (void)cfree_register_frontend(c, CFREE_LANG_TOY, cfree_toy_compile);
- cfree_wasm_register(c);
- driver_diag_set_compiler(c);
+CfreeStatus driver_compiler_new(CfreeTarget t, const CfreeContext *ctx,
+ CfreeCompiler **out) {
+ CfreeCompiler *c = NULL;
+ CfreeStatus st = cfree_compiler_new(t, ctx, &c);
+ if (st != CFREE_OK) {
+ if (out) *out = NULL;
+ return st;
}
- return c;
+ cfree_c_register(c);
+ (void)cfree_register_frontend(c, CFREE_LANG_TOY, cfree_toy_compile);
+ cfree_wasm_register(c);
+ driver_diag_set_compiler(c);
+ if (out) *out = c;
+ return CFREE_OK;
}
void driver_compiler_free(CfreeCompiler *c) {
@@ -160,26 +165,6 @@ void driver_compiler_free(CfreeCompiler *c) {
cfree_compiler_free(c);
}
-CfreePipeline *driver_pipeline_new(CfreeTarget t, const CfreeEnv *env) {
- CfreePipeline *p = cfree_pipeline_new(t, env);
- if (p) {
- CfreeCompiler *c = cfree_pipeline_compiler(p);
- cfree_c_register(c);
- (void)cfree_register_frontend(c, CFREE_LANG_TOY, cfree_toy_compile);
- cfree_wasm_register(c);
- driver_diag_set_compiler(c);
- }
- return p;
-}
-
-void driver_pipeline_free(CfreePipeline *p) {
- if (!p)
- return;
- if (g_diag_active_compiler == cfree_pipeline_compiler(p))
- driver_diag_set_compiler(NULL);
- cfree_pipeline_free(p);
-}
-
static void diag_stderr_emit(CfreeDiagSink *s, CfreeDiagKind k, CfreeSrcLoc loc,
const char *fmt, va_list ap) {
(void)s;
@@ -291,20 +276,22 @@ static int exec_dual_lookup(void *runtime_addr, size_t n, void **write_out) {
return 1;
}
-static int execmem_reserve_single(size_t size, CfreeExecMemRegion *out) {
+static CfreeStatus execmem_reserve_single(size_t size,
+ CfreeExecMemRegion *out) {
void *p =
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (p == MAP_FAILED)
- return 1;
+ return CFREE_NOMEM;
out->write = p;
out->runtime = p;
out->size = size;
out->token = NULL; /* munmap suffices on release */
- return 0;
+ return CFREE_OK;
}
#if DRIVER_DUAL_APPLE
-static int execmem_reserve_dual_apple(size_t size, CfreeExecMemRegion *out) {
+static CfreeStatus execmem_reserve_dual_apple(size_t size,
+ CfreeExecMemRegion *out) {
/* 1) Allocate the writable backing via mmap. */
void *w =
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
@@ -313,7 +300,7 @@ static int execmem_reserve_dual_apple(size_t size, CfreeExecMemRegion *out) {
kern_return_t kr;
ExecMemToken *tok;
if (w == MAP_FAILED)
- return 1;
+ return CFREE_NOMEM;
/* 2) Create an alias mapping. copy=FALSE makes the new VA share the
* same physical pages. We request VM_INHERIT_NONE so child
@@ -324,7 +311,7 @@ static int execmem_reserve_dual_apple(size_t size, CfreeExecMemRegion *out) {
/*copy=*/FALSE, &cur, &max, VM_INHERIT_NONE);
if (kr != KERN_SUCCESS) {
munmap(w, size);
- return 1;
+ return CFREE_ERR;
}
/* 3) Drop the runtime alias to PROT_READ until protect() flips it.
@@ -333,14 +320,14 @@ static int execmem_reserve_dual_apple(size_t size, CfreeExecMemRegion *out) {
if (mprotect((void *)(uintptr_t)r_addr, size, PROT_READ) != 0) {
munmap((void *)(uintptr_t)r_addr, size);
munmap(w, size);
- return 1;
+ return CFREE_ERR;
}
tok = (ExecMemToken *)malloc(sizeof(*tok));
if (!tok) {
munmap((void *)(uintptr_t)r_addr, size);
munmap(w, size);
- return 1;
+ return CFREE_NOMEM;
}
tok->write_addr = w;
tok->runtime_addr = (void *)(uintptr_t)r_addr;
@@ -352,12 +339,13 @@ static int execmem_reserve_dual_apple(size_t size, CfreeExecMemRegion *out) {
out->runtime = (void *)(uintptr_t)r_addr;
out->size = size;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#endif
#if DRIVER_DUAL_LINUX
-static int execmem_reserve_dual_linux(size_t size, CfreeExecMemRegion *out) {
+static CfreeStatus execmem_reserve_dual_linux(size_t size,
+ CfreeExecMemRegion *out) {
/* memfd_create gives us an anonymous fd; two mmaps of that fd alias
* the same physical pages at distinct VAs. */
int fd = (int)syscall(SYS_memfd_create, "cfree-jit", 0u);
@@ -365,22 +353,22 @@ static int execmem_reserve_dual_linux(size_t size, CfreeExecMemRegion *out) {
void *r;
ExecMemToken *tok;
if (fd < 0)
- return 1;
+ return CFREE_ERR;
if (ftruncate(fd, (off_t)size) != 0) {
close(fd);
- return 1;
+ return CFREE_ERR;
}
w = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (w == MAP_FAILED) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
r = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (r == MAP_FAILED) {
munmap(w, size);
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
/* The fd is no longer needed once both aliases are mapped. */
close(fd);
@@ -389,7 +377,7 @@ static int execmem_reserve_dual_linux(size_t size, CfreeExecMemRegion *out) {
if (!tok) {
munmap(r, size);
munmap(w, size);
- return 1;
+ return CFREE_NOMEM;
}
tok->write_addr = w;
tok->runtime_addr = r;
@@ -401,15 +389,15 @@ static int execmem_reserve_dual_linux(size_t size, CfreeExecMemRegion *out) {
out->runtime = r;
out->size = size;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#endif
-static int execmem_reserve(void *user, size_t size, int prot,
- CfreeExecMemRegion *out) {
+static CfreeStatus execmem_reserve(void *user, size_t size, int prot,
+ CfreeExecMemRegion *out) {
(void)user;
if (!out || !size)
- return 1;
+ return CFREE_INVALID;
if (prot & CFREE_PROT_EXEC) {
#if DRIVER_DUAL_APPLE
return execmem_reserve_dual_apple(size, out);
@@ -424,9 +412,11 @@ static int execmem_reserve(void *user, size_t size, int prot,
return execmem_reserve_single(size, out);
}
-static int execmem_protect(void *user, void *addr, size_t size, int prot) {
+static CfreeStatus execmem_protect(void *user, void *addr, size_t size,
+ int prot) {
(void)user;
- return mprotect(addr, size, cfree_to_posix_prot(prot));
+ return mprotect(addr, size, cfree_to_posix_prot(prot)) == 0 ? CFREE_OK
+ : CFREE_ERR;
}
static void execmem_release(void *user, CfreeExecMemRegion *region) {
@@ -513,23 +503,23 @@ static void *dbg_thread_trampoline(void *p) {
return NULL;
}
-static int dbg_thread_start(void *user, void (*fn)(void *), void *arg,
- void **thread_out) {
+static CfreeStatus dbg_thread_start(void *user, void (*fn)(void *), void *arg,
+ void **thread_out) {
DbgThread *t;
(void)user;
t = (DbgThread *)malloc(sizeof(*t));
if (!t)
- return 1;
+ return CFREE_NOMEM;
t->fn = fn;
t->arg = arg;
if (pthread_create(&t->tid, NULL, dbg_thread_trampoline, t) != 0) {
free(t);
- return 1;
+ return CFREE_ERR;
}
g_dbg_worker_tid = t->tid;
g_dbg_worker_tid_valid = 1;
*thread_out = t;
- return 0;
+ return CFREE_OK;
}
static void dbg_thread_join(void *user, void *thread) {
@@ -542,12 +532,12 @@ static void dbg_thread_join(void *user, void *thread) {
free(t);
}
-static int dbg_thread_interrupt(void *user, void *thread) {
+static CfreeStatus dbg_thread_interrupt(void *user, void *thread) {
DbgThread *t = (DbgThread *)thread;
(void)user;
if (!t)
- return 1;
- return pthread_kill(t->tid, DBG_INTERRUPT_SIGNO) == 0 ? 0 : 1;
+ return CFREE_INVALID;
+ return pthread_kill(t->tid, DBG_INTERRUPT_SIGNO) == 0 ? CFREE_OK : CFREE_ERR;
}
/* --- event shim --- */
@@ -558,23 +548,24 @@ typedef struct DbgEvent {
int signaled;
} DbgEvent;
-static void *dbg_event_new(void *user) {
+static CfreeStatus dbg_event_new(void *user, void **event_out) {
DbgEvent *e;
(void)user;
e = (DbgEvent *)malloc(sizeof(*e));
if (!e)
- return NULL;
+ return CFREE_NOMEM;
if (pthread_mutex_init(&e->mu, NULL) != 0) {
free(e);
- return NULL;
+ return CFREE_ERR;
}
if (pthread_cond_init(&e->cv, NULL) != 0) {
pthread_mutex_destroy(&e->mu);
free(e);
- return NULL;
+ return CFREE_ERR;
}
e->signaled = 0;
- return e;
+ *event_out = e;
+ return CFREE_OK;
}
static void dbg_event_free(void *user, void *ev) {
@@ -587,7 +578,7 @@ static void dbg_event_free(void *user, void *ev) {
free(e);
}
-static void dbg_event_wait(void *user, void *ev) {
+static CfreeStatus dbg_event_wait(void *user, void *ev) {
DbgEvent *e = (DbgEvent *)ev;
(void)user;
pthread_mutex_lock(&e->mu);
@@ -595,26 +586,29 @@ static void dbg_event_wait(void *user, void *ev) {
pthread_cond_wait(&e->cv, &e->mu);
e->signaled = 0;
pthread_mutex_unlock(&e->mu);
+ return CFREE_OK;
}
/* pthread_cond_signal is not formally async-signal-safe per POSIX, but
* it is in practice on glibc and Apple libc when callers manage the
* signal mask carefully. LLDB and rr rely on the same pattern. */
-static void dbg_event_signal(void *user, void *ev) {
+static CfreeStatus dbg_event_signal(void *user, void *ev) {
DbgEvent *e = (DbgEvent *)ev;
(void)user;
pthread_mutex_lock(&e->mu);
e->signaled = 1;
pthread_cond_broadcast(&e->cv);
pthread_mutex_unlock(&e->mu);
+ return CFREE_OK;
}
-static void dbg_event_reset(void *user, void *ev) {
+static CfreeStatus dbg_event_reset(void *user, void *ev) {
DbgEvent *e = (DbgEvent *)ev;
(void)user;
pthread_mutex_lock(&e->mu);
e->signaled = 0;
pthread_mutex_unlock(&e->mu);
+ return CFREE_OK;
}
/* --- signal install + ucontext marshalling --- */
@@ -669,7 +663,7 @@ static void dbg_frame_to_ucontext(const CfreeUnwindFrame *f, ucontext_t *uc) {
static void dbg_signal_handler(int signo, siginfo_t *si, void *ucv) {
ucontext_t *uc = (ucontext_t *)ucv;
CfreeUnwindFrame frame;
- int rc;
+ CfreeStatus rc;
(void)si;
/* SIGSEGV/SIGBUS during an armed guarded_copy: bail out to the
@@ -697,7 +691,7 @@ static void dbg_signal_handler(int signo, siginfo_t *si, void *ucv) {
dbg_ucontext_to_frame(uc, &frame);
rc = g_dbg_ops.on_fault(g_dbg_session, signo, &frame);
- if (rc != 0) {
+ if (rc != CFREE_OK) {
/* Session declined to handle: restore default and re-raise so the
* host produces a core dump for the original cause. */
int i;
@@ -713,13 +707,14 @@ static void dbg_signal_handler(int signo, siginfo_t *si, void *ucv) {
dbg_frame_to_ucontext(&frame, uc);
}
-static int dbg_signals_install(void *user, const CfreeDbgSignalOps *ops,
- void *session) {
+static CfreeStatus dbg_signals_install(void *user,
+ const CfreeDbgSignalOps *ops,
+ void *session) {
struct sigaction sa;
int i;
(void)user;
if (g_dbg_installed)
- return 1;
+ return CFREE_ERR;
g_dbg_ops = *ops;
g_dbg_ops_set = 1;
g_dbg_session = session;
@@ -742,11 +737,11 @@ static int dbg_signals_install(void *user, const CfreeDbgSignalOps *ops,
memset(&g_dbg_ops, 0, sizeof(g_dbg_ops));
g_dbg_ops_set = 0;
g_dbg_session = NULL;
- return 1;
+ return CFREE_ERR;
}
}
g_dbg_installed = 1;
- return 0;
+ return CFREE_OK;
}
static void dbg_signals_uninstall(void *user) {
@@ -771,16 +766,17 @@ static size_t dbg_page_ceil(size_t v, size_t pg) {
}
#endif
-static int dbg_code_write_begin(void *user, void *runtime_addr, size_t n,
- void **write_out) {
+static CfreeStatus dbg_code_write_begin(void *user, void *runtime_addr,
+ size_t n, void **write_out) {
(void)user;
if (!runtime_addr || !n || !write_out)
- return 1;
+ return CFREE_INVALID;
#if defined(__APPLE__)
/* Dual-mapped reservation (mach_vm_remap): the write alias is a
* separate VA already RW. Translate via the registry; no protect flip
* is required, so code_write_end is a no-op. */
- return exec_dual_lookup(runtime_addr, n, write_out);
+ return exec_dual_lookup(runtime_addr, n, write_out) == 0 ? CFREE_OK
+ : CFREE_ERR;
#elif defined(__linux__)
{
size_t pg = driver_host_page_size();
@@ -791,11 +787,11 @@ static int dbg_code_write_begin(void *user, void *runtime_addr, size_t n,
* distinct VAs. Prefer the alias lookup; fall back to a transient
* mprotect of the runtime alias for single-mapping reservations. */
if (exec_dual_lookup(runtime_addr, n, write_out) == 0)
- return 0;
+ return CFREE_OK;
if (mprotect((void *)base, span, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
- return 1;
+ return CFREE_ERR;
*write_out = runtime_addr;
- return 0;
+ return CFREE_OK;
}
#else
#error "cfree dbg v1 supports only macOS and Linux"
@@ -832,16 +828,17 @@ static void dbg_flush_icache(void *user, void *runtime_addr, size_t n) {
/* --- guarded copy --- */
-static int dbg_guarded_copy(void *user, void *dst, const void *src, size_t n) {
+static CfreeStatus dbg_guarded_copy(void *user, void *dst, const void *src,
+ size_t n) {
(void)user;
if (sigsetjmp(g_guard_buf, 1) != 0) {
g_guard_armed = 0;
- return 1; /* SIGSEGV/SIGBUS during the copy */
+ return CFREE_ERR; /* SIGSEGV/SIGBUS during the copy */
}
g_guard_armed = 1;
memcpy(dst, src, n);
g_guard_armed = 0;
- return 0;
+ return CFREE_OK;
}
static CfreeDbgOs g_dbg_os_posix;
@@ -972,40 +969,44 @@ typedef struct DriverFdWriter {
CfreeWriter base; /* must be first; libcfree reads via this */
CfreeHeap *heap;
int fd;
- int err;
+ CfreeStatus status;
uint64_t pos;
} DriverFdWriter;
-static void fdw_write(CfreeWriter *w, const void *data, size_t n) {
+static CfreeStatus fdw_write(CfreeWriter *w, const void *data, size_t n) {
DriverFdWriter *fw = (DriverFdWriter *)w;
const unsigned char *p = (const unsigned char *)data;
- if (fw->err)
- return;
+ if (fw->status != CFREE_OK)
+ return fw->status;
while (n > 0) {
ssize_t k = write(fw->fd, p, n);
if (k < 0) {
- fw->err = 1;
- return;
+ fw->status = CFREE_IO;
+ return CFREE_IO;
}
p += (size_t)k;
n -= (size_t)k;
fw->pos += (uint64_t)k;
}
+ return CFREE_OK;
}
-static void fdw_seek(CfreeWriter *w, uint64_t off) {
+static CfreeStatus fdw_seek(CfreeWriter *w, uint64_t off) {
DriverFdWriter *fw = (DriverFdWriter *)w;
- if (fw->err)
- return;
+ if (fw->status != CFREE_OK)
+ return fw->status;
if (lseek(fw->fd, (off_t)off, SEEK_SET) < 0) {
- fw->err = 1;
- return;
+ fw->status = CFREE_IO;
+ return CFREE_IO;
}
fw->pos = off;
+ return CFREE_OK;
}
static uint64_t fdw_tell(CfreeWriter *w) { return ((DriverFdWriter *)w)->pos; }
-static int fdw_error(CfreeWriter *w) { return ((DriverFdWriter *)w)->err; }
+static CfreeStatus fdw_status(CfreeWriter *w) {
+ return ((DriverFdWriter *)w)->status;
+}
static void fdw_close(CfreeWriter *w) {
DriverFdWriter *fw = (DriverFdWriter *)w;
@@ -1022,11 +1023,11 @@ static CfreeWriter *driver_writer_fd(CfreeHeap *h, int fd) {
fw->base.write = fdw_write;
fw->base.seek = fdw_seek;
fw->base.tell = fdw_tell;
- fw->base.error = fdw_error;
+ fw->base.status = fdw_status;
fw->base.close = fdw_close;
fw->heap = h;
fw->fd = fd;
- fw->err = 0;
+ fw->status = CFREE_OK;
fw->pos = 0;
return &fw->base;
}
@@ -1039,23 +1040,33 @@ typedef struct DriverStdioWriter {
CfreeWriter base;
CfreeHeap *heap;
FILE *fp;
+ CfreeStatus status;
} DriverStdioWriter;
-static void stdio_w_write(CfreeWriter *w, const void *data, size_t n) {
+static CfreeStatus stdio_w_write(CfreeWriter *w, const void *data, size_t n) {
DriverStdioWriter *sw = (DriverStdioWriter *)w;
- if (n)
- fwrite(data, 1, n, sw->fp);
+ if (n) {
+ size_t got = fwrite(data, 1, n, sw->fp);
+ if (got != n) {
+ sw->status = CFREE_IO;
+ return CFREE_IO;
+ }
+ }
+ return CFREE_OK;
}
-static void stdio_w_seek(CfreeWriter *w, uint64_t off) {
+static CfreeStatus stdio_w_seek(CfreeWriter *w, uint64_t off) {
DriverStdioWriter *sw = (DriverStdioWriter *)w;
- fseek(sw->fp, (long)off, SEEK_SET);
+ return fseek(sw->fp, (long)off, SEEK_SET) == 0 ? CFREE_OK : CFREE_IO;
}
static uint64_t stdio_w_tell(CfreeWriter *w) {
long t = ftell(((DriverStdioWriter *)w)->fp);
return t < 0 ? 0u : (uint64_t)t;
}
-static int stdio_w_error(CfreeWriter *w) {
- return ferror(((DriverStdioWriter *)w)->fp) ? 1 : 0;
+static CfreeStatus stdio_w_status(CfreeWriter *w) {
+ DriverStdioWriter *sw = (DriverStdioWriter *)w;
+ if (sw->status != CFREE_OK)
+ return sw->status;
+ return ferror(sw->fp) ? CFREE_IO : CFREE_OK;
}
static void stdio_w_close(CfreeWriter *w) {
DriverStdioWriter *sw = (DriverStdioWriter *)w;
@@ -1071,16 +1082,18 @@ CfreeWriter *driver_stdout_writer(DriverEnv *e) {
sw->base.write = stdio_w_write;
sw->base.seek = stdio_w_seek;
sw->base.tell = stdio_w_tell;
- sw->base.error = stdio_w_error;
+ sw->base.status = stdio_w_status;
sw->base.close = stdio_w_close;
sw->heap = e->heap;
sw->fp = stdout;
+ sw->status = CFREE_OK;
return &sw->base;
}
/* ---------------- file_io (POSIX) ---------------- */
-static int posix_read_all(void *user, const char *path, CfreeFileData *out) {
+static CfreeStatus posix_read_all(void *user, const char *path,
+ CfreeFileData *out) {
DriverEnv *env = (DriverEnv *)user;
int fd;
struct stat sb;
@@ -1090,16 +1103,16 @@ static int posix_read_all(void *user, const char *path, CfreeFileData *out) {
fd = open(path, O_RDONLY);
if (fd < 0)
- return 0;
+ return CFREE_NOT_FOUND;
if (fstat(fd, &sb) < 0) {
close(fd);
- return 0;
+ return CFREE_IO;
}
size = (size_t)sb.st_size;
buf = size ? env->heap->alloc(env->heap, size, 1) : NULL;
if (size && !buf) {
close(fd);
- return 0;
+ return CFREE_NOMEM;
}
got = 0;
@@ -1108,7 +1121,7 @@ static int posix_read_all(void *user, const char *path, CfreeFileData *out) {
if (n <= 0) {
env->heap->free(env->heap, buf, size);
close(fd);
- return 0;
+ return CFREE_IO;
}
got += (size_t)n;
}
@@ -1117,7 +1130,7 @@ static int posix_read_all(void *user, const char *path, CfreeFileData *out) {
out->data = (const uint8_t *)buf;
out->size = size;
out->token = buf;
- return 1;
+ return CFREE_OK;
}
static void posix_release(void *user, CfreeFileData *d) {
@@ -1129,12 +1142,20 @@ static void posix_release(void *user, CfreeFileData *d) {
d->token = NULL;
}
-static CfreeWriter *posix_open_writer(void *user, const char *path) {
+static CfreeStatus posix_open_writer(void *user, const char *path,
+ CfreeWriter **out) {
DriverEnv *env = (DriverEnv *)user;
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ CfreeWriter *w;
if (fd < 0)
- return NULL;
- return driver_writer_fd(env->heap, fd);
+ return CFREE_IO;
+ w = driver_writer_fd(env->heap, fd);
+ if (!w) {
+ close(fd);
+ return CFREE_NOMEM;
+ }
+ *out = w;
+ return CFREE_OK;
}
/* ---------------- env wiring ---------------- */
@@ -1201,17 +1222,27 @@ void driver_env_fini(DriverEnv *e) {
(void)e;
}
-CfreeEnv driver_env_to_cfree(const DriverEnv *e) {
- CfreeEnv ce;
- ce.heap = e->heap;
- ce.file_io = &e->file_io;
- ce.diag = e->diag;
- ce.execmem = e->execmem;
- ce.dbg_os = e->dbg_os;
- ce.jit_tls = e->jit_tls;
- ce.metrics = e->metrics;
- ce.now = e->now;
- return ce;
+CfreeContext driver_env_to_context(const DriverEnv *e) {
+ CfreeContext c;
+ c.heap = e->heap;
+ c.file_io = &e->file_io;
+ c.diag = e->diag;
+ c.metrics = e->metrics;
+ c.now = e->now;
+ return c;
+}
+
+CfreeJitHost driver_env_to_jit_host(const DriverEnv *e) {
+ CfreeJitHost h;
+ h.execmem = e->execmem;
+ h.tls = e->jit_tls;
+ return h;
+}
+
+CfreeDbgHost driver_env_to_dbg_host(const DriverEnv *e) {
+ CfreeDbgHost h;
+ h.os = e->dbg_os;
+ return h;
}
/* ---------------- host-shim helpers ---------------- */
@@ -1312,7 +1343,7 @@ uint64_t driver_now_ns(void) {
const char *driver_getenv(const char *name) { return getenv(name); }
int driver_load_bytes(const CfreeFileIO *io, const char *tool, const char *path,
- DriverLoad *out, CfreeBytesInput *in) {
+ DriverLoad *out, CfreeBytes *in) {
out->loaded = 0;
out->fd.data = NULL;
out->fd.size = 0;
@@ -1321,7 +1352,7 @@ int driver_load_bytes(const CfreeFileIO *io, const char *tool, const char *path,
driver_errf(tool, "host file I/O unavailable");
return 1;
}
- if (!io->read_all(io->user, path, &out->fd)) {
+ if (io->read_all(io->user, path, &out->fd) != CFREE_OK) {
driver_errf(tool, "failed to read: %s", path);
return 1;
}
@@ -1329,7 +1360,6 @@ int driver_load_bytes(const CfreeFileIO *io, const char *tool, const char *path,
in->name = path;
in->data = out->fd.data;
in->len = out->fd.size;
- in->lang = cfree_language_for_path(path);
return 0;
}
@@ -1532,7 +1562,7 @@ int driver_edit_temp(DriverEnv *e, const char *suffix, const uint8_t *initial,
fd_data.data = NULL;
fd_data.size = 0;
fd_data.token = NULL;
- if (!posix_read_all(e, path, &fd_data))
+ if (posix_read_all(e, path, &fd_data) != CFREE_OK)
goto out;
*out_data = (uint8_t *)fd_data.data;
*out_size = fd_data.size;
diff --git a/driver/inputs.c b/driver/inputs.c
@@ -41,7 +41,7 @@ void driver_inputs_release(DriverInputs *in) {
* 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;
+ CfreeSourceInput *slot;
if (in->stdin_buf) {
driver_errf(in->tool, "'-' (stdin) may appear at most once");
return -1;
@@ -51,9 +51,9 @@ static int inputs_record_stdin(DriverInputs *in) {
return -1;
}
slot = &in->source_memory[in->nsource_memory++];
- slot->name = "<stdin>";
- slot->data = in->stdin_buf;
- slot->len = in->stdin_size;
+ slot->bytes.name = "<stdin>";
+ slot->bytes.data = in->stdin_buf;
+ slot->bytes.len = in->stdin_size;
slot->lang = CFREE_LANG_C;
return 1;
}
@@ -86,7 +86,7 @@ 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;
+ return in->source_memory[0].bytes.name;
if (in->nobject_files)
return in->object_files[0];
if (in->narchives)
@@ -94,25 +94,29 @@ const char *driver_inputs_first_name(const DriverInputs *in) {
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) {
+/* Load every input through env.file_io, compile sources via `compiler` with
+ * `copts`, and JIT-link. The compiler must outlive the JIT — it backs
+ * jit->c, which cfree_jit_lookup dereferences. */
+int driver_inputs_compile_and_jit(DriverInputs *in, CfreeCompiler *compiler,
+ const CfreeJitHost *host,
+ const CfreeCCompileOptions *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;
+ CfreeContext ctx = driver_env_to_context(env);
+ const CfreeFileIO *io = ctx.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;
+ CfreeBytes *src_bytes = NULL; /* per-path source bytes for loads */
+ CfreeBytes *obj_in = NULL;
+ CfreeLinkArchiveInput *arch_in = NULL;
CfreeObjBuilder **objs = NULL;
- CfreeLinkOptions link_opts;
+ CfreeJitLinkOptions link_opts;
uint32_t nsrc = in->nsources + in->nsource_memory;
uint32_t i;
int rc = 1;
@@ -122,17 +126,17 @@ int driver_inputs_compile_and_jit(
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) {
+ if (in->nsources) {
+ src_bytes = driver_alloc_zeroed(env, in->nsources * sizeof(*src_bytes));
+ src_lf = driver_alloc_zeroed(env, in->nsources * sizeof(*src_lf));
+ if (!src_bytes || !src_lf) {
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) {
+ if (nsrc) {
+ objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs));
+ if (!objs) {
driver_errf(tool, "out of memory");
goto out;
}
@@ -154,13 +158,48 @@ int driver_inputs_compile_and_jit(
}
}
+ /* Load source files into CfreeBytes and compile via cfree_compile_c_obj.
+ * In-memory sources (stdin) compile through cfree_compile_source_obj. */
for (i = 0; i < in->nsources; ++i) {
- if (driver_load_bytes(io, tool, in->sources[i], &src_lf[i], &src_in[i]) !=
- 0)
+ if (driver_load_bytes(io, tool, in->sources[i], &src_lf[i],
+ &src_bytes[i]) != 0)
goto out;
}
+
+ for (i = 0; i < in->nsources; ++i) {
+ CfreeLanguage lang = cfree_language_for_path(in->sources[i]);
+ CfreeStatus st;
+ if (lang == CFREE_LANG_C) {
+ st = cfree_compile_c_obj(compiler, copts, &src_bytes[i], &objs[i]);
+ } else if (lang == CFREE_LANG_ASM) {
+ CfreeAsmCompileOptions aopts = {0};
+ aopts.code = copts->code;
+ aopts.diagnostics = copts->diagnostics;
+ st = cfree_compile_asm_obj(compiler, &aopts, &src_bytes[i], &objs[i]);
+ } else {
+ CfreeFrontendCompileOptions fopts = {0};
+ CfreeSourceInput sin;
+ fopts.code = copts->code;
+ fopts.diagnostics = copts->diagnostics;
+ fopts.language_options = copts;
+ sin.bytes = src_bytes[i];
+ sin.lang = lang;
+ st = cfree_compile_source_obj(compiler, &fopts, &sin, &objs[i]);
+ }
+ if (st != CFREE_OK) goto out;
+ }
for (i = 0; i < in->nsource_memory; ++i) {
- src_in[in->nsources + i] = in->source_memory[i];
+ /* For in-memory inputs, dispatch through the registered frontend so
+ * lang tagging is honoured. */
+ CfreeFrontendCompileOptions fopts;
+ CfreeFrontendCompileOptions z = {0};
+ fopts = z;
+ fopts.code = copts->code;
+ fopts.diagnostics = copts->diagnostics;
+ fopts.language_options = copts;
+ if (cfree_compile_source_obj(compiler, &fopts, &in->source_memory[i],
+ &objs[in->nsources + i]) != CFREE_OK)
+ goto out;
}
for (i = 0; i < in->nobject_files; ++i) {
@@ -170,20 +209,15 @@ int driver_inputs_compile_and_jit(
}
for (i = 0; i < in->narchives; ++i) {
if (driver_load_bytes(io, tool, in->archives[i], &arch_lf[i],
- &arch_in[i].input) != 0)
+ &arch_in[i].bytes) != 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};
+ CfreeJitLinkOptions z = {0};
link_opts = z;
}
link_opts.inputs.objs = objs;
@@ -193,10 +227,10 @@ int driver_inputs_compile_and_jit(
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;
+ link_opts.extern_resolver = extern_resolver;
+ link_opts.extern_resolver_user = extern_resolver_user;
- rc = cfree_pipeline_link_jit(pipe, &link_opts, out_jit);
+ rc = cfree_link_jit(compiler, &link_opts, host, out_jit) == CFREE_OK ? 0 : 1;
out:
if (arch_lf) {
@@ -221,9 +255,9 @@ out:
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 (src_bytes)
+ driver_free(env, src_bytes, in->nsources * sizeof(*src_bytes));
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
@@ -3,6 +3,9 @@
#include "driver.h"
+#include <cfree/compile.h>
+#include <cfree/link.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
@@ -18,7 +21,7 @@
* ... handle other flags / positionals ...
* }
* if (driver_inputs_count(&in) == 0) { ... no inputs ... }
- * rc = driver_inputs_compile_and_jit(&in, pipe, &copts, entry,
+ * rc = driver_inputs_compile_and_jit(&in, compiler, host, &copts, entry,
* driver_dlsym_resolver, NULL, &jit);
* driver_inputs_release(&in);
*
@@ -31,7 +34,7 @@ typedef struct DriverInputs {
const char **sources; /* .c .cc .cpp paths (borrowed) */
uint32_t nsources;
- CfreeBytesInput *source_memory; /* "-" stdin slurp; at most one entry */
+ CfreeSourceInput *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;
@@ -76,14 +79,18 @@ uint32_t driver_inputs_count(const DriverInputs *);
* 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);
+/* Load every input through env.file_io, compile sources via `compiler`
+ * with `copts`, and JIT-link the result. `host` carries execmem/tls;
+ * `extern_resolver` populates the JIT link options. 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 *, CfreeCompiler *,
+ const CfreeJitHost *,
+ const CfreeCCompileOptions *copts,
+ const char *entry,
+ void *(*extern_resolver)(void *,
+ const char *),
+ void *extern_resolver_user,
+ CfreeJit **out_jit);
#endif
diff --git a/driver/ld.c b/driver/ld.c
@@ -3,6 +3,10 @@
#include "driver.h"
#include "lib_resolve.h"
+#include <cfree/core.h>
+#include <cfree/link.h>
+#include <cfree/object.h>
+
/* `cfree ld` — link object/archive inputs into an executable or shared
* library. The driver loads each input via env.file_io, optionally parses a
* `-T` linker script into the structured form, and calls cfree_link_exe or
@@ -39,7 +43,7 @@
#define LD_TOOL "ld"
/* Per-archive metadata mirroring the relevant subset of
- * CfreeBytesInputArchive plus driver-side ownership info. */
+ * CfreeLinkArchiveInput plus driver-side ownership info. */
typedef struct LdArchive {
const char* path; /* path used for both open and CfreeBytesInput.name */
int owned; /* 1 if `path` was alloc'd by lib_resolve */
@@ -50,7 +54,7 @@ typedef struct LdArchive {
} LdArchive;
/* Per-DSO ownership info. The DSO bytes are loaded straight off disk
- * via env->file_io into the CfreeBytesInput passed to libcfree; only
+ * via env->file_io into the CfreeBytes passed to libcfree; only
* the path itself may need to be free'd if it came from -l<name>
* resolution. */
typedef struct LdDso {
@@ -637,7 +641,7 @@ static int load_file(const CfreeFileIO* io, const char* path, LoadedFile* out) {
out->data.data = NULL;
out->data.size = 0;
out->data.token = NULL;
- if (!io->read_all(io->user, path, &out->data)) return 1;
+ if (io->read_all(io->user, path, &out->data) != CFREE_OK) return 1;
out->loaded = 1;
return 0;
}
@@ -661,21 +665,21 @@ static void release_all(LoadedFile* arr, uint32_t n) {
* `o` and any allocations within; this function only owns transient
* loaded-file buffers and the writer/compiler it constructs. */
static int ld_run_link(LdOptions* o) {
- CfreeEnv cenv = driver_env_to_cfree(o->env);
- const CfreeFileIO* io = cenv.file_io;
+ CfreeContext ctx = driver_env_to_context(o->env);
+ const CfreeFileIO* io = ctx.file_io;
CfreeCompiler* compiler = NULL;
CfreeWriter* writer = NULL;
LoadedFile* obj_lf = NULL;
LoadedFile* arch_lf = NULL;
LoadedFile* dso_lf = NULL;
LoadedFile script_lf = {0};
- CfreeBytesInput* obj_in = NULL;
- CfreeBytesInputArchive* arch_in = NULL;
- CfreeBytesInput* dso_in = NULL;
- const CfreeLinkScript* script = NULL;
+ CfreeBytes* obj_in = NULL;
+ CfreeLinkArchiveInput* arch_in = NULL;
+ CfreeBytes* dso_in = NULL;
+ CfreeLinkScript* script = NULL;
CfreeLinkInputs inputs;
- CfreeLinkOptions link_opts;
- CfreeLinkSharedOptions shared_opts;
+ CfreeExeLinkOptions link_opts;
+ CfreeSharedLinkOptions shared_opts;
uint32_t i;
int rc = 1;
@@ -728,9 +732,9 @@ static int ld_run_link(LdOptions* o) {
driver_errf(LD_TOOL, "failed to read: %s", a->path);
goto out;
}
- arch_in[i].input.name = a->path;
- arch_in[i].input.data = arch_lf[i].data.data;
- arch_in[i].input.len = arch_lf[i].data.size;
+ arch_in[i].bytes.name = a->path;
+ arch_in[i].bytes.data = arch_lf[i].data.data;
+ arch_in[i].bytes.len = arch_lf[i].data.size;
arch_in[i].whole_archive = a->whole_archive;
arch_in[i].link_mode = a->link_mode;
arch_in[i].group_id = a->group_id;
@@ -762,29 +766,27 @@ static int ld_run_link(LdOptions* o) {
if (o->nobject_files > 0) {
CfreeTarget detected;
if (cfree_detect_target(obj_lf[0].data.data, obj_lf[0].data.size,
- &detected) == 0) {
+ &detected) == CFREE_OK) {
uint8_t pic = o->target.pic;
o->target = detected;
if (pic != CFREE_PIC_NONE) o->target.pic = pic;
}
}
- compiler = driver_compiler_new(o->target, &cenv);
- if (!compiler) {
+ if (driver_compiler_new(o->target, &ctx, &compiler) != CFREE_OK) {
driver_errf(LD_TOOL, "failed to initialize compiler");
goto out;
}
if (script_lf.loaded) {
- if (cfree_link_script_parse(compiler, (const char*)script_lf.data.data,
- script_lf.data.size, &script) != 0) {
+ if (cfree_link_script_parse(&ctx, (const char*)script_lf.data.data,
+ script_lf.data.size, &script) != CFREE_OK) {
/* The parser reports a diagnostic via env.diag. */
goto out;
}
}
- writer = io->open_writer(io->user, o->output_path);
- if (!writer) {
+ if (io->open_writer(io->user, o->output_path, &writer) != CFREE_OK) {
driver_errf(LD_TOOL, "failed to open output: %s", o->output_path);
goto out;
}
@@ -806,7 +808,7 @@ static int ld_run_link(LdOptions* o) {
inputs.build_id_len = o->build_id_len;
if (o->shared) {
- CfreeLinkSharedOptions zero = {0};
+ CfreeSharedLinkOptions zero = {0};
shared_opts = zero;
shared_opts.inputs = inputs;
shared_opts.soname = o->soname;
@@ -828,30 +830,20 @@ static int ld_run_link(LdOptions* o) {
* object resolves against its loader at runtime). */
shared_opts.allow_undefined = 1;
shared_opts.gc_sections = o->gc_sections;
- /* -E/--export-dynamic is exe-shaped (promote globals into the
- * dynsym of an executable). For shared output every defined global
- * is already exported, so the flag is a no-op here; we accept it
- * silently. */
(void)o->export_dynamic;
- rc = cfree_link_shared(compiler, &shared_opts, writer);
+ rc = cfree_link_shared(compiler, &shared_opts, writer) == CFREE_OK ? 0 : 1;
} else {
- CfreeLinkOptions zero = {0};
+ CfreeExeLinkOptions zero = {0};
link_opts = zero;
link_opts.inputs = inputs;
link_opts.gc_sections = o->gc_sections;
link_opts.pie = o->pie;
link_opts.interp_path = o->interp_path;
- if (o->export_dynamic) {
- /* TODO(#5/exe): once CfreeLinkOptions grows an export_dynamic
- * field (or per-symbol export list for executables), wire it
- * here. For now the flag is recorded but has no effect on the
- * exe link. */
- }
if (o->soname || o->nrpaths || o->nrpath_links) {
driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared");
goto out;
}
- rc = cfree_link_exe(compiler, &link_opts, writer);
+ rc = cfree_link_exe(compiler, &link_opts, writer) == CFREE_OK ? 0 : 1;
}
out:
@@ -866,7 +858,7 @@ out:
rc = 1;
}
}
- if (script && compiler) cfree_link_script_free(compiler, script);
+ if (script) cfree_link_script_free(&ctx, script);
if (compiler) driver_compiler_free(compiler);
release_file(&script_lf);
release_all(arch_lf, o->narchives);
diff --git a/driver/objdump.c b/driver/objdump.c
@@ -2,6 +2,11 @@
#include <stdio.h>
+#include <cfree/archive.h>
+#include <cfree/core.h>
+#include <cfree/disasm.h>
+#include <cfree/object.h>
+
/* `cfree objdump` — print section/symbol info, disassembly, hex contents,
* and relocations for object files and archives. Archives are auto-detected
* by magic; each member is dumped in turn. All display logic lives here;
@@ -206,12 +211,13 @@ static void dump_sections(CfreeObjFile* f, const ObjdumpOpts* opts) {
driver_printf("Sections:\n");
driver_printf("Idx Name Size Align Flags\n");
for (i = 0; i < nsec; ++i) {
- CfreeObjSecInfo sec = cfree_obj_section(f, i);
+ CfreeObjSecInfo sec;
+ if (cfree_obj_section(f, i, &sec) != CFREE_OK) continue;
if (!j_match(opts, sec.name)) continue;
render_sec_flags(&sec, flagbuf, sizeof(flagbuf));
driver_printf(
- "%3u %-20s %08x 2**%-2u %s\n", i, sec.name[0] ? sec.name : "(anon)",
- sec.size,
+ "%3u %-20s %08llx 2**%-2u %s\n", i, sec.name[0] ? sec.name : "(anon)",
+ (unsigned long long)sec.size,
sec.align ? (unsigned)__builtin_ctz(sec.align ? sec.align : 1) : 0,
flagbuf);
}
@@ -219,17 +225,20 @@ static void dump_sections(CfreeObjFile* f, const ObjdumpOpts* opts) {
}
static void dump_symbols(CfreeObjFile* f, const ObjdumpOpts* opts) {
- CfreeObjSymIter* it;
+ CfreeObjSymIter* it = NULL;
CfreeObjSymInfo sym;
driver_printf("SYMBOL TABLE:\n");
- it = cfree_obj_symiter_new(f);
- while (cfree_obj_symiter_next(it, &sym)) {
+ if (cfree_obj_symiter_new(f, &it) != CFREE_OK) return;
+ for (;;) {
+ CfreeIterResult r = cfree_obj_symiter_next(it, &sym);
const char* secname;
+ if (r != CFREE_ITER_ITEM) break;
if (sym.section == CFREE_SECTION_NONE) {
secname = "*UND*";
} else {
- CfreeObjSecInfo sec = cfree_obj_section(f, sym.section);
+ CfreeObjSecInfo sec;
+ if (cfree_obj_section(f, sym.section, &sec) != CFREE_OK) continue;
secname = sec.name[0] ? sec.name : "(none)";
if (opts->nj && !j_match(opts, secname)) continue;
}
@@ -247,13 +256,14 @@ static void dump_hex(CfreeObjFile* f, const ObjdumpOpts* opts) {
uint32_t i;
for (i = 0; i < nsec; ++i) {
- CfreeObjSecInfo sec = cfree_obj_section(f, i);
+ CfreeObjSecInfo sec;
size_t len = 0;
- const uint8_t* data;
+ const uint8_t* data = NULL;
size_t ofs;
+ if (cfree_obj_section(f, i, &sec) != CFREE_OK) continue;
if (!j_match(opts, sec.name)) continue;
- data = cfree_obj_section_data(f, i, &len);
+ if (cfree_obj_section_data(f, i, &data, &len) != CFREE_OK) continue;
if (!data || len == 0) continue;
driver_printf("Contents of section %s:\n",
@@ -282,14 +292,18 @@ static void dump_hex(CfreeObjFile* f, const ObjdumpOpts* opts) {
}
static void dump_relocs(CfreeObjFile* f, const ObjdumpOpts* opts) {
- CfreeObjRelocIter* it = cfree_obj_reliter_new(f);
+ CfreeObjRelocIter* it = NULL;
CfreeObjReloc r;
uint32_t cur_sec = (uint32_t)-1;
int printed_header = 0;
int emitted_any = 0;
- while (cfree_obj_reliter_next(it, &r)) {
- CfreeObjSecInfo sec = cfree_obj_section(f, r.section);
+ if (cfree_obj_reliter_new(f, &it) != CFREE_OK) return;
+ for (;;) {
+ CfreeObjSecInfo sec;
+ CfreeIterResult res = cfree_obj_reliter_next(it, &r);
+ if (res != CFREE_ITER_ITEM) break;
+ if (cfree_obj_section(f, r.section, &sec) != CFREE_OK) continue;
if (!j_match(opts, sec.name)) continue;
if (r.section != cur_sec) {
@@ -303,12 +317,14 @@ static void dump_relocs(CfreeObjFile* f, const ObjdumpOpts* opts) {
if (r.addend) {
driver_printf("%016llx %-17s %s%c0x%llx\n", (unsigned long long)r.offset,
- r.kind_name, r.sym_name[0] ? r.sym_name : "*ABS*",
+ r.kind_name ? r.kind_name : "?",
+ r.sym_name && r.sym_name[0] ? r.sym_name : "*ABS*",
r.addend < 0 ? '-' : '+',
(unsigned long long)(r.addend < 0 ? -r.addend : r.addend));
} else {
driver_printf("%016llx %-17s %s\n", (unsigned long long)r.offset,
- r.kind_name, r.sym_name[0] ? r.sym_name : "*ABS*");
+ r.kind_name ? r.kind_name : "?",
+ r.sym_name && r.sym_name[0] ? r.sym_name : "*ABS*");
}
emitted_any = 1;
}
@@ -316,34 +332,36 @@ static void dump_relocs(CfreeObjFile* f, const ObjdumpOpts* opts) {
if (emitted_any) driver_printf("\n");
}
-static void dump_disasm(CfreeCompiler* dc, CfreeObjFile* f,
+static void dump_disasm(const CfreeDisasmContext* dctx, CfreeObjFile* f,
const ObjdumpOpts* opts) {
- CfreeObjBuilder* ob = cfree_obj_builder(f);
uint32_t nsec = cfree_obj_nsections(f);
uint32_t i;
for (i = 0; i < nsec; ++i) {
- CfreeObjSecInfo sec = cfree_obj_section(f, i);
+ CfreeObjSecInfo sec;
size_t len = 0;
- const uint8_t* data;
- CfreeDisasmIter* dis;
+ const uint8_t* data = NULL;
+ CfreeDisasmIter* dis = NULL;
CfreeInsn insn;
int want;
+ if (cfree_obj_section(f, i, &sec) != CFREE_OK) continue;
want = opts->D ? 1 : ((sec.flags & CFREE_SF_EXEC) != 0);
if (!want) continue;
if (!j_match(opts, sec.name)) continue;
- data = cfree_obj_section_data(f, i, &len);
+ if (cfree_obj_section_data(f, i, &data, &len) != CFREE_OK) continue;
if (!data || len == 0) continue;
driver_printf("Disassembly of section %s:\n\n",
sec.name[0] ? sec.name : "(anon)");
- dis = cfree_disasm_iter_new(dc, data, len, 0, ob);
- if (!dis) continue;
- while (cfree_disasm_iter_next(dis, &insn)) {
+ if (cfree_disasm_iter_new(dctx, data, len, 0, f, &dis) != CFREE_OK)
+ continue;
+ for (;;) {
+ CfreeIterResult r = cfree_disasm_iter_next(dis, &insn);
uint32_t b;
+ if (r != CFREE_ITER_ITEM) break;
driver_printf("%8llx:\t", (unsigned long long)insn.vaddr);
for (b = 0; b < insn.nbytes; ++b) driver_printf("%02x ", insn.bytes[b]);
for (b = insn.nbytes; b < 8; ++b) driver_printf(" ");
@@ -361,8 +379,8 @@ static void dump_disasm(CfreeCompiler* dc, CfreeObjFile* f,
}
}
-static void dump_obj(CfreeCompiler* dc, const char* label, CfreeObjFile* f,
- const ObjdumpOpts* opts) {
+static void dump_obj(const CfreeDisasmContext* dctx, const char* label,
+ CfreeObjFile* f, const ObjdumpOpts* opts) {
CfreeTarget target = cfree_obj_target(f);
CfreeObjFmt fmt = cfree_obj_fmt(f);
@@ -372,24 +390,27 @@ static void dump_obj(CfreeCompiler* dc, const char* label, CfreeObjFile* f,
if (opts->h) dump_sections(f, opts);
if (opts->t) dump_symbols(f, opts);
if (opts->s) dump_hex(f, opts);
- if (opts->d || opts->D) dump_disasm(dc, f, opts);
+ if (opts->d || opts->D) dump_disasm(dctx, f, opts);
if (opts->r) dump_relocs(f, opts);
}
-static int dump_archive(const char* path, const CfreeBytesInput* input,
- const CfreeEnv* cenv, CfreeCompiler* dc,
+static int dump_archive(const char* path, const CfreeBytes* input,
+ const CfreeContext* ctx,
+ const CfreeDisasmContext* dctx,
const ObjdumpOpts* opts) {
- CfreeArIter it;
+ CfreeArIter* it = NULL;
CfreeArMember member;
char label[256];
int j;
driver_printf("In archive %s:\n\n", path);
- cfree_ar_iter_init(&it, input);
- while (cfree_ar_iter_next(&it, &member)) {
- CfreeBytesInput min;
- CfreeObjFile* f;
+ if (cfree_ar_iter_new(ctx, input, &it) != CFREE_OK) return 1;
+ for (;;) {
+ CfreeIterResult r = cfree_ar_iter_next(it, &member);
+ CfreeBytes min;
+ CfreeObjFile* f = NULL;
+ if (r != CFREE_ITER_ITEM) break;
/* Build "archive.a(member.o)" label. */
j = 0;
@@ -409,14 +430,14 @@ static int dump_archive(const char* path, const CfreeBytesInput* input,
min.data = member.data;
min.len = member.size;
- f = cfree_obj_open(cenv, &min);
- if (!f) {
+ if (cfree_obj_open(ctx, &min, &f) != CFREE_OK) {
driver_errf(OBJDUMP_TOOL, "failed to parse member: %s", label);
continue;
}
- dump_obj(dc, label, f, opts);
- cfree_obj_close(f);
+ dump_obj(dctx, label, f, opts);
+ cfree_obj_free(f);
}
+ cfree_ar_iter_free(it);
return 0;
}
@@ -458,8 +479,9 @@ int driver_objdump(int argc, char** argv) {
int rc = 0;
int saw_input = 0;
int saw_op = 0;
- CfreeCompiler* dc = NULL;
- CfreeEnv cenv;
+ CfreeContext ctx;
+ CfreeDisasmContext dctx;
+ CfreeDisasmContext* dctx_p = NULL;
/* For objdump, -h means "section headers" (GNU objdump convention),
* so we only honour --help / -help (and bare invocation) as help. */
@@ -469,7 +491,7 @@ int driver_objdump(int argc, char** argv) {
}
driver_env_init(&env);
- cenv = driver_env_to_cfree(&env);
+ ctx = driver_env_to_context(&env);
/* First pass: parse flags. */
for (i = 1; i < argc; ++i) {
@@ -502,23 +524,19 @@ int driver_objdump(int argc, char** argv) {
opts.t = 1;
}
- /* Compiler used by the disassembler iterator. Created once for the
- * session at the host target — disasm consults the per-file builder
- * for annotation but does not require a target match. */
+ /* Disassembler context: a target + ctx value pair. Disasm consults
+ * the per-file object reader for annotation. */
if (opts.d || opts.D) {
- dc = driver_compiler_new(driver_host_target(), &cenv);
- if (!dc) {
- driver_errf(OBJDUMP_TOOL, "%s", "failed to init disassembler");
- rc = 1;
- goto done;
- }
+ dctx.target = driver_host_target();
+ dctx.context = ctx;
+ dctx_p = &dctx;
}
/* Second pass: process inputs. */
for (i = 1; i < argc && rc == 0; ++i) {
const char* a = argv[i];
CfreeFileData fd = {0};
- CfreeBytesInput input;
+ CfreeBytes input;
CfreeBinFmt bin;
if (a[0] == '-' && a[1] != '\0') {
@@ -527,7 +545,7 @@ int driver_objdump(int argc, char** argv) {
}
saw_input = 1;
- if (!cenv.file_io->read_all(cenv.file_io->user, a, &fd)) {
+ if (ctx.file_io->read_all(ctx.file_io->user, a, &fd) != CFREE_OK) {
driver_errf(OBJDUMP_TOOL, "failed to read: %s", a);
rc = 1;
break;
@@ -540,29 +558,31 @@ int driver_objdump(int argc, char** argv) {
bin = cfree_detect_fmt(input.data, input.len);
switch (bin) {
case CFREE_BIN_AR:
- rc = dump_archive(a, &input, &cenv, dc, &opts);
+ rc = dump_archive(a, &input, &ctx, dctx_p, &opts);
break;
case CFREE_BIN_ELF:
case CFREE_BIN_COFF:
+ case CFREE_BIN_PE:
case CFREE_BIN_MACHO:
case CFREE_BIN_WASM: {
- CfreeObjFile* f = cfree_obj_open(&cenv, &input);
- if (!f) {
+ CfreeObjFile* f = NULL;
+ if (cfree_obj_open(&ctx, &input, &f) != CFREE_OK) {
driver_errf(OBJDUMP_TOOL, "failed to parse: %s", a);
rc = 1;
} else {
- dump_obj(dc, a, f, &opts);
- cfree_obj_close(f);
+ dump_obj(dctx_p, a, f, &opts);
+ cfree_obj_free(f);
}
break;
}
+ case CFREE_BIN_UNKNOWN:
default:
driver_errf(OBJDUMP_TOOL, "unsupported file format: %s", a);
rc = 1;
break;
}
- cenv.file_io->release(cenv.file_io->user, &fd);
+ ctx.file_io->release(ctx.file_io->user, &fd);
}
if (rc == 0 && !saw_input) {
@@ -571,7 +591,6 @@ int driver_objdump(int argc, char** argv) {
}
done:
- if (dc) driver_compiler_free(dc);
driver_env_fini(&env);
return rc;
}
diff --git a/driver/run.c b/driver/run.c
@@ -5,6 +5,11 @@
#include "driver.h"
#include "inputs.h"
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/link.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
* program as argv. Mirrors the cc front-end for input shape (.c / - sources,
@@ -381,27 +386,27 @@ static void run_options_release(RunOptions* o) {
}
static void run_fill_compile_opts(const RunOptions* o,
- CfreeCompileOptions* copts) {
- CfreeCompileOptions z = {0};
+ CfreeCCompileOptions* copts) {
+ CfreeCCompileOptions z = {0};
*copts = z;
- copts->opt_level = o->opt_level;
- copts->debug_info = o->debug_info;
- driver_cflags_fill_pp(&o->cf, &copts->pp);
- copts->warnings_are_errors = o->warnings_are_errors;
- copts->max_errors = o->max_errors;
+ copts->code.opt_level = o->opt_level;
+ copts->code.debug_info = o->debug_info;
+ driver_cflags_fill_pp(&o->cf, &copts->preprocess);
+ copts->diagnostics.warnings_are_errors = o->warnings_are_errors;
+ copts->diagnostics.max_errors = o->max_errors;
}
-/* Compile every C source through the caller-owned pipeline, load .o/.a
+/* Compile every C source through the caller-owned compiler, load .o/.a
* inputs, and JIT-link. On success *out_jit owns the JIT image; caller
- * 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(RunOptions* o, CfreePipeline* pipe,
- CfreeJit** out_jit) {
- CfreeCompileOptions copts;
+ * releases via cfree_jit_free. The compiler must outlive the JIT — it
+ * backs jit->c, which cfree_jit_lookup dereferences. */
+static int run_compile_and_jit(RunOptions* o, CfreeCompiler* compiler,
+ const CfreeJitHost* host, CfreeJit** out_jit) {
+ CfreeCCompileOptions copts;
run_fill_compile_opts(o, &copts);
- return driver_inputs_compile_and_jit(&o->inputs, pipe, &copts, o->entry,
- driver_dlsym_resolver, NULL, out_jit);
+ return driver_inputs_compile_and_jit(&o->inputs, compiler, host, &copts,
+ o->entry, driver_dlsym_resolver, NULL,
+ out_jit);
}
typedef int (*MainFn)(int, char**);
@@ -453,8 +458,9 @@ static int run_call_wasm_entry(RunOptions* ro, CfreeJit* jit, void* entry,
int driver_run(int argc, char** argv) {
DriverEnv env;
RunOptions ro = {0};
- CfreeEnv cenv;
- CfreePipeline* pipe = NULL;
+ CfreeContext ctx;
+ CfreeJitHost jhost;
+ CfreeCompiler* compiler = NULL;
CfreeJit* jit = NULL;
RunMetrics metrics_storage;
RunMetrics* metrics = NULL;
@@ -484,12 +490,11 @@ int driver_run(int argc, char** argv) {
run_metrics_begin(metrics, "run.total");
}
- /* Pipeline owns the Compiler that backs the JIT image — keep it alive
- * across cfree_jit_lookup and the entry call, free after cfree_jit_free.
- */
- cenv = driver_env_to_cfree(&env);
- pipe = driver_pipeline_new(ro.target, &cenv);
- if (!pipe) {
+ /* Compiler backs the JIT image — keep it alive across cfree_jit_lookup
+ * and the entry call, free after cfree_jit_free. */
+ ctx = driver_env_to_context(&env);
+ jhost = driver_env_to_jit_host(&env);
+ if (driver_compiler_new(ro.target, &ctx, &compiler) != CFREE_OK) {
driver_errf(RUN_TOOL, "failed to initialize compiler");
run_metrics_finish(metrics);
run_options_release(&ro);
@@ -498,10 +503,10 @@ int driver_run(int argc, char** argv) {
}
run_metrics_begin(metrics, "run.compile_and_jit");
- rc = run_compile_and_jit(&ro, pipe, &jit);
+ rc = run_compile_and_jit(&ro, compiler, &jhost, &jit);
run_metrics_end(metrics, "run.compile_and_jit");
if (rc != 0) {
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
run_metrics_finish(metrics);
run_options_release(&ro);
driver_env_fini(&env);
@@ -514,17 +519,13 @@ int driver_run(int argc, char** argv) {
if (!sym) {
driver_errf(RUN_TOOL, "entry symbol not found: %s", ro.entry);
cfree_jit_free(jit);
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
run_metrics_finish(metrics);
run_options_release(&ro);
driver_env_fini(&env);
return 1;
}
- /* Object-pointer to function-pointer cast is implementation-defined in
- * standard C, but every host where a JIT runs treats them as
- * interchangeable. Going through a union avoids the -Wpedantic warning
- * a direct cast triggers under -Wpedantic. */
{
union {
void* p;
@@ -540,7 +541,7 @@ int driver_run(int argc, char** argv) {
run_metrics_end(metrics, "run.entry_call");
cfree_jit_free(jit);
- driver_pipeline_free(pipe);
+ driver_compiler_free(compiler);
run_metrics_finish(metrics);
run_options_release(&ro);
driver_env_fini(&env);
diff --git a/lang/c/c.c b/lang/c/c.c
@@ -19,7 +19,7 @@ static _Noreturn void c_bad_options(Compiler* c, const char* msg) {
compiler_panic(c, c_no_loc(), "bad C frontend options: %s", msg);
}
-static void c_apply_pp_options(Pp* pp, const CfreePpOptions* opts) {
+static void c_apply_pp_options(Pp* pp, const CfreePreprocessOptions* opts) {
u32 i;
for (i = 0; i < opts->ninclude_dirs; ++i) {
@@ -38,18 +38,18 @@ static void c_apply_pp_options(Pp* pp, const CfreePpOptions* opts) {
}
typedef struct CPreprocessRun {
- const CfreePpOptions* opts;
- const CfreeBytesInput* input;
+ const CfreePreprocessOptions* opts;
+ const CfreeBytes* input;
CfreeWriter* out;
} CPreprocessRun;
-static int c_preprocess_body(CfreeCompiler* c, void* user) {
+static CfreeStatus c_preprocess_body(CfreeCompiler* c, void* user) {
CPreprocessRun* r = (CPreprocessRun*)user;
Lexer* lex;
Pp* pp;
- const CfreePpOptions* opts = r->opts;
- const CfreeBytesInput* input = r->input;
+ const CfreePreprocessOptions* opts = r->opts;
+ const CfreeBytes* input = r->input;
CfreeWriter* out = r->out;
if (!opts || !input || !out) {
@@ -68,11 +68,12 @@ static int c_preprocess_body(CfreeCompiler* c, void* user) {
pp_push_input(pp, lex);
pp_emit_text(pp, out);
pp_free(pp);
- return 0;
+ return CFREE_OK;
}
-int cfree_c_preprocess(CfreeCompiler* c, const CfreePpOptions* opts,
- const CfreeBytesInput* input, CfreeWriter* out) {
+CfreeStatus cfree_c_preprocess(CfreeCompiler* c,
+ const CfreePreprocessOptions* opts,
+ const CfreeBytes* input, CfreeWriter* out) {
CPreprocessRun r;
r.opts = opts;
r.input = input;
@@ -83,13 +84,13 @@ int cfree_c_preprocess(CfreeCompiler* c, const CfreePpOptions* opts,
static void dump_write_str(CfreeWriter* w, const char* s) {
size_t n = 0;
while (s[n]) ++n;
- w->write(w, s, n);
+ (void)cfree_writer_write(w, s, n);
}
static void dump_write_sym(CfreeWriter* w, Compiler* c, Sym sym) {
size_t len = 0;
const char* s = sym ? compiler_sym_str(c, sym, &len) : NULL;
- if (s && len) w->write(w, s, len);
+ if (s && len) (void)cfree_writer_write(w, s, len);
}
static void dump_emit(CfreeWriter* w, Compiler* c, const Tok* t) {
@@ -136,13 +137,13 @@ static void dump_emit(CfreeWriter* w, Compiler* c, const Tok* t) {
}
typedef struct CDumpTokensRun {
- const CfreeBytesInput* input;
+ const CfreeBytes* input;
CfreeWriter* out;
} CDumpTokensRun;
-static int c_dump_tokens_body(CfreeCompiler* c, void* user) {
+static CfreeStatus c_dump_tokens_body(CfreeCompiler* c, void* user) {
CDumpTokensRun* r = (CDumpTokensRun*)user;
- const CfreeBytesInput* input = r->input;
+ const CfreeBytes* input = r->input;
CfreeWriter* out = r->out;
Lexer* lex;
Tok t;
@@ -163,24 +164,36 @@ static int c_dump_tokens_body(CfreeCompiler* c, void* user) {
if (t.kind == TOK_EOF) break;
}
lex_close(lex);
- return 0;
+ return CFREE_OK;
}
-int cfree_c_dump_tokens(CfreeCompiler* c, const CfreeBytesInput* input,
- CfreeWriter* out) {
+CfreeStatus cfree_c_dump_tokens(CfreeCompiler* c, const CfreeBytes* input,
+ CfreeWriter* out) {
CDumpTokensRun r;
r.input = input;
r.out = out;
return cfree_frontend_run(c, c_dump_tokens_body, &r);
}
-int cfree_c_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, CfreeObjBuilder* out) {
+CfreeStatus cfree_c_compile(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* fe_opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out) {
+ /* The libcfree pipeline plants the original CfreeCCompileOptions* in
+ * language_options. Recover it for preprocessor configuration. */
+ const CfreeCCompileOptions* opts;
+ const CfreeBytes* bytes;
Pool* pool;
Lexer* lex;
Pp* pp;
DeclTable* decls;
CfreeCg* cg;
+ CfreeStatus cg_st;
+
+ if (!fe_opts || !input) c_bad_options(c, "compile args missing");
+ opts = (const CfreeCCompileOptions*)fe_opts->language_options;
+ if (!opts) c_bad_options(c, "C frontend missing CfreeCCompileOptions");
+ bytes = &input->bytes;
cfree_frontend_metrics_scope_begin(c, "compile.c.setup");
cfree_frontend_metrics_scope_begin(c, "compile.c.pool_new");
@@ -188,17 +201,17 @@ int cfree_c_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
cfree_frontend_metrics_scope_end(c, "compile.c.pool_new");
if (!pool) compiler_panic(c, c_no_loc(), "C compiler out of memory");
cfree_frontend_metrics_scope_begin(c, "compile.c.lex_open");
- lex = lex_open_mem(c, input->name, (const char*)input->data, input->len);
+ lex = lex_open_mem(c, bytes->name, (const char*)bytes->data, bytes->len);
cfree_frontend_metrics_scope_end(c, "compile.c.lex_open");
cfree_frontend_metrics_scope_begin(c, "compile.c.pp_new");
pp = pp_new(c);
cfree_frontend_metrics_scope_end(c, "compile.c.pp_new");
cfree_frontend_metrics_scope_begin(c, "compile.c.cg_new");
- cg = cfree_cg_new(c, out, opts);
+ cg = NULL;
+ cg_st = cfree_cg_new(c, out, &fe_opts->code, &cg);
cfree_frontend_metrics_scope_end(c, "compile.c.cg_new");
- if (!lex || !pp || !cg)
+ if (!lex || !pp || cg_st != CFREE_OK || !cg)
compiler_panic(c, c_no_loc(), "C compiler out of memory");
- (void)out;
cfree_frontend_metrics_scope_begin(c, "compile.c.decl_new");
decls = decl_new(c, cg);
cfree_frontend_metrics_scope_end(c, "compile.c.decl_new");
@@ -206,12 +219,14 @@ int cfree_c_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
cfree_frontend_metrics_scope_begin(c, "compile.c.pp_options");
cfree_frontend_metrics_count(c, "compile.c.pp_include_dirs",
- opts->pp.ninclude_dirs);
+ opts->preprocess.ninclude_dirs);
cfree_frontend_metrics_count(c, "compile.c.pp_system_include_dirs",
- opts->pp.nsystem_include_dirs);
- cfree_frontend_metrics_count(c, "compile.c.pp_defines", opts->pp.ndefines);
- cfree_frontend_metrics_count(c, "compile.c.pp_undefines", opts->pp.nundefines);
- c_apply_pp_options(pp, &opts->pp);
+ opts->preprocess.nsystem_include_dirs);
+ cfree_frontend_metrics_count(c, "compile.c.pp_defines",
+ opts->preprocess.ndefines);
+ cfree_frontend_metrics_count(c, "compile.c.pp_undefines",
+ opts->preprocess.nundefines);
+ c_apply_pp_options(pp, &opts->preprocess);
cfree_frontend_metrics_scope_end(c, "compile.c.pp_options");
cfree_frontend_metrics_scope_begin(c, "compile.c.pp_push_input");
pp_push_input(pp, lex);
@@ -227,7 +242,7 @@ int cfree_c_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
pp_free(pp);
c_pool_free(pool);
cfree_frontend_metrics_scope_end(c, "compile.c.cleanup");
- return 0;
+ return CFREE_OK;
}
void cfree_c_register(CfreeCompiler* c) {
diff --git a/lang/c/c.h b/lang/c/c.h
@@ -1,13 +1,34 @@
#ifndef CFREE_LANG_C_H
#define CFREE_LANG_C_H
-#include <cfree.h>
+/* Public surface for the cfree C frontend.
+ *
+ * The C frontend is registered with the compiler via cfree_c_register, which
+ * installs cfree_c_compile under CFREE_LANG_C. cfree_c_compile is shaped to
+ * match the public CfreeCompileFn signature so the libcfree pipeline can
+ * dispatch it through the registered-frontend table.
+ *
+ * The pipeline constructs its CfreeFrontendCompileOptions by copying
+ * CfreeCCompileOptions.code and .diagnostics, then planting the original
+ * CfreeCCompileOptions* in CfreeFrontendCompileOptions.language_options. The
+ * frontend casts language_options back to CfreeCCompileOptions* to recover
+ * the preprocessor and diagnostic configuration.
+ *
+ * cfree_c_preprocess and cfree_c_dump_tokens are standalone helpers driven
+ * by the driver's preprocess-only and -E modes; they run under the same
+ * frontend panic boundary as cfree_c_compile via cfree_frontend_run. */
-int cfree_c_compile(CfreeCompiler*, const CfreeCompileOptions*,
- const CfreeBytesInput* input, CfreeObjBuilder* out);
-int cfree_c_preprocess(CfreeCompiler*, const CfreePpOptions*,
- const CfreeBytesInput*, CfreeWriter*);
-int cfree_c_dump_tokens(CfreeCompiler*, const CfreeBytesInput*, CfreeWriter*);
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/frontend.h>
+#include <cfree/objbuild.h>
+
+CfreeStatus cfree_c_compile(CfreeCompiler*, const CfreeFrontendCompileOptions*,
+ const CfreeSourceInput*, CfreeObjBuilder*);
+CfreeStatus cfree_c_preprocess(CfreeCompiler*, const CfreePreprocessOptions*,
+ const CfreeBytes*, CfreeWriter*);
+CfreeStatus cfree_c_dump_tokens(CfreeCompiler*, const CfreeBytes*,
+ CfreeWriter*);
void cfree_c_register(CfreeCompiler*);
#endif
diff --git a/lang/c/c_support.h b/lang/c/c_support.h
@@ -2,7 +2,7 @@
#define CFREE_LANG_C_SUPPORT_H
#include <cfree/frontend.h>
-#include <cfree/hashmap.h>
+#include <cfree/support/hashmap.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
@@ -40,9 +40,9 @@ static inline Pool* c_pool_new(Compiler* c) {
Pool* p = h ? (Pool*)h->alloc(h, sizeof(*p), _Alignof(Pool)) : NULL;
if (!p) return NULL;
p->c = c;
- p->arena = cfree_arena_new(h, 0);
+ p->arena = NULL;
p->type_cache = NULL;
- if (!p->arena) {
+ if (cfree_arena_new(h, 0, &p->arena) != CFREE_OK || !p->arena) {
h->free(h, p, sizeof(*p));
return NULL;
}
diff --git a/lang/c/lex/lex.c b/lang/c/lex/lex.c
@@ -137,7 +137,8 @@ Lexer* lex_open_mem(Compiler* c, const char* name, const char* src,
l->src = src ? src : "";
l->len = src ? len : 0;
l->pos = 0;
- l->file_id = cfree_source_add_memory(c, name);
+ l->file_id = 0;
+ (void)cfree_source_add_memory(c, name, &l->file_id);
l->line = 1;
l->col = 1;
l->at_bol = 1;
diff --git a/lang/c/pp/pp.c b/lang/c/pp/pp.c
@@ -158,7 +158,7 @@ Tok pp_next(Pp* pp) {
* ============================================================ */
static void w_str(Writer* w, const char* s, size_t n) {
- if (n) w->write(w, s, n);
+ if (n) (void)cfree_writer_write(w, s, n);
}
void pp_emit_text(Pp* pp, Writer* out) {
@@ -505,7 +505,8 @@ Pp* pp_new(Compiler* c) {
memset(pp, 0, sizeof(*pp));
pp->c = c;
pp->pool = c_pool_new(c);
- pp->arena = cfree_arena_new(h, 64 * 1024);
+ pp->arena = NULL;
+ (void)cfree_arena_new(h, 64 * 1024, &pp->arena);
if (!pp->pool || !pp->arena) {
c_pool_free(pp->pool);
cfree_arena_free(pp->arena);
diff --git a/lang/c/pp/pp_directive.c b/lang/c/pp/pp_directive.c
@@ -589,7 +589,7 @@ static int try_open_include(Pp* pp, const char* path, const u8** data_out,
compiler_panic(pp->c, (SrcLoc){0, 0, 0},
"#include: env.file_io is not configured");
}
- if (!io->read_all(io->user, path, &fd)) return 0;
+ if (io->read_all(io->user, path, &fd) != CFREE_OK) return 0;
{
size_t sz = fd.size;
buf = (u8*)arena_alloc(pp->arena, sz ? sz : 1, 1);
diff --git a/lang/c/pp/pp_priv.h b/lang/c/pp/pp_priv.h
@@ -83,7 +83,7 @@ typedef struct IfFrame {
/* MacroMap = Sym -> Macro*. Generated open-addressed hashmap with
* deletion (#undef). See core/hashmap.h. */
-#include <cfree/hashmap.h>
+#include <cfree/support/hashmap.h>
static inline u32 macro_hash_(Sym s) { return cfree_hash_u32((u32)s); }
CFREE_HASHMAP_DEFINE(MacroMap, Sym, Macro*, macro_hash_);
diff --git a/lang/toy/compile.c b/lang/toy/compile.c
@@ -2,32 +2,39 @@
#include "internal.h"
-int cfree_toy_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, CfreeObjBuilder* out) {
+CfreeStatus cfree_toy_compile(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out) {
ToyParser p;
CfreeCg* cg;
const uint8_t* source;
+ size_t source_len;
+ CfreeStatus st;
- if (!c || !input || !out) return 1;
+ if (!c || !opts || !input || !out) return CFREE_INVALID;
+ (void)opts->language_options; /* toy frontend has no per-language options */
- source = input->data ? input->data : (const uint8_t*)"";
- cg = cfree_cg_new(c, out, opts);
- if (!cg) return 1;
+ source = input->bytes.data ? input->bytes.data : (const uint8_t*)"";
+ source_len = input->bytes.data ? input->bytes.len : 0u;
- toy_parser_init(&p, c, cg, source, input->len);
+ st = cfree_cg_new(c, out, &opts->code, &cg);
+ if (st != CFREE_OK) return st;
+
+ toy_parser_init(&p, c, cg, source, source_len);
if (!toy_parse_program(&p) || p.has_error) {
toy_parser_dispose(&p);
cfree_cg_free(cg);
- return 1;
+ return CFREE_ERR;
}
if (p.cur.kind != TOK_EOF) {
toy_error(&p, p.cur.loc, "unexpected token after program end");
toy_parser_dispose(&p);
cfree_cg_free(cg);
- return 1;
+ return CFREE_ERR;
}
toy_parser_dispose(&p);
cfree_cg_free(cg);
- return 0;
+ return CFREE_OK;
}
diff --git a/lang/toy/lexer.h b/lang/toy/lexer.h
@@ -1,7 +1,7 @@
#ifndef CFREE_TOY_LEXER_H
#define CFREE_TOY_LEXER_H
-#include <cfree.h>
+#include <cfree/core.h>
#include <stddef.h>
#include <stdint.h>
diff --git a/lang/toy/toy.h b/lang/toy/toy.h
@@ -1,9 +1,12 @@
#ifndef CFREE_TOY_H
#define CFREE_TOY_H
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/frontend.h>
-int cfree_toy_compile(CfreeCompiler*, const CfreeCompileOptions*,
- const CfreeBytesInput* input, CfreeObjBuilder* out);
+CfreeStatus cfree_toy_compile(CfreeCompiler*,
+ const CfreeFrontendCompileOptions*,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out);
#endif
diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c
@@ -3254,7 +3254,7 @@ static void wat_parse_data_field(WatParser* p) {
wat_expect(p, WT_RPAREN, "')'");
}
-static void wat_parse_module(CfreeCompiler* c, const CfreeBytesInput* input,
+static void wat_parse_module(CfreeCompiler* c, const CfreeBytes* input,
WasmModule* out) {
WatParser p;
memset(&p, 0, sizeof p);
@@ -3441,7 +3441,7 @@ static WasmValType bin_val_type(BinReader* r, int refs_ok) {
return WASM_VAL_I32;
}
-static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input,
+static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytes* input,
WasmModule* out) {
BinReader r;
uint32_t nfunc_types = 0;
@@ -4506,7 +4506,7 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input,
}
}
-static int wasm_is_binary(const CfreeBytesInput* input) {
+static int wasm_is_binary(const CfreeBytes* input) {
return input->len >= 4u && input->data[0] == 0x00 && input->data[1] == 0x61 &&
input->data[2] == 0x73 && input->data[3] == 0x6d;
}
@@ -6293,9 +6293,12 @@ static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg,
}
}
-static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts,
+static void wasm_emit_cg(CfreeCompiler* c, const CfreeCodeOptions* code_opts,
CfreeObjBuilder* out, const WasmModule* m) {
- CfreeCg* cg = cfree_cg_new(c, out, opts);
+ CfreeCg* cg = NULL;
+ CfreeStatus cg_st = cfree_cg_new(c, out, code_opts, &cg);
+ if (cg_st != CFREE_OK)
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
CfreeCgBuiltinTypes b = cfree_cg_builtin_types(c);
WasmCgRuntime rt;
CfreeCgSym syms[64];
@@ -7568,10 +7571,10 @@ static void write_name(CfreeWriter* w, const char* s) {
static void encode_section(CfreeHeap* h, CfreeWriter* out, uint8_t id,
void (*fn)(CfreeWriter*, const WasmModule*),
const WasmModule* m) {
- CfreeWriter* tmp = cfree_writer_mem(h);
+ CfreeWriter* tmp = NULL;
size_t len;
const uint8_t* bytes;
- if (!tmp) return;
+ if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
fn(tmp, m);
bytes = cfree_writer_mem_bytes(tmp, &len);
write_byte(out, id);
@@ -8128,9 +8131,10 @@ static void enc_code(CfreeWriter* w, const WasmModule* m) {
if (!m->funcs[i].is_import) n++;
write_uleb(w, n);
for (i = 0; i < m->nfuncs; ++i) {
- CfreeWriter* body = cfree_writer_mem(m->heap);
+ CfreeWriter* body = NULL;
size_t len;
const uint8_t* bytes;
+ if (cfree_writer_mem(m->heap, &body) != CFREE_OK) continue;
if (m->funcs[i].is_import) {
cfree_writer_close(body);
continue;
@@ -8253,10 +8257,10 @@ static void enc_start(CfreeWriter* w, const WasmModule* m) {
static void encode_custom(CfreeHeap* h, CfreeWriter* out,
const WasmCustom* cs) {
- CfreeWriter* tmp = cfree_writer_mem(h);
+ CfreeWriter* tmp = NULL;
size_t len;
const uint8_t* bytes;
- if (!tmp) return;
+ if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
write_name(tmp, cs->name ? cs->name : "");
if (cs->len) tmp->write(tmp, cs->data, cs->len);
bytes = cfree_writer_mem_bytes(tmp, &len);
@@ -8297,7 +8301,7 @@ static void wasm_encode(CfreeCompiler* c, const WasmModule* m,
}
}
-static void wasm_parse_any(CfreeCompiler* c, const CfreeBytesInput* input,
+static void wasm_parse_any(CfreeCompiler* c, const CfreeBytes* input,
WasmModule* m) {
if (wasm_is_binary(input))
wasm_decode_binary(c, input, m);
@@ -8306,21 +8310,25 @@ static void wasm_parse_any(CfreeCompiler* c, const CfreeBytesInput* input,
wasm_validate(m, c);
}
-int cfree_wasm_compile(CfreeCompiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, CfreeObjBuilder* out) {
+CfreeStatus cfree_wasm_compile(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out) {
WasmModule m;
+ if (!c || !opts || !input || !out) return CFREE_INVALID;
+ (void)opts->language_options; /* wasm frontend has no per-language options */
wasm_module_init(&m, cfree_compiler_heap(c));
- wasm_parse_any(c, input, &m);
- wasm_emit_cg(c, opts, out, &m);
+ wasm_parse_any(c, &input->bytes, &m);
+ wasm_emit_cg(c, &opts->code, out, &m);
wasm_module_free(&m);
- return 0;
+ return CFREE_OK;
}
void cfree_wasm_register(CfreeCompiler* c) {
(void)cfree_register_frontend(c, CFREE_LANG_WASM, cfree_wasm_compile);
}
-int cfree_wasm_wat_to_wasm(CfreeCompiler* c, const CfreeBytesInput* input,
+int cfree_wasm_wat_to_wasm(CfreeCompiler* c, const CfreeBytes* input,
CfreeWriter* out) {
WasmModule m;
wasm_module_init(&m, cfree_compiler_heap(c));
diff --git a/lang/wasm/wasm.h b/lang/wasm/wasm.h
@@ -1,17 +1,21 @@
#ifndef CFREE_LANG_WASM_H
#define CFREE_LANG_WASM_H
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/frontend.h>
#include "runtime_abi.h"
-int cfree_wasm_compile(CfreeCompiler*, const CfreeCompileOptions*,
- const CfreeBytesInput* input, CfreeObjBuilder* out);
+CfreeStatus cfree_wasm_compile(CfreeCompiler*,
+ const CfreeFrontendCompileOptions*,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out);
void cfree_wasm_register(CfreeCompiler*);
/* Internal test/developer helper: parse accepted WAT and write equivalent
* binary Wasm. This is intentionally not part of the installed public API. */
-int cfree_wasm_wat_to_wasm(CfreeCompiler*, const CfreeBytesInput* input,
+int cfree_wasm_wat_to_wasm(CfreeCompiler*, const CfreeBytes* input,
CfreeWriter* out);
#endif
diff --git a/src/abi/abi.c b/src/abi/abi.c
@@ -11,7 +11,6 @@
#include "abi/abi.h"
-#include <cfree.h>
#include <string.h>
#include "abi/abi_internal.h"
@@ -202,7 +201,7 @@ void abi_fini(TargetABI* a) {
}
TargetABI* abi_new(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
TargetABI* a =
(TargetABI*)h->alloc(h, sizeof(TargetABI), _Alignof(TargetABI));
if (!a) return NULL;
@@ -212,7 +211,7 @@ TargetABI* abi_new(Compiler* c) {
void abi_free(TargetABI* a) {
if (!a) return;
- Heap* h = (Heap*)a->c->env->heap;
+ Heap* h = (Heap*)a->c->ctx->heap;
abi_fini(a);
h->free(h, a, sizeof(TargetABI));
}
diff --git a/src/api/ar.c b/src/api/ar.c
@@ -1,28 +1,24 @@
-/* POSIX ar archive reader/writer (cfree_ar_write / cfree_ar_iter /
- * cfree_ar_list). Pure format I/O over CfreeWriter and a const byte
- * range — no C frontend/cg/obj dependencies. Kept in its own TU so
- * consumers that only need the ar surface (e.g. test/ar_test, the
- * driver's `ar` and `ld` paths once split out) don't drag in the full
- * compile/link pipeline through the linker.
- *
- * Archive format: 8-byte magic "!<arch>\n", then zero or more members.
- * Each member has a 60-byte fixed-width ASCII header followed by data
- * bytes (plus one '\n' pad byte when data length is odd). */
-
-#include <cfree.h>
+/* POSIX ar archive reader/writer. */
+
+#include <cfree/archive.h>
+#include <cfree/object.h>
#include "core/bytes.h"
#include "core/core.h"
+#include "core/heap.h"
/* ============================================================
- * Write helpers (file-static; libc-free).
+ * Write helpers
* ============================================================ */
-static void wh_bytes(Writer* w, const void* p, size_t n) { w->write(w, p, n); }
-static void wh_char(Writer* w, char c) { w->write(w, &c, 1); }
-static void wh_nl(Writer* w) { wh_char(w, '\n'); }
+static CfreeStatus wh_bytes(Writer* w, const void* p, size_t n) {
+ return cfree_writer_write(w, p, n);
+}
+static CfreeStatus wh_char(Writer* w, char c) {
+ return cfree_writer_write(w, &c, 1);
+}
+static CfreeStatus wh_nl(Writer* w) { return wh_char(w, '\n'); }
-/* Format v as decimal into dst[width], left-justified, space-padded right. */
static void wh_ar_num(char* dst, int width, u64 v) {
char tmp[20];
int len = 0, i;
@@ -44,18 +40,12 @@ static void wh_ar_num(char* dst, int width, u64 v) {
for (; i < width; ++i) dst[i] = ' ';
}
-/* Emit a 32-bit unsigned integer in big-endian byte order. */
-static void wh_be32(Writer* w, u32 v) {
+static CfreeStatus wh_be32(Writer* w, u32 v) {
u8 b[4];
wr_u32_be(b, v);
- wh_bytes(w, b, 4);
+ return wh_bytes(w, b, 4);
}
-/* ============================================================
- * Member-name helpers.
- * ============================================================ */
-
-/* Compute the basename and length of a member path for ar_name encoding. */
static void ar_name_basename(const char* in, const char** name_out,
size_t* len_out) {
const char* name = in;
@@ -69,9 +59,6 @@ static void ar_name_basename(const char* in, const char** name_out,
*len_out = namelen;
}
-/* Determine whether a member name needs the '//' long-name table.
- * GNU ar uses the table when the basename exceeds 15 chars or contains
- * '/' (since '/' is the in-header terminator). */
static int ar_name_needs_longtable(const char* name, size_t len) {
size_t i;
if (len > 15) return 1;
@@ -80,52 +67,38 @@ static int ar_name_needs_longtable(const char* name, size_t len) {
return 0;
}
-/* libc-free strlen for caller-provided NUL-terminated strings. */
static size_t ar_strlen(const char* s) {
size_t n = 0;
while (s[n]) ++n;
return n;
}
-/* Padded on-archive size of a member: 60-byte header + payload + parity pad. */
static size_t ar_member_padded_size(size_t len) { return 60 + len + (len & 1); }
-/* Fill a 60-byte member header. `name_field` is the 16-byte ar_name encoding
- * to write (already terminated with '/' and space-padded). */
static void ar_fill_header(char hdr[60], const char name_field[16],
uint64_t epoch, uint64_t size) {
size_t j;
for (j = 0; j < 16; ++j) hdr[j] = name_field[j];
- /* ar_date[12] */
for (j = 16; j < 28; ++j) hdr[j] = ' ';
if (epoch)
wh_ar_num(hdr + 16, 12, epoch);
else
hdr[16] = '0';
- /* ar_uid[6]: 0 */
for (j = 28; j < 34; ++j) hdr[j] = ' ';
hdr[28] = '0';
- /* ar_gid[6]: 0 */
for (j = 34; j < 40; ++j) hdr[j] = ' ';
hdr[34] = '0';
- /* ar_mode[8]: 644 */
for (j = 40; j < 48; ++j) hdr[j] = ' ';
hdr[40] = '6';
hdr[41] = '4';
hdr[42] = '4';
- /* ar_size[10] */
wh_ar_num(hdr + 48, 10, size);
- /* ar_fmag[2] */
hdr[58] = '`';
hdr[59] = '\n';
}
-/* ============================================================
- * Public API: write
- * ============================================================ */
-
-int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
- uint32_t nmembers, const CfreeArWriteOptions* opts) {
+CfreeStatus cfree_ar_write(CfreeWriter* out, const CfreeBytes* members,
+ uint32_t nmembers, const CfreeArWriteOptions* opts) {
static const char magic[] = "!<arch>\n";
static const CfreeArWriteOptions default_opts = {0, 0, 0, NULL};
uint32_t i;
@@ -134,15 +107,15 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
int symbol_index;
const CfreeArMemberSymbols* msyms;
uint64_t longtab_size = 0;
- uint32_t nsyms = 0; /* total symbols across all members */
- uint64_t names_size = 0; /* sum of strlen+1 for each symbol */
- uint64_t index_payload = 0; /* bytes of the / member's data */
- uint64_t index_total = 0; /* 60 + index_payload + parity pad */
- uint64_t longtab_total = 0; /* 60 + longtab_size + parity pad */
+ uint32_t nsyms = 0;
+ uint64_t names_size = 0;
+ uint64_t index_payload = 0;
+ uint64_t index_total = 0;
+ uint64_t longtab_total = 0;
char pad = '\n';
- if (!out) return 1;
- if (!members && nmembers) return 1;
+ if (!out) return CFREE_INVALID;
+ if (!members && nmembers) return CFREE_INVALID;
if (!opts) opts = &default_opts;
epoch = opts->epoch;
@@ -150,24 +123,19 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
symbol_index = opts->symbol_index;
msyms = opts->member_symbols;
- /* Sizing pass: '//' long-name table size and '/' symbol-index payload
- * size. Both are needed before emitting magic so the index can encode
- * absolute member-header offsets. The emit phase walks members again
- * and recomputes per-member layout — heap-free by design. */
if (long_names) {
for (i = 0; i < nmembers; ++i) {
const char* name;
size_t namelen;
- if (!members[i].name) return 1;
+ if (!members[i].name) return CFREE_INVALID;
ar_name_basename(members[i].name, &name, &namelen);
if (ar_name_needs_longtable(name, namelen)) {
- longtab_size += (uint64_t)namelen + 2; /* name + "/\n" */
+ longtab_size += (uint64_t)namelen + 2;
}
}
} else {
- /* Validate names. */
for (i = 0; i < nmembers; ++i) {
- if (!members[i].name) return 1;
+ if (!members[i].name) return CFREE_INVALID;
}
}
@@ -175,10 +143,10 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
if (msyms) {
for (i = 0; i < nmembers; ++i) {
u32 k;
- if (msyms[i].count && !msyms[i].names) return 1;
+ if (msyms[i].count && !msyms[i].names) return CFREE_INVALID;
for (k = 0; k < msyms[i].count; ++k) {
const char* nm = msyms[i].names[k];
- if (!nm) return 1;
+ if (!nm) return CFREE_INVALID;
nsyms += 1;
names_size += (uint64_t)ar_strlen(nm) + 1;
}
@@ -193,9 +161,6 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
wh_bytes(out, magic, 8);
- /* Emit '/' symbol-index member, if requested. The 16-byte ar_name field
- * is `/` followed by 15 spaces (no terminating `/`); cfree_ar_iter_next
- * skips members matching that exact prefix. */
if (symbol_index) {
char hdr[60];
char name_field[16];
@@ -209,8 +174,6 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
wh_be32(out, nsyms);
- /* Offsets: each of member i's symbols emits the same offset, equal
- * to the start of member i's 60-byte header. */
cur_offset = (uint64_t)8 + index_total + longtab_total;
if (msyms) {
for (i = 0; i < nmembers; ++i) {
@@ -222,7 +185,6 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
}
}
- /* Names: same walk, NUL-terminated, in member then declaration order. */
if (msyms) {
for (i = 0; i < nmembers; ++i) {
u32 k;
@@ -237,7 +199,6 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
if (index_payload & 1) wh_bytes(out, &pad, 1);
}
- /* Emit '//' long-name table member, if any. */
if (longtab_size) {
char hdr[60];
char name_field[16];
@@ -259,13 +220,10 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
if (longtab_size & 1) wh_bytes(out, &pad, 1);
}
- /* Re-walk members; emit headers + payloads. Track the running offset
- * within the // table so each long-name member's name field encodes
- * `/<offset>`. */
{
uint64_t longtab_off = 0;
for (i = 0; i < nmembers; ++i) {
- const CfreeBytesInput* m = &members[i];
+ const CfreeBytes* m = &members[i];
const char* name;
size_t namelen;
char hdr[60];
@@ -276,7 +234,6 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
for (j = 0; j < 16; ++j) name_field[j] = ' ';
if (long_names && ar_name_needs_longtable(name, namelen)) {
- /* Encode `/<decimal-offset>` in the 16-byte name field. */
name_field[0] = '/';
wh_ar_num(name_field + 1, 15, longtab_off);
longtab_off += (uint64_t)namelen + 2;
@@ -293,45 +250,64 @@ int cfree_ar_write(CfreeWriter* out, const CfreeBytesInput* members,
}
}
- return 0;
+ return cfree_writer_status(out);
}
/* ============================================================
- * Public API: read (iterator) and list
+ * Read (iterator) and list
* ============================================================ */
-int cfree_ar_iter_init(CfreeArIter* it, const CfreeBytesInput* archive) {
- if (!it || !archive) return 0;
- if (!archive->data && archive->len) return 0;
- if (cfree_detect_fmt(archive->data, archive->len) != CFREE_BIN_AR) return 0;
- it->_p = archive->data + 8;
- it->_end = archive->data + archive->len;
- it->_longnames = NULL;
- it->_longnames_len = 0;
- it->_namebuf[0] = '\0';
- return 1;
-}
+#define AR_NAMEBUF 256
+
+struct CfreeArIter {
+ const CfreeContext* ctx;
+ const u8* p;
+ const u8* end;
+ const u8* longnames;
+ size_t longnames_len;
+ char namebuf[AR_NAMEBUF];
+};
-/* Resolve a `/<decimal-offset>` reference into the iterator's `//` table.
- * Names in the table are terminated by '/' or '\n'. Returns the resolved
- * name length, or 0 on failure. Writes the name into it->_namebuf. */
static size_t ar_resolve_longname(CfreeArIter* it, uint64_t off) {
size_t i;
- if (!it->_longnames) return 0;
- if (off >= it->_longnames_len) return 0;
- for (i = 0; i + 1 < sizeof(it->_namebuf); ++i) {
+ if (!it->longnames) return 0;
+ if (off >= it->longnames_len) return 0;
+ for (i = 0; i + 1 < sizeof(it->namebuf); ++i) {
size_t k = (size_t)off + i;
char ch;
- if (k >= it->_longnames_len) break;
- ch = (char)it->_longnames[k];
+ if (k >= it->longnames_len) break;
+ ch = (char)it->longnames[k];
if (ch == '/' || ch == '\n') break;
- it->_namebuf[i] = ch;
+ it->namebuf[i] = ch;
}
- it->_namebuf[i] = '\0';
+ it->namebuf[i] = '\0';
return i;
}
-int cfree_ar_iter_next(CfreeArIter* it, CfreeArMember* out) {
+CfreeStatus cfree_ar_iter_new(const CfreeContext* ctx, const CfreeBytes* archive,
+ CfreeArIter** out) {
+ Heap* h;
+ CfreeArIter* it;
+ if (!out) return CFREE_INVALID;
+ if (!ctx || !ctx->heap || !archive) return CFREE_INVALID;
+ if (!archive->data && archive->len) return CFREE_INVALID;
+ if (cfree_detect_fmt(archive->data, archive->len) != CFREE_BIN_AR)
+ return CFREE_MALFORMED;
+ h = ctx->heap;
+ it = (CfreeArIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeArIter));
+ if (!it) return CFREE_NOMEM;
+ it->ctx = ctx;
+ it->p = archive->data + 8;
+ it->end = archive->data + archive->len;
+ it->longnames = NULL;
+ it->longnames_len = 0;
+ it->namebuf[0] = '\0';
+ *out = it;
+ return CFREE_OK;
+}
+
+CfreeIterResult cfree_ar_iter_next(CfreeArIter* it, CfreeArMember* out) {
+ if (!it || !out) return CFREE_ITER_ERROR;
for (;;) {
uint64_t size;
size_t avail;
@@ -339,38 +315,31 @@ int cfree_ar_iter_next(CfreeArIter* it, CfreeArMember* out) {
int namelen;
char name_field[16];
- if (it->_p + 60 > it->_end) return 0;
+ if (it->p + 60 > it->end) return CFREE_ITER_END;
- for (j = 0; j < 16; ++j) name_field[j] = (char)it->_p[j];
+ for (j = 0; j < 16; ++j) name_field[j] = (char)it->p[j];
size = 0;
for (j = 48; j < 58; ++j) {
- char ch = (char)it->_p[j];
+ char ch = (char)it->p[j];
if (ch < '0' || ch > '9') break;
size = size * 10 + (uint64_t)(unsigned char)(ch - '0');
}
- it->_p += 60;
- avail = (size_t)(it->_end - it->_p);
- if ((uint64_t)avail < size) return 0; /* truncated */
+ it->p += 60;
+ avail = (size_t)(it->end - it->p);
+ if ((uint64_t)avail < size) return CFREE_ITER_END;
- /* Special members (handled before user-visible naming):
- * "//" extended-name (long-name) table
- * "/" alone System V symbol index
- * "__.SYMDEF" BSD symbol index */
if (name_field[0] == '/' && name_field[1] == '/') {
- it->_longnames = it->_p;
- it->_longnames_len = (size_t)size;
+ it->longnames = it->p;
+ it->longnames_len = (size_t)size;
goto advance;
}
if (name_field[0] == '/' && name_field[1] == ' ') {
- /* System V symbol index "/ ". */
goto advance;
}
- /* Decode name. */
if (name_field[0] == '/' && name_field[1] >= '0' && name_field[1] <= '9') {
- /* `/<offset>` long-name reference (System V). */
uint64_t off = 0;
for (j = 1; j < 16; ++j) {
char ch = name_field[j];
@@ -380,9 +349,6 @@ int cfree_ar_iter_next(CfreeArIter* it, CfreeArMember* out) {
namelen = (int)ar_resolve_longname(it, off);
} else if (name_field[0] == '#' && name_field[1] == '1' &&
name_field[2] == '/') {
- /* BSD `#1/<decimal-length>`: the next <length> bytes of the
- * member's data are the real filename; the remainder is the
- * file content. macOS /usr/bin/ar produces this layout. */
uint64_t nlen = 0;
size_t k;
for (j = 3; j < 16; ++j) {
@@ -390,64 +356,80 @@ int cfree_ar_iter_next(CfreeArIter* it, CfreeArMember* out) {
if (ch < '0' || ch > '9') break;
nlen = nlen * 10 + (uint64_t)(unsigned char)(ch - '0');
}
- if (nlen > size || nlen + 1 > sizeof(it->_namebuf)) return 0;
+ if (nlen > size || nlen + 1 > sizeof(it->namebuf))
+ return CFREE_ITER_ERROR;
namelen = 0;
for (k = 0; k < (size_t)nlen; ++k) {
- char ch = (char)it->_p[k];
+ char ch = (char)it->p[k];
if (ch == '\0') break;
- it->_namebuf[namelen++] = ch;
+ it->namebuf[namelen++] = ch;
}
- it->_namebuf[namelen] = '\0';
- /* Trim the name bytes off the front of the payload. */
- it->_p += (size_t)nlen;
+ it->namebuf[namelen] = '\0';
+ it->p += (size_t)nlen;
size -= nlen;
} else {
namelen = 0;
for (j = 0; j < 16; ++j) {
char ch = name_field[j];
if (ch == '/' || ch == ' ' || ch == '\0') break;
- it->_namebuf[namelen++] = ch;
+ it->namebuf[namelen++] = ch;
}
- it->_namebuf[namelen] = '\0';
+ it->namebuf[namelen] = '\0';
}
- out->name = it->_namebuf;
- out->data = it->_p;
+ out->name = it->namebuf;
+ out->data = it->p;
out->size = (size_t)size;
- it->_p += (size_t)size;
- if ((size & 1) && it->_p < it->_end) it->_p++;
+ it->p += (size_t)size;
+ if ((size & 1) && it->p < it->end) it->p++;
- /* Skip special-but-named members (BSD symbol index, e.g.
- * "__.SYMDEF" or "__.SYMDEF SORTED"). */
- if (it->_namebuf[0] == '_' && it->_namebuf[1] == '_' &&
- it->_namebuf[2] == '.') {
+ if (it->namebuf[0] == '_' && it->namebuf[1] == '_' &&
+ it->namebuf[2] == '.') {
continue;
}
- if (namelen > 0) return 1;
+ if (namelen > 0) return CFREE_ITER_ITEM;
continue;
advance:
- it->_p += (size_t)size;
- if ((size & 1) && it->_p < it->_end) it->_p++;
+ it->p += (size_t)size;
+ if ((size & 1) && it->p < it->end) it->p++;
}
}
-int cfree_ar_list(const CfreeBytesInput* archive, CfreeWriter* out) {
- CfreeArIter it;
+void cfree_ar_iter_free(CfreeArIter* it) {
+ Heap* h;
+ if (!it) return;
+ h = it->ctx->heap;
+ h->free(h, it, sizeof(*it));
+}
+
+CfreeStatus cfree_ar_list(const CfreeBytes* archive, CfreeWriter* out) {
+ /* Iter API requires a context; emulate locally without heap allocation. */
+ struct CfreeArIter local;
CfreeArMember m;
size_t namelen;
const char* p;
- if (!out) return 1;
- if (!cfree_ar_iter_init(&it, archive)) return 1;
+ if (!out) return CFREE_INVALID;
+ if (!archive) return CFREE_INVALID;
+ if (cfree_detect_fmt(archive->data, archive->len) != CFREE_BIN_AR)
+ return CFREE_MALFORMED;
+ local.ctx = NULL;
+ local.p = archive->data + 8;
+ local.end = archive->data + archive->len;
+ local.longnames = NULL;
+ local.longnames_len = 0;
+ local.namebuf[0] = '\0';
- while (cfree_ar_iter_next(&it, &m)) {
+ for (;;) {
+ CfreeIterResult r = cfree_ar_iter_next(&local, &m);
+ if (r != CFREE_ITER_ITEM) break;
namelen = 0;
for (p = m.name; *p; ++p) ++namelen;
wh_bytes(out, m.name, namelen);
wh_nl(out);
}
- return 0;
+ return cfree_writer_status(out);
}
diff --git a/src/api/arch_regs.c b/src/api/arch_regs.c
@@ -1,6 +1,6 @@
/* Public arch register name API. */
-#include <cfree.h>
+#include <cfree/arch.h>
#include <stddef.h>
#include "arch/arch.h"
@@ -11,11 +11,13 @@ const char* cfree_arch_register_name(CfreeArchKind arch, uint32_t dwarf_idx) {
return impl->register_name(dwarf_idx);
}
-int cfree_arch_register_index(CfreeArchKind arch, const char* name,
- uint32_t* idx_out) {
- const ArchImpl* impl = arch_lookup(arch);
- if (!impl || !impl->register_index) return 1;
- return impl->register_index(name, idx_out);
+CfreeStatus cfree_arch_register_index(CfreeArchKind arch, const char* name,
+ uint32_t* idx_out) {
+ const ArchImpl* impl;
+ if (!name || !idx_out) return CFREE_INVALID;
+ impl = arch_lookup(arch);
+ if (!impl || !impl->register_index) return CFREE_UNSUPPORTED;
+ return impl->register_index(name, idx_out) == 0 ? CFREE_OK : CFREE_NOT_FOUND;
}
uint32_t cfree_arch_register_count(CfreeArchKind arch) {
@@ -24,10 +26,11 @@ uint32_t cfree_arch_register_count(CfreeArchKind arch) {
return impl->register_count();
}
-int cfree_arch_register_at(CfreeArchKind arch, uint32_t idx,
- CfreeArchReg* out) {
- if (!out) return 1;
- const ArchImpl* impl = arch_lookup(arch);
- if (!impl || !impl->register_at) return 1;
- return impl->register_at(idx, out);
+CfreeStatus cfree_arch_register_at(CfreeArchKind arch, uint32_t idx,
+ CfreeArchReg* out) {
+ const ArchImpl* impl;
+ if (!out) return CFREE_INVALID;
+ impl = arch_lookup(arch);
+ if (!impl || !impl->register_at) return CFREE_UNSUPPORTED;
+ return impl->register_at(idx, out) == 0 ? CFREE_OK : CFREE_NOT_FOUND;
}
diff --git a/src/api/cg.c b/src/api/cg.c
@@ -184,7 +184,7 @@ static CgApiState *cg_api_get(Compiler *c) {
return NULL;
if (c->cg_api)
return (CgApiState *)c->cg_api;
- h = (Heap *)c->env->heap;
+ h = (Heap *)c->ctx->heap;
s = (CgApiState *)h->alloc(h, sizeof(*s), _Alignof(CgApiState));
if (!s)
return NULL;
@@ -955,13 +955,13 @@ uint32_t cfree_cg_type_record_nfields(CfreeCompiler *c, CfreeCgTypeId id) {
return (ty && ty->kind == CFREE_CG_TYPE_RECORD) ? ty->record.nfields : 0;
}
-int cfree_cg_type_record_field(CfreeCompiler *c, CfreeCgTypeId id,
- uint32_t index, CfreeCgField *out,
- uint64_t *offset_out) {
+CfreeStatus cfree_cg_type_record_field(CfreeCompiler *c, CfreeCgTypeId id,
+ uint32_t index, CfreeCgField *out,
+ uint64_t *offset_out) {
const CgType *ty = cg_type_get(c, id);
const CgTypeField *f;
if (!ty || ty->kind != CFREE_CG_TYPE_RECORD || index >= ty->record.nfields) {
- return 1;
+ return CFREE_NOT_FOUND;
}
f = &ty->record.fields[index];
if (out) {
@@ -976,7 +976,7 @@ int cfree_cg_type_record_field(CfreeCompiler *c, CfreeCgTypeId id,
}
if (offset_out)
*offset_out = f->offset;
- return 0;
+ return CFREE_OK;
}
int cfree_cg_target_supports_call_conv(CfreeCompiler *c, CfreeCgCallConv cc) {
@@ -1239,7 +1239,7 @@ static DebugTypeId api_debug_type(CfreeCg *g, CfreeCgTypeId id) {
return debug_type_array(g->debug, elem, count);
}
case CFREE_CG_TYPE_FUNC: {
- Heap *h = (Heap *)g->c->env->heap;
+ Heap *h = (Heap *)g->c->ctx->heap;
DebugTypeId ret = api_debug_type(g, ty->func.ret);
DebugTypeId *params = NULL;
DebugTypeId fn;
@@ -1477,7 +1477,7 @@ static int api_is_lvalue_sv(const ApiSValue *sv) {
}
static void api_stack_grow(CfreeCg *g, u32 want) {
- Heap *h = g->c->env->heap;
+ Heap *h = g->c->ctx->heap;
u32 cap = g->cap;
ApiSValue *nb;
if (cap >= want)
@@ -1570,7 +1570,7 @@ static void api_return_spill_slot(CfreeCg *g, FrameSlot s, u8 cls) {
return;
if (cls >= 3)
return;
- h = g->c->env->heap;
+ h = g->c->ctx->heap;
if (g->slot_pools[cls].n >= g->slot_pools[cls].cap) {
u32 new_cap = g->slot_pools[cls].cap ? g->slot_pools[cls].cap * 2 : 8;
FrameSlot *nb = (FrameSlot *)h->alloc(h, sizeof(FrameSlot) * new_cap,
@@ -2836,7 +2836,7 @@ static void api_remember_sym(CfreeCg *g, ObjSymId sym, CfreeCgTypeId ty,
g->sym_attrs[sym] = decl;
return;
}
- h = g->c->env->heap;
+ h = g->c->ctx->heap;
cap = g->sym_cap ? g->sym_cap : 16u;
while (cap <= sym)
cap *= 2u;
@@ -2918,29 +2918,32 @@ static SrcLoc api_no_loc(void) {
return loc;
}
-CfreeCg *cfree_cg_new(CfreeCompiler *c, CfreeObjBuilder *out,
- const CfreeCompileOptions *opts) {
+CfreeStatus cfree_cg_new(CfreeCompiler *c, CfreeObjBuilder *out,
+ const CfreeCodeOptions *opts, CfreeCg **cg_out) {
Heap *h;
CfreeCg *g;
MCEmitter *mc;
CGTarget *target;
Debug *debug = NULL;
int opt_level = opts ? opts->opt_level : 0;
+ if (!cg_out)
+ return CFREE_INVALID;
+ *cg_out = NULL;
if (!c || !out)
- return NULL;
+ return CFREE_INVALID;
if (opt_level < 0 || opt_level > 2) {
compiler_panic((Compiler *)c, api_no_loc(),
"CfreeCg: unsupported opt_level %d", opt_level);
}
- h = (Heap *)c->env->heap;
+ h = (Heap *)c->ctx->heap;
mc = mc_new((Compiler *)c, (ObjBuilder *)out);
if (!mc)
- return NULL;
+ return CFREE_NOMEM;
if (opts && opts->debug_info) {
debug = debug_new((Compiler *)c, (ObjBuilder *)out);
if (!debug) {
mc_free(mc);
- return NULL;
+ return CFREE_NOMEM;
}
mc->debug = debug;
}
@@ -2949,7 +2952,7 @@ CfreeCg *cfree_cg_new(CfreeCompiler *c, CfreeObjBuilder *out,
if (debug)
debug_free(debug);
mc_free(mc);
- return NULL;
+ return CFREE_UNSUPPORTED;
}
target->debug = debug;
if (opt_level > 0) {
@@ -2963,7 +2966,7 @@ CfreeCg *cfree_cg_new(CfreeCompiler *c, CfreeObjBuilder *out,
debug_free(debug);
cgtarget_free(target);
mc_free(mc);
- return NULL;
+ return CFREE_NOMEM;
}
memset(g, 0, sizeof *g);
g->c = (Compiler *)c;
@@ -2971,7 +2974,8 @@ CfreeCg *cfree_cg_new(CfreeCompiler *c, CfreeObjBuilder *out,
g->target = target;
g->mc = mc;
g->debug = debug;
- return g;
+ *cg_out = g;
+ return CFREE_OK;
}
void cfree_cg_free(CfreeCg *g) {
@@ -2985,7 +2989,7 @@ void cfree_cg_free(CfreeCg *g) {
}
cgtarget_free(g->target);
mc_free(g->mc);
- h = g->c->env->heap;
+ h = g->c->ctx->heap;
if (g->stack)
h->free(h, g->stack, sizeof(ApiSValue) * g->cap);
if (g->locals) {
@@ -3374,7 +3378,7 @@ static CfreeCgLocal api_local_handle(u32 index) {
}
static int api_grow_locals(CfreeCg *g, u32 want) {
- Heap *h = g->c->env->heap;
+ Heap *h = g->c->ctx->heap;
ApiSourceLocal *nb;
u32 cap;
if (g->locals_cap >= want)
@@ -4974,7 +4978,7 @@ void cfree_cg_intrinsic(CfreeCg *g, CfreeCgIntrinsic intrin, uint32_t nargs,
if (!g)
return;
T = g->target;
- h = g->c->env->heap;
+ h = g->c->ctx->heap;
rty = resolve_type(g->c, result_type);
int_ty = builtin_id(CFREE_CG_BUILTIN_I32);
kind = api_map_intrinsic(g, intrin, result_type);
@@ -5330,7 +5334,7 @@ void cfree_cg_inline_asm(CfreeCg *g, CfreeCgInlineAsm asm_block) {
return;
api_local_const_memory_boundary(g);
T = g->target;
- h = g->c->env->heap;
+ h = g->c->ctx->heap;
fallback_ty = builtin_id(CFREE_CG_BUILTIN_I64);
tmpl_str = api_sym_cstr(g, tmpl);
ninout = 0;
diff --git a/src/api/dep.c b/src/api/dep.c
@@ -1,8 +1,6 @@
-/* Header-dependency iteration — public CfreeDepIter wraps the internal
- * SourceManager iterator and translates file ids to the resolved path
- * strings the driver writes into Make rules. */
+/* Header-dependency iteration. */
-#include <cfree.h>
+#include <cfree/compile.h>
#include "core/core.h"
#include "core/heap.h"
@@ -13,29 +11,31 @@ struct CfreeDepIter {
SourceDepIter* inner;
};
-CfreeDepIter* cfree_dep_iter_new(CfreeCompiler* c) {
+CfreeStatus cfree_dep_iter_new(CfreeCompiler* c, CfreeDepIter** out) {
Heap* h;
CfreeDepIter* it;
- if (!c || !c->sources) return NULL;
- h = (Heap*)c->env->heap;
+ if (!out) return CFREE_INVALID;
+ if (!c || !c->sources) return CFREE_INVALID;
+ h = c->ctx->heap;
it = (CfreeDepIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeDepIter));
- if (!it) return NULL;
+ if (!it) return CFREE_NOMEM;
it->c = c;
it->inner = source_depiter_new(c->sources);
if (!it->inner) {
h->free(h, it, sizeof(*it));
- return NULL;
+ return CFREE_NOMEM;
}
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_dep_iter_next(CfreeDepIter* it, CfreeDepEdge* out) {
+CfreeIterResult cfree_dep_iter_next(CfreeDepIter* it, CfreeDepEdge* out) {
const SourceInclude* edge;
const SourceFile* includer;
const SourceFile* included;
- if (!it || !out) return 0;
+ if (!it || !out) return CFREE_ITER_ERROR;
edge = source_depiter_next(it->inner);
- if (!edge) return 0;
+ if (!edge) return CFREE_ITER_END;
includer = source_file(it->c->sources, edge->includer_file_id);
included = source_file(it->c->sources, edge->included_file_id);
out->includer_name =
@@ -43,21 +43,17 @@ int cfree_dep_iter_next(CfreeDepIter* it, CfreeDepEdge* out) {
out->included_name =
included ? pool_str(it->c->global, included->name, NULL) : NULL;
out->include_loc = edge->include_loc;
- /* SourceInclude.system tracks the bracket form, not the resolved
- * directory's system flag — the include resolver doesn't yet
- * distinguish which dir matched. -MM filtering treats every <...>
- * as a system header until the resolver feeds that back. */
out->from_system_path = (uint8_t)(edge->system ? 1 : 0);
out->bracketed = (uint8_t)(edge->system ? 1 : 0);
out->pad[0] = 0;
out->pad[1] = 0;
- return 1;
+ return CFREE_ITER_ITEM;
}
void cfree_dep_iter_free(CfreeDepIter* it) {
Heap* h;
if (!it) return;
- h = (Heap*)it->c->env->heap;
+ h = it->c->ctx->heap;
if (it->inner) source_depiter_free(it->inner);
h->free(h, it, sizeof(*it));
}
diff --git a/src/api/detect.c b/src/api/detect.c
@@ -1,9 +1,6 @@
-/* Binary format and target detection from object header bytes. Pure
- * byte parsing; no libcfree dependencies, kept in its own translation
- * unit so consumers that only need detection (e.g. cfree-roundtrip
- * tests) don't drag the full pipeline in through the linker. */
+/* Binary format and target detection from object header bytes. */
-#include <cfree.h>
+#include <cfree/object.h>
#include "core/core.h"
@@ -76,10 +73,10 @@ static void detect_set_ptr(CfreeTarget* t, CfreeArchKind arch) {
}
}
-static int detect_elf(const u8* d, size_t len, CfreeTarget* out) {
+static CfreeStatus detect_elf(const u8* d, size_t len, CfreeTarget* out) {
u8 ei_class, ei_data, ei_osabi;
u16 e_machine;
- if (len < 20) return 1;
+ if (len < 20) return CFREE_MALFORMED;
ei_class = d[4];
ei_data = d[5];
ei_osabi = d[7];
@@ -88,7 +85,7 @@ static int detect_elf(const u8* d, size_t len, CfreeTarget* out) {
} else if (ei_data == 2) {
e_machine = (u16)d[19] | ((u16)d[18] << 8);
} else {
- return 1;
+ return CFREE_MALFORMED;
}
detect_target_defaults(out);
@@ -114,21 +111,21 @@ static int detect_elf(const u8* d, size_t len, CfreeTarget* out) {
else if (ei_class == 2)
detect_set_ptr(out, CFREE_ARCH_RV64);
else
- return 1;
+ return CFREE_MALFORMED;
break;
default:
- return 1;
+ return CFREE_UNSUPPORTED;
}
if (ei_osabi == 0 || ei_osabi == 3)
out->os = CFREE_OS_LINUX;
else
out->os = CFREE_OS_FREESTANDING;
- return 0;
+ return CFREE_OK;
}
-static int detect_coff(const u8* d, size_t len, CfreeTarget* out) {
+static CfreeStatus detect_coff(const u8* d, size_t len, CfreeTarget* out) {
u16 machine;
- if (len < 2) return 1;
+ if (len < 2) return CFREE_MALFORMED;
machine = (u16)d[0] | ((u16)d[1] << 8);
detect_target_defaults(out);
out->obj = CFREE_OBJ_COFF;
@@ -153,15 +150,15 @@ static int detect_coff(const u8* d, size_t len, CfreeTarget* out) {
detect_set_ptr(out, CFREE_ARCH_RV64);
break;
default:
- return 1;
+ return CFREE_UNSUPPORTED;
}
- return 0;
+ return CFREE_OK;
}
-static int detect_macho(const u8* d, size_t len, CfreeTarget* out) {
+static CfreeStatus detect_macho(const u8* d, size_t len, CfreeTarget* out) {
u32 magic, cputype;
int swap, is64;
- if (len < 8) return 1;
+ if (len < 8) return CFREE_MALFORMED;
magic = (u32)d[0] | ((u32)d[1] << 8) | ((u32)d[2] << 16) | ((u32)d[3] << 24);
switch (magic) {
case 0xFEEDFACEu:
@@ -181,7 +178,7 @@ static int detect_macho(const u8* d, size_t len, CfreeTarget* out) {
is64 = 1;
break;
default:
- return 1;
+ return CFREE_MALFORMED;
}
if (!swap) {
cputype =
@@ -207,15 +204,16 @@ static int detect_macho(const u8* d, size_t len, CfreeTarget* out) {
detect_set_ptr(out, CFREE_ARCH_ARM_64);
break;
default:
- return 1;
+ return CFREE_UNSUPPORTED;
}
(void)is64;
- return 0;
+ return CFREE_OK;
}
-int cfree_detect_target(const uint8_t* data, size_t len, CfreeTarget* out) {
+CfreeStatus cfree_detect_target(const uint8_t* data, size_t len,
+ CfreeTarget* out) {
CfreeBinFmt bin;
- if (!data || !out) return 1;
+ if (!data || !out) return CFREE_INVALID;
bin = cfree_detect_fmt(data, len);
switch (bin) {
case CFREE_BIN_ELF:
@@ -235,9 +233,9 @@ int cfree_detect_target(const uint8_t* data, size_t len, CfreeTarget* out) {
t.obj = CFREE_OBJ_WASM;
t.os = CFREE_OS_WASI;
*out = t;
- return 0;
+ return CFREE_OK;
}
default:
- return 1;
+ return CFREE_UNSUPPORTED;
}
}
diff --git a/src/api/disasm.c b/src/api/disasm.c
@@ -1,14 +1,6 @@
-/* Public disassembler API.
- *
- * cfree_disasm_iter_new / next / free - low-level byte → CfreeInsn walk.
- * cfree_obj_disasm - objdump-style listing over an
- * already-built relocatable.
- *
- * The arch-level decoder (arch_disasm_*) owns the mnemonic/operand text and
- * is reloc-unaware. The annotation overlay (sym + reloc decoration) lives
- * here so the per-arch backends don't grow a dependency on ObjBuilder. */
+/* Public disassembler API. */
-#include <cfree.h>
+#include <cfree/disasm.h>
#include <stdint.h>
#include <string.h>
@@ -22,57 +14,47 @@
#define DASM_ANN_CAP 128u
-/* ---- iterator -------------------------------------------------------- */
+ObjBuilder* cfree_objfile_builder(const CfreeObjFile* f);
+/* The iterator holds its own private Compiler so the arch decoder has a
+ * pool to mint strings from without needing a caller-provided CfreeCompiler. */
struct CfreeDisasmIter {
- Compiler* c;
+ Compiler compiler;
+ const CfreeContext* ctx;
Heap* heap;
ArchDisasm* arch;
const uint8_t* bytes;
size_t len;
- size_t off; /* bytes consumed so far */
- uint64_t vaddr0; /* vaddr of bytes[0] */
- ObjBuilder* obj; /* optional; for reloc/symbol annotation */
- /* Owned annotation buffer; the per-arch decoder writes into its own
- * strings, this one carries the overlay we splice on top. */
+ size_t off;
+ uint64_t vaddr0;
+ const CfreeObjFile* annot;
+ ObjBuilder* annot_ob;
char ann_buf[DASM_ANN_CAP];
StrBuf ann;
};
-/* For (sec_offset == off-since-vaddr0), find a relocation that applies to
- * this 4-byte slot and, if found, render "<sym><+addend> (<kind>)" into the
- * iterator's annotation buffer. Returns the resulting cstr (possibly the
- * empty string). */
static const char* dasm_overlay(CfreeDisasmIter* it, uint64_t vaddr) {
- if (!it->obj) return "";
+ ObjBuilder* obj = it->annot_ob;
+ if (!obj) return "";
strbuf_reset(&it->ann);
- /* Walk every reloc and match (any section, offset == vaddr - vaddr0).
- * For a public-API call where `bytes` is one section's data and
- * vaddr0 == its base, this is correct; multi-section buffers would
- * require a richer key. The doc/ASM.md §4.5 contract is single-section
- * inputs. */
u64 want = vaddr - it->vaddr0;
- u32 nrel = obj_reloc_total(it->obj);
+ u32 nrel = obj_reloc_total(obj);
for (u32 i = 0; i < nrel; ++i) {
- const Reloc* r = obj_reloc_at(it->obj, i);
+ const Reloc* r = obj_reloc_at(obj, i);
if (!r) continue;
if ((u64)r->offset != want) continue;
- /* Only annotate text-section relocs. We don't have the section_id
- * tied to the iterator buffer, so we accept any reloc whose offset
- * matches and let the caller scope the input appropriately. */
const ObjSym* sym = (r->sym != OBJ_SYM_NONE)
- ? obj_symbol_get(it->obj, r->sym)
+ ? obj_symbol_get(obj, r->sym)
: NULL;
if (sym && sym->name) {
- /* The Sym is interned in the obj's owning compiler pool, which may
- * differ from the iterator caller's compiler (e.g. driver loads
- * the .o via cfree_obj_open, which mints its own Compiler). */
- Compiler* oc = obj_compiler(it->obj);
+ Compiler* oc = obj_compiler(obj);
size_t nlen = 0;
const char* nm = oc ? pool_str(oc->global, sym->name, &nlen) : NULL;
- if (nm) strbuf_putn(&it->ann, nm, nlen);
- else strbuf_puts(&it->ann, "<anon>");
+ if (nm)
+ strbuf_putn(&it->ann, nm, nlen);
+ else
+ strbuf_puts(&it->ann, "<anon>");
} else {
strbuf_puts(&it->ann, "<anon>");
}
@@ -87,43 +69,48 @@ static const char* dasm_overlay(CfreeDisasmIter* it, uint64_t vaddr) {
return strbuf_cstr(&it->ann);
}
-CfreeDisasmIter* cfree_disasm_iter_new(CfreeCompiler* c, const uint8_t* bytes,
- size_t len, uint64_t vaddr,
- CfreeObjBuilder* obj) {
- if (!c || (!bytes && len > 0)) return NULL;
- Heap* h = (Heap*)c->env->heap;
- CfreeDisasmIter* it =
- (CfreeDisasmIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeDisasmIter));
- if (!it) return NULL;
+CfreeStatus cfree_disasm_iter_new(const CfreeDisasmContext* dctx,
+ const uint8_t* bytes, size_t len,
+ uint64_t vaddr, const CfreeObjFile* annot,
+ CfreeDisasmIter** out) {
+ Heap* h;
+ CfreeDisasmIter* it;
+ if (!out) return CFREE_INVALID;
+ if (!dctx) return CFREE_INVALID;
+ if (!bytes && len > 0) return CFREE_INVALID;
+ if (!dctx->context.heap) return CFREE_INVALID;
+ h = dctx->context.heap;
+ it = (CfreeDisasmIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeDisasmIter));
+ if (!it) return CFREE_NOMEM;
memset(it, 0, sizeof(*it));
- it->c = c;
+ it->ctx = &dctx->context;
it->heap = h;
- it->arch = arch_disasm_new(c);
+ compiler_init(&it->compiler, dctx->target, &dctx->context);
+ it->arch = arch_disasm_new(&it->compiler);
if (!it->arch) {
+ compiler_fini(&it->compiler);
h->free(h, it, sizeof(*it));
- return NULL;
+ return CFREE_UNSUPPORTED;
}
it->bytes = bytes;
it->len = len;
it->off = 0;
it->vaddr0 = vaddr;
- it->obj = obj;
+ it->annot = annot;
+ it->annot_ob = annot ? cfree_objfile_builder(annot) : NULL;
strbuf_init(&it->ann, it->ann_buf, sizeof it->ann_buf);
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_disasm_iter_next(CfreeDisasmIter* it, CfreeInsn* out) {
- if (!it || it->off >= it->len) return 0;
+CfreeIterResult cfree_disasm_iter_next(CfreeDisasmIter* it, CfreeInsn* out) {
+ if (!it || !out) return CFREE_ITER_ERROR;
+ if (it->off >= it->len) return CFREE_ITER_END;
uint64_t vaddr = it->vaddr0 + (uint64_t)it->off;
u32 n = arch_disasm_decode(it->arch, it->bytes + it->off, it->len - it->off,
vaddr, out);
if (n == 0) {
- /* Undecodable. Advance by the arch's minimum unit (4 for aarch64) so
- * the listing stays in sync; emit a placeholder. The arch decoder
- * already populated mnemonic="(unknown)" via its own .inst path when
- * it had at least 4 bytes — here we ran out of bytes entirely. */
- if (it->off >= it->len) return 0;
- /* Best-effort fixed-step advance for the only supported arch. */
+ if (it->off >= it->len) return CFREE_ITER_END;
n = (u32)(it->len - it->off);
if (n > 4) n = 4;
out->vaddr = vaddr;
@@ -133,26 +120,27 @@ int cfree_disasm_iter_next(CfreeDisasmIter* it, CfreeInsn* out) {
out->operands = "";
out->annotation = "";
it->off += n;
- return 1;
+ return CFREE_ITER_ITEM;
}
out->annotation = dasm_overlay(it, vaddr);
it->off += n;
- return 1;
+ return CFREE_ITER_ITEM;
}
void cfree_disasm_iter_free(CfreeDisasmIter* it) {
+ Heap* h;
if (!it) return;
arch_disasm_free(it->arch);
- it->heap->free(it->heap, it, sizeof(*it));
+ compiler_fini(&it->compiler);
+ h = it->heap;
+ h->free(h, it, sizeof(*it));
}
-/* ---- objdump-style listing ------------------------------------------- */
-
-static void w_str(CfreeWriter* w, const char* s) {
- cfree_writer_write(w, s, strlen(s));
+static CfreeStatus w_str(CfreeWriter* w, const char* s) {
+ return cfree_writer_write(w, s, strlen(s));
}
-static void w_hex(CfreeWriter* w, u64 v, u32 width) {
+static CfreeStatus w_hex(CfreeWriter* w, u64 v, u32 width) {
static const char H[] = "0123456789abcdef";
char buf[17];
u32 i;
@@ -161,14 +149,15 @@ static void w_hex(CfreeWriter* w, u64 v, u32 width) {
buf[width - 1 - i] = H[(v >> (4 * i)) & 0xfu];
}
buf[width] = '\0';
- cfree_writer_write(w, buf, width);
+ return cfree_writer_write(w, buf, width);
}
-/* Right-align v in a `minwidth`-char field as lowercase hex. */
-static void w_hex_padded(CfreeWriter* w, u64 v, u32 minwidth) {
+static CfreeStatus w_hex_padded(CfreeWriter* w, u64 v, u32 minwidth) {
static const char H[] = "0123456789abcdef";
char buf[24];
u32 i = sizeof(buf);
+ u32 nch;
+ CfreeStatus st;
if (v == 0) {
buf[--i] = '0';
} else {
@@ -177,59 +166,61 @@ static void w_hex_padded(CfreeWriter* w, u64 v, u32 minwidth) {
v >>= 4;
}
}
- u32 nch = (u32)(sizeof(buf) - i);
+ nch = (u32)(sizeof(buf) - i);
while (nch < minwidth) {
- cfree_writer_write(w, " ", 1);
+ st = cfree_writer_write(w, " ", 1);
+ if (st != CFREE_OK) return st;
++nch;
}
- cfree_writer_write(w, buf + i, sizeof(buf) - i);
+ return cfree_writer_write(w, buf + i, sizeof(buf) - i);
}
-/* Look up the symbol whose value == offset within `section_idx`, if any.
- * Used to emit objdump-style "<addr> <name>:" headers ahead of each
- * defined function. Returns NULL if no symbol starts at that offset. */
-static const char* dasm_sym_at(CfreeCompiler* c, CfreeObjFile* f,
- uint32_t section_idx, uint64_t offset) {
- CfreeObjSymIter* si = cfree_obj_symiter_new(f);
- if (!si) return NULL;
+static const char* dasm_sym_at(const CfreeObjFile* f, uint32_t section_idx,
+ uint64_t offset) {
+ CfreeObjSymIter* si = NULL;
const char* found = NULL;
CfreeObjSymInfo s;
- while (cfree_obj_symiter_next(si, &s)) {
+ if (cfree_obj_symiter_new((CfreeObjFile*)f, &si) != CFREE_OK) return NULL;
+ for (;;) {
+ CfreeIterResult r = cfree_obj_symiter_next(si, &s);
+ if (r != CFREE_ITER_ITEM) break;
if (s.section != section_idx) continue;
if (s.value != offset) continue;
if (!s.name || !s.name[0]) continue;
- /* Skip AArch64 mapping symbols ($x / $d / $a / $t) — they decorate
- * code/data boundaries, not user-facing entry points. */
if (s.name[0] == '$') continue;
found = s.name;
- if (s.kind == CFREE_SK_FUNC) break; /* preferred; otherwise last wins */
+ if (s.kind == CFREE_SK_FUNC) break;
}
cfree_obj_symiter_free(si);
- (void)c;
return found;
}
-int cfree_obj_disasm(CfreeCompiler* c, const CfreeBytesInput* in,
- CfreeWriter* out) {
- if (!c || !in || !out) return 1;
- CfreeObjFile* f = cfree_obj_open(c->env, in);
- if (!f) return 1;
- CfreeObjBuilder* ob = cfree_obj_builder(f);
- uint32_t nsec = cfree_obj_nsections(f);
- uint32_t i;
+CfreeStatus cfree_disasm_obj(const CfreeContext* ctx, const CfreeObjFile* f,
+ CfreeWriter* out) {
+ uint32_t nsec, i;
+ CfreeDisasmContext dctx;
+ if (!ctx || !f || !out) return CFREE_INVALID;
+ dctx.target = cfree_obj_target(f);
+ dctx.context = *ctx;
+ nsec = cfree_obj_nsections(f);
for (i = 0; i < nsec; ++i) {
- CfreeObjSecInfo s = cfree_obj_section(f, i);
- if (s.kind != CFREE_SEC_TEXT) continue;
+ CfreeObjSecInfo s;
+ const uint8_t* data = NULL;
size_t n = 0;
- const uint8_t* data = cfree_obj_section_data(f, i, &n);
+ CfreeDisasmIter* it = NULL;
+ CfreeStatus st;
+ const char* head;
+
+ if (cfree_obj_section(f, i, &s) != CFREE_OK) continue;
+ if (s.kind != CFREE_SEC_TEXT) continue;
+ if (cfree_obj_section_data(f, i, &data, &n) != CFREE_OK) continue;
if (!data || !n) continue;
w_str(out, "Disassembly of section ");
w_str(out, s.name ? s.name : ".text");
w_str(out, ":\n\n");
- /* Header for the start-of-section symbol, if any. */
- const char* head = dasm_sym_at(c, f, i, 0);
+ head = dasm_sym_at(f, i, 0);
if (head) {
w_hex(out, 0, 16);
w_str(out, " <");
@@ -237,20 +228,14 @@ int cfree_obj_disasm(CfreeCompiler* c, const CfreeBytesInput* in,
w_str(out, ">:\n");
}
- CfreeDisasmIter* it = cfree_disasm_iter_new(c, data, n, 0, ob);
- if (!it) {
- cfree_obj_close(f);
- return 1;
- }
- CfreeInsn ins;
- while (cfree_disasm_iter_next(it, &ins)) {
- /* objdump-ish: right-aligned hex vaddr in 8-char field, ":\t", raw
- * bytes (little-endian word for aarch64 4-byte slots) + " \t",
- * mnemonic [\t operands] [ ; annotation]. */
+ st = cfree_disasm_iter_new(&dctx, data, n, 0, f, &it);
+ if (st != CFREE_OK) return st;
+ for (;;) {
+ CfreeInsn ins;
+ CfreeIterResult r = cfree_disasm_iter_next(it, &ins);
+ if (r != CFREE_ITER_ITEM) break;
w_hex_padded(out, ins.vaddr, 8);
w_str(out, ":\t");
- /* Raw bytes: 4 bytes little-endian, rendered as a single 8-hex-digit
- * word followed by space-tab (objdump convention for aarch64). */
if (ins.nbytes == 4) {
uint32_t w = (uint32_t)ins.bytes[0] | ((uint32_t)ins.bytes[1] << 8) |
((uint32_t)ins.bytes[2] << 16) |
@@ -277,6 +262,17 @@ int cfree_obj_disasm(CfreeCompiler* c, const CfreeBytesInput* in,
}
cfree_disasm_iter_free(it);
}
- cfree_obj_close(f);
- return 0;
+ return cfree_writer_status(out);
+}
+
+CfreeStatus cfree_disasm_obj_bytes(const CfreeContext* ctx,
+ const CfreeBytes* bytes, CfreeWriter* out) {
+ CfreeObjFile* f = NULL;
+ CfreeStatus st;
+ if (!ctx || !bytes || !out) return CFREE_INVALID;
+ st = cfree_obj_open(ctx, bytes, &f);
+ if (st != CFREE_OK) return st;
+ st = cfree_disasm_obj(ctx, f, out);
+ cfree_obj_free(f);
+ return st;
}
diff --git a/src/api/frontend.c b/src/api/frontend.c
@@ -1,4 +1,5 @@
#include <cfree/frontend.h>
+#include <cfree/support/arena.h>
#include <setjmp.h>
#include <stdarg.h>
#include <string.h>
@@ -12,13 +13,15 @@ struct CfreeArena {
Arena inner;
};
-CfreeArena* cfree_arena_new(CfreeHeap* h, size_t block_size) {
+CfreeStatus cfree_arena_new(CfreeHeap* h, size_t block_size, CfreeArena** out) {
CfreeArena* a;
- if (!h) return NULL;
+ if (!out) return CFREE_INVALID;
+ if (!h) return CFREE_INVALID;
a = (CfreeArena*)h->alloc(h, sizeof(*a), _Alignof(CfreeArena));
- if (!a) return NULL;
+ if (!a) return CFREE_NOMEM;
arena_init(&a->inner, (Heap*)h, block_size);
- return a;
+ *out = a;
+ return CFREE_OK;
}
void cfree_arena_free(CfreeArena* a) {
@@ -45,76 +48,16 @@ char* cfree_arena_strdup(CfreeArena* a, const char* s, size_t len) {
return a ? arena_strdup(&a->inner, s, len) : NULL;
}
-CfreeHeap* cfree_compiler_heap(CfreeCompiler* c) {
- return (c && c->env) ? (CfreeHeap*)c->env->heap : NULL;
-}
-
-const CfreeFileIO* cfree_compiler_file_io(CfreeCompiler* c) {
- return (c && c->env) ? c->env->file_io : NULL;
-}
-
-int64_t cfree_compiler_now(CfreeCompiler* c) {
- return (c && c->env) ? c->env->now : -1;
-}
-
-CfreeSym cfree_sym_intern_len(CfreeCompiler* c, const char* str, size_t len) {
- if (!c || !str || len == 0) return 0;
- return pool_intern(c->global, str, len);
-}
-
-const char* cfree_sym_str(CfreeCompiler* c, CfreeSym sym, size_t* len_out) {
- if (!c) {
- if (len_out) *len_out = 0;
- return NULL;
- }
- return pool_str(c->global, (Sym)sym, len_out);
-}
-
-uint32_t cfree_source_add_file(CfreeCompiler* c, const char* path,
- int system_header) {
- return c ? source_add_file(c->sources, path, system_header) : 0;
-}
-
-uint32_t cfree_source_add_memory(CfreeCompiler* c, const char* name) {
- return c ? source_add_memory(c->sources, name) : 0;
-}
-
-uint32_t cfree_source_add_builtin(CfreeCompiler* c, const char* name) {
- return c ? source_add_builtin(c->sources, name) : 0;
-}
-
-void cfree_source_add_include(CfreeCompiler* c, uint32_t includer_file_id,
- uint32_t included_file_id, CfreeSrcLoc loc,
- int system) {
- if (!c) return;
- source_add_include(c->sources, includer_file_id, included_file_id, loc,
- system);
-}
-
-int cfree_source_file(CfreeCompiler* c, uint32_t file_id,
- CfreeSourceFile* out) {
- const SourceFile* f;
- if (!c || !out) return 1;
- f = source_file(c->sources, file_id);
- if (!f) return 1;
- memset(out, 0, sizeof *out);
- out->id = f->id;
- out->name = f->name;
- out->path = f->path;
- out->kind = f->kind;
- out->system_header = f->system_header;
- return 0;
-}
-
-int cfree_frontend_run(CfreeCompiler* c, CfreeFrontendRunFn fn, void* user) {
+CfreeStatus cfree_frontend_run(CfreeCompiler* c, CfreeFrontendRunFn fn,
+ void* user) {
PanicSave saved;
- int rc;
- if (!c || !fn) return 1;
+ CfreeStatus rc;
+ if (!c || !fn) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
+ return CFREE_ERR;
}
rc = fn(c, user);
compiler_panic_restore(c, &saved);
diff --git a/src/api/lifecycle.c b/src/api/lifecycle.c
@@ -1,31 +1,32 @@
-/* CfreeCompiler lifecycle. Kept separate from pipeline.c so consumers
- * that only need the lifecycle (e.g. obj-inspection or roundtrip tests)
- * don't drag in the full compile/link pipeline through the linker. */
+/* CfreeCompiler lifecycle. */
-#include <cfree.h>
-#include <cfree/cg.h>
+#include <cfree/core.h>
+#include <cfree/source.h>
#include <string.h>
#include "core/core.h"
#include "core/heap.h"
#include "core/pool.h"
-CfreeCompiler* cfree_compiler_new(CfreeTarget target, const CfreeEnv* env) {
+CfreeStatus cfree_compiler_new(CfreeTarget target, const CfreeContext* ctx,
+ CfreeCompiler** out) {
Heap* h;
Compiler* c;
- if (!env || !env->heap) return NULL;
- h = env->heap;
+ if (!out) return CFREE_INVALID;
+ if (!ctx || !ctx->heap) return CFREE_INVALID;
+ h = ctx->heap;
c = h->alloc(h, sizeof(*c), _Alignof(Compiler));
- if (!c) return NULL;
- compiler_init(c, target, env);
- return c;
+ if (!c) return CFREE_NOMEM;
+ compiler_init(c, target, ctx);
+ *out = c;
+ return CFREE_OK;
}
void cfree_compiler_free(CfreeCompiler* c) {
Heap* h;
if (!c) return;
- h = (Heap*)c->env->heap;
+ h = c->ctx->heap;
compiler_fini(c);
h->free(h, c, sizeof(*c));
}
@@ -37,6 +38,29 @@ CfreeTarget cfree_compiler_target(CfreeCompiler* c) {
return c->target;
}
+CfreeContext cfree_compiler_context(CfreeCompiler* c) {
+ CfreeContext z;
+ memset(&z, 0, sizeof z);
+ if (!c || !c->ctx) return z;
+ return *c->ctx;
+}
+
+CfreeHeap* cfree_compiler_heap(CfreeCompiler* c) {
+ return (c && c->ctx) ? c->ctx->heap : NULL;
+}
+
+const CfreeFileIO* cfree_compiler_file_io(CfreeCompiler* c) {
+ return (c && c->ctx) ? c->ctx->file_io : NULL;
+}
+
+CfreeDiagSink* cfree_compiler_diag_sink(CfreeCompiler* c) {
+ return (c && c->ctx) ? c->ctx->diag : NULL;
+}
+
+int64_t cfree_compiler_now(CfreeCompiler* c) {
+ return (c && c->ctx) ? c->ctx->now : -1;
+}
+
const char* cfree_compiler_file_name(CfreeCompiler* c, uint32_t file_id) {
const SourceFile* f;
if (!c) return NULL;
@@ -50,6 +74,19 @@ CfreeSym cfree_sym_intern(CfreeCompiler* c, const char* str) {
return pool_intern_cstr(c->global, str);
}
+CfreeSym cfree_sym_intern_len(CfreeCompiler* c, const char* str, size_t len) {
+ if (!c || !str || len == 0) return 0;
+ return pool_intern(c->global, str, (u32)len);
+}
+
+const char* cfree_sym_str(CfreeCompiler* c, CfreeSym sym, size_t* len_out) {
+ if (!c) {
+ if (len_out) *len_out = 0;
+ return NULL;
+ }
+ return pool_str(c->global, (Sym)sym, len_out);
+}
+
CfreeSym cfree_cg_c_linkage_name(CfreeCompiler* c, CfreeSym source_name) {
const char* name;
size_t len;
@@ -62,7 +99,7 @@ CfreeSym cfree_cg_c_linkage_name(CfreeCompiler* c, CfreeSym source_name) {
if (!name) return 0;
if (c->target.obj != CFREE_OBJ_MACHO) return source_name;
- h = (Heap*)c->env->heap;
+ h = c->ctx->heap;
buf = (char*)h->alloc(h, len + 2u, 1);
if (!buf) return 0;
buf[0] = '_';
@@ -73,14 +110,68 @@ CfreeSym cfree_cg_c_linkage_name(CfreeCompiler* c, CfreeSym source_name) {
return out;
}
-int cfree_register_frontend(CfreeCompiler* c, CfreeLanguage lang,
- CfreeCompileFn fn) {
- if (!c || lang >= CFREE_LANG_COUNT) return 1;
+CfreeStatus cfree_register_frontend(CfreeCompiler* c, CfreeLanguage lang,
+ CfreeCompileFn fn) {
+ if (!c) return CFREE_INVALID;
+ if ((unsigned)lang >= CFREE_LANG_COUNT) return CFREE_INVALID;
c->frontends[lang] = fn;
- return 0;
+ return CFREE_OK;
}
-CfreeDiagSink* cfree_compiler_diag_sink(CfreeCompiler* c) {
- if (!c || !c->env) return NULL;
- return c->env->diag;
+/* Source registry public wrappers. */
+
+CfreeStatus cfree_source_add_file(CfreeCompiler* c, const char* path,
+ int system_header, uint32_t* file_id_out) {
+ uint32_t tmp;
+ CfreeStatus st;
+ if (!c || !file_id_out) return CFREE_INVALID;
+ st = source_add_file(c->sources, path, system_header, &tmp);
+ if (st != CFREE_OK) return st;
+ *file_id_out = tmp;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_source_add_memory(CfreeCompiler* c, const char* name,
+ uint32_t* file_id_out) {
+ uint32_t tmp;
+ CfreeStatus st;
+ if (!c || !file_id_out) return CFREE_INVALID;
+ st = source_add_memory(c->sources, name, &tmp);
+ if (st != CFREE_OK) return st;
+ *file_id_out = tmp;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_source_add_builtin(CfreeCompiler* c, const char* name,
+ uint32_t* file_id_out) {
+ uint32_t tmp;
+ CfreeStatus st;
+ if (!c || !file_id_out) return CFREE_INVALID;
+ st = source_add_builtin(c->sources, name, &tmp);
+ if (st != CFREE_OK) return st;
+ *file_id_out = tmp;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_source_add_include(CfreeCompiler* c, uint32_t includer_file_id,
+ uint32_t included_file_id,
+ CfreeSrcLoc loc, int system) {
+ if (!c) return CFREE_INVALID;
+ return source_add_include(c->sources, includer_file_id, included_file_id,
+ loc, system);
+}
+
+CfreeStatus cfree_source_file(CfreeCompiler* c, uint32_t file_id,
+ CfreeSourceFile* out) {
+ const SourceFile* f;
+ if (!c || !out) return CFREE_INVALID;
+ f = source_file(c->sources, file_id);
+ if (!f) return CFREE_NOT_FOUND;
+ memset(out, 0, sizeof *out);
+ out->id = f->id;
+ out->name = f->name;
+ out->path = f->path;
+ out->kind = f->kind;
+ out->system_header = f->system_header;
+ return CFREE_OK;
}
diff --git a/src/api/objbuilder.c b/src/api/objbuilder.c
@@ -0,0 +1,214 @@
+/* Public CfreeObjBuilder API: thin adapter over the internal obj_* surface. */
+
+#include <cfree/objbuild.h>
+#include <string.h>
+
+#include "core/core.h"
+#include "obj/obj.h"
+
+static ObjSecId pub_to_intern_sec(CfreeObjSection s) {
+ if (s == CFREE_SECTION_NONE) return OBJ_SEC_NONE;
+ return (ObjSecId)(s + 1);
+}
+
+static CfreeObjSection intern_to_pub_sec(ObjSecId id) {
+ if (id == OBJ_SEC_NONE) return CFREE_SECTION_NONE;
+ return (CfreeObjSection)(id - 1);
+}
+
+static ObjSymId pub_to_intern_sym(CfreeObjSymbol s) {
+ if (s == CFREE_OBJ_SYMBOL_NONE) return OBJ_SYM_NONE;
+ return (ObjSymId)s;
+}
+
+static CfreeObjSymbol intern_to_pub_sym(ObjSymId id) {
+ if (id == OBJ_SYM_NONE) return CFREE_OBJ_SYMBOL_NONE;
+ return (CfreeObjSymbol)id;
+}
+
+static ObjGroupId pub_to_intern_group(CfreeObjGroup g) {
+ if (g == CFREE_OBJ_GROUP_NONE) return OBJ_GROUP_NONE;
+ return (ObjGroupId)g;
+}
+
+static CfreeObjGroup intern_to_pub_group(ObjGroupId id) {
+ if (id == OBJ_GROUP_NONE) return CFREE_OBJ_GROUP_NONE;
+ return (CfreeObjGroup)id;
+}
+
+CfreeStatus cfree_obj_builder_new(CfreeCompiler* c, CfreeObjBuilder** out) {
+ ObjBuilder* ob;
+ if (!out) return CFREE_INVALID;
+ if (!c) return CFREE_INVALID;
+ ob = obj_new(c);
+ if (!ob) return CFREE_NOMEM;
+ *out = ob;
+ return CFREE_OK;
+}
+
+void cfree_obj_builder_free(CfreeObjBuilder* b) {
+ if (b) obj_free(b);
+}
+
+CfreeStatus cfree_obj_builder_section(CfreeObjBuilder* b,
+ const CfreeObjSectionDesc* desc,
+ CfreeObjSection* out) {
+ ObjSecId id;
+ if (!b || !desc || !out) return CFREE_INVALID;
+ id = obj_section(b, (Sym)desc->name, (SecKind)desc->kind,
+ (u16)desc->flags, desc->align ? desc->align : 1u);
+ if (id == OBJ_SEC_NONE) return CFREE_ERR;
+ if (desc->entsize) {
+ /* Carry entsize through obj_section_ex if needed; obj_section path
+ * uses default 0. Use obj_section_ex when caller specifies entsize. */
+ const Section* sec = obj_section_get(b, id);
+ (void)sec;
+ /* Re-create via the _ex path to set entsize. */
+ /* obj_section dedupes by name+kind+flags+align; calling _ex with the
+ * same fields plus the entsize updates that section. */
+ id = obj_section_ex(b, (Sym)desc->name, (SecKind)desc->kind,
+ SSEM_PROGBITS, (u16)desc->flags,
+ desc->align ? desc->align : 1u, desc->entsize, 0, 0);
+ }
+ *out = intern_to_pub_sec(id);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_section_group(CfreeObjBuilder* b,
+ CfreeObjSection sec,
+ CfreeObjGroup grp) {
+ if (!b) return CFREE_INVALID;
+ obj_section_set_group(b, pub_to_intern_sec(sec), pub_to_intern_group(grp));
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_pos(CfreeObjBuilder* b, CfreeObjSection sec,
+ uint64_t* out) {
+ if (!b || !out) return CFREE_INVALID;
+ *out = obj_pos(b, pub_to_intern_sec(sec));
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_align(CfreeObjBuilder* b, CfreeObjSection sec,
+ uint32_t align, uint64_t* new_pos_out) {
+ u32 pos;
+ if (!b) return CFREE_INVALID;
+ pos = obj_align_to(b, pub_to_intern_sec(sec), align ? align : 1u);
+ if (new_pos_out) *new_pos_out = pos;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_write(CfreeObjBuilder* b, CfreeObjSection sec,
+ const void* data, size_t n) {
+ if (!b) return CFREE_INVALID;
+ obj_write(b, pub_to_intern_sec(sec), data, n);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_reserve(CfreeObjBuilder* b, CfreeObjSection sec,
+ size_t n, void** out) {
+ u8* p;
+ if (!b || !out) return CFREE_INVALID;
+ p = obj_reserve(b, pub_to_intern_sec(sec), n);
+ if (!p) return CFREE_NOMEM;
+ *out = p;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_reserve_bss(CfreeObjBuilder* b,
+ CfreeObjSection sec, uint64_t size,
+ uint32_t align) {
+ if (!b) return CFREE_INVALID;
+ obj_reserve_bss(b, pub_to_intern_sec(sec), (u32)size, align ? align : 1u);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_patch(CfreeObjBuilder* b, CfreeObjSection sec,
+ uint64_t offset, const void* data,
+ size_t n) {
+ if (!b) return CFREE_INVALID;
+ obj_patch(b, pub_to_intern_sec(sec), (u32)offset, data, n);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_symbol(CfreeObjBuilder* b,
+ const CfreeObjSymbolDesc* desc,
+ CfreeObjSymbol* out) {
+ ObjSymId id;
+ if (!b || !desc || !out) return CFREE_INVALID;
+ id = obj_symbol(b, (Sym)desc->name, (SymBind)desc->bind, (SymKind)desc->kind,
+ pub_to_intern_sec(desc->section), desc->value, desc->size);
+ if (id == OBJ_SYM_NONE) return CFREE_ERR;
+ *out = intern_to_pub_sym(id);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_symbol_define(CfreeObjBuilder* b,
+ CfreeObjSymbol sym,
+ CfreeObjSection section,
+ uint64_t value, uint64_t size) {
+ if (!b) return CFREE_INVALID;
+ obj_symbol_define(b, pub_to_intern_sym(sym), pub_to_intern_sec(section),
+ value, size);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_reloc(CfreeObjBuilder* b,
+ const CfreeObjRelocDesc* desc) {
+ if (!b || !desc) return CFREE_INVALID;
+ /* Public CfreeRelocKind.code is the raw internal RelocKind value
+ * (single shared numeric space). */
+ obj_reloc(b, pub_to_intern_sec(desc->section), (u32)desc->offset,
+ (RelocKind)desc->kind.code, pub_to_intern_sym(desc->symbol),
+ desc->addend);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_group(CfreeObjBuilder* b, CfreeSym name,
+ CfreeObjSymbol signature, uint32_t flags,
+ CfreeObjGroup* out) {
+ ObjGroupId id;
+ if (!b || !out) return CFREE_INVALID;
+ id = obj_group(b, (Sym)name, pub_to_intern_sym(signature), flags);
+ if (id == OBJ_GROUP_NONE) return CFREE_ERR;
+ *out = intern_to_pub_group(id);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_group_add_section(CfreeObjBuilder* b,
+ CfreeObjGroup grp,
+ CfreeObjSection sec) {
+ if (!b) return CFREE_INVALID;
+ obj_group_add_section(b, pub_to_intern_group(grp), pub_to_intern_sec(sec));
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_finalize(CfreeObjBuilder* b) {
+ if (!b) return CFREE_INVALID;
+ obj_finalize(b);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_builder_emit(CfreeObjBuilder* b, CfreeWriter* w) {
+ Compiler* c;
+ if (!b || !w) return CFREE_INVALID;
+ c = obj_compiler(b);
+ if (!c) return CFREE_INVALID;
+ switch (c->target.obj) {
+ case CFREE_OBJ_ELF:
+ emit_elf(c, b, w);
+ break;
+ case CFREE_OBJ_COFF:
+ emit_coff(c, b, w);
+ break;
+ case CFREE_OBJ_MACHO:
+ emit_macho(c, b, w);
+ break;
+ case CFREE_OBJ_WASM:
+ emit_wasm(c, b, w);
+ break;
+ default:
+ return CFREE_UNSUPPORTED;
+ }
+ return cfree_writer_status(w);
+}
diff --git a/src/api/objfile.c b/src/api/objfile.c
@@ -0,0 +1,376 @@
+/* Public CfreeObjFile reader. Wraps the internal read_/obj_ surface
+ * and exposes format-neutral object inspection. */
+
+#include <cfree/object.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include "core/buf.h"
+#include "core/core.h"
+#include "core/heap.h"
+#include "core/pool.h"
+#include "obj/obj.h"
+
+struct CfreeObjFile {
+ Compiler compiler;
+ const CfreeContext* ctx;
+ ObjBuilder* ob;
+ ObjFmt fmt;
+ CfreeTarget target;
+ const u8** sec_data_cache;
+ u32* sec_data_size;
+ u32 sec_data_n;
+};
+
+static ObjBuilder* obj_read_bytes(Compiler* c, const char* name, const u8* data,
+ size_t len, ObjFmt fmt) {
+ switch (fmt) {
+ case CFREE_OBJ_ELF:
+ return read_elf(c, name, data, len);
+ case CFREE_OBJ_COFF:
+ return read_coff(c, name, data, len);
+ case CFREE_OBJ_MACHO:
+ return read_macho(c, name, data, len);
+ case CFREE_OBJ_WASM:
+ return read_wasm(c, name, data, len);
+ }
+ return NULL;
+}
+
+CfreeStatus cfree_obj_open(const CfreeContext* ctx, const CfreeBytes* input,
+ CfreeObjFile** out) {
+ Heap* h;
+ CfreeObjFile* f;
+ CfreeTarget target;
+ CfreeStatus st;
+
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!ctx || !ctx->heap || !input) return CFREE_INVALID;
+ if (!input->data && input->len > 0) return CFREE_INVALID;
+
+ st = cfree_detect_target(input->data, input->len, &target);
+ if (st != CFREE_OK) return st;
+
+ h = ctx->heap;
+ f = (CfreeObjFile*)h->alloc(h, sizeof(*f), _Alignof(CfreeObjFile));
+ if (!f) return CFREE_NOMEM;
+ memset(f, 0, sizeof(*f));
+ f->ctx = ctx;
+ f->fmt = target.obj;
+ f->target = target;
+
+ compiler_init(&f->compiler, target, ctx);
+ if (setjmp(f->compiler.panic)) {
+ compiler_run_cleanups(&f->compiler);
+ compiler_fini(&f->compiler);
+ h->free(h, f, sizeof(*f));
+ return CFREE_MALFORMED;
+ }
+ f->ob = obj_read_bytes(&f->compiler, input->name, input->data, input->len,
+ target.obj);
+ if (!f->ob) {
+ compiler_fini(&f->compiler);
+ h->free(h, f, sizeof(*f));
+ return CFREE_MALFORMED;
+ }
+ *out = f;
+ return CFREE_OK;
+}
+
+void cfree_obj_free(CfreeObjFile* f) {
+ Heap* h;
+ if (!f) return;
+ h = f->ctx->heap;
+ if (f->sec_data_cache) {
+ u32 i;
+ for (i = 0; i < f->sec_data_n; ++i) {
+ if (f->sec_data_cache[i]) {
+ h->free(h, (void*)f->sec_data_cache[i], f->sec_data_size[i]);
+ }
+ }
+ h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * f->sec_data_n);
+ h->free(h, f->sec_data_size, sizeof(*f->sec_data_size) * f->sec_data_n);
+ }
+ if (f->ob) obj_free(f->ob);
+ compiler_fini(&f->compiler);
+ h->free(h, f, sizeof(*f));
+}
+
+CfreeObjFmt cfree_obj_fmt(const CfreeObjFile* f) { return f->fmt; }
+
+CfreeTarget cfree_obj_target(const CfreeObjFile* f) { return f->target; }
+
+uint32_t cfree_obj_nsections(const CfreeObjFile* f) {
+ return obj_section_count(f->ob);
+}
+
+CfreeStatus cfree_obj_section(const CfreeObjFile* f, CfreeObjSection idx,
+ CfreeObjSecInfo* out) {
+ const Section* sec;
+ if (!f || !out) return CFREE_INVALID;
+ if (idx >= obj_section_count(f->ob)) return CFREE_NOT_FOUND;
+ sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
+ if (!sec) return CFREE_NOT_FOUND;
+ out->name = sec->name ? pool_str(f->compiler.global, sec->name, NULL) : "";
+ out->kind = (CfreeSecKind)sec->kind;
+ out->flags = (uint32_t)sec->flags;
+ out->size = sec->bss_size ? sec->bss_size : sec->bytes.total;
+ out->align = sec->align > 1u ? sec->align : 1u;
+ out->entsize = sec->entsize;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_section_data(const CfreeObjFile* cf, CfreeObjSection idx,
+ const uint8_t** data_out,
+ size_t* len_out) {
+ CfreeObjFile* f = (CfreeObjFile*)cf;
+ const Section* sec;
+ Heap* h;
+ u32 n;
+ u8* buf;
+
+ if (!f || !data_out || !len_out) return CFREE_INVALID;
+ *data_out = NULL;
+ *len_out = 0;
+
+ n = obj_section_count(f->ob);
+ if (idx >= n) return CFREE_NOT_FOUND;
+
+ sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
+ if (!sec) return CFREE_NOT_FOUND;
+ if (sec->bss_size || sec->bytes.total == 0) return CFREE_OK;
+
+ h = f->ctx->heap;
+
+ if (!f->sec_data_cache) {
+ f->sec_data_cache = (const u8**)h->alloc(
+ h, sizeof(*f->sec_data_cache) * n, _Alignof(const u8*));
+ if (!f->sec_data_cache) return CFREE_NOMEM;
+ f->sec_data_size =
+ (u32*)h->alloc(h, sizeof(*f->sec_data_size) * n, _Alignof(u32));
+ if (!f->sec_data_size) {
+ h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * n);
+ f->sec_data_cache = NULL;
+ return CFREE_NOMEM;
+ }
+ {
+ u32 i;
+ for (i = 0; i < n; ++i) {
+ f->sec_data_cache[i] = NULL;
+ f->sec_data_size[i] = 0;
+ }
+ }
+ f->sec_data_n = n;
+ }
+
+ if (f->sec_data_cache[idx]) {
+ *data_out = f->sec_data_cache[idx];
+ *len_out = f->sec_data_size[idx];
+ return CFREE_OK;
+ }
+
+ buf = (u8*)h->alloc(h, sec->bytes.total, 1);
+ if (!buf) return CFREE_NOMEM;
+ buf_flatten(&sec->bytes, buf);
+ f->sec_data_cache[idx] = buf;
+ f->sec_data_size[idx] = sec->bytes.total;
+ *data_out = buf;
+ *len_out = sec->bytes.total;
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_obj_section_by_name(const CfreeObjFile* f, const char* name,
+ CfreeObjSection* out) {
+ u32 n, i;
+ if (!f || !name || !out) return CFREE_INVALID;
+ n = obj_section_count(f->ob);
+ for (i = 0; i < n; ++i) {
+ const Section* sec = obj_section_get(f->ob, (ObjSecId)(i + 1));
+ const char* sn;
+ if (!sec || !sec->name) continue;
+ sn = pool_str(f->compiler.global, sec->name, NULL);
+ if (sn && strcmp(sn, name) == 0) {
+ *out = i;
+ return CFREE_OK;
+ }
+ }
+ return CFREE_NOT_FOUND;
+}
+
+static void fill_syminfo(const CfreeObjFile* f, const ObjSym* sym,
+ CfreeObjSymInfo* out) {
+ out->name = sym->name ? pool_str(f->compiler.global, sym->name, NULL) : "";
+ out->bind = (CfreeSymBind)sym->bind;
+ out->kind = (CfreeSymKind)sym->kind;
+ out->section = sym->section_id != OBJ_SEC_NONE
+ ? (CfreeObjSection)(sym->section_id - 1)
+ : CFREE_SECTION_NONE;
+ out->value = sym->value;
+ out->size = sym->size;
+}
+
+CfreeStatus cfree_obj_symbol_by_name(const CfreeObjFile* f, const char* name,
+ CfreeObjSymInfo* out) {
+ ObjSymIter* it;
+ ObjSymEntry e;
+ if (!f || !name || !out) return CFREE_INVALID;
+ it = obj_symiter_new(f->ob);
+ if (!it) return CFREE_NOMEM;
+ while (obj_symiter_next(it, &e)) {
+ const char* nm;
+ if (!e.sym || !e.sym->name) continue;
+ nm = pool_str(f->compiler.global, e.sym->name, NULL);
+ if (nm && strcmp(nm, name) == 0) {
+ fill_syminfo(f, e.sym, out);
+ obj_symiter_free(it);
+ return CFREE_OK;
+ }
+ }
+ obj_symiter_free(it);
+ return CFREE_NOT_FOUND;
+}
+
+struct CfreeObjSymIter {
+ CfreeObjFile* file;
+ ObjSymIter* inner;
+};
+
+CfreeStatus cfree_obj_symiter_new(CfreeObjFile* f, CfreeObjSymIter** out) {
+ Heap* h;
+ CfreeObjSymIter* it;
+ if (!f || !out) return CFREE_INVALID;
+ h = f->ctx->heap;
+ it = (CfreeObjSymIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeObjSymIter));
+ if (!it) return CFREE_NOMEM;
+ it->file = f;
+ it->inner = obj_symiter_new(f->ob);
+ if (!it->inner) {
+ h->free(h, it, sizeof(*it));
+ return CFREE_NOMEM;
+ }
+ *out = it;
+ return CFREE_OK;
+}
+
+CfreeIterResult cfree_obj_symiter_next(CfreeObjSymIter* it,
+ CfreeObjSymInfo* out) {
+ ObjSymEntry entry;
+ if (!it || !out) return CFREE_ITER_ERROR;
+ if (!obj_symiter_next(it->inner, &entry)) return CFREE_ITER_END;
+ fill_syminfo(it->file, entry.sym, out);
+ return CFREE_ITER_ITEM;
+}
+
+void cfree_obj_symiter_free(CfreeObjSymIter* it) {
+ Heap* h;
+ if (!it) return;
+ obj_symiter_free(it->inner);
+ h = it->file->ctx->heap;
+ h->free(h, it, sizeof(*it));
+}
+
+struct CfreeObjRelocIter {
+ CfreeObjFile* file;
+ u32 idx;
+ u32 total;
+};
+
+CfreeStatus cfree_obj_reliter_new(CfreeObjFile* f, CfreeObjRelocIter** out) {
+ Heap* h;
+ CfreeObjRelocIter* it;
+ if (!f || !out) return CFREE_INVALID;
+ h = f->ctx->heap;
+ it = (CfreeObjRelocIter*)h->alloc(h, sizeof(*it),
+ _Alignof(CfreeObjRelocIter));
+ if (!it) return CFREE_NOMEM;
+ it->file = f;
+ it->idx = 0;
+ it->total = obj_reloc_total(f->ob);
+ *out = it;
+ return CFREE_OK;
+}
+
+CfreeIterResult cfree_obj_reliter_next(CfreeObjRelocIter* it,
+ CfreeObjReloc* out) {
+ const Reloc* r;
+ const ObjSym* sym;
+ if (!it || !out) return CFREE_ITER_ERROR;
+ if (it->idx >= it->total) return CFREE_ITER_END;
+
+ r = obj_reloc_at(it->file->ob, it->idx++);
+ out->section =
+ r->section_id ? (CfreeObjSection)(r->section_id - 1) : CFREE_SECTION_NONE;
+ out->offset = r->offset;
+ out->addend = r->addend;
+ out->kind.arch = it->file->target.arch;
+ out->kind.obj_fmt = it->file->fmt;
+ out->kind.code = (uint32_t)r->kind;
+ out->kind_name = NULL;
+
+ if (r->sym == OBJ_SYM_NONE) {
+ out->sym = CFREE_OBJ_SYMBOL_NONE;
+ out->sym_name = "";
+ } else {
+ out->sym = (CfreeObjSymbol)r->sym;
+ sym = obj_symbol_get(it->file->ob, r->sym);
+ out->sym_name = (sym && sym->name)
+ ? pool_str(it->file->compiler.global, sym->name, NULL)
+ : "";
+ }
+ return CFREE_ITER_ITEM;
+}
+
+void cfree_obj_reliter_free(CfreeObjRelocIter* it) {
+ Heap* h;
+ if (!it) return;
+ h = it->file->ctx->heap;
+ h->free(h, it, sizeof(*it));
+}
+
+/* Accessor for disasm/jit to access the underlying ObjBuilder when both
+ * are inside libcfree. Not part of the public API. */
+ObjBuilder* cfree_objfile_builder(const CfreeObjFile* f) {
+ return f ? f->ob : NULL;
+}
+
+/* Allocate an empty CfreeObjFile wrapping a private Compiler and a fresh
+ * ObjBuilder. Used by the JIT debug-view builder (src/link/link_jit.c)
+ * to assemble a synthetic object file from merged input debug sections.
+ * The handle is freed via cfree_objfile_internal_free below — the public
+ * cfree_obj_free path is keyed off obj_read_bytes, so the view path uses
+ * its own teardown that does not depend on the cached section data
+ * tables. */
+CfreeObjFile* cfree_objfile_internal_new(const CfreeContext* ctx,
+ CfreeTarget target, CfreeObjFmt fmt) {
+ Heap* h;
+ CfreeObjFile* f;
+ if (!ctx || !ctx->heap) return NULL;
+ h = ctx->heap;
+ f = (CfreeObjFile*)h->alloc(h, sizeof(*f), _Alignof(CfreeObjFile));
+ if (!f) return NULL;
+ memset(f, 0, sizeof(*f));
+ f->ctx = ctx;
+ f->fmt = fmt;
+ f->target = target;
+ compiler_init(&f->compiler, target, ctx);
+ if (setjmp(f->compiler.panic)) {
+ compiler_run_cleanups(&f->compiler);
+ compiler_fini(&f->compiler);
+ h->free(h, f, sizeof(*f));
+ return NULL;
+ }
+ f->ob = obj_new(&f->compiler);
+ if (!f->ob) {
+ compiler_fini(&f->compiler);
+ h->free(h, f, sizeof(*f));
+ return NULL;
+ }
+ return f;
+}
+
+void cfree_objfile_internal_free(CfreeObjFile* f) {
+ /* Same teardown contract as cfree_obj_free: caller may have cached
+ * section data, so we route through the same path. */
+ cfree_obj_free(f);
+}
diff --git a/src/api/pipeline.c b/src/api/pipeline.c
@@ -1,34 +1,18 @@
-/* libcfree's top-level API surface (compile/link/jit/run + the
- * CfreeCompiler lifecycle). The public declarations live under
- * <cfree.h>; this file is the single composition point that owns the
- * setjmp boundary, glues the subsystems together, and converts host inputs
- * (bytes, paths, writers) into work for the internal pipeline. */
+/* libcfree's top-level compile entry points. Status-returning shapes
+ * that drive the C, asm, and registered-frontend paths. */
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
#include "arch/arch.h"
#include "asm/asm.h"
#include "core/arena.h"
+#include "core/core.h"
#include "core/heap.h"
#include "core/metrics.h"
#include "core/pool.h"
-#include "link/link.h"
#include "obj/obj.h"
-/* CfreeCompiler lifecycle (cfree_compiler_new / cfree_compiler_free)
- * lives in src/api/lifecycle.c so consumers that only need lifecycle
- * + read paths don't drag the full compile/link pipeline through ld.
- *
- * Binary/target detection (cfree_detect_fmt / cfree_detect_target) lives
- * in src/api/detect.c for the same reason.
- *
- * In-memory writer (cfree_writer_mem / cfree_writer_mem_bytes) lives in
- * src/api/writer_mem.c. */
-
-/* ============================================================
- * Helpers
- * ============================================================ */
-
static SrcLoc no_loc(void) {
SrcLoc loc;
loc.file_id = 0;
@@ -41,30 +25,76 @@ static _Noreturn void panic_bad_options(Compiler* c, const char* msg) {
compiler_panic(c, no_loc(), "bad cfree options: %s", msg);
}
-/* ============================================================
- * Compile one TU
- * ============================================================ */
+CfreeLanguage cfree_language_for_path(const char* path) {
+ size_t i, len;
+ if (!path) return CFREE_LANG_C;
+ for (len = 0; path[len]; ++len) {
+ }
+ i = len;
+ while (i > 0) {
+ --i;
+ if (path[i] == '/') return CFREE_LANG_C;
+ if (path[i] == '.') {
+ const char* ext = path + i + 1;
+ if (ext[0] == 's' && ext[1] == '\0') return CFREE_LANG_ASM;
+ if (ext[0] == 't' && ext[1] == 'o' && ext[2] == 'y' && ext[3] == '\0')
+ return CFREE_LANG_TOY;
+ if (ext[0] == 'w' && ext[1] == 'a' && ext[2] == 't' && ext[3] == '\0')
+ return CFREE_LANG_WASM;
+ if (ext[0] == 'w' && ext[1] == 'a' && ext[2] == 's' && ext[3] == 'm' &&
+ ext[4] == '\0')
+ return CFREE_LANG_WASM;
+ return CFREE_LANG_C;
+ }
+ }
+ return CFREE_LANG_C;
+}
+
+static void validate_bytes(Compiler* c, const CfreeBytes* in) {
+ if (!in->name) panic_bad_options(c, "input name is NULL");
+ if (!in->data && in->len != 0) {
+ panic_bad_options(c, "input data is NULL but len > 0");
+ }
+}
-/* One-TU compile against a fresh ObjBuilder. The builder is finalized on
- * exit so it is immediately consumable by the linker or an emit_* function.
- * The input bytes must outlive this call. Registered language frontends own
- * their compile path; ASM remains the built-in fallback and feeds tokens
- * straight to the assembler. */
-static void compile_into(Compiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, ObjBuilder* ob) {
+static void emit_object_bytes(Compiler* c, ObjBuilder* ob, Writer* w) {
+ switch (c->target.obj) {
+ case CFREE_OBJ_ELF:
+ emit_elf(c, ob, w);
+ break;
+ case CFREE_OBJ_COFF:
+ emit_coff(c, ob, w);
+ break;
+ case CFREE_OBJ_MACHO:
+ emit_macho(c, ob, w);
+ break;
+ case CFREE_OBJ_WASM:
+ emit_wasm(c, ob, w);
+ break;
+ }
+}
+
+/* Run the source-input-shaped path. */
+static void compile_source_into(Compiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ ObjBuilder* ob) {
CfreeCompileFn frontend = NULL;
AsmLexer* lex;
MCEmitter* mc;
- if (input->lang < CFREE_LANG_COUNT) {
+ if ((unsigned)input->lang < CFREE_LANG_COUNT) {
frontend = c->frontends[input->lang];
}
if (frontend) {
+ CfreeStatus st;
metrics_scope_begin(c, "compile.frontend");
- if (frontend(c, opts, input, ob) != 0) {
- compiler_panic(c, no_loc(), "frontend failed for input: %s", input->name);
- }
+ st = frontend(c, opts, input, ob);
metrics_scope_end(c, "compile.frontend");
+ if (st != CFREE_OK) {
+ compiler_panic(c, no_loc(), "frontend failed for input: %s",
+ input->bytes.name);
+ }
metrics_scope_begin(c, "compile.obj_finalize");
obj_finalize(ob);
metrics_scope_end(c, "compile.obj_finalize");
@@ -75,12 +105,12 @@ static void compile_into(Compiler* c, const CfreeCompileOptions* opts,
if (input->lang == CFREE_LANG_ASM) {
metrics_scope_begin(c, "compile.asm.lex_open");
- lex = asm_lex_open_mem(c, input->name, (const char*)input->data, input->len);
+ lex = asm_lex_open_mem(c, input->bytes.name, (const char*)input->bytes.data,
+ input->bytes.len);
metrics_scope_end(c, "compile.asm.lex_open");
metrics_scope_begin(c, "compile.asm.mc_new");
mc = mc_new(c, ob);
metrics_scope_end(c, "compile.asm.mc_new");
- /* Asm-irrelevant fields on opts (pp, opt_level) are ignored. */
metrics_scope_begin(c, "compile.asm.parse");
asm_parse(c, lex, mc);
metrics_scope_end(c, "compile.asm.parse");
@@ -92,1036 +122,230 @@ static void compile_into(Compiler* c, const CfreeCompileOptions* opts,
metrics_scope_begin(c, "compile.asm.mc_free");
mc_free(mc);
metrics_scope_end(c, "compile.asm.mc_free");
- /* The assembler owns the lexer it was handed; no pp_free release. */
return;
}
- compiler_panic(c, no_loc(), "no frontend registered for input language: %u",
+ compiler_panic(c, no_loc(), "no frontend registered for language: %u",
(u32)input->lang);
}
-/* Suffix-based language inference. See header. */
-CfreeLanguage cfree_language_for_path(const char* path) {
- size_t i, len;
- if (!path) return CFREE_LANG_C;
- for (len = 0; path[len]; ++len) {
- }
- /* Find the last '.' after the last '/'. */
- i = len;
- while (i > 0) {
- --i;
- if (path[i] == '/') return CFREE_LANG_C;
- if (path[i] == '.') {
- const char* ext = path + i + 1;
- if (ext[0] == 's' && ext[1] == '\0') return CFREE_LANG_ASM;
- if (ext[0] == 't' && ext[1] == 'o' && ext[2] == 'y' && ext[3] == '\0')
- return CFREE_LANG_TOY;
- if (ext[0] == 'w' && ext[1] == 'a' && ext[2] == 't' && ext[3] == '\0')
- return CFREE_LANG_WASM;
- if (ext[0] == 'w' && ext[1] == 'a' && ext[2] == 's' &&
- ext[3] == 'm' && ext[4] == '\0')
- return CFREE_LANG_WASM;
- return CFREE_LANG_C;
- }
+/* ============================================================
+ * C
+ * ============================================================ */
+
+static CfreeStatus compile_c_into(Compiler* c,
+ const CfreeCCompileOptions* opts,
+ const CfreeBytes* input, ObjBuilder* ob) {
+ CfreeFrontendCompileOptions fe;
+ CfreeSourceInput si;
+ CfreeCompileFn frontend;
+
+ frontend = c->frontends[CFREE_LANG_C];
+ if (!frontend) {
+ compiler_panic(c, no_loc(), "no C frontend registered");
}
- return CFREE_LANG_C;
-}
-static void validate_bytes_input(Compiler* c, const CfreeBytesInput* in) {
- if (!in->name) panic_bad_options(c, "input name is NULL");
- if (!in->data && in->len != 0) {
- panic_bad_options(c, "input data is NULL but len > 0");
+ fe.code = opts->code;
+ fe.diagnostics = opts->diagnostics;
+ fe.language_options = opts;
+ si.bytes = *input;
+ si.lang = CFREE_LANG_C;
+
+ metrics_scope_begin(c, "compile.frontend");
+ CfreeStatus st = frontend(c, &fe, &si, ob);
+ metrics_scope_end(c, "compile.frontend");
+ if (st != CFREE_OK) {
+ compiler_panic(c, no_loc(), "C frontend failed for input: %s", input->name);
}
+ metrics_scope_begin(c, "compile.obj_finalize");
+ obj_finalize(ob);
+ metrics_scope_end(c, "compile.obj_finalize");
+ return CFREE_OK;
}
-int cfree_compile_obj(CfreeCompiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, CfreeObjBuilder** out) {
+CfreeStatus cfree_compile_c_obj(CfreeCompiler* c,
+ const CfreeCCompileOptions* opts,
+ const CfreeBytes* input,
+ CfreeObjBuilder** out) {
PanicSave saved;
ObjBuilder* ob;
- if (!out) {
- return 1;
- }
+ if (!out) return CFREE_INVALID;
*out = NULL;
+ if (!c || !opts || !input) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
+ return CFREE_ERR;
}
- if (!opts || !input) {
- panic_bad_options(c, "compile options or input is NULL");
- }
- validate_bytes_input(c, input);
+ validate_bytes(c, input);
ob = obj_new(c);
metrics_scope_begin(c, "compile.tu");
metrics_count(c, "compile.input_bytes", (u64)input->len);
- compile_into(c, opts, input, ob);
+ compile_c_into(c, opts, input, ob);
metrics_scope_end(c, "compile.tu");
*out = ob;
compiler_panic_restore(c, &saved);
- return 0;
+ return CFREE_OK;
}
-static void emit_object_bytes(Compiler* c, ObjBuilder* ob, Writer* w) {
- switch (c->target.obj) {
- case CFREE_OBJ_ELF:
- emit_elf(c, ob, w);
- break;
- case CFREE_OBJ_COFF:
- emit_coff(c, ob, w);
- break;
- case CFREE_OBJ_MACHO:
- emit_macho(c, ob, w);
- break;
- case CFREE_OBJ_WASM:
- emit_wasm(c, ob, w);
- break;
- }
-}
-
-int cfree_compile_obj_emit(CfreeCompiler* c, const CfreeCompileOptions* opts,
- const CfreeBytesInput* input, CfreeWriter* out) {
+CfreeStatus cfree_compile_c_obj_emit(CfreeCompiler* c,
+ const CfreeCCompileOptions* opts,
+ const CfreeBytes* input,
+ CfreeWriter* out) {
PanicSave saved;
ObjBuilder* ob;
+ if (!c || !opts || !input || !out) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
- }
- if (!opts || !input || !out) {
- panic_bad_options(c, "compile_emit args missing");
+ return CFREE_ERR;
}
- validate_bytes_input(c, input);
+ validate_bytes(c, input);
ob = obj_new(c);
metrics_scope_begin(c, "compile.tu");
metrics_count(c, "compile.input_bytes", (u64)input->len);
- compile_into(c, opts, input, ob);
- metrics_scope_end(c, "compile.tu");
+ compile_c_into(c, opts, input, ob);
emit_object_bytes(c, ob, out);
+ metrics_scope_end(c, "compile.tu");
obj_free(ob);
compiler_panic_restore(c, &saved);
- return 0;
+ return CFREE_OK;
}
/* ============================================================
- * Link
+ * Asm
* ============================================================ */
-static Linker* build_linker(Compiler* c, const CfreeLinkInputs* in) {
- Linker* linker = link_new(c);
- u32 i;
-
- for (i = 0; i < in->nobjs; ++i) {
- link_add_obj(linker, in->objs[i]);
- }
- for (i = 0; i < in->nobj_bytes; ++i) {
- link_add_obj_bytes(linker, in->obj_bytes[i].name, in->obj_bytes[i].data,
- in->obj_bytes[i].len);
- }
- for (i = 0; i < in->ndso_bytes; ++i) {
- link_add_dso_bytes(linker, in->dso_bytes[i].name, in->dso_bytes[i].data,
- in->dso_bytes[i].len);
- }
- for (i = 0; i < in->narchives; ++i) {
- const CfreeBytesInputArchive* a = &in->archives[i];
- link_add_archive_bytes(linker, a->input.name, a->input.data, a->input.len,
- a->whole_archive, a->link_mode, a->group_id);
- }
- if (in->linker_script) {
- link_set_script(linker, in->linker_script);
- }
- if (in->entry) {
- link_set_entry(linker, in->entry);
- }
- if (in->extern_resolver) {
- link_set_extern_resolver(linker, in->extern_resolver,
- in->extern_resolver_user);
- }
- return linker;
-}
-
-int cfree_link_exe(CfreeCompiler* c, const CfreeLinkOptions* opts,
- CfreeWriter* out) {
+static void compile_asm_into(Compiler* c, const CfreeAsmCompileOptions* opts,
+ const CfreeBytes* input, ObjBuilder* ob) {
+ AsmLexer* lex;
+ MCEmitter* mc;
+ (void)opts;
+ metrics_scope_begin(c, "compile.asm.lex_open");
+ lex = asm_lex_open_mem(c, input->name, (const char*)input->data, input->len);
+ metrics_scope_end(c, "compile.asm.lex_open");
+ metrics_scope_begin(c, "compile.asm.mc_new");
+ mc = mc_new(c, ob);
+ metrics_scope_end(c, "compile.asm.mc_new");
+ metrics_scope_begin(c, "compile.asm.parse");
+ asm_parse(c, lex, mc);
+ metrics_scope_end(c, "compile.asm.parse");
+ metrics_scope_begin(c, "compile.obj_finalize");
+ obj_finalize(ob);
+ metrics_scope_end(c, "compile.obj_finalize");
+ metrics_scope_begin(c, "compile.asm.mc_free");
+ mc_free(mc);
+ metrics_scope_end(c, "compile.asm.mc_free");
+}
+
+CfreeStatus cfree_compile_asm_obj(CfreeCompiler* c,
+ const CfreeAsmCompileOptions* opts,
+ const CfreeBytes* input,
+ CfreeObjBuilder** out) {
PanicSave saved;
- Linker* linker;
- LinkImage* image;
+ ObjBuilder* ob;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!c || !opts || !input) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
- }
- if (!opts || !out) {
- panic_bad_options(c, "link_exe args missing");
+ return CFREE_ERR;
}
- linker = build_linker(c, &opts->inputs);
- link_set_gc_sections(linker, opts->gc_sections);
- link_set_emit_static_exe(linker, 1);
- /* PIE / dynamic-exe (Phase 4 + 6). Triggered by an explicit `pie`
- * flag or by the presence of any DSO input — both shapes need
- * PT_INTERP / PT_DYNAMIC and the synthetic .dynsym machinery.
- *
- * Mach-O has its own dyld machinery (LC_LOAD_DYLIB / chained fixups
- * synthesized in link_emit_macho), so we leave emit_pie off there —
- * layout_dyn is ELF-shaped and would generate spurious PLT/GOT
- * sections the Mach-O writer doesn't know what to do with. */
- if ((opts->pie || opts->inputs.ndso_bytes > 0) &&
- c->target.obj != CFREE_OBJ_MACHO) {
- link_set_pie(linker, 1);
- link_set_interp_path(linker, opts->interp_path);
- }
- image = link_resolve(linker); /* deferred-cleanup-registered */
- link_emit_image_writer(image, out);
- link_image_free(image); /* undefers + frees */
- link_free(linker);
+ validate_bytes(c, input);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->len);
+ compile_asm_into(c, opts, input, ob);
+ metrics_scope_end(c, "compile.tu");
+ *out = ob;
compiler_panic_restore(c, &saved);
- return 0;
+ return CFREE_OK;
}
-/* Shared-library link: not yet implemented in src/. The header API and
- * driver glue are in place ahead of the codegen so callers can wire
- * -shared / -soname / -rpath end-to-end; this entry currently reports
- * a diagnostic and fails. */
-int cfree_link_shared(CfreeCompiler* c, const CfreeLinkSharedOptions* opts,
- CfreeWriter* out) {
+CfreeStatus cfree_compile_asm_obj_emit(CfreeCompiler* c,
+ const CfreeAsmCompileOptions* opts,
+ const CfreeBytes* input,
+ CfreeWriter* out) {
PanicSave saved;
-
+ ObjBuilder* ob;
+ if (!c || !opts || !input || !out) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
- }
- if (!opts || !out) {
- panic_bad_options(c, "link_shared args missing");
+ return CFREE_ERR;
}
- compiler_panic(c, no_loc(),
- "cfree_link_shared: shared-library codegen is not yet "
- "implemented in libcfree");
- /* unreachable */
+ validate_bytes(c, input);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->len);
+ compile_asm_into(c, opts, input, ob);
+ emit_object_bytes(c, ob, out);
+ metrics_scope_end(c, "compile.tu");
+ obj_free(ob);
compiler_panic_restore(c, &saved);
- return 1;
+ return CFREE_OK;
}
-int cfree_link_jit(CfreeCompiler* c, const CfreeLinkOptions* opts,
- CfreeJit** out_jit) {
+/* ============================================================
+ * Source (registered frontend)
+ * ============================================================ */
+
+CfreeStatus cfree_compile_source_obj(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder** out) {
PanicSave saved;
- Linker* linker;
- LinkImage* image;
+ ObjBuilder* ob;
- if (!out_jit) {
- return 1;
- }
- *out_jit = NULL;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!c || !opts || !input) return CFREE_INVALID;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
- }
- if (!opts) {
- panic_bad_options(c, "link_jit options missing");
+ return CFREE_ERR;
}
- linker = build_linker(c, &opts->inputs);
- link_set_gc_sections(linker, opts->gc_sections);
- link_set_jit_mode(linker, 1);
- metrics_scope_begin(c, "link_jit.all");
- image = link_resolve(linker); /* deferred-cleanup-registered */
- *out_jit = cfree_jit_from_image(image); /* undefers + transfers ownership */
- metrics_scope_end(c, "link_jit.all");
- /* cfree_jit_from_image keeps the Linker alive for append-only JIT growth. */
+ validate_bytes(c, &input->bytes);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->bytes.len);
+ compile_source_into(c, opts, input, ob);
+ metrics_scope_end(c, "compile.tu");
+ *out = ob;
compiler_panic_restore(c, &saved);
- return 0;
-}
-
-/* ============================================================
- * CfreePipeline (public)
- * ============================================================
- * Thin owning wrapper over CfreeCompiler. Holds the heap pointer used at
- * allocation so cfree_pipeline_free can release the wrapper without
- * touching the (already-finalized) compiler's env. */
-
-struct CfreePipeline {
- CfreeCompiler* compiler;
- Heap* heap;
-};
-
-CfreePipeline* cfree_pipeline_new(CfreeTarget target, const CfreeEnv* env) {
- Heap* h;
- CfreePipeline* p;
- CfreeCompiler* c;
-
- if (!env || !env->heap) return NULL;
- h = env->heap;
- p = h->alloc(h, sizeof(*p), _Alignof(CfreePipeline));
- if (!p) return NULL;
- c = cfree_compiler_new(target, env);
- if (!c) {
- h->free(h, p, sizeof(*p));
- return NULL;
- }
- p->compiler = c;
- p->heap = h;
- return p;
-}
-
-void cfree_pipeline_free(CfreePipeline* p) {
- Heap* h;
- if (!p) return;
- h = p->heap;
- cfree_compiler_free(p->compiler);
- h->free(h, p, sizeof(*p));
-}
-
-CfreeCompiler* cfree_pipeline_compiler(CfreePipeline* p) {
- return p ? p->compiler : NULL;
-}
-
-int cfree_pipeline_compile_obj(CfreePipeline* p,
- const CfreeCompileOptions* opts,
- const CfreeBytesInput* input,
- CfreeObjBuilder** out) {
- if (!p) return 1;
- return cfree_compile_obj(p->compiler, opts, input, out);
-}
-
-int cfree_pipeline_link_exe(CfreePipeline* p, const CfreeLinkOptions* opts,
- CfreeWriter* out) {
- if (!p) return 1;
- return cfree_link_exe(p->compiler, opts, out);
-}
-
-int cfree_pipeline_link_shared(CfreePipeline* p,
- const CfreeLinkSharedOptions* opts,
- CfreeWriter* out) {
- if (!p) return 1;
- return cfree_link_shared(p->compiler, opts, out);
-}
-
-int cfree_pipeline_link_jit(CfreePipeline* p, const CfreeLinkOptions* opts,
- CfreeJit** out_jit) {
- if (!p) return 1;
- return cfree_link_jit(p->compiler, opts, out_jit);
-}
-
-/* Binary format / target detection (cfree_detect_fmt and cfree_detect_target,
- * plus their detect_* helpers) live in src/api/detect.c — pure byte parsing,
- * no internal-libcfree dependencies, kept separate so consumers that only
- * detect inputs (e.g. cfree-roundtrip tests) don't drag this TU's
- * C frontend/cg/etc. dependencies in through the linker. */
-
-#if 0 /* moved to src/api/detect.c */
-CfreeBinFmt cfree_detect_fmt(const uint8_t* data, size_t len)
-{
- u32 m;
- u16 coff_machine;
-
- if (len >= 8 &&
- data[0] == '!' && data[1] == '<' && data[2] == 'a' && data[3] == 'r' &&
- data[4] == 'c' && data[5] == 'h' && data[6] == '>' && data[7] == '\n') {
- return CFREE_BIN_AR;
- }
- if (len >= 4 &&
- data[0] == 0x7f && data[1] == 'E' && data[2] == 'L' && data[3] == 'F') {
- return CFREE_BIN_ELF;
- }
- if (len >= 4 &&
- data[0] == 0x00 && data[1] == 'a' && data[2] == 's' && data[3] == 'm') {
- return CFREE_BIN_WASM;
- }
- if (len >= 4) {
- m = (u32)data[0] | ((u32)data[1] << 8) |
- ((u32)data[2] << 16) | ((u32)data[3] << 24);
- if (m == 0xFEEDFACEu || m == 0xFEEDFACFu ||
- m == 0xCEFAEDFEu || m == 0xCFFAEDFEu ||
- m == 0xCAFEBABEu) {
- return CFREE_BIN_MACHO;
- }
- }
- /* PE: MZ header — must come before COFF machine-type check since 0x5A4D
- * ('MZ') does not collide with known COFF machine types, but checking
- * explicitly avoids any future ambiguity. */
- if (len >= 2 && data[0] == 'M' && data[1] == 'Z') {
- return CFREE_BIN_PE;
- }
- /* COFF relocatable object: first 2 bytes are the machine type. */
- if (len >= 2) {
- coff_machine = (u16)data[0] | ((u16)data[1] << 8);
- switch (coff_machine) {
- case 0x8664: /* AMD64 */
- case 0x014C: /* I386 */
- case 0xAA64: /* ARM64 */
- case 0x01C4: /* ARMNT */
- case 0x5032: /* RISCV32 */
- case 0x5064: /* RISCV64 */
- return CFREE_BIN_COFF;
- }
- }
- return CFREE_BIN_UNKNOWN;
-}
-
-/* ============================================================
- * Target detection from object headers
- * ============================================================ */
-
-static void detect_target_defaults(CfreeTarget* t)
-{
- t->big_endian = 0;
- t->pic = CFREE_PIC_NONE;
- t->code_model = CFREE_CM_DEFAULT;
-}
-
-static void detect_set_ptr(CfreeTarget* t, CfreeArchKind arch)
-{
- t->arch = arch;
- switch (arch) {
- case CFREE_ARCH_X86_64:
- case CFREE_ARCH_ARM_64:
- case CFREE_ARCH_RV64:
- t->ptr_size = 8; t->ptr_align = 8; break;
- case CFREE_ARCH_X86_32:
- case CFREE_ARCH_ARM_32:
- case CFREE_ARCH_RV32:
- case CFREE_ARCH_WASM:
- t->ptr_size = 4; t->ptr_align = 4; break;
- }
-}
-
-static int detect_elf(const u8* d, size_t len, CfreeTarget* out)
-{
- u8 ei_class, ei_data, ei_osabi;
- u16 e_machine;
- if (len < 20) return 1;
- ei_class = d[4];
- ei_data = d[5];
- ei_osabi = d[7];
- /* e_machine is at offset 18, in the file's endianness. */
- if (ei_data == 1) { /* little */
- e_machine = (u16)d[18] | ((u16)d[19] << 8);
- } else if (ei_data == 2) { /* big */
- e_machine = (u16)d[19] | ((u16)d[18] << 8);
- } else {
- return 1;
- }
-
- detect_target_defaults(out);
- out->big_endian = (ei_data == 2);
- out->obj = CFREE_OBJ_ELF;
-
- switch (e_machine) {
- case 0x03: detect_set_ptr(out, CFREE_ARCH_X86_32); break;
- case 0x3E: detect_set_ptr(out, CFREE_ARCH_X86_64); break;
- case 0x28: detect_set_ptr(out, CFREE_ARCH_ARM_32); break;
- case 0xB7: detect_set_ptr(out, CFREE_ARCH_ARM_64); break;
- case 0xF3:
- if (ei_class == 1) detect_set_ptr(out, CFREE_ARCH_RV32);
- else if (ei_class == 2) detect_set_ptr(out, CFREE_ARCH_RV64);
- else return 1;
- break;
- default: return 1;
- }
-
- /* OSABI: 0=SYSV (treat as Linux), 3=Linux. Anything else: freestanding. */
- if (ei_osabi == 0 || ei_osabi == 3) out->os = CFREE_OS_LINUX;
- else out->os = CFREE_OS_FREESTANDING;
- return 0;
-}
-
-static int detect_coff(const u8* d, size_t len, CfreeTarget* out)
-{
- u16 machine;
- if (len < 2) return 1;
- machine = (u16)d[0] | ((u16)d[1] << 8);
- detect_target_defaults(out);
- out->obj = CFREE_OBJ_COFF;
- out->os = CFREE_OS_WINDOWS;
- switch (machine) {
- case 0x8664: detect_set_ptr(out, CFREE_ARCH_X86_64); break;
- case 0x014C: detect_set_ptr(out, CFREE_ARCH_X86_32); break;
- case 0xAA64: detect_set_ptr(out, CFREE_ARCH_ARM_64); break;
- case 0x01C4: detect_set_ptr(out, CFREE_ARCH_ARM_32); break;
- case 0x5032: detect_set_ptr(out, CFREE_ARCH_RV32); break;
- case 0x5064: detect_set_ptr(out, CFREE_ARCH_RV64); break;
- default: return 1;
- }
- return 0;
-}
-
-static int detect_macho(const u8* d, size_t len, CfreeTarget* out)
-{
- u32 magic, cputype;
- int swap, is64;
- if (len < 8) return 1;
- magic = (u32)d[0] | ((u32)d[1] << 8) | ((u32)d[2] << 16) | ((u32)d[3] << 24);
- switch (magic) {
- case 0xFEEDFACEu: swap = 0; is64 = 0; break;
- case 0xFEEDFACFu: swap = 0; is64 = 1; break;
- case 0xCEFAEDFEu: swap = 1; is64 = 0; break;
- case 0xCFFAEDFEu: swap = 1; is64 = 1; break;
- default: return 1;
- }
- if (!swap) {
- cputype = (u32)d[4] | ((u32)d[5] << 8) | ((u32)d[6] << 16) | ((u32)d[7] << 24);
- } else {
- cputype = (u32)d[7] | ((u32)d[6] << 8) | ((u32)d[5] << 16) | ((u32)d[4] << 24);
- }
- detect_target_defaults(out);
- out->obj = CFREE_OBJ_MACHO;
- out->os = CFREE_OS_MACOS;
- /* CPU_TYPE: 7=x86, 0x01000007=x86_64, 12=ARM, 0x0100000C=ARM64. */
- switch (cputype) {
- case 0x00000007u: detect_set_ptr(out, CFREE_ARCH_X86_32); break;
- case 0x01000007u: detect_set_ptr(out, CFREE_ARCH_X86_64); break;
- case 0x0000000Cu: detect_set_ptr(out, CFREE_ARCH_ARM_32); break;
- case 0x0100000Cu: detect_set_ptr(out, CFREE_ARCH_ARM_64); break;
- default: return 1;
- }
- (void)is64;
- return 0;
-}
-
-int cfree_detect_target(const uint8_t* data, size_t len, CfreeTarget* out)
-{
- CfreeBinFmt bin;
- if (!data || !out) return 1;
- bin = cfree_detect_fmt(data, len);
- switch (bin) {
- case CFREE_BIN_ELF: return detect_elf (data, len, out);
- case CFREE_BIN_COFF: return detect_coff (data, len, out);
- case CFREE_BIN_MACHO: return detect_macho(data, len, out);
- case CFREE_BIN_WASM:
- detect_target_defaults(out);
- detect_set_ptr(out, CFREE_ARCH_WASM);
- out->obj = CFREE_OBJ_WASM;
- out->os = CFREE_OS_WASI;
- return 0;
- default: return 1;
- }
-}
-#endif /* moved to src/api/detect.c */
-
-static ObjBuilder* obj_read_bytes(Compiler* c, const char* name, const u8* data,
- size_t len, ObjFmt fmt) {
- switch (fmt) {
- case CFREE_OBJ_ELF:
- return read_elf(c, name, data, len);
- case CFREE_OBJ_COFF:
- return read_coff(c, name, data, len);
- case CFREE_OBJ_MACHO:
- return read_macho(c, name, data, len);
- case CFREE_OBJ_WASM:
- return read_wasm(c, name, data, len);
- }
- compiler_panic(c, no_loc(), "unknown object format: %s", name);
+ return CFREE_OK;
}
-/* ============================================================
- * Object inspection API (cfree_obj_open / query / close)
- * ============================================================ */
-
-struct CfreeObjFile {
- Compiler compiler;
+CfreeStatus cfree_compile_source_obj_emit(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeWriter* out) {
+ PanicSave saved;
ObjBuilder* ob;
- ObjFmt fmt;
- /* Lazily-flattened section bytes, indexed by 0-based section idx. NULL
- * entries mean "not yet flattened" or "BSS (no bytes)"; size_cache holds
- * the flattened length. Allocated from compiler.global on first use. */
- const u8** sec_data_cache;
- u32* sec_data_size;
- u32 sec_data_n;
-};
-
-struct CfreeObjSymIter {
- CfreeObjFile* file;
- ObjSymIter* inner;
-};
-
-CfreeObjFile* cfree_obj_open(const CfreeEnv* env,
- const CfreeBytesInput* input) {
- Heap* h;
- CfreeObjFile* f;
- CfreeTarget target;
- ObjFmt ofmt;
-
- if (!env || !env->heap || !input) return NULL;
- if (!input->data && input->len > 0) return NULL;
-
- if (cfree_detect_target(input->data, input->len, &target) != 0) return NULL;
- ofmt = target.obj;
-
- h = (Heap*)env->heap;
- f = (CfreeObjFile*)h->alloc(h, sizeof(*f), _Alignof(CfreeObjFile));
- if (!f) return NULL;
-
- compiler_init(&f->compiler, target, env);
- if (setjmp(f->compiler.panic)) {
- compiler_run_cleanups(&f->compiler);
- compiler_fini(&f->compiler);
- h->free(h, f, sizeof(*f));
- return NULL;
- }
- f->fmt = ofmt;
- f->sec_data_cache = NULL;
- f->sec_data_size = NULL;
- f->sec_data_n = 0;
- f->ob =
- obj_read_bytes(&f->compiler, input->name, input->data, input->len, ofmt);
- return f;
-}
-
-/* Internal: allocate an empty CfreeObjFile with a freshly initialized
- * private Compiler and an empty ObjBuilder. The caller populates the
- * builder via the normal obj_section/obj_write/obj_reloc/obj_finalize
- * surface and the returned CfreeObjFile is closed via cfree_obj_close
- * (which compiler_fini's the private compiler and obj_free's the
- * builder), exactly like a file produced by cfree_obj_open.
- *
- * Used by cfree_jit_view to surface the JIT's debug sections to the
- * DWARF consumer without re-emitting the image as an on-disk ELF/Mach-O
- * blob. Format (ELF / Mach-O / etc.) is the caller's choice — DWARF
- * lookup is name-based, so ELF is the natural pick. */
-CfreeObjFile* cfree_objfile_empty_new(const CfreeEnv* env, CfreeTarget target,
- CfreeObjFmt fmt) {
- Heap* h;
- CfreeObjFile* f;
- if (!env || !env->heap) return NULL;
- h = (Heap*)env->heap;
- f = (CfreeObjFile*)h->alloc(h, sizeof(*f), _Alignof(CfreeObjFile));
- if (!f) return NULL;
- compiler_init(&f->compiler, target, env);
- if (setjmp(f->compiler.panic)) {
- compiler_run_cleanups(&f->compiler);
- compiler_fini(&f->compiler);
- h->free(h, f, sizeof(*f));
- return NULL;
- }
- f->fmt = fmt;
- f->sec_data_cache = NULL;
- f->sec_data_size = NULL;
- f->sec_data_n = 0;
- f->ob = obj_new(&f->compiler);
- return f;
-}
-
-void cfree_obj_close(CfreeObjFile* f) {
- Heap* h;
- if (!f) return;
- h = (Heap*)f->compiler.env->heap;
- if (f->sec_data_cache) {
- u32 i;
- for (i = 0; i < f->sec_data_n; ++i) {
- if (f->sec_data_cache[i]) {
- h->free(h, (void*)f->sec_data_cache[i], f->sec_data_size[i]);
- }
- }
- h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * f->sec_data_n);
- h->free(h, f->sec_data_size, sizeof(*f->sec_data_size) * f->sec_data_n);
- }
- obj_free(f->ob);
- compiler_fini(&f->compiler);
- h->free(h, f, sizeof(*f));
-}
-
-CfreeObjFmt cfree_obj_fmt(const CfreeObjFile* f) { return f->fmt; }
-
-CfreeTarget cfree_obj_target(const CfreeObjFile* f) {
- return f->compiler.target;
-}
-
-uint32_t cfree_obj_nsections(const CfreeObjFile* f) {
- return obj_section_count(f->ob);
-}
-
-CfreeObjSecInfo cfree_obj_section(const CfreeObjFile* f, uint32_t idx) {
- const Section* sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
- CfreeObjSecInfo out;
- out.name =
- (sec && sec->name) ? pool_str(f->compiler.global, sec->name, NULL) : "";
- out.kind = sec ? (CfreeSecKind)sec->kind : CFREE_SEC_OTHER;
- out.flags = sec ? (uint32_t)sec->flags : 0u;
- out.size = sec ? (sec->bss_size ? sec->bss_size : sec->bytes.total) : 0u;
- out.align = (sec && sec->align > 1u) ? sec->align : 1u;
- out.entsize = sec ? sec->entsize : 0u;
- return out;
-}
-
-CfreeObjSymIter* cfree_obj_symiter_new(CfreeObjFile* f) {
- Heap* h = (Heap*)f->compiler.env->heap;
- CfreeObjSymIter* it =
- (CfreeObjSymIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeObjSymIter));
- if (!it) return NULL;
- it->file = f;
- it->inner = obj_symiter_new(f->ob);
- return it;
-}
-
-int cfree_obj_symiter_next(CfreeObjSymIter* it, CfreeObjSymInfo* out) {
- ObjSymEntry entry;
- const ObjSym* sym;
- if (!obj_symiter_next(it->inner, &entry)) return 0;
- sym = entry.sym;
- out->name =
- sym->name ? pool_str(it->file->compiler.global, sym->name, NULL) : "";
- out->bind = (CfreeSymBind)sym->bind;
- out->kind = (CfreeSymKind)sym->kind;
- out->section = sym->section_id != OBJ_SEC_NONE
- ? (uint32_t)(sym->section_id - 1)
- : CFREE_SECTION_NONE;
- out->value = sym->value;
- out->size = sym->size;
- return 1;
-}
-
-void cfree_obj_symiter_free(CfreeObjSymIter* it) {
- Heap* h;
- if (!it) return;
- obj_symiter_free(it->inner);
- h = (Heap*)it->file->compiler.env->heap;
- h->free(h, it, sizeof(*it));
-}
-
-CfreeObjBuilder* cfree_obj_builder(const CfreeObjFile* f) {
- return f ? f->ob : NULL;
-}
-
-const uint8_t* cfree_obj_section_data(const CfreeObjFile* cf, uint32_t idx,
- size_t* len_out) {
- /* Cast away const: lazy flatten cache is the only mutation, and it's
- * idempotent under the externally-observable API. */
- CfreeObjFile* f = (CfreeObjFile*)cf;
- const Section* sec;
- Heap* h;
- u32 n;
- u8* buf;
- size_t dummy;
-
- if (!len_out) len_out = &dummy;
- *len_out = 0;
- if (!f) return NULL;
-
- n = obj_section_count(f->ob);
- if (idx >= n) return NULL;
-
- sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
- if (!sec) return NULL;
- /* BSS: no in-file bytes. */
- if (sec->bss_size || sec->bytes.total == 0) return NULL;
-
- h = (Heap*)f->compiler.env->heap;
-
- /* Allocate per-section cache arrays on first call. */
- if (!f->sec_data_cache) {
- f->sec_data_cache = (const u8**)h->alloc(h, sizeof(*f->sec_data_cache) * n,
- _Alignof(const u8*));
- if (!f->sec_data_cache) return NULL;
- f->sec_data_size =
- (u32*)h->alloc(h, sizeof(*f->sec_data_size) * n, _Alignof(u32));
- if (!f->sec_data_size) {
- h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * n);
- f->sec_data_cache = NULL;
- return NULL;
- }
- {
- u32 i;
- for (i = 0; i < n; ++i) {
- f->sec_data_cache[i] = NULL;
- f->sec_data_size[i] = 0;
- }
- }
- f->sec_data_n = n;
- }
-
- if (f->sec_data_cache[idx]) {
- *len_out = f->sec_data_size[idx];
- return f->sec_data_cache[idx];
- }
-
- buf = (u8*)h->alloc(h, sec->bytes.total, 1);
- if (!buf) return NULL;
- buf_flatten(&sec->bytes, buf);
- f->sec_data_cache[idx] = buf;
- f->sec_data_size[idx] = sec->bytes.total;
- *len_out = sec->bytes.total;
- return buf;
-}
-
-/* Generic GNU-ish names for RelocKind. Per-arch ELF-style names ("R_X86_64_*",
- * "R_AARCH64_*", "R_RISCV_*") could be added later — this gives objdump -r a
- * stable label without lying about the arch. Returned pointers are static
- * string literals (lifetime is forever). */
-static const char* reloc_kind_name(u16 kind) {
- switch ((RelocKind)kind) {
- case R_NONE:
- return "R_NONE";
- case R_ABS32:
- return "R_ABS32";
- case R_ABS64:
- return "R_ABS64";
- case R_REL32:
- return "R_REL32";
- case R_REL64:
- return "R_REL64";
- case R_PC32:
- return "R_PC32";
- case R_PC64:
- return "R_PC64";
- case R_GOT32:
- return "R_GOT32";
- case R_PLT32:
- return "R_PLT32";
- case R_ARM_CALL:
- return "R_ARM_CALL";
- case R_ARM_MOVW:
- return "R_ARM_MOVW";
- case R_ARM_MOVT:
- return "R_ARM_MOVT";
- case R_ARM_B26:
- return "R_ARM_B26";
- case R_AARCH64_CALL26:
- return "R_AARCH64_CALL26";
- case R_AARCH64_JUMP26:
- return "R_AARCH64_JUMP26";
- case R_AARCH64_CONDBR19:
- return "R_AARCH64_CONDBR19";
- case R_AARCH64_TSTBR14:
- return "R_AARCH64_TSTBR14";
- case R_AARCH64_LD_PREL_LO19:
- return "R_AARCH64_LD_PREL_LO19";
- case R_AARCH64_ADR_PREL_LO21:
- return "R_AARCH64_ADR_PREL_LO21";
- case R_AARCH64_ADR_GOT_PAGE:
- return "R_AARCH64_ADR_GOT_PAGE";
- case R_AARCH64_LD64_GOT_LO12_NC:
- return "R_AARCH64_LD64_GOT_LO12_NC";
- case R_AARCH64_ADR_PREL_PG_HI21:
- return "R_AARCH64_ADR_PREL_PG_HI21";
- case R_AARCH64_ADR_PREL_PG_HI21_NC:
- return "R_AARCH64_ADR_PREL_PG_HI21_NC";
- case R_AARCH64_ADD_ABS_LO12_NC:
- return "R_AARCH64_ADD_ABS_LO12_NC";
- case R_AARCH64_ABS16:
- return "R_AARCH64_ABS16";
- case R_AARCH64_PREL16:
- return "R_AARCH64_PREL16";
- case R_AARCH64_LDST8_ABS_LO12_NC:
- return "R_AARCH64_LDST8_ABS_LO12_NC";
- case R_AARCH64_LDST16_ABS_LO12_NC:
- return "R_AARCH64_LDST16_ABS_LO12_NC";
- case R_AARCH64_LDST32_ABS_LO12_NC:
- return "R_AARCH64_LDST32_ABS_LO12_NC";
- case R_AARCH64_LDST64_ABS_LO12_NC:
- return "R_AARCH64_LDST64_ABS_LO12_NC";
- case R_AARCH64_LDST128_ABS_LO12_NC:
- return "R_AARCH64_LDST128_ABS_LO12_NC";
- case R_AARCH64_TLVP_LOAD_PAGE21:
- return "R_AARCH64_TLVP_LOAD_PAGE21";
- case R_AARCH64_TLVP_LOAD_PAGEOFF12:
- return "R_AARCH64_TLVP_LOAD_PAGEOFF12";
- case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- return "R_AARCH64_TLSLE_ADD_TPREL_HI12";
- case R_AARCH64_TLSLE_ADD_TPREL_LO12:
- return "R_AARCH64_TLSLE_ADD_TPREL_LO12";
- case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
- return "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC";
- case R_AARCH64_TLSLE_LDST8_TPREL_LO12:
- return "R_AARCH64_TLSLE_LDST8_TPREL_LO12";
- case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
- return "R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC";
- case R_AARCH64_TLSLE_LDST16_TPREL_LO12:
- return "R_AARCH64_TLSLE_LDST16_TPREL_LO12";
- case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- return "R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC";
- case R_AARCH64_TLSLE_LDST32_TPREL_LO12:
- return "R_AARCH64_TLSLE_LDST32_TPREL_LO12";
- case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
- return "R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC";
- case R_AARCH64_TLSLE_LDST64_TPREL_LO12:
- return "R_AARCH64_TLSLE_LDST64_TPREL_LO12";
- case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
- return "R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC";
- case R_AARCH64_GLOB_DAT:
- return "R_AARCH64_GLOB_DAT";
- case R_AARCH64_JUMP_SLOT:
- return "R_AARCH64_JUMP_SLOT";
- case R_AARCH64_RELATIVE:
- return "R_AARCH64_RELATIVE";
- case R_AARCH64_COPY:
- return "R_AARCH64_COPY";
- case R_X64_PC8:
- return "R_X86_64_PC8";
- case R_X64_32S:
- return "R_X86_64_32S";
- case R_X64_PLT32:
- return "R_X86_64_PLT32";
- case R_X64_GOTPCREL:
- return "R_X86_64_GOTPCREL";
- case R_X64_GOTPCRELX:
- return "R_X86_64_GOTPCRELX";
- case R_X64_REX_GOTPCRELX:
- return "R_X86_64_REX_GOTPCRELX";
- case R_X64_GOTPC32:
- return "R_X86_64_GOTPC32";
- case R_X64_GOTOFF64:
- return "R_X86_64_GOTOFF64";
- case R_X64_TPOFF32:
- return "R_X86_64_TPOFF32";
- case R_X64_TPOFF64:
- return "R_X86_64_TPOFF64";
- case R_X64_DTPOFF32:
- return "R_X86_64_DTPOFF32";
- case R_X64_DTPMOD64:
- return "R_X86_64_DTPMOD64";
- case R_X64_DTPOFF64:
- return "R_X86_64_DTPOFF64";
- case R_X64_TLSGD:
- return "R_X86_64_TLSGD";
- case R_X64_TLSLD:
- return "R_X86_64_TLSLD";
- case R_X64_GOTTPOFF:
- return "R_X86_64_GOTTPOFF";
- case R_X64_GLOB_DAT:
- return "R_X86_64_GLOB_DAT";
- case R_X64_JUMP_SLOT:
- return "R_X86_64_JUMP_SLOT";
- case R_X64_RELATIVE:
- return "R_X86_64_RELATIVE";
- case R_X64_COPY:
- return "R_X86_64_COPY";
- case R_RV_HI20:
- return "R_RISCV_HI20";
- case R_RV_LO12_I:
- return "R_RISCV_LO12_I";
- case R_RV_LO12_S:
- return "R_RISCV_LO12_S";
- case R_RV_BRANCH:
- return "R_RISCV_BRANCH";
- case R_RV_JAL:
- return "R_RISCV_JAL";
- case R_RV_CALL:
- return "R_RISCV_CALL";
- case R_RV_PCREL_HI20:
- return "R_RISCV_PCREL_HI20";
- case R_RV_PCREL_LO12_I:
- return "R_RISCV_PCREL_LO12_I";
- case R_RV_PCREL_LO12_S:
- return "R_RISCV_PCREL_LO12_S";
- case R_RV_GOT_HI20:
- return "R_RISCV_GOT_HI20";
- case R_RV_TPREL_HI20:
- return "R_RISCV_TPREL_HI20";
- case R_RV_TPREL_LO12_I:
- return "R_RISCV_TPREL_LO12_I";
- case R_RV_TPREL_LO12_S:
- return "R_RISCV_TPREL_LO12_S";
- case R_RV_TPREL_ADD:
- return "R_RISCV_TPREL_ADD";
- case R_RV_ADD8:
- return "R_RISCV_ADD8";
- case R_RV_ADD16:
- return "R_RISCV_ADD16";
- case R_RV_ADD32:
- return "R_RISCV_ADD32";
- case R_RV_ADD64:
- return "R_RISCV_ADD64";
- case R_RV_SUB8:
- return "R_RISCV_SUB8";
- case R_RV_SUB16:
- return "R_RISCV_SUB16";
- case R_RV_SUB32:
- return "R_RISCV_SUB32";
- case R_RV_SUB64:
- return "R_RISCV_SUB64";
- case R_RV_ALIGN:
- return "R_RISCV_ALIGN";
- case R_RV_RVC_BRANCH:
- return "R_RISCV_RVC_BRANCH";
- case R_RV_RVC_JUMP:
- return "R_RISCV_RVC_JUMP";
- case R_RV_RELAX:
- return "R_RISCV_RELAX";
- case R_RV_SUB6:
- return "R_RISCV_SUB6";
- case R_RV_SET6:
- return "R_RISCV_SET6";
- case R_RV_SET8:
- return "R_RISCV_SET8";
- case R_RV_SET16:
- return "R_RISCV_SET16";
- case R_RV_SET32:
- return "R_RISCV_SET32";
- case R_RV_SET_ULEB128:
- return "R_RISCV_SET_ULEB128";
- case R_RV_SUB_ULEB128:
- return "R_RISCV_SUB_ULEB128";
- case R_WASM_FUNCIDX:
- return "R_WASM_FUNCTION_INDEX_LEB";
- case R_WASM_TABLEIDX:
- return "R_WASM_TABLE_INDEX_SLEB";
- case R_WASM_MEMOFS:
- return "R_WASM_MEMORY_ADDR_LEB";
- case R_WASM_TYPEIDX:
- return "R_WASM_TYPE_INDEX_LEB";
- }
- return "R_UNKNOWN";
-}
-
-struct CfreeObjRelocIter {
- CfreeObjFile* file;
- u32 idx; /* index into the global flat reloc array */
- u32 total; /* obj_reloc_total snapshot */
-};
-
-CfreeObjRelocIter* cfree_obj_reliter_new(CfreeObjFile* f) {
- Heap* h;
- CfreeObjRelocIter* it;
- if (!f) return NULL;
- h = (Heap*)f->compiler.env->heap;
- it =
- (CfreeObjRelocIter*)h->alloc(h, sizeof(*it), _Alignof(CfreeObjRelocIter));
- if (!it) return NULL;
- it->file = f;
- it->idx = 0;
- it->total = obj_reloc_total(f->ob);
- return it;
-}
-
-int cfree_obj_reliter_next(CfreeObjRelocIter* it, CfreeObjReloc* out) {
- const Reloc* r;
- const ObjSym* sym;
-
- if (!it || !out) return 0;
- if (it->idx >= it->total) return 0;
-
- r = obj_reloc_at(it->file->ob, it->idx++);
- /* CfreeObjReloc.section is the 0-based external section index;
- * Reloc.section_id is 1-based with id 0 reserved as "none". */
- out->section =
- r->section_id ? (uint32_t)(r->section_id - 1) : CFREE_SECTION_NONE;
- out->offset = r->offset;
- out->addend = r->addend;
- out->kind = r->kind;
- out->kind_name = reloc_kind_name(r->kind);
-
- if (r->sym == OBJ_SYM_NONE) {
- out->sym = CFREE_SECTION_NONE;
- out->sym_name = "";
- } else {
- out->sym = (uint32_t)r->sym;
- sym = obj_symbol_get(it->file->ob, r->sym);
- out->sym_name = (sym && sym->name)
- ? pool_str(it->file->compiler.global, sym->name, NULL)
- : "";
+ if (!c || !opts || !input || !out) return CFREE_INVALID;
+ compiler_panic_save(c, &saved);
+ if (setjmp(c->panic)) {
+ compiler_run_cleanups(c);
+ compiler_panic_restore(c, &saved);
+ return CFREE_ERR;
}
- return 1;
-}
-
-void cfree_obj_reliter_free(CfreeObjRelocIter* it) {
- Heap* h;
- if (!it) return;
- h = (Heap*)it->file->compiler.env->heap;
- h->free(h, it, sizeof(*it));
+ validate_bytes(c, &input->bytes);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->bytes.len);
+ compile_source_into(c, opts, input, ob);
+ emit_object_bytes(c, ob, out);
+ metrics_scope_end(c, "compile.tu");
+ obj_free(ob);
+ compiler_panic_restore(c, &saved);
+ return CFREE_OK;
}
diff --git a/src/api/stubs.c b/src/api/stubs.c
@@ -1,52 +1,14 @@
-/* Stub implementations for libcfree subsystems that are not yet wired up.
- *
- * libcfree's public surface (cfree.h) and internal pipeline (api/pipeline.c)
- * reference the full compile/link/JIT/DWARF stack. The current build only
- * implements the foundation (core, obj/elf, ar, lex). Everything else lives
- * here as a panic stub: the symbol resolves at link time but calling it
- * raises a clean diagnostic instead of jumping into an uninitialized
- * subsystem.
- *
- * As real implementations land, the matching stub block is deleted from this
- * file. The lex test path (cfree cc --dump-tokens) does not exercise any
- * stubbed entry, so the binary links and the test runs without touching
- * unimplemented code. */
+/* Stub implementations for libcfree subsystems not yet implemented. */
-#include <cfree.h>
-
-#include "arch/arch.h"
-#include "asm/asm.h"
-#include "debug/debug.h"
-#include "link/link.h"
+#include "core/core.h"
#include "obj/obj.h"
-/* Internal panic stub used when a not-yet-implemented subsystem is invoked
- * with a Compiler in hand. Public-API stubs that don't have a Compiler
- * pointer return safe defaults instead. */
static _Noreturn void unimplemented(Compiler* c, const char* what) {
SrcLoc loc = {0, 0, 0};
compiler_panic(c, loc, "subsystem not implemented: %s", what);
}
-/* C preprocessing is owned by the C frontend. */
-
-/* asm_parse lives in src/asm/asm.c. */
-
-/* mc_new / mc_free live in src/arch/mc.c.
- * cgtarget_new / cgtarget_finalize / cgtarget_free live in src/arch/<target>.c
- * (dispatched through src/arch/arch.c). */
-
-/* Optimizer (opt_cgtarget_new) lives in src/opt/opt.c. */
-
-/* Debug info producer lives in src/debug/. */
-
-/* ============================================================
- * Object emit/read for non-ELF formats
- * ============================================================
- * ELF emit/read are real (src/obj/elf_emit.c, elf_read.c). Mach-O
- * emit/read are real (src/obj/macho_emit.c, macho_read.c). COFF and
- * WASM remain stubs; callers receive a diagnostic if they ask for a
- * COFF/WASM target. */
+/* COFF / WASM emit/read remain stubs until those writers/readers land. */
void emit_coff(Compiler* c, ObjBuilder* o, Writer* w) {
(void)o;
@@ -71,36 +33,3 @@ ObjBuilder* read_wasm(Compiler* c, const char* n, const u8* d, size_t l) {
(void)l;
unimplemented(c, "read_wasm");
}
-
-/* Linker, JIT mapper, and JIT lookup live in src/link/. */
-
-/* ============================================================
- * Public API stubs
- * ============================================================
- * Public entries that don't have a Compiler in hand return safe
- * defaults and document the missing capability through the return value
- * (NULL handle / nonzero error). */
-
-/* Header-dep iterator lives in src/api/dep.c. */
-
-/* Disassembler is real (src/api/disasm.c, src/arch/disasm.c,
- * src/arch/aa64/disasm.c). Per-arch register name lookups and the
- * indexed enumeration (cfree_arch_register_count / _at) are real
- * (src/api/arch_regs.c + src/arch/aa64/regs.c). */
-
-/* Linker script parsing lives in src/link/link_script.c. */
-
-/* JIT lookup, view, addr_to_sym, and the symbol iterator live in
- * src/link/link_jit.c. */
-
-/* JIT session implementation lives in src/dbg/ (session.c, bp.c, step.c,
- * displaced.c, arch/aa64/dbg.c, mem.c). */
-
-/* DWARF consumer: the cfree_dwarf_* implementations live in src/debug/.
- * Their stubs were removed when src/debug/dwarf_*.c took ownership of
- * the symbols. */
-
-/* Emulator (cfree emu) lives under src/emu/ — cfree_emu_run / new /
- * step / lookup / free are real implementations there, with the
- * per-ISA decode/lift, CPUState, and runtime helper layers stubbed
- * one level down. */
diff --git a/src/api/writer_mem.c b/src/api/writer_mem.c
@@ -1,9 +1,6 @@
-/* In-memory CfreeWriter. Backing is a single grow-as-you-write byte
- * buffer allocated through the host CfreeHeap. seek/tell let formats
- * that need to back-patch (ELF/COFF/Mach-O) place an Ehdr/Shdr pass
- * after computing later offsets. */
+/* In-memory CfreeWriter. */
-#include <cfree.h>
+#include <cfree/core.h>
#include <string.h>
#include "core/core.h"
@@ -12,68 +9,71 @@
typedef struct MemWriter {
CfreeWriter base;
Heap* heap;
- u8* data; /* heap-allocated buffer; NULL means cap == 0 */
+ u8* data;
size_t cap;
- size_t len; /* high-water mark of valid bytes */
- size_t pos; /* current write/seek position; may equal or exceed len
- momentarily */
- int err;
+ size_t len;
+ size_t pos;
+ CfreeStatus status;
} MemWriter;
-static int mw_grow(MemWriter* mw, size_t needed) {
+static CfreeStatus mw_grow(MemWriter* mw, size_t needed) {
size_t new_cap;
u8* p;
- if (needed <= mw->cap) return 0;
+ if (needed <= mw->cap) return CFREE_OK;
new_cap = mw->cap ? mw->cap : 64;
while (new_cap < needed) {
size_t doubled = new_cap * 2;
if (doubled <= new_cap) {
- mw->err = 1;
- return 1;
+ mw->status = CFREE_NOMEM;
+ return CFREE_NOMEM;
}
new_cap = doubled;
}
p = (u8*)mw->heap->realloc(mw->heap, mw->data, mw->cap, new_cap, 1);
if (!p) {
- mw->err = 1;
- return 1;
+ mw->status = CFREE_NOMEM;
+ return CFREE_NOMEM;
}
- /* Zero-fill any newly-extended range so a subsequent seek-past-len
- * read sees deterministic zero bytes. */
if (new_cap > mw->cap) memset(p + mw->cap, 0, new_cap - mw->cap);
mw->data = p;
mw->cap = new_cap;
- return 0;
+ return CFREE_OK;
}
-static void mw_write(CfreeWriter* w, const void* data, size_t n) {
+static CfreeStatus mw_write(CfreeWriter* w, const void* data, size_t n) {
MemWriter* mw = (MemWriter*)w;
size_t end;
+ CfreeStatus st;
- if (mw->err || n == 0) return;
+ if (mw->status != CFREE_OK) return mw->status;
+ if (n == 0) return CFREE_OK;
end = mw->pos + n;
if (end < mw->pos) {
- mw->err = 1;
- return;
- } /* overflow */
- if (mw_grow(mw, end)) return;
+ mw->status = CFREE_NOMEM;
+ return CFREE_NOMEM;
+ }
+ st = mw_grow(mw, end);
+ if (st != CFREE_OK) return st;
memcpy(mw->data + mw->pos, data, n);
mw->pos = end;
if (mw->pos > mw->len) mw->len = mw->pos;
+ return CFREE_OK;
}
-static void mw_seek(CfreeWriter* w, uint64_t off) {
+static CfreeStatus mw_seek(CfreeWriter* w, uint64_t off) {
MemWriter* mw = (MemWriter*)w;
- if (mw->err) return;
- /* Allow seeking past `len`; subsequent writes extend through the
- * zero-filled tail. The cap grows on first such write. */
+ if (mw->status != CFREE_OK) return mw->status;
mw->pos = (size_t)off;
+ return CFREE_OK;
}
static uint64_t mw_tell(CfreeWriter* w) { return ((MemWriter*)w)->pos; }
-static int mw_error(CfreeWriter* w) { return ((MemWriter*)w)->err; }
+
+static CfreeStatus mw_status(CfreeWriter* w) {
+ return ((MemWriter*)w)->status;
+}
static void mw_close(CfreeWriter* w) {
MemWriter* mw = (MemWriter*)w;
@@ -82,23 +82,25 @@ static void mw_close(CfreeWriter* w) {
h->free(h, mw, sizeof(*mw));
}
-CfreeWriter* cfree_writer_mem(CfreeHeap* heap) {
+CfreeStatus cfree_writer_mem(CfreeHeap* heap, CfreeWriter** out) {
MemWriter* mw;
- if (!heap) return NULL;
+ if (!out) return CFREE_INVALID;
+ if (!heap) return CFREE_INVALID;
mw = (MemWriter*)heap->alloc(heap, sizeof(*mw), _Alignof(MemWriter));
- if (!mw) return NULL;
+ if (!mw) return CFREE_NOMEM;
mw->base.write = mw_write;
mw->base.seek = mw_seek;
mw->base.tell = mw_tell;
- mw->base.error = mw_error;
+ mw->base.status = mw_status;
mw->base.close = mw_close;
mw->heap = heap;
mw->data = NULL;
mw->cap = 0;
mw->len = 0;
mw->pos = 0;
- mw->err = 0;
- return &mw->base;
+ mw->status = CFREE_OK;
+ *out = &mw->base;
+ return CFREE_OK;
}
const uint8_t* cfree_writer_mem_bytes(CfreeWriter* w, size_t* len_out) {
diff --git a/src/arch/aa64/disasm.c b/src/arch/aa64/disasm.c
@@ -118,7 +118,7 @@ static void aa64_destroy(ArchDisasm* base) {
}
ArchDisasm* aa64_disasm_new(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
AA64Disasm* d = (AA64Disasm*)h->alloc(h, sizeof(*d), _Alignof(AA64Disasm));
if (!d) return NULL;
memset(d, 0, sizeof(*d));
diff --git a/src/arch/arch.h b/src/arch/arch.h
@@ -1,5 +1,8 @@
-#ifndef CFREE_ARCH_H
-#define CFREE_ARCH_H
+#ifndef CFREE_INTERNAL_ARCH_H
+#define CFREE_INTERNAL_ARCH_H
+
+#include <cfree/arch.h>
+#include <cfree/disasm.h>
#include "abi/abi.h"
#include "core/core.h"
diff --git a/src/asm/asm.c b/src/asm/asm.c
@@ -937,7 +937,7 @@ static Sym maybe_compose_mnemonic(AsmDriver* d, Sym head) {
* per-arch handle (`d->arch_asm`) — the caller has already opened its own
* arch asm handle to thread per-block bound state through. */
AsmDriver* asm_driver_open_inline(Compiler* c, MCEmitter* mc, AsmLexer* lex) {
- Heap* heap = (Heap*)c->env->heap;
+ Heap* heap = (Heap*)c->ctx->heap;
AsmDriver* d = (AsmDriver*)heap->alloc(heap, sizeof *d, _Alignof(AsmDriver));
memset(d, 0, sizeof *d);
d->c = c;
@@ -977,7 +977,7 @@ void asm_parse(Compiler* c, AsmLexer* l, MCEmitter* mc) {
d.mc = mc;
d.ob = mc->obj;
d.pool = c->global;
- d.heap = (Heap*)c->env->heap;
+ d.heap = (Heap*)c->ctx->heap;
d.cur_sec = OBJ_SEC_NONE;
SymSecMap_init(&d.sec_map, d.heap);
SymSymMap_init(&d.sym_map, d.heap);
diff --git a/src/asm/asm_helpers.h b/src/asm/asm_helpers.h
@@ -52,7 +52,7 @@ void asm_driver_parse_sym_expr(AsmDriver*, ObjSymId* sym_out, i64* off_out);
* line) to reuse the existing per-arch instruction parsers verbatim
* over a substituted source buffer.
*
- * The driver is heap-allocated through c->env->heap and must be released
+ * The driver is heap-allocated through c->ctx->heap and must be released
* with asm_driver_close_inline. It does not own the AsmLexer or the
* MCEmitter — the caller retains ownership of both. The driver does
* not initialize a default section; inline asm always emits into the
diff --git a/src/asm/asm_lex.c b/src/asm/asm_lex.c
@@ -119,7 +119,7 @@ static SrcLoc asm_lex_here(const AsmLexer* l) {
AsmLexer* asm_lex_open_mem(Compiler* c, const char* name, const char* src,
size_t len) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
AsmLexer* l = (AsmLexer*)h->alloc(h, sizeof(*l), _Alignof(AsmLexer));
if (!l) return NULL;
memset(l, 0, sizeof(*l));
@@ -129,7 +129,10 @@ AsmLexer* asm_lex_open_mem(Compiler* c, const char* name, const char* src,
l->src = src ? src : "";
l->len = src ? len : 0;
l->pos = 0;
- l->file_id = source_add_memory(c->sources, name);
+ if (source_add_memory(c->sources, name, &l->file_id) != CFREE_OK) {
+ h->free(h, l, sizeof(*l));
+ return NULL;
+ }
l->line = 1;
l->col = 1;
l->at_bol = 1;
diff --git a/src/core/core.c b/src/core/core.c
@@ -1,20 +1,7 @@
-/* Compiler lifecycle, panic, and cleanup-stack machinery.
- *
- * compiler_init wires up the per-Compiler allocators (Pool, tu Arena,
- * scratch Arena) from the host CfreeEnv. Subsystems that need stable
- * source identity (lexer/parser/diagnostics/DWARF) look up SourceManager
- * through Compiler.sources; that lives in src/core/source.c.
- *
- * Panic flow: compiler_panic emits the diagnostic, runs the deferred
- * cleanups, and longjmp's c->panic. Top-level entry points install a
- * setjmp boundary and use compiler_panic_save/restore to nest.
- *
- * abi is brought up here via abi_init using c->target; sizes, alignments,
- * record layouts, and call classification go through it. */
+/* Compiler lifecycle, panic, and cleanup-stack machinery. */
#include "core/core.h"
-#include <cfree.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -25,7 +12,6 @@
#include "core/heap.h"
#include "core/pool.h"
-/* Forward decls for SourceManager — implemented in source.c. */
SourceManager* source_new(Compiler*);
void source_free(SourceManager*);
@@ -35,11 +21,11 @@ struct CompilerCleanup {
CompilerCleanup* prev;
};
-void compiler_init(Compiler* c, Target target, const CfreeEnv* env) {
- Heap* h = (Heap*)env->heap;
+void compiler_init(Compiler* c, Target target, const CfreeContext* ctx) {
+ Heap* h = ctx->heap;
memset(c, 0, sizeof(*c));
- c->env = env;
+ c->ctx = ctx;
c->target = target;
c->global = (Pool*)h->alloc(h, sizeof(Pool), _Alignof(Pool));
@@ -59,11 +45,8 @@ void compiler_init(Compiler* c, Target target, const CfreeEnv* env) {
}
void compiler_fini(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = c->ctx->heap;
- /* Anything still on the cleanup stack at fini-time is a programming
- * error — every _new should have a matching _free that undefers.
- * Run the stack defensively so memory still gets released. */
compiler_run_cleanups(c);
if (c->cg_api_free) {
@@ -95,8 +78,6 @@ void compiler_fini(Compiler* c) {
CompilerCleanup* compiler_defer(Compiler* c, void (*fn)(void*), void* arg) {
CompilerCleanup* node;
- /* Cleanups live in scratch — they're bounded by pipeline depth and
- * are walked LIFO from the panic handler. */
node = (CompilerCleanup*)arena_alloc(c->scratch, sizeof(*node),
_Alignof(CompilerCleanup));
if (!node) return NULL;
@@ -108,8 +89,6 @@ CompilerCleanup* compiler_defer(Compiler* c, void (*fn)(void*), void* arg) {
}
void compiler_undefer(Compiler* c, CompilerCleanup* node) {
- /* Common case: undefer the top of stack after a successful _free.
- * Off-top removals are rare but legal; walk to splice. */
CompilerCleanup** link = &c->cleanup;
while (*link) {
if (*link == node) {
@@ -140,14 +119,12 @@ void compiler_panic(Compiler* c, SrcLoc loc, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
compiler_panicv(c, loc, fmt, ap);
- /* compiler_panicv is _Noreturn; va_end is unreachable but kept
- * for the unlikely future where it is. */
va_end(ap);
}
void compiler_panicv(Compiler* c, SrcLoc loc, const char* fmt, va_list ap) {
- if (c->env && c->env->diag && c->env->diag->emit) {
- c->env->diag->emit(c->env->diag, CFREE_DIAG_FATAL, loc, fmt, ap);
+ if (c->ctx && c->ctx->diag && c->ctx->diag->emit) {
+ c->ctx->diag->emit(c->ctx->diag, CFREE_DIAG_FATAL, loc, fmt, ap);
}
longjmp(c->panic, 1);
}
diff --git a/src/core/core.h b/src/core/core.h
@@ -1,7 +1,10 @@
#ifndef CFREE_INTERNAL_CORE_H
#define CFREE_INTERNAL_CORE_H
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/objmodel.h>
+#include <cfree/source.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
@@ -17,11 +20,9 @@ typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
-/* Internal aliases for types that also have a public Cfree-prefixed name.
- * <cfree.h> is the single source of truth for those types' identities;
- * src/ keeps its terser, plain names so the internal call sites don't
- * change. Both names refer to the same struct. */
+/* Internal aliases for types that also have a public Cfree-prefixed name. */
typedef CfreeCompiler Compiler;
+typedef CfreeContext Context;
typedef CfreeHeap Heap;
typedef CfreeDiagSink DiagSink;
typedef CfreeWriter Writer;
@@ -37,18 +38,11 @@ typedef struct Pool Pool;
typedef struct TargetABI TargetABI;
typedef struct SourceManager SourceManager;
-/* Interned string ids. 0 reserved as "none". Object and linker symbols are
- * intentionally not intern-pool concepts; see obj/obj.h and link/link.h. */
typedef u32 Sym;
-/* Binary-blob handle into the same Pool. Shares the numeric value space with
- * Sym but is a distinct typedef so callers don't accidentally mix interned
- * strings with decoded literal bytes. */
typedef u32 BytesId;
#define BYTES_NONE 0u
-/* SrcLoc is the public CfreeSrcLoc; the alias keeps internal call sites
- * terse. */
typedef CfreeSrcLoc SrcLoc;
typedef struct SrcRange {
@@ -65,9 +59,9 @@ typedef enum SourceFileKind {
typedef struct SourceFile {
u32 id;
- Sym name; /* spelling used in diagnostics */
- Sym path; /* normalized path, 0 for memory/builtin-only input */
- u8 kind; /* SourceFileKind */
+ Sym name;
+ Sym path;
+ u8 kind;
u8 system_header;
u16 pad;
} SourceFile;
@@ -81,8 +75,8 @@ typedef struct SourceInclude {
} SourceInclude;
typedef struct SourceExpansion {
- SrcLoc spelling_loc; /* where the token text came from */
- SrcLoc expansion_loc; /* where the macro expansion was requested */
+ SrcLoc spelling_loc;
+ SrcLoc expansion_loc;
Sym macro_name;
} SourceExpansion;
@@ -91,13 +85,16 @@ typedef struct SourceDepIter SourceDepIter;
SourceManager* source_new(Compiler*);
void source_free(SourceManager*);
-u32 source_add_file(SourceManager*, const char* path, int system_header);
-u32 source_add_memory(SourceManager*, const char* name);
-u32 source_add_builtin(SourceManager*, const char* name);
-void source_add_include(SourceManager*, u32 includer_file_id,
- u32 included_file_id, SrcLoc include_loc, int system);
-u32 source_add_macro_expansion(SourceManager*, Sym macro_name,
- SrcLoc spelling_loc, SrcLoc expansion_loc);
+CfreeStatus source_add_file(SourceManager*, const char* path, int system_header,
+ u32* id_out);
+CfreeStatus source_add_memory(SourceManager*, const char* name, u32* id_out);
+CfreeStatus source_add_builtin(SourceManager*, const char* name, u32* id_out);
+CfreeStatus source_add_include(SourceManager*, u32 includer_file_id,
+ u32 included_file_id, SrcLoc include_loc,
+ int system);
+CfreeStatus source_add_macro_expansion(SourceManager*, Sym macro_name,
+ SrcLoc spelling_loc,
+ SrcLoc expansion_loc, u32* id_out);
const SourceFile* source_file(SourceManager*, u32 file_id);
const SourceExpansion* source_expansion(SourceManager*, u32 expansion_file_id);
@@ -108,49 +105,33 @@ SourceDepIter* source_depiter_new(SourceManager*);
const SourceInclude* source_depiter_next(SourceDepIter*);
void source_depiter_free(SourceDepIter*);
-/* compiler_defer registers a cleanup that runs LIFO from cfree_run's panic
- * boundary (or any caller that establishes its own setjmp). Each subsystem
- * _new registers its matching _free; the matched _free calls compiler_undefer
- * with the returned handle. The cleanup stack lives in scratch arena and is
- * bounded by pipeline depth (~10 entries). */
typedef struct CompilerCleanup CompilerCleanup;
struct CfreeCompiler {
jmp_buf panic;
- const CfreeEnv* env;
+ const CfreeContext* ctx;
Pool* global;
Arena* tu;
Arena* scratch;
SourceManager* sources;
TargetABI* abi;
Target target;
- CompilerCleanup* cleanup; /* top of LIFO cleanup stack */
+ CompilerCleanup* cleanup;
CfreeCompileFn frontends[CFREE_LANG_COUNT];
- void* cg_api; /* public cfree/cg.h adapter state */
+ void* cg_api;
void (*cg_api_free)(Compiler*);
};
-void compiler_init(Compiler*, Target, const CfreeEnv*);
+void compiler_init(Compiler*, Target, const CfreeContext*);
void compiler_fini(Compiler*);
-/* Cleanup stack. compiler_defer returns an opaque handle; compiler_undefer
- * removes the entry without running it (use after a successful _free).
- * compiler_run_cleanups runs everything LIFO, used by the panic handler. */
CompilerCleanup* compiler_defer(Compiler*, void (*fn)(void*), void* arg);
void compiler_undefer(Compiler*, CompilerCleanup*);
void compiler_run_cleanups(Compiler*);
-/* Emits a diagnostic and longjmps c->panic. Used by parse/cg/arch fatal paths.
- */
_Noreturn void compiler_panic(Compiler*, SrcLoc, const char* fmt, ...);
_Noreturn void compiler_panicv(Compiler*, SrcLoc, const char* fmt, va_list);
-/* Save/restore the panic jmp_buf so layered APIs can nest. Each top-level
- * driver function (cfree_compile_obj, cfree_link_*, ...) saves c->panic,
- * installs its own setjmp, and restores on every exit path — both the
- * panic-return path (after compiler_run_cleanups) and the success path.
- * Without this, an inner setjmp clobbers an outer setjmp's jmp_buf, and a
- * subsequent compiler_panic by the outer caller longjmps to a dead frame. */
typedef struct PanicSave {
jmp_buf buf;
} PanicSave;
diff --git a/src/core/hashmap.h b/src/core/hashmap.h
@@ -1,7 +1,7 @@
#ifndef CFREE_INTERNAL_HASHMAP_H
#define CFREE_INTERNAL_HASHMAP_H
-#include <cfree/hashmap.h>
+#include <cfree/support/hashmap.h>
#include "core/core.h"
diff --git a/src/core/metrics.h b/src/core/metrics.h
@@ -4,7 +4,7 @@
#include "core/core.h"
static inline const CfreeMetrics* metrics_sink(Compiler* c) {
- return (c && c->env) ? c->env->metrics : NULL;
+ return (c && c->ctx) ? c->ctx->metrics : NULL;
}
static inline void metrics_scope_begin(Compiler* c, const char* name) {
diff --git a/src/core/source.c b/src/core/source.c
@@ -1,10 +1,5 @@
/* SourceManager — file-id authority for diagnostics, dependency output,
- * and DWARF. The C frontend subsystems aren't part of the obj/ELF
- * foundation, so this implementation is minimal: it stores a flat array
- * of registered files and the include-edge list, and exposes lookups.
- * It does not yet support macro-expansion pseudo files or
- * spelling/expansion location translation; those land with the
- * preprocessor. */
+ * and DWARF. */
#include <stdlib.h>
#include <string.h>
@@ -69,14 +64,13 @@ static int includes_grow(SourceManager* sm) {
}
SourceManager* source_new(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = c->ctx->heap;
SourceManager* sm =
(SourceManager*)h->alloc(h, sizeof(*sm), _Alignof(SourceManager));
if (!sm) return NULL;
memset(sm, 0, sizeof(*sm));
sm->c = c;
sm->heap = h;
- /* Reserve id 0 as "none" — never returned to callers. */
if (files_grow(sm, 1)) {
h->free(h, sm, sizeof(*sm));
return NULL;
@@ -96,11 +90,12 @@ void source_free(SourceManager* sm) {
sm->heap->free(sm->heap, sm, sizeof(*sm));
}
-static u32 file_register(SourceManager* sm, const char* name,
- SourceFileKind kind, int system_header) {
+static CfreeStatus file_register(SourceManager* sm, const char* name,
+ SourceFileKind kind, int system_header,
+ u32* id_out) {
Sym sym;
u32 id;
- if (files_grow(sm, sm->nfiles + 1)) return 0;
+ if (files_grow(sm, sm->nfiles + 1)) return CFREE_NOMEM;
sym = pool_intern_cstr(sm->c->global, name ? name : "");
id = sm->nfiles++;
memset(&sm->files[id], 0, sizeof(sm->files[id]));
@@ -109,47 +104,52 @@ static u32 file_register(SourceManager* sm, const char* name,
sm->files[id].info.path = (kind == SRC_FILE_REAL) ? sym : 0;
sm->files[id].info.kind = (u8)kind;
sm->files[id].info.system_header = (u8)(system_header ? 1 : 0);
- return id;
+ *id_out = id;
+ return CFREE_OK;
}
-u32 source_add_file(SourceManager* sm, const char* path, int system_header) {
- return file_register(sm, path, SRC_FILE_REAL, system_header);
+CfreeStatus source_add_file(SourceManager* sm, const char* path,
+ int system_header, u32* id_out) {
+ return file_register(sm, path, SRC_FILE_REAL, system_header, id_out);
}
-u32 source_add_memory(SourceManager* sm, const char* name) {
- return file_register(sm, name, SRC_FILE_MEMORY, 0);
+CfreeStatus source_add_memory(SourceManager* sm, const char* name,
+ u32* id_out) {
+ return file_register(sm, name, SRC_FILE_MEMORY, 0, id_out);
}
-u32 source_add_builtin(SourceManager* sm, const char* name) {
- return file_register(sm, name, SRC_FILE_BUILTIN, 0);
+CfreeStatus source_add_builtin(SourceManager* sm, const char* name,
+ u32* id_out) {
+ return file_register(sm, name, SRC_FILE_BUILTIN, 0, id_out);
}
-void source_add_include(SourceManager* sm, u32 includer_file_id,
- u32 included_file_id, SrcLoc include_loc, int system) {
- if (includes_grow(sm)) return;
+CfreeStatus source_add_include(SourceManager* sm, u32 includer_file_id,
+ u32 included_file_id, SrcLoc include_loc,
+ int system) {
+ if (includes_grow(sm)) return CFREE_NOMEM;
sm->includes[sm->nincludes].info.includer_file_id = includer_file_id;
sm->includes[sm->nincludes].info.included_file_id = included_file_id;
sm->includes[sm->nincludes].info.include_loc = include_loc;
sm->includes[sm->nincludes].info.system = (u8)(system ? 1 : 0);
sm->nincludes++;
+ return CFREE_OK;
}
-u32 source_add_macro_expansion(SourceManager* sm, Sym macro_name,
- SrcLoc spelling_loc, SrcLoc expansion_loc) {
- /* Macro expansion file ids are needed when the preprocessor lands;
- * not on the obj/ELF path. Register it as a synthetic file so any
- * SrcLoc passing through stays referenceable. */
+CfreeStatus source_add_macro_expansion(SourceManager* sm, Sym macro_name,
+ SrcLoc spelling_loc,
+ SrcLoc expansion_loc, u32* id_out) {
(void)spelling_loc;
(void)expansion_loc;
- if (files_grow(sm, sm->nfiles + 1)) return 0;
+ if (files_grow(sm, sm->nfiles + 1)) return CFREE_NOMEM;
{
u32 id = sm->nfiles++;
memset(&sm->files[id], 0, sizeof(sm->files[id]));
sm->files[id].info.id = id;
sm->files[id].info.name = macro_name;
sm->files[id].info.kind = SRC_FILE_MACRO;
+ *id_out = id;
}
- return sm->nfiles - 1;
+ return CFREE_OK;
}
const SourceFile* source_file(SourceManager* sm, u32 file_id) {
diff --git a/src/dbg/bp.c b/src/dbg/bp.c
@@ -9,8 +9,6 @@
#include <string.h>
-#include "core/heap.h"
-
static u32 bp_find_slot(DbgBpTable* t, uint64_t addr) {
u32 i;
for (i = 0; i < t->cap; ++i) {
@@ -59,7 +57,7 @@ void dbg_bp_fini(CfreeJitSession* s) {
if (b->user_id != 0 && b->enabled && b->saved_len) {
void* write_addr = NULL;
if (s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
- b->saved_len, &write_addr) == 0 &&
+ b->saved_len, &write_addr) == CFREE_OK &&
write_addr) {
memcpy(write_addr, b->saved, b->saved_len);
s->os->code_write_end(s->os->user, (void*)(uintptr_t)b->addr,
@@ -76,16 +74,17 @@ void dbg_bp_fini(CfreeJitSession* s) {
}
}
-static int bp_install_patch(CfreeJitSession* s, DbgBp* b) {
+static CfreeStatus bp_install_patch(CfreeJitSession* s, DbgBp* b) {
void* write_addr = NULL;
uint32_t brk;
- if (s->arch != CFREE_ARCH_ARM_64) return 1;
+ CfreeStatus st;
+ if (s->arch != CFREE_ARCH_ARM_64) return CFREE_UNSUPPORTED;
brk = dbg_aa64_brk_word();
b->saved_len = DBG_AA64_INSN_LEN;
- if (s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
- b->saved_len, &write_addr) != 0 ||
- !write_addr) {
- return 1;
+ st = s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
+ b->saved_len, &write_addr);
+ if (st != CFREE_OK || !write_addr) {
+ return st != CFREE_OK ? st : CFREE_ERR;
}
memcpy(b->saved, write_addr, b->saved_len);
memcpy(write_addr, &brk, sizeof(brk));
@@ -93,14 +92,14 @@ static int bp_install_patch(CfreeJitSession* s, DbgBp* b) {
if (s->os->flush_icache)
s->os->flush_icache(s->os->user, (void*)(uintptr_t)b->addr, b->saved_len);
b->enabled = 1;
- return 0;
+ return CFREE_OK;
}
static void bp_remove_patch(CfreeJitSession* s, DbgBp* b) {
void* write_addr = NULL;
if (!b->enabled || !b->saved_len) return;
if (s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
- b->saved_len, &write_addr) != 0 ||
+ b->saved_len, &write_addr) != CFREE_OK ||
!write_addr) {
return;
}
@@ -111,14 +110,15 @@ static void bp_remove_patch(CfreeJitSession* s, DbgBp* b) {
b->enabled = 0;
}
-static int bp_set_common(CfreeJitSession* s, const CfreeBreakpointSpec* spec,
- int internal, u32* id_out) {
+static CfreeStatus bp_set_common(CfreeJitSession* s,
+ const CfreeBreakpointSpec* spec, int internal,
+ u32* id_out) {
uint64_t addr = spec->addr;
u32 slot;
DbgBp* b;
/* Internal bps may live in the displaced-step scratch page, which is
* outside the JIT image; only user bps are constrained to image range. */
- if (!internal && !cfree_jit_image_contains(s->jit, addr)) return 1;
+ if (!internal && !cfree_jit_image_contains(s->jit, addr)) return CFREE_INVALID;
/* idempotent: existing slot at this address bumps refcount and returns
* its already-issued id. */
@@ -127,12 +127,13 @@ static int bp_set_common(CfreeJitSession* s, const CfreeBreakpointSpec* spec,
b = &s->bps.slots[slot - 1];
b->refcount++;
if (id_out) *id_out = b->user_id;
- return 0;
+ return CFREE_OK;
}
{
u32 i = bp_alloc_slot(s);
- if (i == (u32)-1) return 1;
+ CfreeStatus st;
+ if (i == (u32)-1) return CFREE_NOMEM;
b = &s->bps.slots[i];
memset(b, 0, sizeof(*b));
b->addr = addr;
@@ -147,52 +148,54 @@ static int bp_set_common(CfreeJitSession* s, const CfreeBreakpointSpec* spec,
} else {
b->user_id = s->bps.next_user_id++;
}
- if (bp_install_patch(s, b) != 0) {
+ st = bp_install_patch(s, b);
+ if (st != CFREE_OK) {
memset(b, 0, sizeof(*b));
- return 1;
+ return st;
}
s->bps.count++;
if (id_out) *id_out = b->user_id;
}
- return 0;
+ return CFREE_OK;
}
-int dbg_bp_set(CfreeJitSession* s, uint64_t addr, u32* id_out) {
+CfreeStatus dbg_bp_set(CfreeJitSession* s, uint64_t addr, u32* id_out) {
CfreeBreakpointSpec spec;
memset(&spec, 0, sizeof(spec));
spec.addr = addr;
return bp_set_common(s, &spec, 0, id_out);
}
-int dbg_bp_set_spec(CfreeJitSession* s, const CfreeBreakpointSpec* spec,
- u32* id_out) {
- if (!spec) return 1;
+CfreeStatus dbg_bp_set_spec(CfreeJitSession* s, const CfreeBreakpointSpec* spec,
+ u32* id_out) {
+ if (!spec) return CFREE_INVALID;
return bp_set_common(s, spec, 0, id_out);
}
-int dbg_bp_set_internal(CfreeJitSession* s, uint64_t addr, u32* id_out) {
+CfreeStatus dbg_bp_set_internal(CfreeJitSession* s, uint64_t addr,
+ u32* id_out) {
CfreeBreakpointSpec spec;
memset(&spec, 0, sizeof(spec));
spec.addr = addr;
return bp_set_common(s, &spec, 1, id_out);
}
-int dbg_bp_clear(CfreeJitSession* s, u32 id) {
+CfreeStatus dbg_bp_clear(CfreeJitSession* s, u32 id) {
u32 i;
- if (id == 0) return 0;
+ if (id == 0) return CFREE_OK;
for (i = 0; i < s->bps.cap; ++i) {
DbgBp* b = &s->bps.slots[i];
if (b->user_id != id) continue;
if (b->refcount > 1) {
b->refcount--;
- return 0;
+ return CFREE_OK;
}
bp_remove_patch(s, b);
memset(b, 0, sizeof(*b));
s->bps.count--;
- return 0;
+ return CFREE_OK;
}
- return 0; /* silent on unknown id, per contract */
+ return CFREE_OK; /* silent on unknown id, per contract */
}
u32 dbg_bp_lookup_index(CfreeJitSession* s, uint64_t addr) {
diff --git a/src/dbg/dbg.h b/src/dbg/dbg.h
@@ -5,7 +5,9 @@
* defined in session.c on top of these primitives; bp.c, step.c, mem.c,
* displaced.c, and arch/aa64/dbg.c each own one slice. */
-#include <cfree.h>
+#include <cfree/dbg.h>
+#include <cfree/dwarf.h>
+#include <cfree/jit.h>
#include "core/core.h"
@@ -20,6 +22,10 @@
int cfree_jit_image_contains(CfreeJit*, uint64_t runtime_addr);
CfreeArchKind cfree_jit_image_arch(CfreeJit*);
Compiler* cfree_jit_compiler(CfreeJit*);
+/* The JIT image owns the execmem used to map its code/data; the debugger's
+ * displaced-step scratch is allocated from the same pool. Implemented in
+ * src/link/link_jit.c. */
+const CfreeExecMem* cfree_jit_image_execmem(CfreeJit*);
/* ---- breakpoint table ------------------------------------------------ */
@@ -54,11 +60,12 @@ void dbg_bp_fini(struct CfreeJitSession*);
/* set/clear with the user-facing handle space. The internal variants are
* used by step.c for one-shot temporaries. */
-int dbg_bp_set(struct CfreeJitSession*, uint64_t addr, u32* id_out);
-int dbg_bp_set_spec(struct CfreeJitSession*, const CfreeBreakpointSpec*,
- u32* id_out);
-int dbg_bp_set_internal(struct CfreeJitSession*, uint64_t addr, u32* id_out);
-int dbg_bp_clear(struct CfreeJitSession*, u32 id);
+CfreeStatus dbg_bp_set(struct CfreeJitSession*, uint64_t addr, u32* id_out);
+CfreeStatus dbg_bp_set_spec(struct CfreeJitSession*,
+ const CfreeBreakpointSpec*, u32* id_out);
+CfreeStatus dbg_bp_set_internal(struct CfreeJitSession*, uint64_t addr,
+ u32* id_out);
+CfreeStatus dbg_bp_clear(struct CfreeJitSession*, u32 id);
/* Lookup at a PC. Returns the slot index + 1 (so 0 means "not patched");
* the caller uses dbg_bp_at_index to fetch the entry. */
@@ -71,9 +78,10 @@ void dbg_bp_unpatch_read(struct CfreeJitSession*, uint64_t addr, void* buf,
size_t n);
/* ---- memory --------------------------------------------------------- */
-int dbg_mem_read(struct CfreeJitSession*, uint64_t addr, void* dst, size_t n);
-int dbg_mem_write(struct CfreeJitSession*, uint64_t addr, const void* src,
- size_t n);
+CfreeStatus dbg_mem_read(struct CfreeJitSession*, uint64_t addr, void* dst,
+ size_t n);
+CfreeStatus dbg_mem_write(struct CfreeJitSession*, uint64_t addr,
+ const void* src, size_t n);
/* ---- displaced step ------------------------------------------------- */
/* The session owns a single executable scratch region. arch/aa64/dbg.c writes
@@ -89,14 +97,15 @@ typedef struct DbgDisplaced {
uint32_t internal_bp; /* id of the one-shot bp at return_pc */
} DbgDisplaced;
-int dbg_displaced_init(struct CfreeJitSession*);
+CfreeStatus dbg_displaced_init(struct CfreeJitSession*);
void dbg_displaced_fini(struct CfreeJitSession*);
/* Prepare an out-of-line single-step at `insn_pc`. Sets *new_pc to the
* scratch entry the worker should branch to; arms an internal bp on the
- * shim's BRK. Returns 0 on success, 1 if the insn family is not supported. */
-int dbg_displaced_prepare(struct CfreeJitSession*, uint64_t insn_pc,
- uint64_t* new_pc);
+ * shim's BRK. Returns CFREE_OK on success, CFREE_UNSUPPORTED if the insn
+ * family is not supported. */
+CfreeStatus dbg_displaced_prepare(struct CfreeJitSession*, uint64_t insn_pc,
+ uint64_t* new_pc);
/* After the shim BRK fires, finalize: clear the internal bp, restore the
* user-visible PC to insn_pc + 4 (or branch target captured by the shim). */
void dbg_displaced_finalize(struct CfreeJitSession*);
@@ -109,13 +118,14 @@ uint32_t dbg_aa64_brk_word(void);
* On success returns 0 and sets *brk_offset to the byte offset of the BRK
* sentinel from `scratch_runtime`; the caller arms an internal bp at
* `scratch_runtime + *brk_offset` and flushes the whole slot. Returns 1
- * for unsupported instruction families. */
+ * for unsupported instruction families. Kept as int so arch/aa64/dbg.c
+ * does not have to learn the dbg-internal status type. */
int dbg_aa64_build_shim(uint32_t orig_insn, uint64_t orig_pc,
void* scratch_write, uint64_t scratch_runtime,
u32* brk_offset);
/* ---- step state machine --------------------------------------------- */
-int dbg_step_resume(struct CfreeJitSession*, CfreeResumeMode mode);
+CfreeStatus dbg_step_resume(struct CfreeJitSession*, CfreeResumeMode mode);
/* ---- session state -------------------------------------------------- */
typedef enum DbgSessionState {
@@ -130,6 +140,10 @@ struct CfreeJitSession {
Compiler* c;
Heap* heap;
const CfreeDbgOs* os;
+ /* Borrowed from the CfreeJit. Used to allocate displaced-step scratch
+ * (and only that — the JIT image itself was already mapped at link
+ * time). NULL if the host's JitHost did not supply an execmem. */
+ const CfreeExecMem* execmem;
CfreeArchKind arch;
/* worker thread + event handshake */
@@ -180,7 +194,7 @@ struct CfreeJitSession {
};
/* internal helpers shared between session.c and step.c */
-int dbg_session_wait_stop(struct CfreeJitSession*);
-int dbg_session_signal_resume(struct CfreeJitSession*);
+CfreeStatus dbg_session_wait_stop(struct CfreeJitSession*);
+CfreeStatus dbg_session_signal_resume(struct CfreeJitSession*);
#endif
diff --git a/src/dbg/displaced.c b/src/dbg/displaced.c
@@ -1,56 +1,59 @@
/* Displaced single-step plumbing.
*
- * Reserves a single executable page (W^X dual-mapped via env->execmem)
- * the first time STEP_INSN is requested. The per-arch lifter copies a
- * fixed-up version of the instruction at insn_pc into that page, followed
- * by a BRK sentinel; the session arms an internal breakpoint on the
- * sentinel and resumes with PC = scratch_runtime. On the BRK fault, the
- * fault classifier sees the internal bp and the session uses
+ * Reserves a single executable page (W^X dual-mapped via the JitHost
+ * execmem) the first time STEP_INSN is requested. The per-arch lifter
+ * copies a fixed-up version of the instruction at insn_pc into that page,
+ * followed by a BRK sentinel; the session arms an internal breakpoint on
+ * the sentinel and resumes with PC = scratch_runtime. On the BRK fault,
+ * the fault classifier sees the internal bp and the session uses
* dbg_displaced_finalize to restore the user-visible PC. */
#include "dbg/dbg.h"
#include <string.h>
-int dbg_displaced_init(CfreeJitSession* s) {
+CfreeStatus dbg_displaced_init(CfreeJitSession* s) {
const CfreeExecMem* mem;
- if (s->displaced.valid) return 0;
- mem = s->c->env ? s->c->env->execmem : NULL;
- if (!mem || !mem->reserve || !mem->protect) return 1;
+ CfreeStatus st;
+ if (s->displaced.valid) return CFREE_OK;
+ mem = s->execmem;
+ if (!mem || !mem->reserve || !mem->protect) return CFREE_UNSUPPORTED;
memset(&s->displaced.region, 0, sizeof(s->displaced.region));
- if (mem->reserve(mem->user, DBG_SCRATCH_PAGE_SIZE,
- CFREE_PROT_READ | CFREE_PROT_EXEC,
- &s->displaced.region) != 0) {
- return 1;
- }
- if (mem->protect(mem->user, s->displaced.region.runtime,
- s->displaced.region.size,
- CFREE_PROT_READ | CFREE_PROT_EXEC) != 0) {
+ st = mem->reserve(mem->user, DBG_SCRATCH_PAGE_SIZE,
+ CFREE_PROT_READ | CFREE_PROT_EXEC, &s->displaced.region);
+ if (st != CFREE_OK) return st;
+ st = mem->protect(mem->user, s->displaced.region.runtime,
+ s->displaced.region.size,
+ CFREE_PROT_READ | CFREE_PROT_EXEC);
+ if (st != CFREE_OK) {
mem->release(mem->user, &s->displaced.region);
memset(&s->displaced.region, 0, sizeof(s->displaced.region));
- return 1;
+ return st;
}
s->displaced.valid = 1;
- return 0;
+ return CFREE_OK;
}
void dbg_displaced_fini(CfreeJitSession* s) {
- const CfreeExecMem* mem = s->c->env ? s->c->env->execmem : NULL;
+ const CfreeExecMem* mem = s->execmem;
if (!s->displaced.valid) return;
if (mem && mem->release) mem->release(mem->user, &s->displaced.region);
memset(&s->displaced, 0, sizeof(s->displaced));
}
-int dbg_displaced_prepare(CfreeJitSession* s, uint64_t insn_pc,
- uint64_t* new_pc) {
+CfreeStatus dbg_displaced_prepare(CfreeJitSession* s, uint64_t insn_pc,
+ uint64_t* new_pc) {
uint32_t orig_word = 0;
u32 brk_off = 0;
uint64_t scratch_runtime;
uint8_t* scratch_write;
u32 bp_id = 0;
+ CfreeStatus st;
+ const CfreeExecMem* mem;
- if (s->arch != CFREE_ARCH_ARM_64) return 1;
- if (dbg_displaced_init(s) != 0) return 1;
+ if (s->arch != CFREE_ARCH_ARM_64) return CFREE_UNSUPPORTED;
+ st = dbg_displaced_init(s);
+ if (st != CFREE_OK) return st;
/* A previous step whose shim transferred control (indirect branch or
* a CBZ/TBZ trampoline that took) never ran finalize, leaving a stale
@@ -71,11 +74,10 @@ int dbg_displaced_prepare(CfreeJitSession* s, uint64_t insn_pc,
DbgBp* b = dbg_bp_at_index(s, idx);
memcpy(&orig_word, b->saved, sizeof(orig_word));
} else {
- if (s->os->guarded_copy(s->os->user, &orig_word,
- (const void*)(uintptr_t)insn_pc,
- sizeof(orig_word)) != 0) {
- return 1;
- }
+ st = s->os->guarded_copy(s->os->user, &orig_word,
+ (const void*)(uintptr_t)insn_pc,
+ sizeof(orig_word));
+ if (st != CFREE_OK) return st;
}
}
@@ -83,26 +85,25 @@ int dbg_displaced_prepare(CfreeJitSession* s, uint64_t insn_pc,
scratch_write = (uint8_t*)s->displaced.region.write;
if (dbg_aa64_build_shim(orig_word, insn_pc, scratch_write, scratch_runtime,
&brk_off) != 0) {
- return 1;
+ return CFREE_UNSUPPORTED;
}
/* Flush the entire slot — trampoline forms write up to 24 bytes plus a
* literal pool; arch/aa64/dbg.c returns the BRK *offset*, not the length. */
- if (s->c->env->execmem->flush_icache) {
- s->c->env->execmem->flush_icache(s->c->env->execmem->user,
- s->displaced.region.runtime,
- DBG_DISPLACED_SLOT_BYTES);
+ mem = s->execmem;
+ if (mem && mem->flush_icache) {
+ mem->flush_icache(mem->user, s->displaced.region.runtime,
+ DBG_DISPLACED_SLOT_BYTES);
}
/* Arm an internal breakpoint on the shim's BRK sentinel so the fault
* classifier identifies it as a displaced-step completion. */
- if (dbg_bp_set_internal(s, scratch_runtime + brk_off, &bp_id) != 0) {
- return 1;
- }
+ st = dbg_bp_set_internal(s, scratch_runtime + brk_off, &bp_id);
+ if (st != CFREE_OK) return st;
s->displaced.orig_pc = insn_pc;
s->displaced.return_pc = scratch_runtime + brk_off;
s->displaced.internal_bp = bp_id;
if (new_pc) *new_pc = scratch_runtime;
- return 0;
+ return CFREE_OK;
}
void dbg_displaced_finalize(CfreeJitSession* s) {
diff --git a/src/dbg/mem.c b/src/dbg/mem.c
@@ -7,21 +7,18 @@
#include <string.h>
-int dbg_mem_read(CfreeJitSession* s, uint64_t addr, void* dst, size_t n) {
- if (!s || !dst || n == 0) return 1;
- if (s->os->guarded_copy(s->os->user, dst, (const void*)(uintptr_t)addr, n) !=
- 0) {
- return 1;
- }
+CfreeStatus dbg_mem_read(CfreeJitSession* s, uint64_t addr, void* dst,
+ size_t n) {
+ CfreeStatus st;
+ if (!s || !dst || n == 0) return CFREE_INVALID;
+ st = s->os->guarded_copy(s->os->user, dst, (const void*)(uintptr_t)addr, n);
+ if (st != CFREE_OK) return st;
dbg_bp_unpatch_read(s, addr, dst, n);
- return 0;
+ return CFREE_OK;
}
-int dbg_mem_write(CfreeJitSession* s, uint64_t addr, const void* src,
- size_t n) {
- if (!s || !src || n == 0) return 1;
- if (s->os->guarded_copy(s->os->user, (void*)(uintptr_t)addr, src, n) != 0) {
- return 1;
- }
- return 0;
+CfreeStatus dbg_mem_write(CfreeJitSession* s, uint64_t addr, const void* src,
+ size_t n) {
+ if (!s || !src || n == 0) return CFREE_INVALID;
+ return s->os->guarded_copy(s->os->user, (void*)(uintptr_t)addr, src, n);
}
diff --git a/src/dbg/session.c b/src/dbg/session.c
@@ -11,8 +11,6 @@
#include <string.h>
-#include "core/heap.h"
-
/* ---- fault classification ------------------------------------------- */
static int signo_is_trap(int signo) {
@@ -27,12 +25,13 @@ static int signo_is_trap(int signo) {
return 1;
}
-static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
+static CfreeStatus on_fault(void* session_v, int signo,
+ CfreeUnwindFrame* regs) {
CfreeJitSession* s = (CfreeJitSession*)session_v;
u32 idx;
DbgBp* bp;
- if (!s) return 1;
+ if (!s) return CFREE_INVALID;
/* Snapshot the regs into our stop slot up-front. */
memcpy(&s->stop.regs, regs, sizeof(*regs));
@@ -62,7 +61,7 @@ static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
if (s->pending_step_pending) {
/* CONTINUE-over-bp: do not park; just resume. */
s->pending_step_pending = 0;
- return 0;
+ return CFREE_OK;
}
s->stop.kind = CFREE_STOP_BREAKPOINT;
s->stop.bp_id = 0;
@@ -76,7 +75,7 @@ static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
dbg_bp_clear(s, bp->user_id);
if (s->pending_step_pending) {
s->pending_step_pending = 0;
- return 0;
+ return CFREE_OK;
}
s->stop.kind = CFREE_STOP_BREAKPOINT;
s->stop.bp_id = 0;
@@ -92,7 +91,7 @@ static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
* Implemented by arming a displaced step then deferring the
* "do not park" decision via pending_step_pending. */
s->pending_step_pending = 1;
- if (dbg_step_resume(s, CFREE_RESUME_CONTINUE) != 0) {
+ if (dbg_step_resume(s, CFREE_RESUME_CONTINUE) != CFREE_OK) {
/* Couldn't arm a step; surface the stop after all. */
s->pending_step_pending = 0;
s->stop.kind = CFREE_STOP_BREAKPOINT;
@@ -104,12 +103,12 @@ static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
regs->pc = s->pending_pc_override;
s->pending_has_pc = 0;
}
- return 0;
+ return CFREE_OK;
}
if (bp->condition && bp->condition(bp->condition_user, regs) == 0) {
/* Condition rejected: same silent-resume path. */
s->pending_step_pending = 1;
- if (dbg_step_resume(s, CFREE_RESUME_CONTINUE) != 0) {
+ if (dbg_step_resume(s, CFREE_RESUME_CONTINUE) != CFREE_OK) {
s->pending_step_pending = 0;
s->stop.kind = CFREE_STOP_BREAKPOINT;
s->stop.bp_id = bp->user_id;
@@ -119,7 +118,7 @@ static int on_fault(void* session_v, int signo, CfreeUnwindFrame* regs) {
regs->pc = s->pending_pc_override;
s->pending_has_pc = 0;
}
- return 0;
+ return CFREE_OK;
}
s->stop.kind = CFREE_STOP_BREAKPOINT;
s->stop.bp_id = bp->user_id;
@@ -150,7 +149,7 @@ park:
/* Allow REPL set_regs to mutate any field. */
memcpy(regs, &s->stop.regs, sizeof(*regs));
}
- return 0;
+ return CFREE_OK;
}
/* ---- worker thread -------------------------------------------------- */
@@ -249,87 +248,100 @@ static void worker_main(void* arg) {
/* ---- public entries ------------------------------------------------- */
-CfreeJitSession* cfree_jit_session_new(CfreeJit* jit) {
+CfreeStatus cfree_jit_session_new(CfreeJit* jit, const CfreeDbgHost* host,
+ CfreeJitSession** out) {
CfreeJitSession* s;
Compiler* c;
Heap* heap;
const CfreeDbgOs* os;
CfreeDbgSignalOps ops;
+ CfreeStatus st;
- if (!jit) return NULL;
+ if (out) *out = NULL;
+ if (!jit || !host || !host->os || !out) return CFREE_INVALID;
c = cfree_jit_compiler(jit);
- if (!c || !c->env) return NULL;
- os = c->env->dbg_os;
- if (!os) return NULL;
+ if (!c || !c->ctx) return CFREE_INVALID;
+ os = host->os;
if (!os->thread_start || !os->thread_join || !os->event_new ||
!os->event_wait || !os->event_signal || !os->event_reset ||
!os->event_free || !os->signals_install || !os->signals_uninstall ||
!os->code_write_begin || !os->code_write_end || !os->guarded_copy) {
- return NULL;
+ return CFREE_INVALID;
}
/* v1 only supports aarch64 lifters; refuse other targets early so we
* don't end up with patched bytes we can't roll back. */
- if (cfree_jit_image_arch(jit) != CFREE_ARCH_ARM_64) return NULL;
+ if (cfree_jit_image_arch(jit) != CFREE_ARCH_ARM_64) return CFREE_UNSUPPORTED;
- heap = (Heap*)c->env->heap;
+ heap = c->ctx->heap;
s = (CfreeJitSession*)heap->alloc(heap, sizeof(*s), _Alignof(CfreeJitSession));
- if (!s) return NULL;
+ if (!s) return CFREE_NOMEM;
memset(s, 0, sizeof(*s));
s->jit = jit;
s->c = c;
s->heap = heap;
s->os = os;
+ /* Borrow execmem from the JIT image; displaced-step scratch is the only
+ * consumer. May be NULL if the JIT was constructed without one, in
+ * which case STEP_INSN paths will surface CFREE_UNSUPPORTED. */
+ s->execmem = cfree_jit_image_execmem(jit);
s->arch = cfree_jit_image_arch(jit);
s->state = DBG_STATE_IDLE;
- s->ev_resume = os->event_new(os->user);
- s->ev_stop = os->event_new(os->user);
- if (!s->ev_resume || !s->ev_stop) {
- if (s->ev_resume) os->event_free(os->user, s->ev_resume);
- if (s->ev_stop) os->event_free(os->user, s->ev_stop);
+ st = os->event_new(os->user, &s->ev_resume);
+ if (st != CFREE_OK) {
+ heap->free(heap, s, sizeof(*s));
+ return st;
+ }
+ st = os->event_new(os->user, &s->ev_stop);
+ if (st != CFREE_OK) {
+ os->event_free(os->user, s->ev_resume);
heap->free(heap, s, sizeof(*s));
- return NULL;
+ return st;
}
dbg_bp_init(s);
ops.on_fault = on_fault;
- if (os->signals_install(os->user, &ops, s) != 0) {
+ st = os->signals_install(os->user, &ops, s);
+ if (st != CFREE_OK) {
os->event_free(os->user, s->ev_resume);
os->event_free(os->user, s->ev_stop);
heap->free(heap, s, sizeof(*s));
- return NULL;
+ return st;
}
- if (os->thread_start(os->user, worker_main, s, &s->worker) != 0) {
+ st = os->thread_start(os->user, worker_main, s, &s->worker);
+ if (st != CFREE_OK) {
os->signals_uninstall(os->user);
os->event_free(os->user, s->ev_resume);
os->event_free(os->user, s->ev_stop);
heap->free(heap, s, sizeof(*s));
- return NULL;
+ return st;
}
s->worker_alive = 1;
- return s;
+ *out = s;
+ return CFREE_OK;
}
-int cfree_jit_session_attach_dwarf(CfreeJitSession* s, CfreeDebugInfo* dw) {
- if (!s) return 1;
+CfreeStatus cfree_jit_session_attach_dwarf(CfreeJitSession* s,
+ CfreeDebugInfo* dw) {
+ if (!s) return CFREE_INVALID;
s->dwarf = dw;
- return 0;
+ return CFREE_OK;
}
-int dbg_session_signal_resume(CfreeJitSession* s) {
- if (!s) return 1;
+CfreeStatus dbg_session_signal_resume(CfreeJitSession* s) {
+ if (!s) return CFREE_INVALID;
s->state = DBG_STATE_RUNNING;
- s->os->event_signal(s->os->user, s->ev_resume);
- return 0;
+ return s->os->event_signal(s->os->user, s->ev_resume);
}
-int dbg_session_wait_stop(CfreeJitSession* s) {
- if (!s) return 1;
- s->os->event_wait(s->os->user, s->ev_stop);
- s->os->event_reset(s->os->user, s->ev_stop);
- return 0;
+CfreeStatus dbg_session_wait_stop(CfreeJitSession* s) {
+ CfreeStatus st;
+ if (!s) return CFREE_INVALID;
+ st = s->os->event_wait(s->os->user, s->ev_stop);
+ if (st != CFREE_OK) return st;
+ return s->os->event_reset(s->os->user, s->ev_stop);
}
void cfree_jit_session_free(CfreeJitSession* s) {
@@ -356,10 +368,12 @@ void cfree_jit_session_free(CfreeJitSession* s) {
s->heap->free(s->heap, s, sizeof(*s));
}
-int cfree_jit_session_call(CfreeJitSession* s, void* entry, CfreeEntryKind kind,
- int argc, char** argv, CfreeStopInfo* stop_out) {
- if (!s || !entry) return 1;
- if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) return 1;
+CfreeStatus cfree_jit_session_call(CfreeJitSession* s, void* entry,
+ CfreeEntryKind kind, int argc, char** argv,
+ CfreeStopInfo* stop_out) {
+ if (!s || !entry) return CFREE_INVALID;
+ if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED)
+ return CFREE_INVALID;
s->entry = entry;
s->entry_kind = kind;
s->entry_argc = argc;
@@ -373,30 +387,32 @@ int cfree_jit_session_call(CfreeJitSession* s, void* entry, CfreeEntryKind kind,
s->os->event_wait(s->os->user, s->ev_stop);
s->os->event_reset(s->os->user, s->ev_stop);
if (stop_out) *stop_out = s->stop;
- return 0;
+ return CFREE_OK;
}
-int cfree_jit_session_call_u64(CfreeJitSession* s, void* entry,
- const uint64_t* args, uint32_t nargs,
- uint64_t* ret_out, CfreeStopInfo* stop_out) {
+CfreeStatus cfree_jit_session_call_u64(CfreeJitSession* s, void* entry,
+ const uint64_t* args, uint32_t nargs,
+ uint64_t* ret_out,
+ CfreeStopInfo* stop_out) {
uint32_t i;
- int rc;
- if (!s || !entry || nargs > 8u) return 1;
- if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) return 1;
+ CfreeStatus st;
+ if (!s || !entry || nargs > 8u) return CFREE_INVALID;
+ if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED)
+ return CFREE_INVALID;
for (i = 0; i < nargs; ++i) s->entry_u64_args[i] = args ? args[i] : 0;
for (; i < 8u; ++i) s->entry_u64_args[i] = 0;
s->entry_u64_nargs = nargs;
s->entry_u64_ret = 0;
- rc = cfree_jit_session_call(s, entry, CFREE_ENTRY_U64, 0, NULL, stop_out);
- if (rc == 0 && ret_out) *ret_out = s->entry_u64_ret;
- return rc;
+ st = cfree_jit_session_call(s, entry, CFREE_ENTRY_U64, 0, NULL, stop_out);
+ if (st == CFREE_OK && ret_out) *ret_out = s->entry_u64_ret;
+ return st;
}
-int cfree_jit_session_resume(CfreeJitSession* s, CfreeResumeMode mode,
- CfreeStopInfo* stop_out) {
- if (!s) return 1;
- if (s->state == DBG_STATE_EXITED) return 1;
- if (s->state != DBG_STATE_STOPPED) return 1;
+CfreeStatus cfree_jit_session_resume(CfreeJitSession* s, CfreeResumeMode mode,
+ CfreeStopInfo* stop_out) {
+ if (!s) return CFREE_INVALID;
+ if (s->state == DBG_STATE_EXITED) return CFREE_INVALID;
+ if (s->state != DBG_STATE_STOPPED) return CFREE_INVALID;
s->pending_mode = mode;
s->pending_has_pc = 0;
@@ -409,12 +425,13 @@ int cfree_jit_session_resume(CfreeJitSession* s, CfreeResumeMode mode,
if (mode == CFREE_RESUME_CONTINUE &&
dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) {
s->pending_step_pending = 1;
- if (dbg_step_resume(s, CFREE_RESUME_STEP_INSN) != 0) {
+ if (dbg_step_resume(s, CFREE_RESUME_STEP_INSN) != CFREE_OK) {
s->pending_step_pending = 0;
- return 1;
+ return CFREE_ERR;
}
} else if (mode != CFREE_RESUME_CONTINUE) {
- if (dbg_step_resume(s, mode) != 0) return 1;
+ CfreeStatus st = dbg_step_resume(s, mode);
+ if (st != CFREE_OK) return st;
}
if (!s->pending_done) {
@@ -425,59 +442,63 @@ int cfree_jit_session_resume(CfreeJitSession* s, CfreeResumeMode mode,
}
s->pending_done = 0;
if (stop_out) *stop_out = s->stop;
- return 0;
+ return CFREE_OK;
}
-int cfree_jit_session_interrupt(CfreeJitSession* s) {
- if (!s) return 1;
- if (s->state != DBG_STATE_RUNNING) return 1;
- if (!s->os->thread_interrupt) return 1;
+CfreeStatus cfree_jit_session_interrupt(CfreeJitSession* s) {
+ if (!s) return CFREE_INVALID;
+ if (s->state != DBG_STATE_RUNNING) return CFREE_INVALID;
+ if (!s->os->thread_interrupt) return CFREE_UNSUPPORTED;
return s->os->thread_interrupt(s->os->user, s->worker);
}
-int cfree_jit_session_read_mem(CfreeJitSession* s, uint64_t addr, void* dst,
- size_t n) {
- if (!s) return 1;
- if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED) return 1;
+CfreeStatus cfree_jit_session_read_mem(CfreeJitSession* s, uint64_t addr,
+ void* dst, size_t n) {
+ if (!s) return CFREE_INVALID;
+ if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED)
+ return CFREE_INVALID;
return dbg_mem_read(s, addr, dst, n);
}
-int cfree_jit_session_write_mem(CfreeJitSession* s, uint64_t addr,
- const void* src, size_t n) {
- if (!s) return 1;
- if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED) return 1;
+CfreeStatus cfree_jit_session_write_mem(CfreeJitSession* s, uint64_t addr,
+ const void* src, size_t n) {
+ if (!s) return CFREE_INVALID;
+ if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED)
+ return CFREE_INVALID;
return dbg_mem_write(s, addr, src, n);
}
-int cfree_jit_session_get_regs(CfreeJitSession* s, CfreeUnwindFrame* out) {
- if (!s || !out) return 1;
- if (s->state != DBG_STATE_STOPPED) return 1;
+CfreeStatus cfree_jit_session_get_regs(CfreeJitSession* s,
+ CfreeUnwindFrame* out) {
+ if (!s || !out) return CFREE_INVALID;
+ if (s->state != DBG_STATE_STOPPED) return CFREE_INVALID;
*out = s->stop.regs;
- return 0;
+ return CFREE_OK;
}
-int cfree_jit_session_set_regs(CfreeJitSession* s, const CfreeUnwindFrame* in) {
- if (!s || !in) return 1;
- if (s->state != DBG_STATE_STOPPED) return 1;
- if (!cfree_jit_image_contains(s->jit, in->pc)) return 1;
+CfreeStatus cfree_jit_session_set_regs(CfreeJitSession* s,
+ const CfreeUnwindFrame* in) {
+ if (!s || !in) return CFREE_INVALID;
+ if (s->state != DBG_STATE_STOPPED) return CFREE_INVALID;
+ if (!cfree_jit_image_contains(s->jit, in->pc)) return CFREE_INVALID;
s->stop.regs = *in;
- return 0;
+ return CFREE_OK;
}
-int cfree_jit_session_breakpoint_set(CfreeJitSession* s, uint64_t addr,
- uint32_t* bp_id_out) {
- if (!s) return 1;
+CfreeStatus cfree_jit_session_breakpoint_set(CfreeJitSession* s, uint64_t addr,
+ uint32_t* bp_id_out) {
+ if (!s) return CFREE_INVALID;
return dbg_bp_set(s, addr, bp_id_out);
}
-int cfree_jit_session_breakpoint_clear(CfreeJitSession* s, uint32_t bp_id) {
- if (!s) return 1;
+CfreeStatus cfree_jit_session_breakpoint_clear(CfreeJitSession* s,
+ uint32_t bp_id) {
+ if (!s) return CFREE_INVALID;
return dbg_bp_clear(s, bp_id);
}
-int cfree_jit_session_breakpoint_set_spec(CfreeJitSession* s,
- const CfreeBreakpointSpec* spec,
- uint32_t* bp_id_out) {
- if (!s || !spec) return 1;
+CfreeStatus cfree_jit_session_breakpoint_set_spec(
+ CfreeJitSession* s, const CfreeBreakpointSpec* spec, uint32_t* bp_id_out) {
+ if (!s || !spec) return CFREE_INVALID;
return dbg_bp_set_spec(s, spec, bp_id_out);
}
diff --git a/src/dbg/step.c b/src/dbg/step.c
@@ -23,34 +23,32 @@ static uint64_t step_rt_to_img(CfreeJitSession* s, uint64_t pc) {
return v ? v : pc;
}
-static int prepare_step_insn(CfreeJitSession* s) {
+static CfreeStatus prepare_step_insn(CfreeJitSession* s) {
uint64_t pc = s->stop.regs.pc;
uint64_t scratch_entry = 0;
-
- if (dbg_displaced_prepare(s, pc, &scratch_entry) != 0) {
- return 1;
- }
+ CfreeStatus st = dbg_displaced_prepare(s, pc, &scratch_entry);
+ if (st != CFREE_OK) return st;
s->pending_has_pc = 1;
s->pending_pc_override = scratch_entry;
- return 0;
+ return CFREE_OK;
}
-/* Drive a single displaced-step cycle synchronously. Returns 0 on success;
- * the session is parked again at the post-step PC. Returns 1 on prepare
- * failure (worker not advanced). */
-static int do_one_displaced(CfreeJitSession* s) {
- if (prepare_step_insn(s) != 0) return 1;
- if (dbg_session_signal_resume(s) != 0) return 1;
- if (dbg_session_wait_stop(s) != 0) return 1;
- return 0;
+/* Drive a single displaced-step cycle synchronously. Returns CFREE_OK on
+ * success; the session is parked again at the post-step PC. */
+static CfreeStatus do_one_displaced(CfreeJitSession* s) {
+ CfreeStatus st = prepare_step_insn(s);
+ if (st != CFREE_OK) return st;
+ st = dbg_session_signal_resume(s);
+ if (st != CFREE_OK) return st;
+ return dbg_session_wait_stop(s);
}
static int stop_is_internal_completion(const CfreeJitSession* s) {
return s->stop.kind == CFREE_STOP_BREAKPOINT && s->stop.bp_id == 0;
}
-static int dwarf_line_for(CfreeJitSession* s, uint64_t pc, const char** file,
- uint32_t* line) {
+static CfreeStatus dwarf_line_for(CfreeJitSession* s, uint64_t pc,
+ const char** file, uint32_t* line) {
uint32_t col = 0;
*file = NULL;
*line = 0;
@@ -58,8 +56,8 @@ static int dwarf_line_for(CfreeJitSession* s, uint64_t pc, const char** file,
&col);
}
-static int dwarf_sub_for(CfreeJitSession* s, uint64_t pc,
- CfreeDwarfSubprogram* out) {
+static CfreeStatus dwarf_sub_for(CfreeJitSession* s, uint64_t pc,
+ CfreeDwarfSubprogram* out) {
memset(out, 0, sizeof(*out));
return cfree_dwarf_subprogram_at(s->dwarf, step_rt_to_img(s, pc), out);
}
@@ -76,7 +74,7 @@ static int line_changed(const char* base_file, uint32_t base_line,
return 0;
}
-static int run_step_line_loop(CfreeJitSession* s) {
+static CfreeStatus run_step_line_loop(CfreeJitSession* s) {
const char* base_file = NULL;
uint32_t base_line = 0;
CfreeDwarfSubprogram base_sub;
@@ -84,7 +82,7 @@ static int run_step_line_loop(CfreeJitSession* s) {
u32 i;
(void)dwarf_line_for(s, s->stop.regs.pc, &base_file, &base_line);
- have_sub = (dwarf_sub_for(s, s->stop.regs.pc, &base_sub) == 0);
+ have_sub = (dwarf_sub_for(s, s->stop.regs.pc, &base_sub) == CFREE_OK);
for (i = 0; i < DBG_STEP_LINE_INSN_CAP; ++i) {
const char* new_file = NULL;
@@ -92,67 +90,72 @@ static int run_step_line_loop(CfreeJitSession* s) {
CfreeDwarfSubprogram new_sub;
int have_new_sub;
- if (do_one_displaced(s) != 0) {
+ if (do_one_displaced(s) != CFREE_OK) {
/* Lifter declined; surface whatever the current stop is. */
- return 0;
+ return CFREE_OK;
}
if (!stop_is_internal_completion(s)) {
/* User breakpoint, signal, or exit — surface. */
- return 0;
+ return CFREE_OK;
}
(void)dwarf_line_for(s, s->stop.regs.pc, &new_file, &new_line);
- have_new_sub = (dwarf_sub_for(s, s->stop.regs.pc, &new_sub) == 0);
+ have_new_sub = (dwarf_sub_for(s, s->stop.regs.pc, &new_sub) == CFREE_OK);
if (have_sub && have_new_sub) {
if (new_sub.low_pc != base_sub.low_pc) {
/* Left the original subprogram — STEP_LINE follows in. */
- return 0;
+ return CFREE_OK;
}
} else if (have_sub != have_new_sub) {
- return 0;
+ return CFREE_OK;
}
if (line_changed(base_file, base_line, new_file, new_line)) {
- return 0;
+ return CFREE_OK;
}
}
- return 0;
+ return CFREE_OK;
}
-static int read_insn_word(CfreeJitSession* s, uint64_t pc, uint32_t* out) {
+static CfreeStatus read_insn_word(CfreeJitSession* s, uint64_t pc,
+ uint32_t* out) {
uint8_t buf[4];
- if (dbg_mem_read(s, pc, buf, 4) != 0) return 1;
+ CfreeStatus st = dbg_mem_read(s, pc, buf, 4);
+ if (st != CFREE_OK) return st;
*out = ((uint32_t)buf[0]) | ((uint32_t)buf[1] << 8) |
((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);
- return 0;
+ return CFREE_OK;
}
static int aa64_is_bl(uint32_t insn) {
return (insn & DBG_AA64_BL_MASK) == DBG_AA64_BL_OP;
}
-static int run_step_out(CfreeJitSession* s) {
+static CfreeStatus run_step_out(CfreeJitSession* s) {
CfreeUnwindFrame frame;
u32 bp_id = 0;
+ CfreeStatus st;
frame = s->stop.regs;
frame.pc = step_rt_to_img(s, frame.pc); /* CFI lookup is in image space */
- if (cfree_dwarf_unwind_step(s->dwarf, &frame) != 0) return 1;
+ if (cfree_dwarf_unwind_step(s->dwarf, &frame) != CFREE_OK)
+ return CFREE_UNSUPPORTED;
/* On success unwind_step writes frame.pc from the saved return-address
* register / stack slot — already a runtime PC, no inverse translation
* needed before the internal bp install. */
- if (frame.pc == 0) return 1;
- if (dbg_bp_set_internal(s, frame.pc, &bp_id) != 0) return 1;
- if (dbg_session_signal_resume(s) != 0) return 1;
- if (dbg_session_wait_stop(s) != 0) return 1;
- return 0;
+ if (frame.pc == 0) return CFREE_NOT_FOUND;
+ st = dbg_bp_set_internal(s, frame.pc, &bp_id);
+ if (st != CFREE_OK) return st;
+ st = dbg_session_signal_resume(s);
+ if (st != CFREE_OK) return st;
+ return dbg_session_wait_stop(s);
}
-static int run_next_line(CfreeJitSession* s) {
+static CfreeStatus run_next_line(CfreeJitSession* s) {
uint32_t insn = 0;
- if (s->arch != CFREE_ARCH_ARM_64) return 1;
+ if (s->arch != CFREE_ARCH_ARM_64) return CFREE_UNSUPPORTED;
- if (read_insn_word(s, s->stop.regs.pc, &insn) != 0) {
+ if (read_insn_word(s, s->stop.regs.pc, &insn) != CFREE_OK) {
return run_step_line_loop(s);
}
if (!aa64_is_bl(insn)) {
@@ -165,53 +168,65 @@ static int run_next_line(CfreeJitSession* s) {
{
CfreeUnwindFrame frame = s->stop.regs;
u32 bp_id = 0;
+ CfreeStatus st;
frame.pc = step_rt_to_img(s, frame.pc);
- if (cfree_dwarf_unwind_step(s->dwarf, &frame) != 0 || frame.pc == 0) {
+ if (cfree_dwarf_unwind_step(s->dwarf, &frame) != CFREE_OK || frame.pc == 0) {
/* Fall back to stepping into the call. */
return run_step_line_loop(s);
}
/* frame.pc is now a runtime return-address (from the stack). */
- if (dbg_bp_set_internal(s, frame.pc, &bp_id) != 0) {
+ if (dbg_bp_set_internal(s, frame.pc, &bp_id) != CFREE_OK) {
return run_step_line_loop(s);
}
- if (dbg_session_signal_resume(s) != 0) return 1;
- if (dbg_session_wait_stop(s) != 0) return 1;
+ st = dbg_session_signal_resume(s);
+ if (st != CFREE_OK) return st;
+ st = dbg_session_wait_stop(s);
+ if (st != CFREE_OK) return st;
if (stop_is_internal_completion(s)) {
return run_step_line_loop(s);
}
- return 0;
+ return CFREE_OK;
}
}
-int dbg_step_resume(CfreeJitSession* s, CfreeResumeMode mode) {
+CfreeStatus dbg_step_resume(CfreeJitSession* s, CfreeResumeMode mode) {
switch (mode) {
case CFREE_RESUME_CONTINUE:
if (dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) {
return prepare_step_insn(s);
}
- return 0;
+ return CFREE_OK;
case CFREE_RESUME_STEP_INSN:
return prepare_step_insn(s);
- case CFREE_RESUME_STEP_LINE:
- if (!s->dwarf) return 1;
- if (run_step_line_loop(s) != 0) return 1;
+ case CFREE_RESUME_STEP_LINE: {
+ CfreeStatus st;
+ if (!s->dwarf) return CFREE_INVALID;
+ st = run_step_line_loop(s);
+ if (st != CFREE_OK) return st;
s->pending_done = 1;
- return 0;
+ return CFREE_OK;
+ }
- case CFREE_RESUME_NEXT_LINE:
- if (!s->dwarf) return 1;
- if (s->arch != CFREE_ARCH_ARM_64) return 1;
- if (run_next_line(s) != 0) return 1;
+ case CFREE_RESUME_NEXT_LINE: {
+ CfreeStatus st;
+ if (!s->dwarf) return CFREE_INVALID;
+ if (s->arch != CFREE_ARCH_ARM_64) return CFREE_UNSUPPORTED;
+ st = run_next_line(s);
+ if (st != CFREE_OK) return st;
s->pending_done = 1;
- return 0;
+ return CFREE_OK;
+ }
- case CFREE_RESUME_STEP_OUT:
- if (!s->dwarf) return 1;
- if (run_step_out(s) != 0) return 1;
+ case CFREE_RESUME_STEP_OUT: {
+ CfreeStatus st;
+ if (!s->dwarf) return CFREE_INVALID;
+ st = run_step_out(s);
+ if (st != CFREE_OK) return st;
s->pending_done = 1;
- return 0;
+ return CFREE_OK;
+ }
}
- return 1;
+ return CFREE_INVALID;
}
diff --git a/src/debug/debug.c b/src/debug/debug.c
@@ -34,7 +34,7 @@ DebugType* debug_type_at(Debug* d, DebugTypeId id) {
/* ---- public API: lifecycle ---- */
Debug* debug_new(Compiler* c, ObjBuilder* ob) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
Debug* d = (Debug*)h->alloc(h, sizeof(*d), _Alignof(Debug));
SrcLoc no_loc = {0, 0, 0};
if (!d) return NULL;
diff --git a/src/debug/dwarf_cfi.c b/src/debug/dwarf_cfi.c
@@ -12,10 +12,12 @@
* callers must treat 1 as "stack bottom" per the API contract.
*/
-#include <cfree.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/arch.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "debug/dwarf_internal.h"
@@ -209,10 +211,11 @@ static void run_cfi(const u8* prog, u32 plen, CfiState* st, u64* loc,
}
}
-int cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
+CfreeStatus cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
u32 off;
- if (!d || !frame) return 1;
- if (d->eh_frame.sec_idx == UINT32_MAX || d->eh_frame.size == 0) return 1;
+ if (!d || !frame) return CFREE_INVALID;
+ if (d->eh_frame.sec_idx == UINT32_MAX || d->eh_frame.size == 0)
+ return CFREE_NOT_FOUND;
/* Sweep .eh_frame entries, locating the FDE that covers frame->pc. */
off = 0;
while (off < d->eh_frame.size) {
@@ -221,7 +224,7 @@ int cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
u32 cie_id_off = off;
u32 cie_id;
if (length == 0) break; /* terminator */
- if (length == 0xffffffffu) return 1; /* 64-bit eh_frame unsupported */
+ if (length == 0xffffffffu) return CFREE_UNSUPPORTED; /* 64-bit eh_frame */
entry_end = off + length;
cie_id = dw_u32(d->eh_frame.data, d->eh_frame.size, &off);
if (cie_id == 0) {
@@ -356,7 +359,7 @@ int cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
&st, &loc, frame->pc);
}
/* Compute caller frame. */
- if (st.cfa_kind != 0 || st.cfa_reg >= 32) return 1;
+ if (st.cfa_kind != 0 || st.cfa_reg >= 32) return CFREE_UNSUPPORTED;
{
u64 cfa = frame->regs[st.cfa_reg] + (u64)st.cfa_offset;
u32 r;
@@ -375,7 +378,7 @@ int cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
} else if (st.return_reg < 32 && st.rules[st.return_reg].kind == 2) {
ret_addr = frame->regs[st.rules[st.return_reg].reg];
} else {
- return 1; /* bottom of stack */
+ return CFREE_NOT_FOUND; /* bottom of stack */
}
frame->cfa = cfa;
frame->pc = ret_addr;
@@ -385,8 +388,8 @@ int cfree_dwarf_unwind_step(CfreeDebugInfo* d, CfreeUnwindFrame* frame) {
(void)r;
}
}
- return 0;
+ return CFREE_OK;
}
}
- return 1;
+ return CFREE_NOT_FOUND;
}
diff --git a/src/debug/dwarf_die.c b/src/debug/dwarf_die.c
@@ -5,11 +5,12 @@
* variables. Cross-CU refs land later when needed.
*/
-#include <cfree.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "core/util.h"
diff --git a/src/debug/dwarf_internal.h b/src/debug/dwarf_internal.h
@@ -9,7 +9,9 @@
* DWARF wire format is the only contract between producer and consumer.
*/
-#include <cfree.h>
+#include <cfree/arch.h>
+#include <cfree/dwarf.h>
+#include <cfree/object.h>
#include "core/core.h"
#include "core/heap.h"
@@ -211,8 +213,12 @@ typedef struct DwString {
} DwString;
struct CfreeDebugInfo {
- CfreeCompiler *c;
+ const CfreeContext *ctx;
Heap *h;
+ /* Local string pool for interned strings (file paths, etc). The
+ * consumer used to borrow the compiler's global pool, but the new
+ * cfree_dwarf_open takes only a CfreeContext — no compiler. */
+ struct Pool *strs;
const CfreeObjFile *obj;
/* Sections */
diff --git a/src/debug/dwarf_line.c b/src/debug/dwarf_line.c
@@ -4,11 +4,12 @@
* a row matrix, and index it for addr→line and (file, line)→addr lookup.
*/
-#include <cfree.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "core/util.h"
@@ -439,37 +440,30 @@ void dw_build_line(CfreeDebugInfo* d, u32 cu_idx) {
/* Lookup helpers. Build all CU line tables on demand, walk each. */
-int cfree_dwarf_addr_to_line(CfreeDebugInfo* d, uint64_t pc,
- const char** file_out, uint32_t* line_out,
- uint32_t* col_out) {
- /* Return codes:
- * 0 — PC has a line entry; outputs filled.
- * 1 — PC sits inside a CU's coverage range but no row matched.
- * 2 — PC outside every CU's address coverage (e.g. JIT-emitted thunk
- * or a frame inside a `.o` linked without `-g`). REPL: "no
- * debug info for this frame". */
+CfreeStatus cfree_dwarf_addr_to_line(CfreeDebugInfo* d, uint64_t pc,
+ const char** file_out, uint32_t* line_out,
+ uint32_t* col_out) {
+ /* Status codes:
+ * CFREE_OK — PC has a line entry; outputs filled.
+ * CFREE_NOT_FOUND — PC has no line entry (either inside a CU's
+ * coverage with no row, or outside every CU). */
u32 i;
- int any_in_range = 0;
if (file_out) *file_out = NULL;
if (line_out) *line_out = 0;
if (col_out) *col_out = 0;
- if (!d) return 1;
+ if (!d) return CFREE_INVALID;
for (i = 0; i < d->ncus; ++i) {
DwLineProgram* lp;
u32 j;
DwLineRow* best = NULL;
- uint64_t cu_lo = (uint64_t)-1, cu_hi = 0;
if (!d->lines_built[i]) dw_build_line(d, i);
lp = &d->lines_by_cu[i];
for (j = 0; j < lp->nrows; ++j) {
DwLineRow* r = &lp->rows[j];
- if (r->address < cu_lo) cu_lo = r->address;
- if (r->address > cu_hi) cu_hi = r->address;
if (r->end_sequence) continue;
if (r->address > pc) break;
best = r;
}
- if (pc >= cu_lo && pc <= cu_hi) any_in_range = 1;
if (best) {
const char* f = "";
if (best->file_index < lp->nfile_norm && lp->file_norm)
@@ -477,10 +471,10 @@ int cfree_dwarf_addr_to_line(CfreeDebugInfo* d, uint64_t pc,
if (file_out) *file_out = f;
if (line_out) *line_out = best->line;
if (col_out) *col_out = best->column;
- return 0;
+ return CFREE_OK;
}
}
- return any_in_range ? 1 : 2;
+ return CFREE_NOT_FOUND;
}
/* file_norm matches user-typed `file` if either it is exactly equal, or it
@@ -496,33 +490,24 @@ static int dw_file_matches(const char* file_norm, const char* user, size_t ulen)
return memcmp(file_norm + flen - ulen, user, ulen) == 0;
}
-int cfree_dwarf_line_to_addr(CfreeDebugInfo* d, const char* file, uint32_t line,
- uint64_t* pc_out) {
- /* Returns:
- * 0 — unique match; pc_out filled with that PC.
- * 1 — file `file` does not appear in any CU we scanned (per-DWARF.md
- * "no data" semantics: caller can format this as "file not
- * covered" if it cares to distinguish from a stale line).
- * 2 — `file` appears in some CU but no row matches (file, line).
- * 3 — ambiguous: more than one distinct PC matches (file, line) via
- * suffix. pc_out is filled with the first match so callers that
- * don't disambiguate still get a usable PC. Use
- * cfree_dwarf_line_to_addr_all to enumerate candidates. */
- /* Ambiguity is keyed on distinct file_norm *paths* matching the
- * suffix, not on distinct PCs. Multiple PCs on the same line of the
- * same source file are expected (one row per instruction) — they're
- * not ambiguity, just line-program granularity. */
+CfreeStatus cfree_dwarf_line_to_addr(CfreeDebugInfo* d, const char* file,
+ uint32_t line, uint64_t* pc_out) {
+ /* Status:
+ * CFREE_OK — unique match; *pc_out filled.
+ * CFREE_NOT_FOUND — file unknown, or known but no row at `line`.
+ * CFREE_AMBIGUOUS — multiple distinct file paths match (file,line);
+ * *pc_out is the first hit, use
+ * cfree_dwarf_line_to_addr_all to enumerate. */
u32 i;
size_t ulen;
const char* first_path = NULL;
uint64_t first_pc = 0;
const char* alt_path = NULL;
- int file_seen = 0;
int line_hits = 0;
if (pc_out) *pc_out = 0;
- if (!d || !file) return 1;
+ if (!d || !file) return CFREE_INVALID;
ulen = strlen(file);
- if (ulen == 0) return 1;
+ if (ulen == 0) return CFREE_INVALID;
for (i = 0; i < d->ncus; ++i) {
DwLineProgram* lp;
u32 j;
@@ -535,7 +520,6 @@ int cfree_dwarf_line_to_addr(CfreeDebugInfo* d, const char* file, uint32_t line,
if (r->file_index >= lp->nfile_norm || !lp->file_norm) continue;
f = lp->file_norm[r->file_index];
if (!dw_file_matches(f, file, ulen)) continue;
- file_seen = 1;
if (r->line != line) continue;
++line_hits;
if (!first_path) {
@@ -547,10 +531,9 @@ int cfree_dwarf_line_to_addr(CfreeDebugInfo* d, const char* file, uint32_t line,
}
}
if (pc_out) *pc_out = first_pc;
- if (alt_path) return 3;
- if (line_hits > 0) return 0;
- if (file_seen) return 2;
- return 1;
+ if (alt_path) return CFREE_AMBIGUOUS;
+ if (line_hits > 0) return CFREE_OK;
+ return CFREE_NOT_FOUND;
}
/* Enumerate all distinct candidate (pc, file_norm) pairs for the given
@@ -559,9 +542,10 @@ int cfree_dwarf_line_to_addr(CfreeDebugInfo* d, const char* file, uint32_t line,
* which case only the first `cap` are written). Returns 0 on success
* (including 0 candidates), 1 on invalid args. Intended for REPL
* disambiguation after cfree_dwarf_line_to_addr returns 3. */
-int cfree_dwarf_line_to_addr_all(CfreeDebugInfo* d, const char* file,
- uint32_t line, CfreeDwarfLineMatch* out,
- uint32_t cap, uint32_t* n_out) {
+CfreeStatus cfree_dwarf_line_to_addr_all(CfreeDebugInfo* d, const char* file,
+ uint32_t line,
+ CfreeDwarfLineMatch* out, uint32_t cap,
+ uint32_t* n_out) {
/* One candidate per distinct file_norm path (not per PC). PC is the
* first matching row's address for that file_norm — i.e. the same PC
* that cfree_dwarf_line_to_addr would have returned for that file. */
@@ -569,9 +553,9 @@ int cfree_dwarf_line_to_addr_all(CfreeDebugInfo* d, const char* file,
size_t ulen;
uint32_t total = 0;
if (n_out) *n_out = 0;
- if (!d || !file) return 1;
+ if (!d || !file) return CFREE_INVALID;
ulen = strlen(file);
- if (ulen == 0) return 1;
+ if (ulen == 0) return CFREE_INVALID;
for (i = 0; i < d->ncus; ++i) {
DwLineProgram* lp;
u32 j;
@@ -607,5 +591,5 @@ int cfree_dwarf_line_to_addr_all(CfreeDebugInfo* d, const char* file,
}
}
if (n_out) *n_out = total;
- return 0;
+ return CFREE_OK;
}
diff --git a/src/debug/dwarf_loc.c b/src/debug/dwarf_loc.c
@@ -6,10 +6,12 @@
* caller passes the CFA in via frame->cfa.
*/
-#include <cfree.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/arch.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "debug/dwarf_internal.h"
diff --git a/src/debug/dwarf_open.c b/src/debug/dwarf_open.c
@@ -5,10 +5,13 @@
* Return NULL if any of those mandatory five are missing.
*/
-#include <cfree.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/arch.h>
+#include <cfree/dwarf.h>
+#include <cfree/object.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "core/pool.h"
@@ -26,10 +29,12 @@ void dw_find_section(CfreeDebugInfo* d, const char* name, DwSection* out) {
if (!d->obj) return;
n = cfree_obj_nsections(d->obj);
for (i = 0; i < n; ++i) {
- CfreeObjSecInfo info = cfree_obj_section(d->obj, i);
+ CfreeObjSecInfo info;
+ if (cfree_obj_section(d->obj, i, &info) != CFREE_OK) continue;
if (info.name && dw_streq(info.name, name)) {
size_t len = 0;
- const uint8_t* p = cfree_obj_section_data(d->obj, i, &len);
+ const uint8_t* p = NULL;
+ if (cfree_obj_section_data(d->obj, i, &p, &len) != CFREE_OK) continue;
out->data = p;
out->size = (u32)len;
out->sec_idx = i;
@@ -131,8 +136,8 @@ const char* dw_cstr(const u8* base, u32 size, u32* off) {
/* ---- string interning ------------------------------------------------- */
const char* dw_intern(CfreeDebugInfo* d, const char* s, size_t len) {
- Sym sym = pool_intern(d->c->global, s, len);
- return pool_str(d->c->global, sym, NULL);
+ Sym sym = pool_intern(d->strs, s, len);
+ return pool_str(d->strs, sym, NULL);
}
/* Resolve a .debug_str offset. */
@@ -638,17 +643,26 @@ int dw_die_attr(CfreeDebugInfo* d, const DwCu* cu, DwDie* die, u32 attr,
/* ---- public open/close ----------------------------------------------- */
-CfreeDebugInfo* cfree_dwarf_open(CfreeCompiler* c, const CfreeObjFile* obj) {
+CfreeStatus cfree_dwarf_open(const CfreeContext* ctx, const CfreeObjFile* obj,
+ CfreeDebugInfo** out) {
Heap* h;
CfreeDebugInfo* d;
- if (!c || !obj) return NULL;
- h = (Heap*)c->env->heap;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!ctx || !ctx->heap || !obj) return CFREE_INVALID;
+ h = ctx->heap;
d = (CfreeDebugInfo*)h->alloc(h, sizeof(*d), _Alignof(CfreeDebugInfo));
- if (!d) return NULL;
+ if (!d) return CFREE_NOMEM;
memset(d, 0, sizeof(*d));
- d->c = c;
+ d->ctx = ctx;
d->h = h;
d->obj = obj;
+ d->strs = (Pool*)h->alloc(h, sizeof(*d->strs), _Alignof(Pool));
+ if (!d->strs) {
+ cfree_dwarf_free(d);
+ return CFREE_NOMEM;
+ }
+ pool_init(d->strs, h);
dw_find_section(d, ".debug_abbrev", &d->abbrev);
dw_find_section(d, ".debug_info", &d->info);
@@ -665,16 +679,16 @@ CfreeDebugInfo* cfree_dwarf_open(CfreeCompiler* c, const CfreeObjFile* obj) {
if (d->abbrev.sec_idx == UINT32_MAX || d->info.sec_idx == UINT32_MAX ||
d->line.sec_idx == UINT32_MAX || d->str.sec_idx == UINT32_MAX ||
d->line_str.sec_idx == UINT32_MAX) {
- cfree_dwarf_close(d);
- return NULL;
+ cfree_dwarf_free(d);
+ return CFREE_NOT_FOUND;
}
/* str_offsets_base default: in the absence of DW_AT_str_offsets_base, the
* offsets section starts with an 8-byte header (uniform for DW5). */
dw_parse_all_cus(d);
if (d->ncus == 0) {
- cfree_dwarf_close(d);
- return NULL;
+ cfree_dwarf_free(d);
+ return CFREE_MALFORMED;
}
/* Allocate per-CU lazy line-program state. */
@@ -683,14 +697,15 @@ CfreeDebugInfo* cfree_dwarf_open(CfreeCompiler* c, const CfreeObjFile* obj) {
h, d->ncus * sizeof(DwLineProgram), _Alignof(DwLineProgram));
d->lines_built = (u8*)h->alloc(h, d->ncus, 1);
if (!d->lines_by_cu || !d->lines_built) {
- cfree_dwarf_close(d);
- return NULL;
+ cfree_dwarf_free(d);
+ return CFREE_NOMEM;
}
memset(d->lines_by_cu, 0, d->ncus * sizeof(DwLineProgram));
memset(d->lines_built, 0, d->ncus);
}
- return d;
+ *out = d;
+ return CFREE_OK;
}
static void free_subprog(Heap* h, DwSubprog* sp) {
@@ -698,7 +713,7 @@ static void free_subprog(Heap* h, DwSubprog* sp) {
if (sp->locals) h->free(h, sp->locals, sp->nlocals * sizeof(DwLocal));
}
-void cfree_dwarf_close(CfreeDebugInfo* d) {
+void cfree_dwarf_free(CfreeDebugInfo* d) {
Heap* h;
u32 i;
if (!d) return;
@@ -746,5 +761,10 @@ void cfree_dwarf_close(CfreeDebugInfo* d) {
if (d->globals) h->free(h, d->globals, d->globals_cap * sizeof(DwLocal));
+ if (d->strs) {
+ pool_fini(d->strs);
+ h->free(h, d->strs, sizeof(*d->strs));
+ }
+
h->free(h, d, sizeof(*d));
}
diff --git a/src/debug/dwarf_query.c b/src/debug/dwarf_query.c
@@ -4,11 +4,13 @@
* subprogram_at / func_at, var_at, vars_at_*, param_iter_*, loc_read.
*/
-#include <cfree.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/arch.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "debug/dwarf_internal.h"
@@ -27,16 +29,16 @@ static void fill_subprogram(CfreeDebugInfo *d, DwSubprog *sp,
out->inlined = sp->inlined;
}
-int cfree_dwarf_subprogram_at(CfreeDebugInfo *d, uint64_t pc,
- CfreeDwarfSubprogram *out) {
+CfreeStatus cfree_dwarf_subprogram_at(CfreeDebugInfo *d, uint64_t pc,
+ CfreeDwarfSubprogram *out) {
DwSubprog *sp;
if (!d || !out)
- return 1;
+ return CFREE_INVALID;
sp = dw_find_subprog(d, pc);
if (!sp)
- return 1;
+ return CFREE_NOT_FOUND;
fill_subprogram(d, sp, out);
- return 0;
+ return CFREE_OK;
}
static DwSubprog *dw_find_subprog_named(CfreeDebugInfo *d, const char *name) {
@@ -63,30 +65,32 @@ static DwSubprog *dw_find_subprog_named(CfreeDebugInfo *d, const char *name) {
return NULL;
}
-int cfree_dwarf_subprogram_named(CfreeDebugInfo *d, const char *name,
- CfreeDwarfSubprogram *out) {
+CfreeStatus cfree_dwarf_subprogram_named(CfreeDebugInfo *d, const char *name,
+ CfreeDwarfSubprogram *out) {
DwSubprog *sp;
if (!d || !name || !out)
- return 1;
+ return CFREE_INVALID;
sp = dw_find_subprog_named(d, name);
if (!sp)
- return 1;
+ return CFREE_NOT_FOUND;
fill_subprogram(d, sp, out);
- return 0;
+ return CFREE_OK;
}
-int cfree_dwarf_func_at(CfreeDebugInfo *d, uint64_t pc, const char **name_out,
- uint64_t *low_out, uint64_t *high_out) {
+CfreeStatus cfree_dwarf_func_at(CfreeDebugInfo *d, uint64_t pc,
+ const char **name_out, uint64_t *low_out,
+ uint64_t *high_out) {
CfreeDwarfSubprogram sp;
- if (cfree_dwarf_subprogram_at(d, pc, &sp) != 0)
- return 1;
+ CfreeStatus st = cfree_dwarf_subprogram_at(d, pc, &sp);
+ if (st != CFREE_OK)
+ return st;
if (name_out)
*name_out = sp.name;
if (low_out)
*low_out = sp.low_pc;
if (high_out)
*high_out = sp.high_pc;
- return 0;
+ return CFREE_OK;
}
/* ---- variable resolution -------------------------------------------- */
@@ -167,20 +171,18 @@ static void fill_varloc(CfreeDebugInfo *d, u32 cu_idx, const DwLocal *v, u64 pc,
out->v.expr.len = 0;
}
-int cfree_dwarf_var_at(CfreeDebugInfo *d, uint64_t pc, const char *name,
- CfreeDwarfVarLoc *out) {
- /* Return codes:
- * 0 — found; *out filled.
- * 1 — invalid args, or `pc` lies inside a known subprogram but no
- * variable named `name` is visible there (the user typo case).
- * 2 — `pc` is not covered by any subprogram (no debug info for this
- * frame). REPL: "no debug info for this frame". Globals are
- * still consulted before returning 2 so a name lookup against a
- * global from a -g-less frame still resolves. */
+CfreeStatus cfree_dwarf_var_at(CfreeDebugInfo *d, uint64_t pc, const char *name,
+ CfreeDwarfVarLoc *out) {
+ /* Status codes:
+ * CFREE_OK — found; *out filled.
+ * CFREE_INVALID — bad args.
+ * CFREE_NOT_FOUND — pc inside a subprog but no var named `name`, or
+ * pc outside any subprogram and not a global.
+ */
DwSubprog *sp;
u32 i;
if (!d || !name || !out)
- return 1;
+ return CFREE_INVALID;
memset(out, 0, sizeof(*out));
sp = dw_find_subprog(d, pc);
if (sp) {
@@ -194,7 +196,7 @@ int cfree_dwarf_var_at(CfreeDebugInfo *d, uint64_t pc, const char *name,
if (v->has_scope && (pc < v->scope_lo || pc >= v->scope_hi))
continue;
fill_varloc(d, sp->cu_idx, v, pc, out);
- return 0;
+ return CFREE_OK;
}
/* Then params. */
for (i = 0; i < sp->nparams; ++i) {
@@ -202,7 +204,7 @@ int cfree_dwarf_var_at(CfreeDebugInfo *d, uint64_t pc, const char *name,
if (!v->name || !dw_streq(v->name, name))
continue;
fill_varloc(d, sp->cu_idx, v, pc, out);
- return 0;
+ return CFREE_OK;
}
}
/* Globals. */
@@ -212,19 +214,20 @@ int cfree_dwarf_var_at(CfreeDebugInfo *d, uint64_t pc, const char *name,
if (!v->name || !dw_streq(v->name, name))
continue;
fill_varloc(d, 0, v, pc, out);
- return 0;
+ return CFREE_OK;
}
- return sp ? 1 : 2;
+ return CFREE_NOT_FOUND;
}
-int cfree_dwarf_loc_read(CfreeDebugInfo *d, const CfreeDwarfVarLoc *loc,
- const CfreeUnwindFrame *frame, CfreeJitSession *sess,
- void *dst, size_t cap, size_t *read_out) {
+CfreeStatus cfree_dwarf_loc_read(CfreeDebugInfo *d, const CfreeDwarfVarLoc *loc,
+ const CfreeUnwindFrame *frame,
+ CfreeDwarfReadMemFn read_mem, void *read_user,
+ void *dst, size_t cap, size_t *read_out) {
size_t want;
if (read_out)
*read_out = 0;
if (!d || !loc || !frame || !dst)
- return 1;
+ return CFREE_INVALID;
want = loc->byte_size ? loc->byte_size : cap;
if (want > cap)
want = cap;
@@ -235,27 +238,31 @@ int cfree_dwarf_loc_read(CfreeDebugInfo *d, const CfreeDwarfVarLoc *loc,
memcpy(dst, &v, n);
if (read_out)
*read_out = n;
- return 0;
+ return CFREE_OK;
}
case CFREE_DLOC_FRAME_OFS: {
uint64_t addr = frame->cfa + (uint64_t)(int64_t)loc->v.frame_ofs;
- if (!sess)
- return 1;
- if (cfree_jit_session_read_mem(sess, addr, dst, want) != 0)
- return 1;
+ CfreeStatus st;
+ if (!read_mem)
+ return CFREE_INVALID;
+ st = read_mem(read_user, addr, dst, want);
+ if (st != CFREE_OK)
+ return st;
if (read_out)
*read_out = want;
- return 0;
+ return CFREE_OK;
}
case CFREE_DLOC_GLOBAL: {
uint64_t addr = loc->v.global;
- if (!sess)
- return 1;
- if (cfree_jit_session_read_mem(sess, addr, dst, want) != 0)
- return 1;
+ CfreeStatus st;
+ if (!read_mem)
+ return CFREE_INVALID;
+ st = read_mem(read_user, addr, dst, want);
+ if (st != CFREE_OK)
+ return st;
if (read_out)
*read_out = want;
- return 0;
+ return CFREE_OK;
}
case CFREE_DLOC_EXPR: {
/* Evaluate. We don't have direct access to the variable's
@@ -264,36 +271,38 @@ int cfree_dwarf_loc_read(CfreeDebugInfo *d, const CfreeDwarfVarLoc *loc,
* + DW_OP_consts + DW_OP_plus, etc. */
DwExprResult r;
if (loc->v.expr.bytes == NULL || loc->v.expr.len == 0)
- return 1;
+ return CFREE_NOT_FOUND;
if (dw_eval_expr(d, loc->v.expr.bytes, (u32)loc->v.expr.len, NULL, 0, frame,
&r) != 0)
- return 1;
+ return CFREE_UNSUPPORTED;
if (r.kind == 0) {
- if (!sess)
- return 1;
- if (cfree_jit_session_read_mem(sess, r.value, dst, want) != 0)
- return 1;
+ CfreeStatus st;
+ if (!read_mem)
+ return CFREE_INVALID;
+ st = read_mem(read_user, r.value, dst, want);
+ if (st != CFREE_OK)
+ return st;
if (read_out)
*read_out = want;
- return 0;
+ return CFREE_OK;
} else if (r.kind == 1) {
size_t n = want > sizeof(r.value) ? sizeof(r.value) : want;
memcpy(dst, &r.value, n);
if (read_out)
*read_out = n;
- return 0;
+ return CFREE_OK;
} else if (r.kind == 2) {
u64 v = (r.value < 32) ? frame->regs[r.value] : 0;
size_t n = want > sizeof(v) ? sizeof(v) : want;
memcpy(dst, &v, n);
if (read_out)
*read_out = n;
- return 0;
+ return CFREE_OK;
}
- return 1;
+ return CFREE_UNSUPPORTED;
}
}
- return 1;
+ return CFREE_UNSUPPORTED;
}
/* ---- vars_at_* iterator --------------------------------------------- */
@@ -307,15 +316,18 @@ struct CfreeDwarfVarIter {
u32 idx;
};
-CfreeDwarfVarIter *cfree_dwarf_vars_at_new(CfreeDebugInfo *d, uint64_t pc,
- uint32_t mask) {
+CfreeStatus cfree_dwarf_vars_at_new(CfreeDebugInfo *d, uint64_t pc,
+ uint32_t mask, CfreeDwarfVarIter **out) {
CfreeDwarfVarIter *it;
+ if (!out)
+ return CFREE_INVALID;
+ *out = NULL;
if (!d)
- return NULL;
+ return CFREE_INVALID;
it = (CfreeDwarfVarIter *)d->h->alloc(d->h, sizeof(*it),
_Alignof(CfreeDwarfVarIter));
if (!it)
- return NULL;
+ return CFREE_NOMEM;
it->d = d;
it->pc = pc;
it->mask = mask;
@@ -324,12 +336,14 @@ CfreeDwarfVarIter *cfree_dwarf_vars_at_new(CfreeDebugInfo *d, uint64_t pc,
dw_build_locals(d, it->sp);
it->phase = 0;
it->idx = it->sp ? it->sp->nlocals : 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_dwarf_vars_at_next(CfreeDwarfVarIter *it, CfreeDwarfVar *out) {
+CfreeIterResult cfree_dwarf_vars_at_next(CfreeDwarfVarIter *it,
+ CfreeDwarfVar *out) {
if (!it || !out)
- return 0;
+ return CFREE_ITER_ERROR;
for (;;) {
switch (it->phase) {
case 0: {
@@ -350,7 +364,7 @@ int cfree_dwarf_vars_at_next(CfreeDwarfVarIter *it, CfreeDwarfVar *out) {
out->name = v->name ? v->name : "";
out->role = CFREE_DVR_LOCAL;
fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
- return 1;
+ return CFREE_ITER_ITEM;
}
}
case 1: {
@@ -369,7 +383,7 @@ int cfree_dwarf_vars_at_next(CfreeDwarfVarIter *it, CfreeDwarfVar *out) {
out->name = v->name ? v->name : "";
out->role = CFREE_DVR_ARG;
fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
- return 1;
+ return CFREE_ITER_ITEM;
}
}
case 2: {
@@ -387,11 +401,11 @@ int cfree_dwarf_vars_at_next(CfreeDwarfVarIter *it, CfreeDwarfVar *out) {
out->name = v->name ? v->name : "";
out->role = CFREE_DVR_GLOBAL;
fill_varloc(it->d, 0, v, it->pc, &out->loc);
- return 1;
+ return CFREE_ITER_ITEM;
}
}
default:
- return 0;
+ return CFREE_ITER_END;
}
}
}
@@ -411,60 +425,70 @@ struct CfreeDwarfParamIter {
u32 idx;
};
-CfreeDwarfParamIter *cfree_dwarf_param_iter_new(CfreeDebugInfo *d,
- uint64_t pc) {
+CfreeStatus cfree_dwarf_param_iter_new(CfreeDebugInfo *d, uint64_t pc,
+ CfreeDwarfParamIter **out) {
CfreeDwarfParamIter *it;
DwSubprog *sp;
+ if (!out)
+ return CFREE_INVALID;
+ *out = NULL;
if (!d)
- return NULL;
+ return CFREE_INVALID;
sp = dw_find_subprog(d, pc);
if (!sp)
- return NULL;
+ return CFREE_NOT_FOUND;
dw_build_locals(d, sp);
it = (CfreeDwarfParamIter *)d->h->alloc(d->h, sizeof(*it),
_Alignof(CfreeDwarfParamIter));
if (!it)
- return NULL;
+ return CFREE_NOMEM;
it->d = d;
it->sp = sp;
it->pc = pc;
it->idx = 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-CfreeDwarfParamIter *cfree_dwarf_param_iter_new_named(CfreeDebugInfo *d,
- const char *name) {
+CfreeStatus cfree_dwarf_param_iter_new_named(CfreeDebugInfo *d,
+ const char *name,
+ CfreeDwarfParamIter **out) {
CfreeDwarfParamIter *it;
DwSubprog *sp;
+ if (!out)
+ return CFREE_INVALID;
+ *out = NULL;
if (!d || !name)
- return NULL;
+ return CFREE_INVALID;
sp = dw_find_subprog_named(d, name);
if (!sp)
- return NULL;
+ return CFREE_NOT_FOUND;
dw_build_locals(d, sp);
it = (CfreeDwarfParamIter *)d->h->alloc(d->h, sizeof(*it),
_Alignof(CfreeDwarfParamIter));
if (!it)
- return NULL;
+ return CFREE_NOMEM;
it->d = d;
it->sp = sp;
it->pc = sp->low_pc;
it->idx = 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_dwarf_param_iter_next(CfreeDwarfParamIter *it, CfreeDwarfVar *out) {
+CfreeIterResult cfree_dwarf_param_iter_next(CfreeDwarfParamIter *it,
+ CfreeDwarfVar *out) {
if (!it || !out)
- return 0;
+ return CFREE_ITER_ERROR;
if (it->idx >= it->sp->nparams)
- return 0;
+ return CFREE_ITER_END;
{
DwLocal *v = &it->sp->params[it->idx++];
out->name = v->name ? v->name : "";
out->role = CFREE_DVR_ARG;
fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
}
- return 1;
+ return CFREE_ITER_ITEM;
}
void cfree_dwarf_param_iter_free(CfreeDwarfParamIter *it) {
diff --git a/src/debug/dwarf_type.c b/src/debug/dwarf_type.c
@@ -5,11 +5,12 @@
* and qualifier-types (const/volatile/restrict transparent to inner).
*/
-#include <cfree.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include <cfree/dwarf.h>
+
#include "core/core.h"
#include "core/heap.h"
#include "core/util.h"
@@ -430,13 +431,16 @@ struct CfreeDwarfFieldIter {
u32 idx;
};
-CfreeDwarfFieldIter* cfree_dwarf_field_iter_new(CfreeDebugInfo* d,
- const CfreeDwarfType* t) {
+CfreeStatus cfree_dwarf_field_iter_new(CfreeDebugInfo* d,
+ const CfreeDwarfType* t,
+ CfreeDwarfFieldIter** out) {
CfreeDwarfFieldIter* it;
- if (!d || !t) return NULL;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!d || !t) return CFREE_INVALID;
it = (CfreeDwarfFieldIter*)d->h->alloc(d->h, sizeof(*it),
_Alignof(CfreeDwarfFieldIter));
- if (!it) return NULL;
+ if (!it) return CFREE_NOMEM;
it->d = d;
/* Look through typedef / qualifiers to the underlying aggregate. */
while (t && (t->kind == DTK_TYPEDEF || t->kind == DTK_CONST ||
@@ -444,15 +448,18 @@ CfreeDwarfFieldIter* cfree_dwarf_field_iter_new(CfreeDebugInfo* d,
t = t->inner;
it->t = t;
it->idx = 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_dwarf_field_iter_next(CfreeDwarfFieldIter* it, CfreeDwarfField* out) {
+CfreeIterResult cfree_dwarf_field_iter_next(CfreeDwarfFieldIter* it,
+ CfreeDwarfField* out) {
const CfreeDwarfType* t;
- if (!it || !out || !it->t) return 0;
+ if (!it || !out) return CFREE_ITER_ERROR;
+ if (!it->t) return CFREE_ITER_END;
t = it->t;
- if (t->kind != DTK_STRUCT && t->kind != DTK_UNION) return 0;
- if (it->idx >= t->nfields) return 0;
+ if (t->kind != DTK_STRUCT && t->kind != DTK_UNION) return CFREE_ITER_END;
+ if (it->idx >= t->nfields) return CFREE_ITER_END;
{
DwField* f = &t->fields[it->idx++];
out->name = f->name ? f->name : "";
@@ -461,7 +468,7 @@ int cfree_dwarf_field_iter_next(CfreeDwarfFieldIter* it, CfreeDwarfField* out) {
out->bit_size = f->bit_size;
out->type = f->type;
}
- return 1;
+ return CFREE_ITER_ITEM;
}
void cfree_dwarf_field_iter_free(CfreeDwarfFieldIter* it) {
@@ -475,32 +482,37 @@ struct CfreeDwarfEnumIter {
u32 idx;
};
-CfreeDwarfEnumIter* cfree_dwarf_enum_iter_new(CfreeDebugInfo* d,
- const CfreeDwarfType* t) {
+CfreeStatus cfree_dwarf_enum_iter_new(CfreeDebugInfo* d, const CfreeDwarfType* t,
+ CfreeDwarfEnumIter** out) {
CfreeDwarfEnumIter* it;
- if (!d || !t) return NULL;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!d || !t) return CFREE_INVALID;
it = (CfreeDwarfEnumIter*)d->h->alloc(d->h, sizeof(*it),
_Alignof(CfreeDwarfEnumIter));
- if (!it) return NULL;
+ if (!it) return CFREE_NOMEM;
it->d = d;
while (t && (t->kind == DTK_TYPEDEF || t->kind == DTK_CONST ||
t->kind == DTK_VOLATILE || t->kind == DTK_RESTRICT))
t = t->inner;
it->t = t;
it->idx = 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_dwarf_enum_iter_next(CfreeDwarfEnumIter* it, CfreeDwarfEnumVal* out) {
+CfreeIterResult cfree_dwarf_enum_iter_next(CfreeDwarfEnumIter* it,
+ CfreeDwarfEnumVal* out) {
const CfreeDwarfType* t;
- if (!it || !out || !it->t) return 0;
+ if (!it || !out) return CFREE_ITER_ERROR;
+ if (!it->t) return CFREE_ITER_END;
t = it->t;
- if (t->kind != DTK_ENUM) return 0;
- if (it->idx >= t->nevals) return 0;
+ if (t->kind != DTK_ENUM) return CFREE_ITER_END;
+ if (it->idx >= t->nevals) return CFREE_ITER_END;
out->name = t->evals[it->idx].name ? t->evals[it->idx].name : "";
out->value = t->evals[it->idx].value;
it->idx++;
- return 1;
+ return CFREE_ITER_ITEM;
}
void cfree_dwarf_enum_iter_free(CfreeDwarfEnumIter* it) {
diff --git a/src/emu/cpu.c b/src/emu/cpu.c
@@ -8,10 +8,8 @@
* lifecycle real (alloc, free, PC/SP getters, trap reason) so emu.c
* does not need to know anything about per-arch register files. */
-#include <cfree.h>
#include <string.h>
-#include "core/heap.h"
#include "emu/emu.h"
struct EmuCPUState {
@@ -31,7 +29,7 @@ EmuCPUState* emu_cpu_new(Compiler* c, CfreeEmuArch arch, u64 initial_pc,
Heap* h;
EmuCPUState* s;
if (!c) return NULL;
- h = (Heap*)c->env->heap;
+ h = c->ctx->heap;
s = (EmuCPUState*)h->alloc(h, sizeof(*s), _Alignof(EmuCPUState));
if (!s) return NULL;
memset(s, 0, sizeof(*s));
@@ -46,7 +44,7 @@ EmuCPUState* emu_cpu_new(Compiler* c, CfreeEmuArch arch, u64 initial_pc,
void emu_cpu_free(EmuCPUState* s) {
Heap* h;
if (!s) return;
- h = (Heap*)s->c->env->heap;
+ h = s->c->ctx->heap;
h->free(h, s, sizeof(*s));
}
diff --git a/src/emu/decode.c b/src/emu/decode.c
@@ -3,8 +3,6 @@
* disassembler (textual format) so there's one source of truth per
* ISA. v1 targets aarch64 and riscv64; backends land separately. */
-#include <cfree.h>
-
#include "core/core.h"
#include "emu/emu.h"
diff --git a/src/emu/elf_load.c b/src/emu/elf_load.c
@@ -10,10 +10,8 @@
* (typically `_start`). v1 executes statically-linked guest ELFs
* — dynamic-loader work is deferred (see doc/EMU.md §2). */
-#include <cfree.h>
#include <string.h>
-#include "core/heap.h"
#include "emu/emu.h"
#include "obj/obj.h"
diff --git a/src/emu/emu.c b/src/emu/emu.c
@@ -10,12 +10,9 @@
#include "emu/emu.h"
-#include <cfree.h>
-#include <cfree/cg.h>
#include <setjmp.h>
#include <string.h>
-#include "core/heap.h"
#include "core/pool.h"
#include "link/link.h"
#include "obj/obj.h"
@@ -28,6 +25,12 @@ struct CfreeEmu {
int opt_level;
CfreeEmuTraceFlags trace;
+ /* Borrowed JIT host (execmem + tls). The public CfreeEmuOptions has no
+ * field for this; the driver attaches one via emu_set_jit_host between
+ * cfree_emu_new and the first cfree_emu_run / cfree_emu_step. When
+ * NULL, runs of this emu surface CFREE_UNSUPPORTED. */
+ const CfreeJitHost* host;
+
EmuLoadedImage guest;
EmuCPUState* cpu;
@@ -56,23 +59,34 @@ static int arch_supported(CfreeEmuArch a) {
* a typedef so the call site reads cleanly in the dispatcher. */
typedef u64 (*EmuBlockFn)(EmuCPUState*);
-CfreeEmu* cfree_emu_new(CfreeCompiler* c, const CfreeEmuOptions* opts) {
+void emu_set_jit_host(CfreeEmu* e, const CfreeJitHost* host) {
+ if (!e) return;
+ e->host = host;
+}
+
+const CfreeJitHost* emu_get_jit_host(const CfreeEmu* e) {
+ return e ? e->host : NULL;
+}
+
+CfreeStatus cfree_emu_new(CfreeCompiler* c, const CfreeEmuOptions* opts,
+ CfreeEmu** out) {
PanicSave saved;
Heap* heap;
CfreeEmu* e;
- if (!c || !opts || !opts->guest_elf_bytes || opts->guest_elf_len == 0)
- return NULL;
- if (!arch_supported(opts->guest_arch)) return NULL;
+ if (out) *out = NULL;
+ if (!c || !opts || !out) return CFREE_INVALID;
+ if (!opts->guest_elf_bytes || opts->guest_elf_len == 0) return CFREE_INVALID;
+ if (!arch_supported(opts->guest_arch)) return CFREE_UNSUPPORTED;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return NULL;
+ return CFREE_ERR;
}
- heap = (Heap*)c->env->heap;
+ heap = c->ctx->heap;
e = (CfreeEmu*)heap->alloc(heap, sizeof(*e), _Alignof(CfreeEmu));
if (!e) compiler_panic(c, no_loc(), "emu: out of memory");
memset(e, 0, sizeof(*e));
@@ -93,8 +107,10 @@ CfreeEmu* cfree_emu_new(CfreeCompiler* c, const CfreeEmuOptions* opts) {
e->cpu =
emu_cpu_new(c, opts->guest_arch, e->guest.entry_pc, e->guest.initial_sp);
- /* 3. Reserve a fixed-VA code region for translated host blocks. */
- e->code_region = emu_code_region_new(c, EMU_CODE_REGION_SIZE);
+ /* 3. Code region is deferred until a JIT host is attached — the driver
+ * calls emu_set_jit_host before cfree_emu_run / cfree_emu_step.
+ * The linker session itself doesn't need execmem; only block
+ * materialization does, and that runs from cfree_emu_lookup. */
/* 4. Stand up the session linker. The extern resolver maps each
* EMU_SYM_* helper name to the host address of its trampoline /
@@ -103,24 +119,15 @@ CfreeEmu* cfree_emu_new(CfreeCompiler* c, const CfreeEmuOptions* opts) {
if (!e->linker) compiler_panic(c, no_loc(), "emu: link_new failed");
link_set_extern_resolver(e->linker, emu_runtime_extern_resolver, e);
- /* 5. Seed the initial empty image at the code region's base VA.
- * Subsequent cold blocks land via link_resolve_extend, which
- * must keep already-placed sections at stable host addresses
- * (block chaining patches them). */
- e->image = link_resolve_at(e->linker, emu_code_region_base(e->code_region));
- if (!e->image) compiler_panic(c, no_loc(), "emu: link_resolve_at failed");
-
- /* 6. Code cache: guest_pc -> host entry. Grows unbounded in v1. */
- e->cache = emu_cache_new(c);
-
compiler_panic_restore(c, &saved);
- return e;
+ *out = e;
+ return CFREE_OK;
}
void cfree_emu_free(CfreeEmu* e) {
Heap* heap;
if (!e) return;
- heap = (Heap*)e->c->env->heap;
+ heap = e->c->ctx->heap;
if (e->cache) emu_cache_free(e->cache);
if (e->image) link_image_free(e->image);
@@ -132,6 +139,25 @@ void cfree_emu_free(CfreeEmu* e) {
heap->free(heap, e, sizeof(*e));
}
+/* Lazily allocate the code region + initial image + cache the first time
+ * cfree_emu_lookup runs. Requires a wired JIT host. Returns CFREE_OK on
+ * success, CFREE_UNSUPPORTED if no host is wired, or panics for setup
+ * failures. */
+static CfreeStatus ensure_runtime(CfreeEmu* e) {
+ if (e->image) return CFREE_OK;
+ if (!e->host || !e->host->execmem) return CFREE_UNSUPPORTED;
+
+ e->code_region =
+ emu_code_region_new(e->c, e->host->execmem, EMU_CODE_REGION_SIZE);
+ if (!e->code_region) {
+ compiler_panic(e->c, no_loc(), "emu: emu_code_region_new failed");
+ }
+ e->image = link_resolve_at(e->linker, emu_code_region_base(e->code_region));
+ if (!e->image) compiler_panic(e->c, no_loc(), "emu: link_resolve_at failed");
+ e->cache = emu_cache_new(e->c);
+ return CFREE_OK;
+}
+
/* ---- Translation (cold-miss path) ---- */
static void* translate_block(CfreeEmu* e, u64 guest_pc) {
@@ -139,13 +165,14 @@ static void* translate_block(CfreeEmu* e, u64 guest_pc) {
u32 ninsts;
ObjBuilder* ob;
CfreeCg* cg;
- CfreeCompileOptions copts;
+ CfreeCodeOptions copts;
Sym block_name;
ObjSymId block_sym;
EmuLiftCtx ctx;
LinkSymId sym_id;
const LinkSymbol* sym;
void* entry;
+ CfreeStatus st;
if (e->trace & CFREE_EMU_TRACE_BLOCK) emu_trace_block(e->c, guest_pc);
@@ -174,8 +201,9 @@ static void* translate_block(CfreeEmu* e, u64 guest_pc) {
ob = obj_new(e->c);
memset(&copts, 0, sizeof(copts));
copts.opt_level = e->opt_level;
- cg = cfree_cg_new(e->c, ob, &copts);
- if (!cg) compiler_panic(e->c, no_loc(), "emu: cfree_cg_new failed");
+ st = cfree_cg_new(e->c, (CfreeObjBuilder*)ob, &copts, &cg);
+ if (st != CFREE_OK || !cg)
+ compiler_panic(e->c, no_loc(), "emu: cfree_cg_new failed");
block_name = emu_block_sym_name(e->c, guest_pc);
/* Forward-declare the block's symbol so the lifter can refer to it
@@ -235,8 +263,12 @@ void* cfree_emu_lookup(CfreeEmu* e, uint64_t guest_pc) {
if (!e) return NULL;
/* Cache hit short-circuits the panic boundary. */
- entry = emu_cache_lookup(e->cache, guest_pc);
- if (entry) return entry;
+ if (e->cache) {
+ entry = emu_cache_lookup(e->cache, guest_pc);
+ if (entry) return entry;
+ }
+
+ if (ensure_runtime(e) != CFREE_OK) return NULL;
compiler_panic_save(e->c, &saved);
if (setjmp(e->c->panic)) {
@@ -253,18 +285,21 @@ void* cfree_emu_lookup(CfreeEmu* e, uint64_t guest_pc) {
/* ---- Dispatcher ---- */
-int cfree_emu_step(CfreeEmu* e, uint32_t nblocks) {
+CfreeStatus cfree_emu_step(CfreeEmu* e, uint32_t nblocks) {
PanicSave saved;
uint32_t i;
+ CfreeStatus st;
- if (!e) return 1;
- if (e->done) return 0;
+ if (!e) return CFREE_INVALID;
+ if (e->done) return CFREE_OK;
+ st = ensure_runtime(e);
+ if (st != CFREE_OK) return st;
compiler_panic_save(e->c, &saved);
if (setjmp(e->c->panic)) {
compiler_run_cleanups(e->c);
compiler_panic_restore(e->c, &saved);
- return 1;
+ return CFREE_ERR;
}
for (i = 0; i < nblocks && !e->done; ++i) {
@@ -298,30 +333,32 @@ int cfree_emu_step(CfreeEmu* e, uint32_t nblocks) {
}
compiler_panic_restore(e->c, &saved);
- return 0;
+ return CFREE_OK;
}
-int cfree_emu_run(CfreeCompiler* c, const CfreeEmuOptions* opts,
- int* out_exit_code) {
- CfreeEmu* e;
- int rc = 0;
+CfreeStatus cfree_emu_run(CfreeCompiler* c, const CfreeEmuOptions* opts,
+ int* out_exit_code) {
+ CfreeEmu* e = NULL;
+ CfreeStatus st;
if (out_exit_code) *out_exit_code = 0;
- if (!c || !opts) return 1;
+ if (!c || !opts) return CFREE_INVALID;
- e = cfree_emu_new(c, opts);
- if (!e) return 1;
+ st = cfree_emu_new(c, opts, &e);
+ if (st != CFREE_OK) return st;
+ /* The public cfree_emu_run does not carry a JitHost, so the emu must
+ * already have one wired (driver-side) before we get here. Without it
+ * the first ensure_runtime call returns CFREE_UNSUPPORTED and we drop
+ * the emu cleanly. */
while (!e->done) {
- if (cfree_emu_step(e, 1024) != 0) {
- rc = 1;
- break;
- }
+ st = cfree_emu_step(e, 1024);
+ if (st != CFREE_OK) break;
}
- if (rc == 0 && out_exit_code) *out_exit_code = e->exit_code;
+ if (st == CFREE_OK && out_exit_code) *out_exit_code = e->exit_code;
cfree_emu_free(e);
- return rc;
+ return st;
}
/* Runtime accessor for the resolver — exposes the running emu's
diff --git a/src/emu/emu.h b/src/emu/emu.h
@@ -1,8 +1,8 @@
-#ifndef CFREE_EMU_H
-#define CFREE_EMU_H
+#ifndef CFREE_EMU_INTERNAL_H
+#define CFREE_EMU_INTERNAL_H
/* Internal API for libcfree's guest-ISA emulator. Public surface is
- * cfree_emu_* in <cfree.h>; the implementation in src/emu/emu.c
+ * cfree_emu_* in <cfree/emu.h>; the implementation in src/emu/emu.c
* composes the pieces declared here. See doc/EMU.md for design.
*
* Layering: emu.c owns CfreeEmu lifecycle and the translate/dispatch
@@ -11,8 +11,9 @@
* each live behind one of the surfaces below so the top-level driver
* never reaches into ISA-specific code. */
-#include <cfree.h>
#include <cfree/cg.h>
+#include <cfree/emu.h>
+#include <cfree/jit.h>
#include "core/core.h"
#include "obj/obj.h"
@@ -31,6 +32,13 @@ typedef struct Linker Linker;
* region also never grows. */
#define EMU_CODE_REGION_SIZE (128ull * 1024ull * 1024ull)
+/* ---- Per-emu JIT host wiring ------------------------------------ */
+/* The public CfreeEmuOptions has no field for a JitHost — drivers attach
+ * one through this internal-only setter before calling cfree_emu_run /
+ * cfree_emu_step. `host` is borrowed and must outlive the CfreeEmu. */
+void emu_set_jit_host(CfreeEmu*, const CfreeJitHost*);
+const CfreeJitHost* emu_get_jit_host(const CfreeEmu*);
+
/* ---- Guest ELF loader ------------------------------------------- */
typedef struct EmuLoadedImage {
@@ -123,10 +131,12 @@ void* emu_cache_lookup(const EmuCodeCache*, u64 guest_pc);
/* PROT_NONE mmap that backs the linker's bump-allocated VA range.
* Pages are committed and flipped to RX after each link_resolve_extend
* lands new sections. The base address is fed to link_resolve_at as the
- * image's runtime VA. */
+ * image's runtime VA. The CfreeExecMem is borrowed (from the emu's
+ * JitHost) and must outlive the region. */
typedef struct EmuCodeRegion EmuCodeRegion;
-EmuCodeRegion* emu_code_region_new(Compiler*, size_t reserve_size);
+EmuCodeRegion* emu_code_region_new(Compiler*, const CfreeExecMem*,
+ size_t reserve_size);
void emu_code_region_free(EmuCodeRegion*);
uintptr_t emu_code_region_base(const EmuCodeRegion*);
size_t emu_code_region_size(const EmuCodeRegion*);
diff --git a/src/emu/lift.c b/src/emu/lift.c
@@ -3,7 +3,6 @@
* Lifters target CG exclusively — never CGTarget directly — so the
* pipeline below CG is unchanged from the C front-end. */
-#include <cfree.h>
#include <cfree/cg.h>
#include "emu/emu.h"
diff --git a/src/emu/runtime.c b/src/emu/runtime.c
@@ -8,21 +8,20 @@
* outside the linker) but lands with the per-ISA lifter; see
* doc/EMU.md §6 for why it sits outside link/. */
-#include <cfree.h>
#include <string.h>
-#include "core/heap.h"
#include "core/util.h"
#include "emu/emu.h"
/* ============================================================
* Reserved code region
* ============================================================
- * One up-front PROT_NONE reservation through env->execmem. The base
- * address is fed to link_resolve_at as the image's runtime VA; per-
- * block link_resolve_extend bump-allocates within. Pages are committed
- * (protect to RX) lazily as blocks land — the runtime flips them
- * after the linker writes the section bytes and applies relocations.
+ * One up-front PROT_NONE reservation through the JitHost-supplied
+ * execmem. The base address is fed to link_resolve_at as the image's
+ * runtime VA; per-block link_resolve_extend bump-allocates within.
+ * Pages are committed (protect to RX) lazily as blocks land — the
+ * runtime flips them after the linker writes the section bytes and
+ * applies relocations.
*/
static SrcLoc no_loc(void) {
@@ -30,36 +29,29 @@ static SrcLoc no_loc(void) {
return l;
}
-static const CfreeExecMem* require_execmem(Compiler* c) {
- const CfreeExecMem* m = c->env ? c->env->execmem : NULL;
- if (!m || !m->reserve || !m->protect || !m->release) {
- compiler_panic(c, no_loc(),
- "emu: env->execmem is required for the code region");
- }
- return m;
-}
-
static u64 page_size_bytes(const CfreeExecMem* m) {
return m->page_size ? (u64)m->page_size : 0x4000u;
}
struct EmuCodeRegion {
Compiler* c;
+ const CfreeExecMem* mem; /* borrowed; outlives the region */
CfreeExecMemRegion region; /* dual-aliased on hosts that support it */
uintptr_t rx_end; /* high-water of runtime-alias pages
currently flipped to RX */
};
-EmuCodeRegion* emu_code_region_new(Compiler* c, size_t reserve_size) {
+EmuCodeRegion* emu_code_region_new(Compiler* c, const CfreeExecMem* mem,
+ size_t reserve_size) {
Heap* h;
- const CfreeExecMem* mem;
EmuCodeRegion* r;
size_t aligned;
CfreeExecMemRegion region;
- if (!c) return NULL;
- h = (Heap*)c->env->heap;
- mem = require_execmem(c);
+ if (!c || !mem || !mem->reserve || !mem->protect || !mem->release) {
+ return NULL;
+ }
+ h = c->ctx->heap;
aligned = (size_t)ALIGN_UP((u64)reserve_size, page_size_bytes(mem));
/* Reserve as a code region. The host returns dual-mapped memory
@@ -67,7 +59,7 @@ EmuCodeRegion* emu_code_region_new(Compiler* c, size_t reserve_size) {
* the write alias while the runtime alias starts read-only and is
* flipped to RX page-by-page as cold blocks are committed. */
if (mem->reserve(mem->user, aligned, CFREE_PROT_READ | CFREE_PROT_EXEC,
- ®ion) != 0) {
+ ®ion) != CFREE_OK) {
return NULL;
}
@@ -77,6 +69,7 @@ EmuCodeRegion* emu_code_region_new(Compiler* c, size_t reserve_size) {
return NULL;
}
r->c = c;
+ r->mem = mem;
r->region = region;
r->rx_end = (uintptr_t)region.runtime;
return r;
@@ -84,12 +77,10 @@ EmuCodeRegion* emu_code_region_new(Compiler* c, size_t reserve_size) {
void emu_code_region_free(EmuCodeRegion* r) {
Heap* h;
- const CfreeExecMem* mem;
if (!r) return;
- h = (Heap*)r->c->env->heap;
- mem = r->c->env->execmem;
- if (r->region.size && mem && mem->release) {
- mem->release(mem->user, &r->region);
+ h = r->c->ctx->heap;
+ if (r->region.size && r->mem && r->mem->release) {
+ r->mem->release(r->mem->user, &r->region);
}
h->free(h, r, sizeof(*r));
}
@@ -104,13 +95,11 @@ size_t emu_code_region_size(const EmuCodeRegion* r) {
}
void emu_code_region_commit_rx_to(EmuCodeRegion* r, uintptr_t end) {
- const CfreeExecMem* mem;
uintptr_t base, page_end;
size_t len;
- if (!r) return;
- mem = require_execmem(r->c);
+ if (!r || !r->mem) return;
base = (uintptr_t)r->region.runtime;
- page_end = (uintptr_t)ALIGN_UP((u64)end, page_size_bytes(mem));
+ page_end = (uintptr_t)ALIGN_UP((u64)end, page_size_bytes(r->mem));
/* Monotonic: never lower the high-water; chaining patches
* already-committed code and depends on it staying RX. */
if (page_end <= r->rx_end) return;
@@ -123,10 +112,10 @@ void emu_code_region_commit_rx_to(EmuCodeRegion* r, uintptr_t end) {
* it to PROT_READ|PROT_EXEC here; W^X is preserved because the two
* aliases are distinct VAs and neither holds W and X at the same
* time. */
- if (mem->protect(mem->user, (void*)r->rx_end, len,
- CFREE_PROT_READ | CFREE_PROT_EXEC) == 0) {
- if (mem->flush_icache) {
- mem->flush_icache(mem->user, (void*)r->rx_end, len);
+ if (r->mem->protect(r->mem->user, (void*)r->rx_end, len,
+ CFREE_PROT_READ | CFREE_PROT_EXEC) == CFREE_OK) {
+ if (r->mem->flush_icache) {
+ r->mem->flush_icache(r->mem->user, (void*)r->rx_end, len);
}
r->rx_end = page_end;
}
@@ -151,7 +140,7 @@ EmuCodeCache* emu_cache_new(Compiler* c) {
Heap* h;
EmuCodeCache* k;
if (!c) return NULL;
- h = (Heap*)c->env->heap;
+ h = c->ctx->heap;
k = (EmuCodeCache*)h->alloc(h, sizeof(*k), _Alignof(EmuCodeCache));
if (!k) return NULL;
memset(k, 0, sizeof(*k));
@@ -163,7 +152,7 @@ EmuCodeCache* emu_cache_new(Compiler* c) {
void emu_cache_free(EmuCodeCache* c) {
Heap* h;
if (!c) return;
- h = (Heap*)c->c->env->heap;
+ h = c->c->ctx->heap;
PcMap_fini(&c->map);
h->free(h, c, sizeof(*c));
}
@@ -191,8 +180,7 @@ void* emu_cache_lookup(const EmuCodeCache* c, u64 guest_pc) {
/* Forward-declare the host-private CfreeEmu shape so the resolver
* can pull the CPUState pointer without dragging emu.c's struct
* definition into this TU's contract. */
-struct CfreeEmu;
-EmuCPUState* emu_internal_cpu(struct CfreeEmu*);
+EmuCPUState* emu_internal_cpu(CfreeEmu*);
/* Memory helpers. Per EMU.md §5.4 these bounds-check the guest
* address against the mapped guest AS and trap on miss. v1 stubs
@@ -263,7 +251,7 @@ void* emu_runtime_extern_resolver(void* user, const char* name) {
if (!name) return NULL;
if (streq(name, EMU_SYM_CPU_STATE)) {
- struct CfreeEmu* e = (struct CfreeEmu*)user;
+ CfreeEmu* e = (CfreeEmu*)user;
return (void*)emu_internal_cpu(e);
}
@@ -283,10 +271,11 @@ void* emu_runtime_extern_resolver(void* user, const char* name) {
* a return-of-next_pc instead of a real call here. v1 returns
* NULL — lifters that don't yet emit DISPATCH calls are fine. */
+ (void)no_loc;
return NULL;
}
-/* Tracing. v1 emits to the env's diag sink at CFREE_DIAG_NOTE. The
+/* Tracing. v1 emits to the ctx's diag sink at CFREE_DIAG_NOTE. The
* full implementation lands with the lifter so it can format guest
* PCs and decoded instruction text consistently. */
diff --git a/src/link/link.c b/src/link/link.c
@@ -12,7 +12,9 @@
#include "link/link.h"
-#include <cfree.h>
+#include <cfree/archive.h>
+#include <cfree/core.h>
+#include <cfree/object.h>
#include <string.h>
#include "core/heap.h"
@@ -64,7 +66,7 @@ static void linker_release(Linker* l) {
static void linker_cleanup(void* arg) { linker_release((Linker*)arg); }
Linker* link_new(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
Linker* l = (Linker*)h->alloc(h, sizeof(*l), _Alignof(Linker));
if (!l) return NULL;
memset(l, 0, sizeof(*l));
@@ -217,18 +219,20 @@ LinkInputId link_add_dso_bytes(Linker* l, const char* name, const u8* data,
LinkInputId link_add_archive_bytes(Linker* l, const char* name, const u8* data,
size_t len, u8 whole_archive, u8 link_mode,
u8 group_id) {
- CfreeBytesInput in_arc;
- CfreeArIter it;
+ CfreeBytes in_arc;
+ CfreeArIter* it = NULL;
CfreeArMember mem;
LinkArchive* ar;
u32 n;
+ CfreeContext ctx;
if (!l || !data || !len) return LINK_INPUT_NONE;
in_arc.name = name;
in_arc.data = data;
in_arc.len = len;
- if (!cfree_ar_iter_init(&it, &in_arc))
+ ctx = cfree_compiler_context(l->c);
+ if (cfree_ar_iter_new(&ctx, &in_arc, &it) != CFREE_OK || !it)
compiler_panic(l->c, no_loc(),
"link_add_archive_bytes: '%s' is not a valid ar archive",
name ? name : "(unnamed)");
@@ -237,7 +241,9 @@ LinkInputId link_add_archive_bytes(Linker* l, const char* name, const u8* data,
* once. The linker_release path frees by nmembers, so we need
* allocation size to match. */
n = 0;
- while (cfree_ar_iter_next(&it, &mem)) ++n;
+ while (cfree_ar_iter_next(it, &mem) == CFREE_ITER_ITEM) ++n;
+ cfree_ar_iter_free(it);
+ it = NULL;
ar = LinkArchives_push(&l->archives, NULL);
if (!ar)
@@ -260,13 +266,13 @@ LinkInputId link_add_archive_bytes(Linker* l, const char* name, const u8* data,
* for us, so every member returned here is a real object file.
* Format is detected per-member so a single archive could in
* principle hold mixed formats (in practice it never does). */
- if (!cfree_ar_iter_init(&it, &in_arc))
+ if (cfree_ar_iter_new(&ctx, &in_arc, &it) != CFREE_OK || !it)
compiler_panic(l->c, no_loc(),
"link_add_archive_bytes: ar_iter_init failed on '%s' "
"second pass",
name ? name : "(unnamed)");
n = 0;
- while (cfree_ar_iter_next(&it, &mem) && n < ar->nmembers) {
+ while (cfree_ar_iter_next(it, &mem) == CFREE_ITER_ITEM && n < ar->nmembers) {
ObjBuilder* ob = NULL;
CfreeBinFmt mfmt = cfree_detect_fmt(mem.data, mem.size);
switch (mfmt) {
@@ -295,6 +301,7 @@ LinkInputId link_add_archive_bytes(Linker* l, const char* name, const u8* data,
ar->members[n].obj = ob;
++n;
}
+ cfree_ar_iter_free(it);
return (LinkInputId)LinkArchives_count(
&l->archives); /* opaque non-zero handle */
}
@@ -346,6 +353,11 @@ void link_set_pie(Linker* l, int enable) {
l->emit_pie = enable ? 1 : 0;
}
+void link_set_jit_host(Linker* l, const CfreeJitHost* host) {
+ if (!l) return;
+ l->jit_host = host;
+}
+
void link_set_interp_path(Linker* l, const char* path) {
if (!l) return;
l->interp_path = (path && path[0]) ? pool_intern_cstr(l->c->global, path) : 0;
@@ -510,7 +522,7 @@ static void link_image_cleanup(void* arg) {
}
LinkImage* link_image_alloc(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
LinkImage* img = (LinkImage*)h->alloc(h, sizeof(*img), _Alignof(LinkImage));
if (!img) compiler_panic(c, no_loc(), "link: out of memory allocating image");
memset(img, 0, sizeof(*img));
diff --git a/src/link/link.h b/src/link/link.h
@@ -1,7 +1,9 @@
-#ifndef CFREE_LINK_H
-#define CFREE_LINK_H
+#ifndef CFREE_INTERNAL_LINK_H
+#define CFREE_INTERNAL_LINK_H
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/link.h>
#include "obj/obj.h"
@@ -187,6 +189,12 @@ void link_set_pie(Linker*, int enable);
* consulted when -pie is enabled (or any DSO input is present). */
void link_set_interp_path(Linker*, const char* path);
+/* Borrowed JIT host. The layout passes read execmem->page_size; the JIT
+ * mapper reads the full host (execmem reserve/protect, tls). NULL on
+ * AOT exe/shared lanes. The host and its sub-tables must outlive the
+ * link / the produced CfreeJit. */
+void link_set_jit_host(Linker*, const CfreeJitHost*);
+
/* Symbol resolution and layout are explicit so file linking and JIT share the
* same resolved image. Fatal diagnostics use Compiler.panic.
*
@@ -245,7 +253,7 @@ void link_emit_image_writer(LinkImage*, Writer*);
* the cleanup stack registered by link_resolve); on cfree_jit_free both the
* JIT mapping and the LinkImage are released. Lookup is by name; the public
* `cfree_jit_lookup` and `cfree_jit_free` declarations live in
- * <cfree.h>. */
+ * <cfree/jit.h>. */
CfreeJit* cfree_jit_from_image(LinkImage*);
#endif
diff --git a/src/link/link_api.c b/src/link/link_api.c
@@ -0,0 +1,168 @@
+/* Public link API entries.
+ *
+ * Thin orchestrators over the Linker primitives in link.c / link_resolve.c /
+ * link_layout.c / link_jit.c. Each entry:
+ * - allocates a Linker against the caller's Compiler,
+ * - feeds in the CfreeLinkInputs,
+ * - configures lane-specific flags (pie, shared, jit),
+ * - calls link_resolve,
+ * - dispatches to the emit (writer) or JIT-map (cfree_jit_from_image) tail.
+ *
+ * The driver's job ends at populating CfreeLinkInputs; everything below this
+ * line is libcfree-internal. */
+
+#include "link/link.h"
+
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/link.h>
+#include <setjmp.h>
+
+#include "core/core.h"
+#include "link/link_internal.h"
+
+CfreeJit *cfree_jit_from_image(LinkImage *);
+
+static void load_inputs(Linker *l, const CfreeLinkInputs *in) {
+ uint32_t i;
+ for (i = 0; i < in->nobjs; ++i) {
+ if (in->objs[i]) link_add_obj(l, in->objs[i]);
+ }
+ for (i = 0; i < in->nobj_bytes; ++i) {
+ const CfreeBytes *b = &in->obj_bytes[i];
+ link_add_obj_bytes(l, b->name, b->data, b->len);
+ }
+ for (i = 0; i < in->narchives; ++i) {
+ const CfreeLinkArchiveInput *a = &in->archives[i];
+ link_add_archive_bytes(l, a->bytes.name, a->bytes.data, a->bytes.len,
+ a->whole_archive, a->link_mode, a->group_id);
+ }
+ for (i = 0; i < in->ndso_bytes; ++i) {
+ const CfreeBytes *b = &in->dso_bytes[i];
+ link_add_dso_bytes(l, b->name, b->data, b->len);
+ }
+ if (in->linker_script) link_set_script(l, in->linker_script);
+ if (in->entry) link_set_entry(l, in->entry);
+ /* build_id_* fields are accepted but not yet plumbed through the
+ * Linker — the elf emit currently derives the build-id from the
+ * image bytes unconditionally. Left as a TODO when the linker
+ * grows a configurable build-id mode. */
+ (void)in->build_id_mode;
+ (void)in->build_id_bytes;
+ (void)in->build_id_len;
+}
+
+CfreeStatus cfree_link_exe(CfreeCompiler *c, const CfreeExeLinkOptions *opts,
+ CfreeWriter *out) {
+ PanicSave saved;
+ Linker *l;
+ LinkImage *img;
+ if (!c || !opts || !out) return CFREE_INVALID;
+ compiler_panic_save(c, &saved);
+ if (setjmp(c->panic)) {
+ compiler_run_cleanups(c);
+ compiler_panic_restore(c, &saved);
+ return CFREE_ERR;
+ }
+ l = link_new(c);
+ if (!l) {
+ compiler_panic_restore(c, &saved);
+ return CFREE_NOMEM;
+ }
+ load_inputs(l, &opts->inputs);
+ link_set_emit_static_exe(l, 1);
+ link_set_gc_sections(l, opts->gc_sections);
+ link_set_pie(l, opts->pie);
+ link_set_interp_path(l, opts->interp_path);
+ img = link_resolve(l);
+ link_emit_image_writer(img, out);
+ link_image_free(img);
+ link_free(l);
+ compiler_panic_restore(c, &saved);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_link_shared(CfreeCompiler *c,
+ const CfreeSharedLinkOptions *opts,
+ CfreeWriter *out) {
+ PanicSave saved;
+ Linker *l;
+ LinkImage *img;
+ if (!c || !opts || !out) return CFREE_INVALID;
+ compiler_panic_save(c, &saved);
+ if (setjmp(c->panic)) {
+ compiler_run_cleanups(c);
+ compiler_panic_restore(c, &saved);
+ return CFREE_ERR;
+ }
+ l = link_new(c);
+ if (!l) {
+ compiler_panic_restore(c, &saved);
+ return CFREE_NOMEM;
+ }
+ load_inputs(l, &opts->inputs);
+ link_set_gc_sections(l, opts->gc_sections);
+ /* Shared output is intrinsically PIC and uses the dynamic emit path. */
+ link_set_pie(l, 1);
+ /* soname / rpaths / runpaths / exports / allow_undefined: forwarded
+ * fields not yet wired into the Linker. Recorded here so the surface
+ * matches the public API; the linker emits a static shared-friendly
+ * image regardless for the time being. */
+ (void)opts->soname;
+ (void)opts->rpaths;
+ (void)opts->nrpaths;
+ (void)opts->runpaths;
+ (void)opts->nrunpaths;
+ (void)opts->exports;
+ (void)opts->nexports;
+ (void)opts->allow_undefined;
+ img = link_resolve(l);
+ link_emit_image_writer(img, out);
+ link_image_free(img);
+ link_free(l);
+ compiler_panic_restore(c, &saved);
+ return CFREE_OK;
+}
+
+CfreeStatus cfree_link_jit(CfreeCompiler *c, const CfreeJitLinkOptions *opts,
+ const CfreeJitHost *host, CfreeJit **out_jit) {
+ PanicSave saved;
+ Linker *l;
+ LinkImage *img;
+ CfreeJit *jit;
+ if (!out_jit) return CFREE_INVALID;
+ *out_jit = NULL;
+ if (!c || !opts || !host) return CFREE_INVALID;
+ compiler_panic_save(c, &saved);
+ if (setjmp(c->panic)) {
+ compiler_run_cleanups(c);
+ compiler_panic_restore(c, &saved);
+ return CFREE_ERR;
+ }
+ l = link_new(c);
+ if (!l) {
+ compiler_panic_restore(c, &saved);
+ return CFREE_NOMEM;
+ }
+ link_set_jit_host(l, host);
+ link_set_jit_mode(l, 1);
+ link_set_gc_sections(l, opts->gc_sections);
+ if (opts->extern_resolver) {
+ link_set_extern_resolver(l, opts->extern_resolver,
+ opts->extern_resolver_user);
+ }
+ load_inputs(l, &opts->inputs);
+ img = link_resolve(l);
+ /* cfree_jit_from_image undefers the Linker / image and binds the JIT
+ * to them — do not link_free(l) on success. */
+ jit = cfree_jit_from_image(img);
+ if (!jit) {
+ link_image_free(img);
+ link_free(l);
+ compiler_panic_restore(c, &saved);
+ return CFREE_ERR;
+ }
+ *out_jit = jit;
+ compiler_panic_restore(c, &saved);
+ return CFREE_OK;
+}
diff --git a/src/link/link_dyn.c b/src/link/link_dyn.c
@@ -624,8 +624,7 @@ void layout_dyn(Linker* l, LinkImage* img) {
/* Read the page size from layout_page_size by re-using the
* configured execmem if present — duplicates the helper rather
* than expose it; the value is only used for alignment. */
- const CfreeExecMem* m =
- (l && l->c && l->c->env) ? l->c->env->execmem : NULL;
+ const CfreeExecMem* m = (l && l->jit_host) ? l->jit_host->execmem : NULL;
if (m && m->page_size) page = (u64)m->page_size;
}
diff --git a/src/link/link_internal.h b/src/link/link_internal.h
@@ -114,6 +114,12 @@ struct Linker {
Sym interp_path;
LinkExternResolver resolver;
void* resolver_user;
+ /* Borrowed JIT host. Set by cfree_link_jit before link_resolve so the
+ * layout/reloc passes can read execmem->page_size and the JIT mapper
+ * can reach the host's reserve/protect/tls hooks without rummaging
+ * through Compiler.ctx (which no longer carries those). NULL on the
+ * AOT exe/shared lanes. */
+ const CfreeJitHost* jit_host;
CompilerCleanup* deferred; /* registered by link_new */
};
diff --git a/src/link/link_jit.c b/src/link/link_jit.c
@@ -8,7 +8,10 @@
* stubbed for now — the linker can land without dragging the inspector
* surface up. */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/objbuild.h>
+#include <cfree/object.h>
#include <string.h>
#include "core/buf.h"
@@ -22,26 +25,40 @@
#include "link/link_internal.h"
#include "obj/obj.h"
-/* Defined in src/api/pipeline.c — allocates an empty CfreeObjFile with
- * a private Compiler and an empty ObjBuilder for caller population. */
-CfreeObjFile *cfree_objfile_empty_new(const CfreeEnv *env, CfreeTarget target,
- CfreeObjFmt fmt);
+/* Defined in src/api/objfile.c — exposes the underlying ObjBuilder of a
+ * CfreeObjFile and the internal-only helpers for allocating an empty
+ * CfreeObjFile (used by the JIT debug-view builder). */
+ObjBuilder *cfree_objfile_builder(const CfreeObjFile *);
+CfreeObjFile *cfree_objfile_internal_new(const CfreeContext *ctx,
+ CfreeTarget target, CfreeObjFmt fmt);
+void cfree_objfile_internal_free(CfreeObjFile *);
+#define jit_view_objfile_free(f) cfree_objfile_internal_free(f)
static SrcLoc no_loc(void) {
SrcLoc l = {0, 0, 0};
return l;
}
-static const CfreeExecMem *require_execmem(Compiler *c) {
- const CfreeExecMem *m = c->env ? c->env->execmem : NULL;
+static const CfreeJitHost *jit_host_from_linker(Linker *l, Compiler *c) {
+ const CfreeJitHost *host = l ? l->jit_host : NULL;
+ if (!host)
+ compiler_panic(c, no_loc(),
+ "cfree_jit: link jit host is required for JIT");
+ return host;
+}
+
+static const CfreeExecMem *require_execmem_h(const CfreeJitHost *host,
+ Compiler *c) {
+ const CfreeExecMem *m = host ? host->execmem : NULL;
if (!m || !m->reserve || !m->protect || !m->release) {
- compiler_panic(c, no_loc(), "cfree_jit: env->execmem is required for JIT");
+ compiler_panic(c, no_loc(),
+ "cfree_jit: jit host execmem is required for JIT");
}
return m;
}
-static u64 jit_page_size(Compiler *c) {
- const CfreeExecMem *m = require_execmem(c);
+static u64 jit_page_size_h(const CfreeJitHost *host, Compiler *c) {
+ const CfreeExecMem *m = require_execmem_h(host, c);
return m->page_size ? (u64)m->page_size : 0x4000u;
}
@@ -83,6 +100,11 @@ struct CfreeJit {
* cfree_jit_free. */
const CfreeJitTls *tls_vtable;
void *tls_ctx;
+ /* Borrowed JIT host: execmem + tls vtables. Mirrors the Linker's
+ * jit_host so the JIT's lifetime accessors don't need to walk back to
+ * the Linker (which is still live behind jit->linker but may be
+ * decoupled in incremental flows). */
+ const CfreeJitHost *jit_host;
u8 view_built;
u8 pad[7];
};
@@ -222,7 +244,7 @@ static void jit_patch_tlv_descriptors(CfreeJit *jit) {
* Bitmap over LinkSymId so the inner reloc test is O(1). */
Sym tlv_name = pool_intern_cstr(c->global, "__tlv_bootstrap");
u32 nsyms = LinkSyms_count(&img->syms);
- Heap *h = (Heap *)c->env->heap;
+ Heap *h = (Heap *)c->ctx->heap;
u8 *is_tlv_bootstrap = (u8 *)h->alloc(h, nsyms + 1u, 1u);
if (!is_tlv_bootstrap)
compiler_panic(c, no_loc(), "cfree_jit: oom on tlv-bootstrap bitmap");
@@ -240,10 +262,10 @@ static void jit_patch_tlv_descriptors(CfreeJit *jit) {
return;
}
- const CfreeJitTls *tls = c->env ? c->env->jit_tls : NULL;
+ const CfreeJitTls *tls = jit->jit_host ? jit->jit_host->tls : NULL;
if (!tls || !tls->ctx_new || !tls->ctx_destroy)
compiler_panic(c, no_loc(),
- "cfree_jit: image needs TLV thunk but env->jit_tls is NULL "
+ "cfree_jit: image needs TLV thunk but jit host tls is NULL "
"or incomplete");
/* Snapshot the TLS image's init bytes from the write alias. The
@@ -312,6 +334,7 @@ CfreeJit *cfree_jit_from_image(LinkImage *img) {
Compiler *c;
Heap *heap;
const CfreeExecMem *mem;
+ const CfreeJitHost *host;
CfreeJit *jit;
CfreeExecMemRegion *segs;
CfreeExecMemRegion master;
@@ -329,8 +352,9 @@ CfreeJit *cfree_jit_from_image(LinkImage *img) {
return NULL;
c = img->c;
heap = img->heap;
- mem = require_execmem(c);
- page = jit_page_size(c);
+ host = jit_host_from_linker(img->linker, c);
+ mem = require_execmem_h(host, c);
+ page = jit_page_size_h(host, c);
if (img->nsegments == 0) {
compiler_panic(c, no_loc(), "cfree_jit_from_image: image has no segments");
@@ -578,6 +602,7 @@ CfreeJit *cfree_jit_from_image(LinkImage *img) {
jit->view_built = 0u;
jit->tls_vtable = NULL;
jit->tls_ctx = NULL;
+ jit->jit_host = host;
/* Take ownership of the image: undefer it from the compiler so a
* future panic doesn't reap something we still hold. */
@@ -615,18 +640,24 @@ CfreeJit *cfree_jit_from_image(LinkImage *img) {
return jit;
}
+const CfreeExecMem *cfree_jit_image_execmem(CfreeJit *jit) {
+ if (!jit || !jit->jit_host)
+ return NULL;
+ return jit->jit_host->execmem;
+}
+
void cfree_jit_free(CfreeJit *jit) {
Heap *heap;
const CfreeExecMem *mem;
if (!jit)
return;
- heap = (Heap *)jit->c->env->heap;
- mem = jit->c->env->execmem;
+ heap = (Heap *)jit->c->ctx->heap;
+ mem = jit->jit_host ? jit->jit_host->execmem : NULL;
/* The debug view (if built) is closed first — it owns a private
* Compiler whose pools must be released before the image's
* referenced builders are freed in link_image_free. */
if (jit->view) {
- cfree_obj_close(jit->view);
+ jit_view_objfile_free(jit->view);
jit->view = NULL;
}
/* TLV ctx: pthread_key_delete inside ctx_destroy triggers POSIX
@@ -771,7 +802,7 @@ static InputMap jit_input_map_alloc(CfreeJit *jit, ObjBuilder *ob) {
static void jit_invalidate_view(CfreeJit *jit) {
if (jit->view) {
- cfree_obj_close(jit->view);
+ jit_view_objfile_free(jit->view);
jit->view = NULL;
}
jit->view_built = 0u;
@@ -819,8 +850,8 @@ static void jit_apply_one_reloc(CfreeJit *jit, const LinkRelocApply *r) {
static void jit_append_obj_inner(CfreeJit *jit, ObjBuilder *ob) {
LinkImage *img = jit->image;
Heap *h = img->heap;
- const CfreeExecMem *mem = require_execmem(jit->c);
- u64 page = jit_page_size(jit->c);
+ const CfreeExecMem *mem = require_execmem_h(jit->jit_host, jit->c);
+ u64 page = jit_page_size_h(jit->jit_host, jit->c);
u32 old_nsections = img->nsections;
u32 old_nsegments = img->nsegments;
u32 old_nsegs = jit->nsegs;
@@ -1285,21 +1316,21 @@ static void jit_append_obj_inner(CfreeJit *jit, ObjBuilder *ob) {
(void)old_nmaps;
}
-int cfree_jit_append_obj(CfreeJit *jit, CfreeObjBuilder *ob) {
+CfreeStatus cfree_jit_append_obj(CfreeJit *jit, CfreeObjBuilder *ob) {
PanicSave saved;
Compiler *c;
if (!jit || !ob)
- return 1;
+ return CFREE_INVALID;
c = jit->c;
compiler_panic_save(c, &saved);
if (setjmp(c->panic)) {
compiler_run_cleanups(c);
compiler_panic_restore(c, &saved);
- return 1;
+ return CFREE_ERR;
}
jit_append_obj_inner(jit, ob);
compiler_panic_restore(c, &saved);
- return 0;
+ return CFREE_OK;
}
/* ---- inspector entries ---- */
@@ -1392,7 +1423,7 @@ static ViewSec *view_sec_for(CfreeJit *jit, ViewSec *tab, u32 *ntab,
u32 *cap_inout, const char *name, u16 flags,
u32 align, u32 entsize, ObjBuilder *view_ob,
ViewSec **tab_out) {
- Heap *h = (Heap *)jit->c->env->heap;
+ Heap *h = (Heap *)jit->c->ctx->heap;
Pool *view_pool = obj_compiler(view_ob)->global;
Sym vn = pool_intern_cstr(view_pool, name);
u32 i;
@@ -1461,7 +1492,7 @@ static void jit_view_copy_debug_section(CfreeJit *jit, u32 ii,
if (nbytes == 0)
return;
in_pool = obj_compiler(in_ob)->global;
- h = (Heap *)jit->c->env->heap;
+ h = (Heap *)jit->c->ctx->heap;
(void)in_pool;
bytes = (u8 *)h->alloc(h, nbytes, 1);
@@ -1544,16 +1575,16 @@ static CfreeObjFile *jit_view_build(CfreeJit *jit) {
if (!any)
return NULL;
- view =
- cfree_objfile_empty_new(jit->c->env, jit->c->target, jit->c->target.obj);
+ view = cfree_objfile_internal_new(jit->c->ctx, jit->c->target,
+ jit->c->target.obj);
if (!view)
return NULL;
- view_ob = cfree_obj_builder(view);
+ view_ob = cfree_objfile_builder(view);
if (!view_ob) {
- cfree_obj_close(view);
+ cfree_objfile_internal_free(view);
return NULL;
}
- h = (Heap *)jit->c->env->heap;
+ h = (Heap *)jit->c->ctx->heap;
for (ii = 0; ii < jit->image->dbg_objs_n; ++ii) {
ObjBuilder *in_ob;
@@ -1649,8 +1680,8 @@ static uint64_t jit_sym_runtime_addr(CfreeJit *jit, const LinkSymbol *s) {
return (uint64_t)vaddr_to_runtime(jit->image, jit->segs, s->vaddr);
}
-int cfree_jit_addr_to_sym(CfreeJit *jit, uint64_t addr, const char **name_out,
- uint64_t *off_out) {
+CfreeStatus cfree_jit_addr_to_sym(CfreeJit *jit, uint64_t addr,
+ const char **name_out, uint64_t *off_out) {
u32 n;
u32 i;
const LinkSymbol *best = NULL;
@@ -1661,7 +1692,7 @@ int cfree_jit_addr_to_sym(CfreeJit *jit, uint64_t addr, const char **name_out,
if (off_out)
*off_out = 0;
if (!jit)
- return 1;
+ return CFREE_INVALID;
n = LinkSyms_count(&jit->image->syms);
for (i = 0; i < n; ++i) {
LinkSymbol *s = LinkSyms_at(&jit->image->syms, i);
@@ -1688,9 +1719,9 @@ int cfree_jit_addr_to_sym(CfreeJit *jit, uint64_t addr, const char **name_out,
*name_out = jit_sym_name_cstr(jit, best);
if (off_out)
*off_out = addr - best_base;
- return 0;
+ return CFREE_OK;
}
- return 1;
+ return CFREE_NOT_FOUND;
}
struct CfreeJitSymIter {
@@ -1698,24 +1729,29 @@ struct CfreeJitSymIter {
u32 next;
};
-CfreeJitSymIter *cfree_jit_sym_iter_new(CfreeJit *jit) {
+CfreeStatus cfree_jit_sym_iter_new(CfreeJit *jit, CfreeJitSymIter **out) {
Heap *h;
CfreeJitSymIter *it;
+ if (!out)
+ return CFREE_INVALID;
+ *out = NULL;
if (!jit)
- return NULL;
- h = (Heap *)jit->c->env->heap;
+ return CFREE_INVALID;
+ h = (Heap *)jit->c->ctx->heap;
it = (CfreeJitSymIter *)h->alloc(h, sizeof(*it), _Alignof(CfreeJitSymIter));
if (!it)
- return NULL;
+ return CFREE_NOMEM;
it->jit = jit;
it->next = 0;
- return it;
+ *out = it;
+ return CFREE_OK;
}
-int cfree_jit_sym_iter_next(CfreeJitSymIter *it, CfreeJitSym *out) {
+CfreeIterResult cfree_jit_sym_iter_next(CfreeJitSymIter *it,
+ CfreeJitSym *out) {
u32 n;
if (!it || !out)
- return 0;
+ return CFREE_ITER_ERROR;
n = LinkSyms_count(&it->jit->image->syms);
while (it->next < n) {
LinkSymbol *s = LinkSyms_at(&it->jit->image->syms, it->next++);
@@ -1727,16 +1763,16 @@ int cfree_jit_sym_iter_next(CfreeJitSymIter *it, CfreeJitSym *out) {
out->addr = jit_sym_runtime_addr(it->jit, s);
out->size = s->size;
out->kind = (CfreeSymKind)s->kind;
- return 1;
+ return CFREE_ITER_ITEM;
}
- return 0;
+ return CFREE_ITER_END;
}
void cfree_jit_sym_iter_free(CfreeJitSymIter *it) {
Heap *h;
if (!it)
return;
- h = (Heap *)it->jit->c->env->heap;
+ h = (Heap *)it->jit->c->ctx->heap;
h->free(h, it, sizeof(*it));
}
diff --git a/src/link/link_layout.c b/src/link/link_layout.c
@@ -9,7 +9,8 @@
* input section bytes — no relocations are applied here, in line with
* the incremental-link discipline (link.h:136). */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
#include <string.h>
#include "core/buf.h"
@@ -36,7 +37,8 @@ static SrcLoc no_loc(void) {
* loader. A future cross-link with mismatched host/target page sizes
* will need a target-derived value here instead. */
u64 link_layout_page_size(Linker* l) {
- const CfreeExecMem* m = (l && l->c && l->c->env) ? l->c->env->execmem : NULL;
+ const CfreeExecMem* m =
+ (l && l->jit_host) ? l->jit_host->execmem : NULL;
if (m && m->page_size) return (u64)m->page_size;
return 0x4000u;
}
diff --git a/src/link/link_reloc_layout.c b/src/link/link_reloc_layout.c
@@ -10,7 +10,7 @@
* link_resolve_entry — entry symbol lookup
*/
-#include <cfree.h>
+#include <cfree/core.h>
#include <string.h>
#include "core/buf.h"
diff --git a/src/link/link_resolve.c b/src/link/link_resolve.c
@@ -8,7 +8,7 @@
* link_gc_drop_dead_globals — clear `defined` on syms in dropped sections
*/
-#include <cfree.h>
+#include <cfree/core.h>
#include <string.h>
#include "core/buf.h"
diff --git a/src/link/link_script.c b/src/link/link_script.c
@@ -1,5 +1,5 @@
/* Linker-script parser: a minimal GNU-ld-subset front end that produces
- * the structured CfreeLinkScript form documented in <cfree.h>. The
+ * the structured CfreeLinkScript form documented in <cfree/link.h>. The
* applicator (link_layout.c) consumes the structured form; this file
* never speaks ELF or layout.
*
@@ -38,8 +38,10 @@
* cleanup; for now diagnostics carry file_id = 0 and pack the byte
* offset into the SrcLoc.line field (col is computed inline). */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/link.h>
#include <stdarg.h>
+#include <stddef.h>
#include <string.h>
#include "core/arena.h"
@@ -47,9 +49,20 @@
#include "core/diag.h"
#include "core/heap.h"
+/* The public CfreeLinkScript has no place to carry its backing-arena
+ * pointer, so we allocate a fixed-shape owner block via heap and arena-
+ * init it inline. cfree_link_script_free recovers the owner by stepping
+ * back from the script field to the wrapping struct. The arena's first
+ * member must match struct Arena (defined in core/arena.h). */
+typedef struct ScriptOwner {
+ Arena arena;
+ CfreeLinkScript script;
+} ScriptOwner;
+
typedef struct LSP {
- Compiler* c;
+ Arena* arena;
Heap* heap;
+ CfreeDiagSink* diag;
const char* src;
size_t len;
size_t pos;
@@ -82,12 +95,12 @@ static SrcLoc lsp_loc(const LSP* p, size_t off) {
static void lsp_errf(LSP* p, size_t off, const char* fmt, ...) {
va_list ap;
- if (!p->c || !p->c->env || !p->c->env->diag) {
+ if (!p->diag) {
p->err = 1;
return;
}
va_start(ap, fmt);
- diag_emitv(p->c->env->diag, DIAG_ERROR, lsp_loc(p, off), fmt, ap);
+ diag_emitv(p->diag, DIAG_ERROR, lsp_loc(p, off), fmt, ap);
va_end(ap);
p->err = 1;
}
@@ -95,11 +108,11 @@ static void lsp_errf(LSP* p, size_t off, const char* fmt, ...) {
/* ---- arena helpers ---- */
static char* lsp_strdup(LSP* p, const char* s, size_t n) {
- return arena_strdup(p->c->tu, s, n);
+ return arena_strdup(p->arena, s, n);
}
static CfreeLinkExpr* lsp_new_expr(LSP* p) {
- return arena_znew(p->c->tu, CfreeLinkExpr);
+ return arena_znew(p->arena, CfreeLinkExpr);
}
/* ---- heap-backed temp vectors (copied to the arena at finish) ---- */
@@ -656,12 +669,12 @@ static int parse_output_section(LSP* p, const char* name_buf, size_t name_len,
CfreeLinkInputMatch* arr_in = NULL;
CfreeLinkAssignment* arr_as = NULL;
if (inputs.n) {
- arr_in = arena_array(p->c->tu, CfreeLinkInputMatch, inputs.n);
+ arr_in = arena_array(p->arena, CfreeLinkInputMatch, inputs.n);
if (!arr_in) goto fail;
memcpy(arr_in, inputs.p, sizeof(*arr_in) * inputs.n);
}
if (asns.n) {
- arr_as = arena_array(p->c->tu, CfreeLinkAssignment, asns.n);
+ arr_as = arena_array(p->arena, CfreeLinkAssignment, asns.n);
if (!arr_as) goto fail;
memcpy(arr_as, asns.p, sizeof(*arr_as) * asns.n);
}
@@ -849,7 +862,7 @@ static int parse_top(LSP* p, CfreeLinkScript* out) {
if (top_asns.n) {
CfreeLinkAssignment* a =
- arena_array(p->c->tu, CfreeLinkAssignment, top_asns.n);
+ arena_array(p->arena, CfreeLinkAssignment, top_asns.n);
if (!a) goto done;
memcpy(a, top_asns.p, sizeof(*a) * top_asns.n);
out->top_asns = a;
@@ -857,7 +870,7 @@ static int parse_top(LSP* p, CfreeLinkScript* out) {
}
if (sections.n) {
CfreeLinkOutputSection* s =
- arena_array(p->c->tu, CfreeLinkOutputSection, sections.n);
+ arena_array(p->arena, CfreeLinkOutputSection, sections.n);
if (!s) goto done;
memcpy(s, sections.p, sizeof(*s) * sections.n);
out->sections = s;
@@ -873,32 +886,48 @@ done:
/* ---- public API ---- */
-int cfree_link_script_parse(CfreeCompiler* c, const char* text, size_t len,
- const CfreeLinkScript** out) {
+CfreeStatus cfree_link_script_parse(const CfreeContext* ctx, const char* text,
+ size_t len, CfreeLinkScript** out) {
+ ScriptOwner* owner;
LSP p;
- CfreeLinkScript* script;
int rc;
+ Heap* h;
- if (!c || !text || !out) return 1;
- if (!c->env || !c->env->heap) return 1;
+ if (!out) return CFREE_INVALID;
+ *out = NULL;
+ if (!ctx || !ctx->heap || !text) return CFREE_INVALID;
- script = arena_znew(c->tu, CfreeLinkScript);
- if (!script) return 1;
+ h = ctx->heap;
+ owner = (ScriptOwner*)h->alloc(h, sizeof(*owner), _Alignof(ScriptOwner));
+ if (!owner) return CFREE_NOMEM;
+ memset(owner, 0, sizeof(*owner));
+ /* 16 KiB blocks: matches the linker's tu arena defaults and is plenty
+ * for the script subset we support. */
+ arena_init(&owner->arena, h, 16u * 1024u);
memset(&p, 0, sizeof(p));
- p.c = c;
- p.heap = (Heap*)c->env->heap;
+ p.arena = &owner->arena;
+ p.heap = h;
+ p.diag = ctx->diag;
p.src = text;
p.len = len;
- rc = parse_top(&p, script);
- if (rc != 0 || p.err) return 1;
- *out = script;
- return 0;
+ rc = parse_top(&p, &owner->script);
+ if (rc != 0 || p.err) {
+ arena_fini(&owner->arena);
+ h->free(h, owner, sizeof(*owner));
+ return CFREE_ERR;
+ }
+ *out = &owner->script;
+ return CFREE_OK;
}
-void cfree_link_script_free(CfreeCompiler* c, const CfreeLinkScript* s) {
- /* Arena-owned: lifetime tied to the compiler's tu arena. No-op. */
- (void)c;
- (void)s;
+void cfree_link_script_free(const CfreeContext* ctx, CfreeLinkScript* s) {
+ ScriptOwner* owner;
+ Heap* h;
+ if (!ctx || !ctx->heap || !s) return;
+ owner = (ScriptOwner*)((char*)s - offsetof(ScriptOwner, script));
+ h = ctx->heap;
+ arena_fini(&owner->arena);
+ h->free(h, owner, sizeof(*owner));
}
diff --git a/src/obj/elf.h b/src/obj/elf.h
@@ -12,7 +12,7 @@
#ifndef CFREE_OBJ_ELF_H
#define CFREE_OBJ_ELF_H
-#include <cfree.h>
+#include <cfree/core.h>
#include "core/core.h"
#include "obj/obj.h"
diff --git a/src/obj/elf_emit.c b/src/obj/elf_emit.c
@@ -243,7 +243,7 @@ static u32 strtab_add(Buf* b, const char* s, u32 len) {
}
void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
/* ---- target validation ------------------------------------------ */
const ArchImpl* arch = arch_for_compiler(c);
diff --git a/src/obj/macho.h b/src/obj/macho.h
@@ -14,8 +14,6 @@
#ifndef CFREE_OBJ_MACHO_H
#define CFREE_OBJ_MACHO_H
-#include <cfree.h>
-
#include "core/core.h"
#include "obj/obj.h"
diff --git a/src/obj/macho_emit.c b/src/obj/macho_emit.c
@@ -216,7 +216,7 @@ static int sym_is_extdef(const ObjSym* s) {
* C symbols is added inline in the writer below. */
void emit_macho(Compiler* c, ObjBuilder* ob, Writer* w) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
/* ---- target validation ---------------------------------------- */
const ArchImpl* arch = arch_for_compiler(c);
diff --git a/src/obj/obj.c b/src/obj/obj.c
@@ -47,7 +47,7 @@ struct ObjSymIter {
/* ---- lifecycle ---- */
ObjBuilder* obj_new(Compiler* c) {
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
ObjBuilder* ob = (ObjBuilder*)h->alloc(h, sizeof(*ob), _Alignof(ObjBuilder));
if (!ob) return NULL;
memset(ob, 0, sizeof(*ob));
diff --git a/src/obj/obj.h b/src/obj/obj.h
@@ -418,7 +418,7 @@ int obj_symiter_next(ObjSymIter*, ObjSymEntry* out); /* returns 0 at end */
void obj_symiter_free(ObjSymIter*);
/* Writer is the public CfreeWriter type aliased to Writer inside libcfree
- * (see src/core/core.h). The streaming API lives in <cfree.h> as
+ * (see src/core/core.h). The streaming API lives in <cfree/core.h> as
* cfree_writer_*. */
/* ---- format-aware canonical section names ----
@@ -504,7 +504,7 @@ int obj_symbol_extern_via_got(const Compiler*, ObjBuilder*, ObjSymId);
* verbatim. Mirrors the on-disk policy that decl.c / cc.c emit, so
* link-time and JIT-time lookups by source-level name find the symbol
* regardless of target. Mach-O temp buffer is allocated from
- * `c->env->heap`. */
+ * `c->ctx->heap`. */
Sym obj_format_c_mangle(Compiler*, const char* name);
/* Inverse of obj_format_c_mangle for diagnostic display: if `*name`
diff --git a/src/obj/obj_secnames.c b/src/obj/obj_secnames.c
@@ -125,7 +125,7 @@ int obj_symbol_extern_via_got(const Compiler* c, ObjBuilder* obj,
* and decl.c): "main" → `_main`, "_start" → `__start`,
* "__init_array_start" → `___init_array_start`. ELF / COFF / Wasm
* intern verbatim. The temp buffer for the Mach-O case comes from
- * `c->env->heap`, the same allocator the existing call sites
+ * `c->ctx->heap`, the same allocator the existing call sites
* (boundary_name, cfree_jit_lookup, link_intern_c_name) already use. */
Sym obj_format_c_mangle(Compiler* c, const char* name) {
size_t n;
@@ -137,7 +137,7 @@ Sym obj_format_c_mangle(Compiler* c, const char* name) {
if (c->target.obj != CFREE_OBJ_MACHO)
return pool_intern_cstr(c->global, name);
n = strlen(name);
- h = (Heap*)c->env->heap;
+ h = (Heap*)c->ctx->heap;
buf = (char*)h->alloc(h, n + 2u, 1);
if (!buf)
compiler_panic(c, loc, "obj_format_c_mangle: oom prefixing '%s'", name);
diff --git a/src/obj/obj_tls.c b/src/obj/obj_tls.c
@@ -91,7 +91,7 @@ static ObjSymId mint_init_sym(ObjBuilder* ob, Compiler* c, Sym desc_name) {
const char* nm = pool_str(c->global, desc_name, &nlen);
static const char suffix[] = "$tlv$init";
size_t slen = sizeof(suffix) - 1u;
- Heap* h = (Heap*)c->env->heap;
+ Heap* h = (Heap*)c->ctx->heap;
char* buf = (char*)h->alloc(h, nlen + slen + 1u, 1);
if (!buf)
compiler_panic(c, (SrcLoc){0, 0, 0}, "obj_define_tls: oom interning init name");
diff --git a/test/api/cg_type_test.c b/test/api/cg_type_test.c
@@ -1,5 +1,5 @@
-#include <cfree.h>
#include <cfree/cg.h>
+#include <cfree/core.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -79,7 +79,7 @@ static int expect_panic_contains(CfreeCompiler* c, void (*fn)(void*),
static void exercise_cg_handles(CfreeCompiler* c, CfreeCgTypeId i32_ty,
int opt_level) {
char name_buf[32];
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc;
@@ -96,7 +96,8 @@ static void exercise_cg_handles(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "obj builder allocation failed");
if (!ob) return;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -158,7 +159,7 @@ static void exercise_cg_handles(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static void exercise_cg_scalar_local(CfreeCompiler* c, CfreeCgTypeId i32_ty,
int opt_level) {
char name_buf[40];
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -173,7 +174,8 @@ static void exercise_cg_scalar_local(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "obj builder allocation failed");
if (!ob) return;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -222,7 +224,7 @@ static void exercise_cg_scalar_local(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static void exercise_cg_late_local_addr(CfreeCompiler* c, CfreeCgTypeId i32_ty,
int opt_level) {
char name_buf[40];
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -237,7 +239,8 @@ static void exercise_cg_late_local_addr(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "obj builder allocation failed");
if (!ob) return;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -296,7 +299,7 @@ static uint32_t text_size(const ObjBuilder* ob) {
static void exercise_cg_data_entsize(CfreeCompiler* c, CfreeCgTypeId i8_ty) {
static const uint8_t bytes[] = {'t', 'o', 'y', '\0'};
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgDecl decl;
@@ -309,7 +312,8 @@ static void exercise_cg_data_entsize(CfreeCompiler* c, CfreeCgTypeId i8_ty) {
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "entsize obj builder allocation failed");
if (!ob) return;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "entsize cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -383,7 +387,7 @@ static void exercise_cg_literal_folds(CfreeCompiler* c, CfreeCgTypeId i32_ty) {
"cg_fold_cmp_o1",
"cg_fold_unop_o1",
};
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
@@ -392,7 +396,8 @@ static void exercise_cg_literal_folds(CfreeCompiler* c, CfreeCgTypeId i32_ty) {
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "literal fold obj builder allocation failed");
if (!ob) return;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "literal fold cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -444,7 +449,7 @@ static void exercise_cg_literal_folds(CfreeCompiler* c, CfreeCgTypeId i32_ty) {
static uint32_t cg_emit_delayed_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc;
@@ -461,7 +466,8 @@ static uint32_t cg_emit_delayed_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "delayed chain obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "delayed chain cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -511,7 +517,7 @@ static uint32_t cg_emit_delayed_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static uint32_t cg_emit_unary_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc;
@@ -528,7 +534,8 @@ static uint32_t cg_emit_unary_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "unary chain obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "unary chain cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -576,7 +583,7 @@ static uint32_t cg_emit_unary_chain(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static uint32_t cg_emit_local_shadow(CfreeCompiler* c, CfreeCgTypeId i32_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -592,7 +599,8 @@ static uint32_t cg_emit_local_shadow(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "local shadow obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "local shadow cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -639,7 +647,7 @@ static uint32_t cg_emit_local_shadow(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static uint32_t cg_emit_delayed_cmp(CfreeCompiler* c, CfreeCgTypeId i32_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc;
@@ -656,7 +664,8 @@ static uint32_t cg_emit_delayed_cmp(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "delayed cmp obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "delayed cmp cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -708,7 +717,7 @@ static uint32_t cg_emit_delayed_cmp(CfreeCompiler* c, CfreeCgTypeId i32_ty,
static uint32_t cg_emit_delayed_store(CfreeCompiler* c, CfreeCgTypeId i32_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc;
@@ -726,7 +735,8 @@ static uint32_t cg_emit_delayed_store(CfreeCompiler* c, CfreeCgTypeId i32_ty,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "delayed store obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "delayed store cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -785,7 +795,7 @@ static uint32_t cg_emit_delayed_pressure(CfreeCompiler* c,
CfreeCgTypeId i32_ty,
const char* name) {
enum { NPARAMS = 13 };
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncParam param_desc[NPARAMS];
@@ -802,7 +812,8 @@ static uint32_t cg_emit_delayed_pressure(CfreeCompiler* c,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "delayed pressure obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "delayed pressure cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -874,7 +885,7 @@ static uint32_t cg_emit_local_shadow_boundary(CfreeCompiler* c,
CfreeCgTypeId i32_ty,
const char* name,
CgShadowBoundary boundary) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -890,7 +901,8 @@ static uint32_t cg_emit_local_shadow_boundary(CfreeCompiler* c,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "local shadow boundary obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "local shadow boundary cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -973,7 +985,7 @@ static uint32_t cg_emit_local_shadow_partial_store(CfreeCompiler* c,
CfreeCgTypeId i32_ty,
CfreeCgTypeId i8_ty,
const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -990,7 +1002,8 @@ static uint32_t cg_emit_local_shadow_partial_store(CfreeCompiler* c,
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "partial shadow obj builder allocation failed");
if (!ob) return 0;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "partial shadow cg allocation failed");
if (!cg) {
obj_free((ObjBuilder*)ob);
@@ -1115,7 +1128,7 @@ typedef struct BadStoreCtx {
} BadStoreCtx;
static CfreeCg* cg_begin_bad_store_func(CfreeCompiler* c, const char* name) {
- CfreeCompileOptions opts;
+ CfreeCodeOptions opts;
CfreeObjBuilder* ob;
CfreeCg* cg;
CfreeCgFuncSig sig;
@@ -1126,7 +1139,8 @@ static CfreeCg* cg_begin_bad_store_func(CfreeCompiler* c, const char* name) {
ob = (CfreeObjBuilder*)obj_new((Compiler*)c);
EXPECT(ob != NULL, "bad-store obj builder allocation failed");
if (!ob) return NULL;
- cg = cfree_cg_new(c, ob, &opts);
+ cg = NULL;
+ (void)cfree_cg_new(c, ob, &opts, &cg);
EXPECT(cg != NULL, "bad-store cg allocation failed");
if (!cg) return NULL;
@@ -1204,7 +1218,7 @@ static void exercise_cg_memory_mismatch_diags(CfreeCompiler* c,
int main(void) {
CfreeTarget target;
- CfreeEnv env;
+ CfreeContext ctx;
CfreeCompiler* c;
CfreeCgBuiltinTypes bi;
CfreeCgTypeId void_ty;
@@ -1235,17 +1249,14 @@ int main(void) {
target.ptr_size = 8;
target.ptr_align = 8;
- memset(&env, 0, sizeof(env));
- env.heap = &g_heap;
- env.file_io = NULL;
- env.diag = &g_diag;
- env.execmem = NULL;
- env.dbg_os = NULL;
- env.jit_tls = NULL;
- env.now = 0;
-
- c = cfree_compiler_new(target, &env);
- if (!c) {
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.heap = &g_heap;
+ ctx.file_io = NULL;
+ ctx.diag = &g_diag;
+ ctx.now = 0;
+
+ c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "compiler_new failed\n");
return 2;
}
diff --git a/test/ar_test.c b/test/ar_test.c
@@ -7,7 +7,8 @@
* Set CFREE_AR_TEST_HOST=1 to also dump the produced symbol-index
* archive to /tmp/cfree_ar_test.a and run the host's `ar t` and
* `nm --print-armap` on it as a cross-check. */
-#include <cfree.h>
+#include <cfree/archive.h>
+#include <cfree/core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -19,49 +20,72 @@ typedef struct BufW {
uint8_t* data;
size_t len;
size_t cap;
- int err;
+ CfreeStatus st;
} BufW;
-static void bufw_write(CfreeWriter* w, const void* data, size_t n) {
+static CfreeStatus bufw_write(CfreeWriter* w, const void* data, size_t n) {
BufW* b = (BufW*)w;
- if (b->err) return;
+ if (b->st != CFREE_OK) return b->st;
if (b->len + n > b->cap) {
size_t nc = b->cap ? b->cap * 2 : 256;
while (nc < b->len + n) nc *= 2;
uint8_t* p = (uint8_t*)realloc(b->data, nc);
if (!p) {
- b->err = 1;
- return;
+ b->st = CFREE_NOMEM;
+ return CFREE_NOMEM;
}
b->data = p;
b->cap = nc;
}
memcpy(b->data + b->len, data, n);
b->len += n;
+ return CFREE_OK;
}
-static void bufw_seek(CfreeWriter* w, uint64_t off) {
+static CfreeStatus bufw_seek(CfreeWriter* w, uint64_t off) {
(void)w;
(void)off;
+ return CFREE_OK;
}
static uint64_t bufw_tell(CfreeWriter* w) { return ((BufW*)w)->len; }
-static int bufw_error(CfreeWriter* w) { return ((BufW*)w)->err; }
+static CfreeStatus bufw_status(CfreeWriter* w) { return ((BufW*)w)->st; }
static void bufw_close(CfreeWriter* w) { (void)w; }
static void bufw_init(BufW* b) {
b->base.write = bufw_write;
b->base.seek = bufw_seek;
b->base.tell = bufw_tell;
- b->base.error = bufw_error;
+ b->base.status = bufw_status;
b->base.close = bufw_close;
b->data = NULL;
b->len = 0;
b->cap = 0;
- b->err = 0;
+ b->st = CFREE_OK;
}
static void bufw_fini(BufW* b) { free(b->data); }
+/* ===== libc heap + context (needed by cfree_ar_iter_new) ===== */
+
+static void* h_alloc(CfreeHeap* h, size_t n, size_t a) {
+ (void)h;
+ (void)a;
+ return n ? malloc(n) : NULL;
+}
+static void* h_realloc(CfreeHeap* h, void* p, size_t o, size_t n, size_t a) {
+ (void)h;
+ (void)o;
+ (void)a;
+ return realloc(p, n);
+}
+static void h_free(CfreeHeap* h, void* p, size_t n) {
+ (void)h;
+ (void)n;
+ free(p);
+}
+static CfreeHeap g_heap = {h_alloc, h_realloc, h_free, NULL};
+static CfreeContext g_ctx = {.heap = &g_heap, .now = -1};
+
/* ===== assertion helpers ===== */
static int g_fail;
@@ -108,55 +132,53 @@ static uint64_t ar_date_field(const uint8_t* hdr) {
/* ===== tests ===== */
static int test_basic_roundtrip(void) {
- CfreeBytesInput ms[2];
+ CfreeBytes ms[2];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
int rc;
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"AAAA";
ms[0].len = 4;
- ms[0].lang = 0;
ms[1].name = "b.o";
ms[1].data = (const uint8_t*)"BBBBB";
ms[1].len = 5;
- ms[1].lang = 0;
bufw_init(&bw);
rc = cfree_ar_write(&bw.base, ms, 2, NULL);
EXPECT(rc == 0, "cfree_ar_write returned %d", rc);
- EXPECT(!bw.err, "writer error");
+ EXPECT(bw.st == CFREE_OK, "writer error");
EXPECT(bw.len >= 8, "archive too short");
EXPECT(memcmp(bw.data, "!<arch>\n", 8) == 0, "magic");
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first member");
+ EXPECT(cfree_ar_iter_next(it, &m), "first member");
EXPECT(strcmp(m.name, "a.o") == 0, "name 0 = %s", m.name);
EXPECT(m.size == 4 && memcmp(m.data, "AAAA", 4) == 0, "data 0");
- EXPECT(cfree_ar_iter_next(&it, &m), "second member");
+ EXPECT(cfree_ar_iter_next(it, &m), "second member");
EXPECT(strcmp(m.name, "b.o") == 0, "name 1 = %s", m.name);
EXPECT(m.size == 5 && memcmp(m.data, "BBBBB", 5) == 0, "data 1");
- EXPECT(!cfree_ar_iter_next(&it, &m), "iter end");
+ EXPECT(!cfree_ar_iter_next(it, &m), "iter end");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_long_name_table(void) {
/* >15 chars triggers the // long-name table. */
- CfreeBytesInput ms[2];
+ CfreeBytes ms[2];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeArWriteOptions opts = {0};
int rc;
@@ -165,11 +187,9 @@ static int test_long_name_table(void) {
ms[0].name = "short.o";
ms[0].data = (const uint8_t*)"x";
ms[0].len = 1;
- ms[0].lang = 0;
ms[1].name = "this_name_is_long_enough.o";
ms[1].data = (const uint8_t*)"yy";
ms[1].len = 2;
- ms[1].lang = 0;
bufw_init(&bw);
rc = cfree_ar_write(&bw.base, ms, 2, &opts);
@@ -178,24 +198,24 @@ static int test_long_name_table(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first member");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first member");
EXPECT(strcmp(m.name, "short.o") == 0, "name 0 = %s", m.name);
- EXPECT(cfree_ar_iter_next(&it, &m), "second member");
+ EXPECT(cfree_ar_iter_next(it, &m), "second member");
EXPECT(strcmp(m.name, "this_name_is_long_enough.o") == 0, "long name = %s",
m.name);
- EXPECT(!cfree_ar_iter_next(&it, &m), "iter end");
+ EXPECT(!cfree_ar_iter_next(it, &m), "iter end");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_symbol_index_empty(void) {
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeArWriteOptions opts = {0};
int rc;
@@ -205,7 +225,6 @@ static int test_symbol_index_empty(void) {
ms[0].name = "lonely.o";
ms[0].data = (const uint8_t*)"P";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
rc = cfree_ar_write(&bw.base, ms, 1, &opts);
@@ -222,12 +241,12 @@ static int test_symbol_index_empty(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first user member");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first user member");
EXPECT(strcmp(m.name, "lonely.o") == 0, "name = %s", m.name);
- EXPECT(!cfree_ar_iter_next(&it, &m), "iter end");
+ EXPECT(!cfree_ar_iter_next(it, &m), "iter end");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
@@ -237,7 +256,7 @@ static int test_symbol_index_basic(void) {
const char* a_syms[] = {"foo", "bar"};
const char* b_syms[] = {"baz"};
CfreeArMemberSymbols msyms[2];
- CfreeBytesInput ms[2];
+ CfreeBytes ms[2];
BufW bw;
CfreeArWriteOptions opts = {0};
int rc;
@@ -260,11 +279,9 @@ static int test_symbol_index_basic(void) {
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"AAAA";
ms[0].len = 4;
- ms[0].lang = 0;
ms[1].name = "b.o";
ms[1].data = (const uint8_t*)"BBBBB";
ms[1].len = 5;
- ms[1].lang = 0;
bufw_init(&bw);
rc = cfree_ar_write(&bw.base, ms, 2, &opts);
@@ -324,10 +341,10 @@ static int test_symbol_index_with_long_names(void) {
const char* syms0[] = {"alpha"};
const char* syms1[] = {"beta"};
CfreeArMemberSymbols msyms[2];
- CfreeBytesInput ms[2];
+ CfreeBytes ms[2];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeArWriteOptions opts = {0};
int rc;
@@ -348,11 +365,9 @@ static int test_symbol_index_with_long_names(void) {
ms[0].name = "this_name_is_long_enough.o"; /* 26 chars → // */
ms[0].data = (const uint8_t*)"X";
ms[0].len = 1;
- ms[0].lang = 0;
ms[1].name = "short.o";
ms[1].data = (const uint8_t*)"YY";
ms[1].len = 2;
- ms[1].lang = 0;
bufw_init(&bw);
rc = cfree_ar_write(&bw.base, ms, 2, &opts);
@@ -395,14 +410,13 @@ static int test_symbol_index_with_long_names(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "m0");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "m0");
EXPECT(strcmp(m.name, "this_name_is_long_enough.o") == 0, "m0 name = %s",
m.name);
- EXPECT(cfree_ar_iter_next(&it, &m), "m1");
+ EXPECT(cfree_ar_iter_next(it, &m), "m1");
EXPECT(strcmp(m.name, "short.o") == 0, "m1 name = %s", m.name);
- EXPECT(!cfree_ar_iter_next(&it, &m), "iter end");
+ EXPECT(!cfree_ar_iter_next(it, &m), "iter end");
/* Optional host cross-check. */
if (getenv("CFREE_AR_TEST_HOST")) {
@@ -418,6 +432,7 @@ static int test_symbol_index_with_long_names(void) {
}
}
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
@@ -426,9 +441,9 @@ static int test_iter_skips_index(void) {
/* Make sure the iterator never surfaces the `/` member as a user member. */
const char* s[] = {"only_sym"};
CfreeArMemberSymbols msyms[1];
- CfreeBytesInput ms[1];
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes ms[1];
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
BufW bw;
CfreeArWriteOptions opts = {0};
@@ -441,7 +456,6 @@ static int test_iter_skips_index(void) {
ms[0].name = "only.o";
ms[0].data = (const uint8_t*)"Z";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, &opts) == 0, "write");
@@ -449,15 +463,15 @@ static int test_iter_skips_index(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- while (cfree_ar_iter_next(&it, &m)) {
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ while (cfree_ar_iter_next(it, &m)) {
EXPECT(m.name[0] != '/' || m.name[1] != '\0',
"iter surfaced raw `/` member");
seen++;
}
EXPECT(seen == 1, "saw %d members", seen);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
@@ -465,8 +479,8 @@ static int test_iter_skips_index(void) {
static int test_empty_archive(void) {
/* nmembers == 0 with NULL members should produce a magic-only archive. */
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
int rc;
@@ -479,17 +493,17 @@ static int test_empty_archive(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(!cfree_ar_iter_next(&it, &m), "no members");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(!cfree_ar_iter_next(it, &m), "no members");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_epoch_field(void) {
/* opts.epoch is written into ar_date for every member. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
CfreeArWriteOptions opts = {0};
int rc;
@@ -497,7 +511,6 @@ static int test_epoch_field(void) {
ms[0].name = "x.o";
ms[0].data = (const uint8_t*)"q";
ms[0].len = 1;
- ms[0].lang = 0;
opts.epoch = 1234567890u;
bufw_init(&bw);
@@ -521,16 +534,15 @@ static int test_epoch_field(void) {
static int test_path_basename(void) {
/* Member name with path components is stored as basename only. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
ms[0].name = "src/sub/foo.o";
ms[0].data = (const uint8_t*)"D";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, NULL) == 0, "write");
@@ -538,27 +550,26 @@ static int test_path_basename(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(strcmp(m.name, "foo.o") == 0, "basename = %s", m.name);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_truncate_when_long_names_off(void) {
/* >15 chars without long_names: name is truncated to 15. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
ms[0].name = "abcdefghijklmnopqrst.o"; /* 22 chars */
ms[0].data = (const uint8_t*)"D";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, NULL) == 0, "write");
@@ -566,21 +577,21 @@ static int test_truncate_when_long_names_off(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(strcmp(m.name, "abcdefghijklmno") == 0, "truncated = %s", m.name);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_name_15_char_boundary(void) {
/* Exactly 15 chars: fits in-header even with long_names enabled. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeArWriteOptions opts = {0};
@@ -588,7 +599,6 @@ static int test_name_15_char_boundary(void) {
ms[0].name = "abcdefghijklmno"; /* 15 chars */
ms[0].data = (const uint8_t*)"X";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, &opts) == 0, "write");
@@ -600,21 +610,21 @@ static int test_name_15_char_boundary(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(strcmp(m.name, "abcdefghijklmno") == 0, "name = %s", m.name);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_name_16_char_boundary(void) {
/* Exactly 16 chars: triggers // long-name table. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
CfreeArWriteOptions opts = {0};
@@ -622,7 +632,6 @@ static int test_name_16_char_boundary(void) {
ms[0].name = "abcdefghijklmnop"; /* 16 chars */
ms[0].data = (const uint8_t*)"Y";
ms[0].len = 1;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, &opts) == 0, "write");
@@ -631,31 +640,29 @@ static int test_name_16_char_boundary(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(strcmp(m.name, "abcdefghijklmnop") == 0, "name = %s", m.name);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_empty_member_payload(void) {
/* len=0 (data=NULL): header only, no pad; followed by the next member. */
- CfreeBytesInput ms[2];
+ CfreeBytes ms[2];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
ms[0].name = "empty.o";
ms[0].data = NULL;
ms[0].len = 0;
- ms[0].lang = 0;
ms[1].name = "next.o";
ms[1].data = (const uint8_t*)"N";
ms[1].len = 1;
- ms[1].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 2, NULL) == 0, "write");
@@ -665,37 +672,34 @@ static int test_empty_member_payload(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(strcmp(m.name, "empty.o") == 0 && m.size == 0, "empty.o size=%zu",
m.size);
- EXPECT(cfree_ar_iter_next(&it, &m), "second");
+ EXPECT(cfree_ar_iter_next(it, &m), "second");
EXPECT(strcmp(m.name, "next.o") == 0 && m.size == 1 && m.data[0] == 'N',
"next.o");
- EXPECT(!cfree_ar_iter_next(&it, &m), "iter end");
+ EXPECT(!cfree_ar_iter_next(it, &m), "iter end");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_odd_payload_pad(void) {
/* Odd-length payloads add a '\n' parity pad; even lengths do not. */
- CfreeBytesInput ms[3];
+ CfreeBytes ms[3];
BufW bw;
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"x";
ms[0].len = 1;
- ms[0].lang = 0;
ms[1].name = "b.o";
ms[1].data = (const uint8_t*)"yy";
ms[1].len = 2;
- ms[1].lang = 0;
ms[2].name = "c.o";
ms[2].data = (const uint8_t*)"z";
ms[2].len = 1;
- ms[2].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 3, NULL) == 0, "write");
@@ -712,9 +716,9 @@ static int test_odd_payload_pad(void) {
static int test_ar_list_output(void) {
/* cfree_ar_list emits one user member per line, skipping / and //. */
- CfreeBytesInput ms[3];
+ CfreeBytes ms[3];
BufW bw, lw;
- CfreeBytesInput in;
+ CfreeBytes in;
CfreeArWriteOptions opts = {0};
const char* expected = "a.o\nlong_name_member.o\nb.o\n";
@@ -723,15 +727,12 @@ static int test_ar_list_output(void) {
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"A";
ms[0].len = 1;
- ms[0].lang = 0;
ms[1].name = "long_name_member.o";
ms[1].data = (const uint8_t*)"B";
ms[1].len = 1;
- ms[1].lang = 0;
ms[2].name = "b.o";
ms[2].data = (const uint8_t*)"C";
ms[2].len = 1;
- ms[2].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 3, &opts) == 0, "write");
@@ -739,7 +740,6 @@ static int test_ar_list_output(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
bufw_init(&lw);
EXPECT(cfree_ar_list(&in, &lw.base) == 0, "list");
EXPECT(lw.len == strlen(expected), "list len = %zu, want %zu", lw.len,
@@ -755,29 +755,29 @@ static int test_ar_list_output(void) {
static int test_iter_bad_magic(void) {
/* iter_init must reject non-ar inputs. */
static const uint8_t bad[] = "NOT-AN-AR";
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
in.name = "bad";
in.data = bad;
in.len = sizeof(bad) - 1;
- in.lang = 0;
- EXPECT(!cfree_ar_iter_init(&it, &in), "rejects bad magic");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) != CFREE_OK,
+ "rejects bad magic");
in.data = NULL;
in.len = 0;
- EXPECT(!cfree_ar_iter_init(&it, &in), "rejects empty");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) != CFREE_OK, "rejects empty");
in.data = bad;
in.len = 4; /* too short */
- EXPECT(!cfree_ar_iter_init(&it, &in), "rejects short");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) != CFREE_OK, "rejects short");
return 1;
}
static int test_write_invalid_args(void) {
/* Bad argument combinations must return 1 from cfree_ar_write. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
CfreeArWriteOptions opts = {0};
const char* bad_syms[1];
@@ -785,15 +785,14 @@ static int test_write_invalid_args(void) {
bufw_init(&bw);
- EXPECT(cfree_ar_write(NULL, NULL, 0, NULL) == 1, "NULL writer rejected");
- EXPECT(cfree_ar_write(&bw.base, NULL, 1, NULL) == 1,
+ EXPECT(cfree_ar_write(NULL, NULL, 0, NULL) != CFREE_OK, "NULL writer rejected");
+ EXPECT(cfree_ar_write(&bw.base, NULL, 1, NULL) != CFREE_OK,
"NULL members with nmembers>0 rejected");
ms[0].name = NULL;
ms[0].data = (const uint8_t*)"X";
ms[0].len = 1;
- ms[0].lang = 0;
- EXPECT(cfree_ar_write(&bw.base, ms, 1, NULL) == 1, "NULL name rejected");
+ EXPECT(cfree_ar_write(&bw.base, ms, 1, NULL) != CFREE_OK, "NULL name rejected");
/* NULL symbol-name with nonzero count. */
bad_syms[0] = NULL;
@@ -802,7 +801,7 @@ static int test_write_invalid_args(void) {
opts.symbol_index = 1;
opts.member_symbols = msyms;
ms[0].name = "ok.o";
- EXPECT(cfree_ar_write(&bw.base, ms, 1, &opts) == 1,
+ EXPECT(cfree_ar_write(&bw.base, ms, 1, &opts) != CFREE_OK,
"NULL symbol name rejected");
bufw_fini(&bw);
@@ -814,8 +813,8 @@ static int test_iter_skips_bsd_symdef(void) {
* iterator must not surface it. (cfree_ar_write never emits BSD indexes,
* but the iterator is documented to handle them on read.) */
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
char hdr[60];
size_t j;
@@ -863,30 +862,29 @@ static int test_iter_skips_bsd_symdef(void) {
in.name = "bsd";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- while (cfree_ar_iter_next(&it, &m)) {
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ while (cfree_ar_iter_next(it, &m)) {
EXPECT(strcmp(m.name, "u.o") == 0, "unexpected name = %s", m.name);
seen++;
}
EXPECT(seen == 1, "saw %d members", seen);
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
static int test_iter_data_aliases_archive(void) {
/* CfreeArMember.data must point into the archive's own bytes. */
- CfreeBytesInput ms[1];
+ CfreeBytes ms[1];
BufW bw;
- CfreeBytesInput in;
- CfreeArIter it;
+ CfreeBytes in;
+ CfreeArIter* it = NULL;
CfreeArMember m;
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"PAYLOAD";
ms[0].len = 7;
- ms[0].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 1, NULL) == 0, "write");
@@ -894,13 +892,13 @@ static int test_iter_data_aliases_archive(void) {
in.name = "test";
in.data = bw.data;
in.len = bw.len;
- in.lang = 0;
- EXPECT(cfree_ar_iter_init(&it, &in), "iter_init");
- EXPECT(cfree_ar_iter_next(&it, &m), "first");
+ EXPECT(cfree_ar_iter_new(&g_ctx, &in, &it) == CFREE_OK, "iter_init");
+ EXPECT(cfree_ar_iter_next(it, &m), "first");
EXPECT(m.data >= bw.data && m.data + m.size <= bw.data + bw.len,
"data aliases archive bytes");
EXPECT(memcmp(m.data, "PAYLOAD", 7) == 0, "payload");
+ cfree_ar_iter_free(it);
bufw_fini(&bw);
return 1;
}
@@ -910,7 +908,7 @@ static int test_symbol_index_partial_members(void) {
* every member so later offsets land on the right header. */
const char* mid_syms[] = {"midsym"};
CfreeArMemberSymbols msyms[3];
- CfreeBytesInput ms[3];
+ CfreeBytes ms[3];
BufW bw;
CfreeArWriteOptions opts = {0};
uint32_t nsyms, off;
@@ -928,15 +926,12 @@ static int test_symbol_index_partial_members(void) {
ms[0].name = "a.o";
ms[0].data = (const uint8_t*)"AA";
ms[0].len = 2;
- ms[0].lang = 0;
ms[1].name = "b.o";
ms[1].data = (const uint8_t*)"BB";
ms[1].len = 2;
- ms[1].lang = 0;
ms[2].name = "c.o";
ms[2].data = (const uint8_t*)"CC";
ms[2].len = 2;
- ms[2].lang = 0;
bufw_init(&bw);
EXPECT(cfree_ar_write(&bw.base, ms, 3, &opts) == 0, "write");
diff --git a/test/arch/aa64_inline_test.c b/test/arch/aa64_inline_test.c
@@ -18,7 +18,7 @@
* -Isrc). No public-API dependency for the inline machinery itself —
* that lands once Track A (parser) + Track B (cg) are wired. */
-#include <cfree.h>
+#include <cfree/core.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -63,7 +63,7 @@ static void diag_emit(CfreeDiagSink* s, CfreeDiagKind k, CfreeSrcLoc loc,
fputc('\n', stderr);
}
static CfreeDiagSink g_sink = {diag_emit, 0, 0, 0};
-static CfreeEnv g_env = {.heap = &g_heap, .diag = &g_sink, .now = -1};
+static CfreeContext g_ctx = {.heap = &g_heap, .diag = &g_sink, .now = -1};
static int g_fail = 0;
#define EXPECT(cond, ...) \
@@ -104,8 +104,8 @@ int main(void) {
t.ptr_size = 8;
t.ptr_align = 8;
- CfreeCompiler* cc = cfree_compiler_new(t, &g_env);
- if (!cc) {
+ CfreeCompiler* cc = NULL;
+ if (cfree_compiler_new(t, &g_ctx, &cc) != CFREE_OK || !cc) {
fprintf(stderr, "compiler_new failed\n");
return 2;
}
diff --git a/test/asm/harness/asm_runner.c b/test/asm/harness/asm_runner.c
@@ -19,7 +19,12 @@
* — strict dual-mapping on Apple/Linux, single mapping elsewhere. Only
* --jit exercises it. */
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/disasm.h>
+#include <cfree/jit.h>
+#include <cfree/link.h>
+#include <cfree/object.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -93,18 +98,19 @@ typedef struct XmTok {
void* r;
size_t n;
} XmTok;
-static int xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
void* p =
mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (p == MAP_FAILED) return 1;
+ if (p == MAP_FAILED) return CFREE_NOMEM;
out->write = out->runtime = p;
out->size = n;
out->token = NULL;
- return 0;
+ return CFREE_OK;
}
-static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve(void* u, size_t n, int p,
+ CfreeExecMemRegion* out) {
(void)u;
- if (!out || !n) return 1;
+ if (!out || !n) return CFREE_INVALID;
if (!(p & CFREE_PROT_EXEC)) return xm_reserve_single(n, out);
#if XM_DUAL_APPLE
{
@@ -113,24 +119,24 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
mach_vm_address_t r = 0;
vm_prot_t cur = 0, max = 0;
XmTok* tok;
- if (w == MAP_FAILED) return 1;
+ if (w == MAP_FAILED) return CFREE_NOMEM;
if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
VM_FLAGS_ANYWHERE, mach_task_self(),
(mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
VM_INHERIT_NONE) != KERN_SUCCESS) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = (void*)(uintptr_t)r;
@@ -139,34 +145,34 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = (void*)(uintptr_t)r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#elif XM_DUAL_LINUX
{
int fd = (int)syscall(SYS_memfd_create, "cfree-asm-jit", 0u);
void *w, *r;
XmTok* tok;
- if (fd < 0) return 1;
+ if (fd < 0) return CFREE_NOMEM;
if (ftruncate(fd, (off_t)n) != 0) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (w == MAP_FAILED) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
r = mmap(NULL, n, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (r == MAP_FAILED) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap(r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = r;
@@ -175,15 +181,15 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#else
return xm_reserve_single(n, out);
#endif
}
-static int xm_protect(void* u, void* a, size_t n, int p) {
+static CfreeStatus xm_protect(void* u, void* a, size_t n, int p) {
(void)u;
- return mprotect(a, n, xm_to_posix(p));
+ return mprotect(a, n, xm_to_posix(p)) == 0 ? CFREE_OK : CFREE_IO;
}
static void xm_release(void* u, CfreeExecMemRegion* region) {
(void)u;
@@ -213,12 +219,11 @@ static CfreeExecMem g_execmem = {
16 * 1024, xm_reserve, xm_protect, xm_release, xm_flush, NULL,
};
-static void env_init(CfreeEnv* env) {
- memset(env, 0, sizeof *env);
- env->heap = &g_heap;
- env->diag = &g_diag;
- env->execmem = &g_execmem;
- env->now = -1;
+static void ctx_init(CfreeContext* ctx) {
+ memset(ctx, 0, sizeof *ctx);
+ ctx->heap = &g_heap;
+ ctx->diag = &g_diag;
+ ctx->now = -1;
}
static void target_from_env(CfreeTarget* t) {
@@ -335,15 +340,15 @@ static int mode_encode(const char* src_path, const char* out_path) {
uint8_t* src = NULL;
size_t src_len = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeCompileOptions opts;
- CfreeWriter* w;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeAsmCompileOptions opts;
+ CfreeWriter* w = NULL;
const uint8_t* obj_bytes;
size_t obj_len = 0;
- CfreeObjFile* of;
- CfreeBytesInput obj_in;
+ CfreeObjFile* of = NULL;
+ CfreeBytes obj_in;
uint32_t nsec, i;
uint8_t* hex = NULL;
size_t hex_len = 0;
@@ -354,9 +359,8 @@ static int mode_encode(const char* src_path, const char* out_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
@@ -365,11 +369,10 @@ static int mode_encode(const char* src_path, const char* out_path) {
in.name = src_path;
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_ASM;
memset(&opts, 0, sizeof opts);
- w = cfree_writer_mem(&g_heap);
- if (cfree_compile_obj_emit(c, &opts, &in, w) != 0) {
+ (void)cfree_writer_mem(&g_heap, &w);
+ if (cfree_compile_asm_obj_emit(c, &opts, &in, w) != CFREE_OK) {
cfree_writer_close(w);
cfree_compiler_free(c);
free(src);
@@ -381,8 +384,7 @@ static int mode_encode(const char* src_path, const char* out_path) {
obj_in.name = src_path;
obj_in.data = obj_bytes;
obj_in.len = obj_len;
- of = cfree_obj_open(&env, &obj_in);
- if (!of) {
+ if (cfree_obj_open(&ctx, &obj_in, &of) != CFREE_OK || !of) {
cfree_writer_close(w);
cfree_compiler_free(c);
free(src);
@@ -391,13 +393,14 @@ static int mode_encode(const char* src_path, const char* out_path) {
nsec = cfree_obj_nsections(of);
for (i = 0; i < nsec; ++i) {
- CfreeObjSecInfo s = cfree_obj_section(of, i);
+ CfreeObjSecInfo s;
size_t n = 0;
- const uint8_t* data;
+ const uint8_t* data = NULL;
size_t j;
char* p;
+ if (cfree_obj_section(of, i, &s) != CFREE_OK) continue;
if (s.kind != CFREE_SEC_TEXT) continue;
- data = cfree_obj_section_data(of, i, &n);
+ if (cfree_obj_section_data(of, i, &data, &n) != CFREE_OK) continue;
if (!data || !n) continue;
p = (char*)realloc(hex, hex_len + n * 2 + 1);
if (!p) {
@@ -428,7 +431,7 @@ static int mode_encode(const char* src_path, const char* out_path) {
}
free(hex);
- cfree_obj_close(of);
+ cfree_obj_free(of);
cfree_writer_close(w);
cfree_compiler_free(c);
free(src);
@@ -446,9 +449,10 @@ static int mode_decode(const char* in_path, const char* out_path) {
uint8_t* bytes = NULL;
size_t nbytes = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeDisasmIter* it;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeDisasmIter* it = NULL;
+ CfreeDisasmContext dctx;
FILE* out;
CfreeInsn ins;
int rc = 0;
@@ -464,15 +468,17 @@ static int mode_decode(const char* in_path, const char* out_path) {
free(raw);
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(bytes);
return 2;
}
- it = cfree_disasm_iter_new(c, bytes, nbytes, 0, NULL);
- if (!it) {
+ memset(&dctx, 0, sizeof dctx);
+ dctx.target = tgt;
+ dctx.context = ctx;
+ if (cfree_disasm_iter_new(&dctx, bytes, nbytes, 0, NULL, &it) != CFREE_OK ||
+ !it) {
cfree_compiler_free(c);
free(bytes);
return 1;
@@ -487,7 +493,9 @@ static int mode_decode(const char* in_path, const char* out_path) {
return 2;
}
- while (cfree_disasm_iter_next(it, &ins)) {
+ for (;;) {
+ CfreeIterResult r = cfree_disasm_iter_next(it, &ins);
+ if (r != CFREE_ITER_ITEM) break;
fprintf(out, "%llx:\t%s", (unsigned long long)ins.vaddr,
ins.mnemonic ? ins.mnemonic : "");
if (ins.operands && ins.operands[0]) {
@@ -514,10 +522,10 @@ static int mode_listing(const char* in_path, const char* out_path) {
uint8_t* bytes = NULL;
size_t nbytes = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeWriter* w;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeWriter* w = NULL;
const uint8_t* data;
size_t len = 0;
int rc;
@@ -527,9 +535,8 @@ static int mode_listing(const char* in_path, const char* out_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(bytes);
return 2;
}
@@ -538,10 +545,9 @@ static int mode_listing(const char* in_path, const char* out_path) {
in.name = in_path;
in.data = bytes;
in.len = nbytes;
- in.lang = CFREE_LANG_C; /* unused for obj_disasm but keep the field set */
- w = cfree_writer_mem(&g_heap);
- if (cfree_obj_disasm(c, &in, w) != 0) {
+ (void)cfree_writer_mem(&g_heap, &w);
+ if (cfree_disasm_obj_bytes(&ctx, &in, w) != CFREE_OK) {
cfree_writer_close(w);
cfree_compiler_free(c);
free(bytes);
@@ -563,11 +569,11 @@ static int mode_emit(const char* src_path, const char* out_path) {
uint8_t* src = NULL;
size_t src_len = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeCompileOptions opts;
- CfreeWriter* w;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeAsmCompileOptions opts;
+ CfreeWriter* w = NULL;
int rc = 0;
size_t len = 0;
const uint8_t* data;
@@ -577,9 +583,8 @@ static int mode_emit(const char* src_path, const char* out_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
@@ -588,11 +593,10 @@ static int mode_emit(const char* src_path, const char* out_path) {
in.name = src_path;
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_ASM;
memset(&opts, 0, sizeof opts);
- w = cfree_writer_mem(&g_heap);
- if (cfree_compile_obj_emit(c, &opts, &in, w) != 0) {
+ (void)cfree_writer_mem(&g_heap, &w);
+ if (cfree_compile_asm_obj_emit(c, &opts, &in, w) != CFREE_OK) {
cfree_writer_close(w);
cfree_compiler_free(c);
free(src);
@@ -622,12 +626,13 @@ static int mode_jit(const char* src_path) {
uint8_t* src = NULL;
size_t src_len = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeCompileOptions opts;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeAsmCompileOptions opts;
CfreeObjBuilder* ob = NULL;
- CfreeLinkOptions lopts;
+ CfreeJitLinkOptions lopts;
+ CfreeJitHost jhost;
CfreeObjBuilder* arr[1];
CfreeJit* jit = NULL;
int (*fn)(void);
@@ -638,9 +643,8 @@ static int mode_jit(const char* src_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
@@ -649,10 +653,9 @@ static int mode_jit(const char* src_path) {
in.name = src_path;
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_ASM;
memset(&opts, 0, sizeof opts);
- if (cfree_compile_obj(c, &opts, &in, &ob) != 0 || !ob) {
+ if (cfree_compile_asm_obj(c, &opts, &in, &ob) != CFREE_OK || !ob) {
cfree_compiler_free(c);
free(src);
return 1;
@@ -664,7 +667,10 @@ static int mode_jit(const char* src_path) {
lopts.inputs.nobjs = 1;
lopts.inputs.entry = "test_main";
- if (cfree_link_jit(c, &lopts, &jit) != 0 || !jit) {
+ memset(&jhost, 0, sizeof jhost);
+ jhost.execmem = &g_execmem;
+
+ if (cfree_link_jit(c, &lopts, &jhost, &jit) != CFREE_OK || !jit) {
cfree_compiler_free(c);
free(src);
return 1;
diff --git a/test/debug/roundtrip_unit.c b/test/debug/roundtrip_unit.c
@@ -13,7 +13,7 @@
* fields). The end-to-end producer↔consumer round trip is exercised by
* test/cg path W. */
-#include <cfree.h>
+#include <cfree/core.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -56,7 +56,7 @@ static void diag_emit(CfreeDiagSink* s, CfreeDiagKind k, CfreeSrcLoc loc,
fputc('\n', stderr);
}
static CfreeDiagSink g_sink = {diag_emit, 0, 0, 0};
-static CfreeEnv g_env = {.heap = &g_heap, .diag = &g_sink, .now = -1};
+static CfreeContext g_ctx = {.heap = &g_heap, .diag = &g_sink, .now = -1};
/* ---- fail counters ---- */
@@ -123,8 +123,7 @@ int main(void) {
t.ptr_size = 8;
t.ptr_align = 8;
- c = cfree_compiler_new(t, &g_env);
- if (!c) {
+ if (cfree_compiler_new(t, &g_ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "compiler_new failed\n");
return 2;
}
@@ -151,7 +150,8 @@ int main(void) {
}
{
/* Set a primary file. */
- u32 fid = source_add_memory(c->sources, "p01.c");
+ u32 fid = 0;
+ (void)source_add_memory(c->sources, "p01.c", &fid);
SrcLoc decl = {fid, 1, 0};
SrcLoc l10 = {fid, 10, 0};
DebugTypeId int_tid =
diff --git a/test/dwarf/dwarf_test.c b/test/dwarf/dwarf_test.c
@@ -13,7 +13,9 @@
* Only depends on libcfree.a (the public surface) plus libc.
*/
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/dwarf.h>
+#include <cfree/object.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -780,12 +782,15 @@ static void run_tests(CfreeDebugInfo* di) {
}
/* 7. param_iter — should yield arg. */
- CfreeDwarfParamIter* pi = cfree_dwarf_param_iter_new(di, 0x1000);
- EXPECT(pi != NULL, "param_iter_new returned NULL");
+ CfreeDwarfParamIter* pi = NULL;
+ EXPECT(cfree_dwarf_param_iter_new(di, 0x1000, &pi) == CFREE_OK && pi != NULL,
+ "param_iter_new failed");
if (pi) {
CfreeDwarfVar v;
int n = 0;
- while (cfree_dwarf_param_iter_next(pi, &v)) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_param_iter_next(pi, &v);
+ if (r != CFREE_ITER_ITEM) break;
n++;
EXPECT(strcmp(v.name, "arg") == 0, "param name %s != arg", v.name);
}
@@ -808,12 +813,16 @@ static void run_tests(CfreeDebugInfo* di) {
/* 9. vars_at_new — yields x, y as locals plus arg as ARG. */
{
uint32_t mask = (1u << CFREE_DVR_LOCAL) | (1u << CFREE_DVR_ARG);
- CfreeDwarfVarIter* vi = cfree_dwarf_vars_at_new(di, 0x1000, mask);
+ CfreeDwarfVarIter* vi = NULL;
int n_local = 0, n_arg = 0, saw_x = 0, saw_y = 0, saw_arg = 0;
- EXPECT(vi != NULL, "vars_at_new returned NULL");
+ EXPECT(cfree_dwarf_vars_at_new(di, 0x1000, mask, &vi) == CFREE_OK &&
+ vi != NULL,
+ "vars_at_new failed");
if (vi) {
CfreeDwarfVar v;
- while (cfree_dwarf_vars_at_next(vi, &v)) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_vars_at_next(vi, &v);
+ if (r != CFREE_ITER_ITEM) break;
if (v.role == CFREE_DVR_LOCAL) {
n_local++;
if (strcmp(v.name, "x") == 0) saw_x = 1;
@@ -835,7 +844,7 @@ static void run_tests(CfreeDebugInfo* di) {
/* 10. loc_read REG fast path: pull arg via a fake unwind frame. */
{
CfreeDwarfVarLoc varg;
- if (cfree_dwarf_var_at(di, 0x1000, "arg", &varg) == 0) {
+ if (cfree_dwarf_var_at(di, 0x1000, "arg", &varg) == CFREE_OK) {
CfreeUnwindFrame fr;
uint32_t v32 = 0;
size_t got = 0;
@@ -843,8 +852,8 @@ static void run_tests(CfreeDebugInfo* di) {
fr.regs[0] = 0xdeadbeefULL;
fr.cfa = 0x7000;
fr.pc = 0x1000;
- EXPECT(cfree_dwarf_loc_read(di, &varg, &fr, NULL, &v32, sizeof v32,
- &got) == 0,
+ EXPECT(cfree_dwarf_loc_read(di, &varg, &fr, NULL, NULL, &v32, sizeof v32,
+ &got) == CFREE_OK,
"loc_read REG failed");
EXPECT(got >= sizeof v32 && v32 == 0xdeadbeefU,
"REG read got %u bytes, val 0x%x", (unsigned)got, v32);
@@ -904,13 +913,17 @@ static void run_tests(CfreeDebugInfo* di) {
(int)ti.kind);
EXPECT(ti.byte_size == 8, "struct byte_size=%u", ti.byte_size);
EXPECT(strcmp(ti.name, "Point") == 0, "struct name=%s", ti.name);
- CfreeDwarfFieldIter* fi = cfree_dwarf_field_iter_new(di, lp.type);
- EXPECT(fi != NULL, "field_iter_new returned NULL");
+ CfreeDwarfFieldIter* fi = NULL;
+ EXPECT(cfree_dwarf_field_iter_new(di, lp.type, &fi) == CFREE_OK &&
+ fi != NULL,
+ "field_iter_new failed");
if (fi) {
CfreeDwarfField f;
int count = 0;
int saw_x = 0, saw_y = 0;
- while (cfree_dwarf_field_iter_next(fi, &f)) {
+ for (;;) {
+ CfreeIterResult r = cfree_dwarf_field_iter_next(fi, &f);
+ if (r != CFREE_ITER_ITEM) break;
count++;
if (strcmp(f.name, "x_field") == 0 && f.byte_offset == 0) saw_x = 1;
if (strcmp(f.name, "y_field") == 0 && f.byte_offset == 4) saw_y = 1;
@@ -931,14 +944,14 @@ int main(void) {
target.obj = CFREE_OBJ_ELF;
target.ptr_size = 8;
target.ptr_align = 8;
- CfreeEnv env;
- memset(&env, 0, sizeof env);
- env.heap = &g_heap;
- env.diag = &g_diag;
- env.now = -1;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* cc = cfree_compiler_new(target, &env);
- if (!cc) {
+ CfreeCompiler* cc = NULL;
+ if (cfree_compiler_new(target, &ctx, &cc) != CFREE_OK || !cc) {
fprintf(stderr, "compiler_new failed\n");
return 1;
}
@@ -988,28 +1001,31 @@ int main(void) {
obj_finalize(ob);
/* Emit ELF to memory. */
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
emit_elf(cc, ob, w);
size_t obj_len = 0;
const uint8_t* obj_bytes = cfree_writer_mem_bytes(w, &obj_len);
fprintf(stderr, "built obj: %zu bytes\n", obj_len);
/* Re-open via the public API. */
- CfreeBytesInput in;
+ CfreeBytes in;
memset(&in, 0, sizeof in);
in.name = "test.o";
in.data = obj_bytes;
in.len = obj_len;
- CfreeObjFile* obj = cfree_obj_open(&env, &in);
- EXPECT(obj != NULL, "cfree_obj_open returned NULL");
+ CfreeObjFile* obj = NULL;
+ EXPECT(cfree_obj_open(&ctx, &in, &obj) == CFREE_OK && obj != NULL,
+ "cfree_obj_open failed");
if (obj) {
- CfreeDebugInfo* di = cfree_dwarf_open(cc, obj);
- EXPECT(di != NULL, "cfree_dwarf_open returned NULL");
+ CfreeDebugInfo* di = NULL;
+ EXPECT(cfree_dwarf_open(&ctx, obj, &di) == CFREE_OK && di != NULL,
+ "cfree_dwarf_open failed");
if (di) {
run_tests(di);
- cfree_dwarf_close(di);
+ cfree_dwarf_free(di);
}
- cfree_obj_close(obj);
+ cfree_obj_free(obj);
}
if (w->close) w->close(w);
diff --git a/test/elf/cfree-roundtrip.c b/test/elf/cfree-roundtrip.c
@@ -14,7 +14,8 @@
* headers — this is a test binary, not a libcfree consumer, so seeing the
* internal surface is fine. */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/object.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
@@ -127,19 +128,20 @@ int main(int argc, char** argv) {
}
CfreeTarget target;
- if (cfree_detect_target(in_data, in_len, &target) != 0) {
+ if (cfree_detect_target(in_data, in_len, &target) != CFREE_OK) {
fprintf(stderr, "error: %s: not a recognized object file\n", in_path);
free(in_data);
return 1;
}
- CfreeEnv env;
- env.heap = &g_heap;
- env.file_io = NULL;
- env.diag = &g_diag;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* c = cfree_compiler_new(target, &env);
- if (!c) {
+ CfreeCompiler* c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "error: cfree_compiler_new failed\n");
free(in_data);
return 1;
@@ -156,8 +158,8 @@ int main(int argc, char** argv) {
ObjBuilder* ob = read_elf((Compiler*)c, in_path, in_data, in_len);
- CfreeWriter* w = cfree_writer_mem(&g_heap);
- if (!w) {
+ CfreeWriter* w = NULL;
+ if (cfree_writer_mem(&g_heap, &w) != CFREE_OK || !w) {
fprintf(stderr, "error: cfree_writer_mem failed\n");
obj_free(ob);
cfree_compiler_free(c);
diff --git a/test/elf/unit/align_4k.c b/test/elf/unit/align_4k.c
@@ -5,7 +5,7 @@
* Clang doesn't emit such large alignments naturally for .o files,
* so this gap can only be covered by a hand-built case. */
-#include <cfree.h>
+#include <cfree/core.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
@@ -71,9 +71,10 @@ int main(void) {
return 1;
}
- CfreeEnv env = {.heap = &g_heap, .file_io = NULL, .diag = &g_diag};
- CfreeCompiler* cc = cfree_compiler_new(target, &env);
- if (!cc) {
+ CfreeContext ctx = {.heap = &g_heap, .file_io = NULL, .diag = &g_diag,
+ .metrics = NULL, .now = -1};
+ CfreeCompiler* cc = NULL;
+ if (cfree_compiler_new(target, &ctx, &cc) != CFREE_OK || !cc) {
fprintf(stderr, "FAIL: cfree_compiler_new\n");
return 1;
}
@@ -95,7 +96,8 @@ int main(void) {
sizeof TEXT_BYTES);
obj_finalize(in);
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
emit_elf(c, in, w);
size_t out_len = 0;
const uint8_t* out_data = cfree_writer_mem_bytes(w, &out_len);
diff --git a/test/elf/unit/smoke.c b/test/elf/unit/smoke.c
@@ -13,7 +13,7 @@
*
* Exit 0 = pass; non-zero = fail (with a one-line stderr explanation). */
-#include <cfree.h>
+#include <cfree/core.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
@@ -237,13 +237,14 @@ int main(void) {
return 1;
}
- CfreeEnv env;
- env.heap = &g_heap;
- env.file_io = NULL;
- env.diag = &g_diag;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* cc = cfree_compiler_new(target, &env);
- if (!cc) {
+ CfreeCompiler* cc = NULL;
+ if (cfree_compiler_new(target, &ctx, &cc) != CFREE_OK || !cc) {
fprintf(stderr, "FAIL: cfree_compiler_new\n");
return 1;
}
@@ -259,7 +260,8 @@ int main(void) {
/* Build, emit, read back, inspect. */
ObjBuilder* in = build_input(c);
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
emit_elf(c, in, w);
size_t out_len = 0;
const uint8_t* out_data = cfree_writer_mem_bytes(w, &out_len);
diff --git a/test/lib/cfree_test_target.h b/test/lib/cfree_test_target.h
@@ -15,7 +15,7 @@
#ifndef CFREE_TEST_TARGET_H
#define CFREE_TEST_TARGET_H
-#include <cfree.h>
+#include <cfree/core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/test/link/harness/jit_runner.c b/test/link/harness/jit_runner.c
@@ -21,7 +21,9 @@
* Runs only on aarch64 hosts. Compiled against libcfree.a with
* -I$(ROOT)/include. */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/link.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
@@ -89,18 +91,19 @@ typedef struct XmTok {
void* r;
size_t n;
} XmTok;
-static int xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
void* p =
mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (p == MAP_FAILED) return 1;
+ if (p == MAP_FAILED) return CFREE_NOMEM;
out->write = out->runtime = p;
out->size = n;
out->token = NULL;
- return 0;
+ return CFREE_OK;
}
-static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve(void* u, size_t n, int p,
+ CfreeExecMemRegion* out) {
(void)u;
- if (!out || !n) return 1;
+ if (!out || !n) return CFREE_INVALID;
if (!(p & CFREE_PROT_EXEC)) return xm_reserve_single(n, out);
#if XM_DUAL_APPLE
{
@@ -109,24 +112,24 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
mach_vm_address_t r = 0;
vm_prot_t cur = 0, max = 0;
XmTok* tok;
- if (w == MAP_FAILED) return 1;
+ if (w == MAP_FAILED) return CFREE_NOMEM;
if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
VM_FLAGS_ANYWHERE, mach_task_self(),
(mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
VM_INHERIT_NONE) != KERN_SUCCESS) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = (void*)(uintptr_t)r;
@@ -135,34 +138,34 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = (void*)(uintptr_t)r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#elif XM_DUAL_LINUX
{
int fd = (int)syscall(SYS_memfd_create, "cfree-jit-test", 0u);
void *w, *r;
XmTok* tok;
- if (fd < 0) return 1;
+ if (fd < 0) return CFREE_NOMEM;
if (ftruncate(fd, (off_t)n) != 0) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (w == MAP_FAILED) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
r = mmap(NULL, n, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (r == MAP_FAILED) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap(r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = r;
@@ -171,15 +174,15 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#else
return xm_reserve_single(n, out);
#endif
}
-static int xm_protect(void* u, void* a, size_t n, int p) {
+static CfreeStatus xm_protect(void* u, void* a, size_t n, int p) {
(void)u;
- return mprotect(a, n, xm_to_posix(p));
+ return mprotect(a, n, xm_to_posix(p)) == 0 ? CFREE_OK : CFREE_IO;
}
static void xm_release(void* u, CfreeExecMemRegion* region) {
(void)u;
@@ -357,8 +360,8 @@ int main(int argc, char** argv) {
const char* check_present = NULL;
const char* script_path = NULL;
- CfreeBytesInput objs[64];
- CfreeBytesInputArchive archives[16];
+ CfreeBytes objs[64];
+ CfreeLinkArchiveInput archives[16];
uint32_t nobj = 0, narc = 0;
uint8_t* bufs[80];
int nbufs = 0;
@@ -388,16 +391,16 @@ int main(int argc, char** argv) {
}
bufs[nbufs++] = data;
if (next_archive) {
- CfreeBytesInputArchive* a = &archives[narc++];
+ CfreeLinkArchiveInput* a = &archives[narc++];
memset(a, 0, sizeof(*a));
- a->input.name = argv[i];
- a->input.data = data;
- a->input.len = len;
+ a->bytes.name = argv[i];
+ a->bytes.data = data;
+ a->bytes.len = len;
a->whole_archive = (uint8_t)next_whole;
next_archive = 0;
next_whole = 0;
} else {
- CfreeBytesInput* o = &objs[nobj++];
+ CfreeBytes* o = &objs[nobj++];
memset(o, 0, sizeof(*o));
o->name = argv[i];
o->data = data;
@@ -412,21 +415,24 @@ int main(int argc, char** argv) {
return 2;
}
- CfreeEnv env;
- memset(&env, 0, sizeof(env));
- env.heap = &g_heap;
- env.diag = &g_diag;
- env.execmem = &g_execmem;
- env.jit_tls = &g_jit_tls;
- env.now = -1;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* c = cfree_compiler_new(target, &env);
- if (!c) {
+ CfreeCompiler* c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "jit-runner: compiler_new failed\n");
return 2;
}
- CfreeLinkOptions opts;
+ CfreeJitHost jhost;
+ memset(&jhost, 0, sizeof(jhost));
+ jhost.execmem = &g_execmem;
+ jhost.tls = &g_jit_tls;
+
+ CfreeJitLinkOptions opts;
memset(&opts, 0, sizeof(opts));
opts.inputs.obj_bytes = nobj ? objs : NULL;
opts.inputs.nobj_bytes = nobj;
@@ -435,8 +441,8 @@ int main(int argc, char** argv) {
opts.inputs.entry = "test_main";
opts.gc_sections = gc_sections;
if (use_resolver) {
- opts.inputs.extern_resolver = extern_resolver;
- opts.inputs.extern_resolver_user = NULL;
+ opts.extern_resolver = extern_resolver;
+ opts.extern_resolver_user = NULL;
}
if (script_path) {
@@ -447,10 +453,11 @@ int main(int argc, char** argv) {
cfree_compiler_free(c);
return 2;
}
- const CfreeLinkScript* script = NULL;
- int prc = cfree_link_script_parse(c, (const char*)sbytes, slen, &script);
+ CfreeLinkScript* script = NULL;
+ CfreeStatus prc =
+ cfree_link_script_parse(&ctx, (const char*)sbytes, slen, &script);
free(sbytes);
- if (prc) {
+ if (prc != CFREE_OK) {
fprintf(stderr, "jit-runner: linker script parse failed: %s\n",
script_path);
cfree_compiler_free(c);
@@ -460,9 +467,9 @@ int main(int argc, char** argv) {
}
CfreeJit* jit = NULL;
- int rc = cfree_link_jit(c, &opts, &jit);
+ CfreeStatus rc = cfree_link_jit(c, &opts, &jhost, &jit);
for (int i = 0; i < nbufs; i++) free(bufs[i]);
- if (rc || !jit) {
+ if (rc != CFREE_OK || !jit) {
cfree_compiler_free(c);
return 1;
}
diff --git a/test/link/harness/link_exe_runner.c b/test/link/harness/link_exe_runner.c
@@ -12,7 +12,8 @@
*
* Compiled against libcfree.a with -I$(ROOT)/include. */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/link.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
@@ -111,9 +112,9 @@ int main(int argc, char** argv) {
int next_archive = 0;
int next_whole = 0;
- CfreeBytesInput objs[64];
- CfreeBytesInputArchive archives[16];
- CfreeBytesInput dsos[16];
+ CfreeBytes objs[64];
+ CfreeLinkArchiveInput archives[16];
+ CfreeBytes dsos[16];
uint32_t nobj = 0, narc = 0, ndso = 0;
uint8_t* bufs[96];
int nbufs = 0;
@@ -144,23 +145,23 @@ int main(int argc, char** argv) {
}
bufs[nbufs++] = data;
if (next_dso) {
- CfreeBytesInput* d = &dsos[ndso++];
+ CfreeBytes* d = &dsos[ndso++];
memset(d, 0, sizeof(*d));
d->name = argv[i];
d->data = data;
d->len = len;
next_dso = 0;
} else if (next_archive) {
- CfreeBytesInputArchive* a = &archives[narc++];
+ CfreeLinkArchiveInput* a = &archives[narc++];
memset(a, 0, sizeof(*a));
- a->input.name = argv[i];
- a->input.data = data;
- a->input.len = len;
+ a->bytes.name = argv[i];
+ a->bytes.data = data;
+ a->bytes.len = len;
a->whole_archive = (uint8_t)next_whole;
next_archive = 0;
next_whole = 0;
} else {
- CfreeBytesInput* o = &objs[nobj++];
+ CfreeBytes* o = &objs[nobj++];
memset(o, 0, sizeof(*o));
o->name = argv[i];
o->data = data;
@@ -179,18 +180,19 @@ int main(int argc, char** argv) {
return 2;
}
- CfreeEnv env;
- memset(&env, 0, sizeof(env));
- env.heap = &g_heap;
- env.diag = &g_diag;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* c = cfree_compiler_new(target, &env);
- if (!c) {
+ CfreeCompiler* c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "link-exe-runner: compiler_new failed\n");
return 2;
}
- CfreeLinkOptions opts;
+ CfreeExeLinkOptions opts;
memset(&opts, 0, sizeof(opts));
opts.inputs.obj_bytes = nobj ? objs : NULL;
opts.inputs.nobj_bytes = nobj;
@@ -209,10 +211,11 @@ int main(int argc, char** argv) {
cfree_compiler_free(c);
return 2;
}
- const CfreeLinkScript* script = NULL;
- int prc = cfree_link_script_parse(c, (const char*)sbytes, slen, &script);
+ CfreeLinkScript* script = NULL;
+ CfreeStatus prc =
+ cfree_link_script_parse(&ctx, (const char*)sbytes, slen, &script);
free(sbytes);
- if (prc) {
+ if (prc != CFREE_OK) {
fprintf(stderr, "link-exe-runner: linker script parse failed: %s\n",
script_path);
cfree_compiler_free(c);
@@ -221,17 +224,17 @@ int main(int argc, char** argv) {
opts.inputs.linker_script = script;
}
- CfreeWriter* w = cfree_writer_mem(&g_heap);
- if (!w) {
+ CfreeWriter* w = NULL;
+ if (cfree_writer_mem(&g_heap, &w) != CFREE_OK || !w) {
cfree_compiler_free(c);
return 2;
}
- int rc = cfree_link_exe(c, &opts, w);
+ CfreeStatus rc = cfree_link_exe(c, &opts, w);
for (int i = 0; i < nbufs; i++) free(bufs[i]);
- if (rc) {
+ if (rc != CFREE_OK) {
cfree_writer_close(w);
cfree_compiler_free(c);
return 1;
diff --git a/test/macho/cfree-roundtrip-macho.c b/test/macho/cfree-roundtrip-macho.c
@@ -13,7 +13,8 @@
* headers — this is a test binary, not a libcfree consumer, so seeing
* the internal surface is fine. */
-#include <cfree.h>
+#include <cfree/core.h>
+#include <cfree/object.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
@@ -120,19 +121,20 @@ int main(int argc, char** argv) {
}
CfreeTarget target;
- if (cfree_detect_target(in_data, in_len, &target) != 0) {
+ if (cfree_detect_target(in_data, in_len, &target) != CFREE_OK) {
fprintf(stderr, "error: %s: not a recognized object file\n", in_path);
free(in_data);
return 1;
}
- CfreeEnv env;
- env.heap = &g_heap;
- env.file_io = NULL;
- env.diag = &g_diag;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
- CfreeCompiler* c = cfree_compiler_new(target, &env);
- if (!c) {
+ CfreeCompiler* c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
fprintf(stderr, "error: cfree_compiler_new failed\n");
free(in_data);
return 1;
@@ -147,8 +149,8 @@ int main(int argc, char** argv) {
ObjBuilder* ob = read_macho((Compiler*)c, in_path, in_data, in_len);
- CfreeWriter* w = cfree_writer_mem(&g_heap);
- if (!w) {
+ CfreeWriter* w = NULL;
+ if (cfree_writer_mem(&g_heap, &w) != CFREE_OK || !w) {
fprintf(stderr, "error: cfree_writer_mem failed\n");
obj_free(ob);
cfree_compiler_free(c);
diff --git a/test/opt/opt_test.c b/test/opt/opt_test.c
@@ -1,6 +1,7 @@
#include "opt/opt.h"
-#include <cfree.h>
+#include <cfree/cg.h>
+#include <cfree/core.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -71,11 +72,11 @@ typedef struct TestCtx {
} TestCtx;
static void tc_init_target(TestCtx* tc, CfreeArchKind arch, CfreeOSKind os) {
- CfreeEnv env;
- memset(&env, 0, sizeof env);
- env.heap = &g_heap;
- env.diag = &g_diag;
- env.now = -1;
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
CfreeTarget tgt;
memset(&tgt, 0, sizeof tgt);
@@ -85,9 +86,9 @@ static void tc_init_target(TestCtx* tc, CfreeArchKind arch, CfreeOSKind os) {
tgt.ptr_size = 8;
tgt.ptr_align = 8;
- static CfreeEnv s_env;
- s_env = env;
- compiler_init(&tc->cc, tgt, &s_env);
+ static CfreeContext s_ctx;
+ s_ctx = ctx;
+ compiler_init(&tc->cc, tgt, &s_ctx);
tc->c = &tc->cc;
{
CfreeCgBuiltinTypes b = cfree_cg_builtin_types(tc->c);
@@ -1412,7 +1413,8 @@ static void opt_block_liveness_phase1(void) {
EXPECT(live.set_bit_scans != 0,
"block-liveness set-bit scan metric should be set");
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
opt_live_dump_blocks(f, &live, w);
size_t len = 0;
const unsigned char* bytes = cfree_writer_mem_bytes(w, &len);
@@ -1489,7 +1491,8 @@ static void opt_live_ranges_phase2(void) {
EXPECT(ranges.max_ranges_per_val >= live_range_count_for(&ranges, loop_v),
"range metrics should record max ranges per value");
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
opt_live_dump_ranges(f, &ranges, w);
size_t len = 0;
const unsigned char* bytes = cfree_writer_mem_bytes(w, &len);
@@ -2190,7 +2193,8 @@ static void opt_rewrite_spill_use_def(void) {
if (f->frame_slots[i].kind == FS_SPILL) saw_spill_slot = 1;
EXPECT(saw_spill_slot, "rewrite should allocate FS_SPILL slot");
- CfreeWriter* w = cfree_writer_mem(&g_heap);
+ CfreeWriter* w = NULL;
+ (void)cfree_writer_mem(&g_heap, &w);
opt_rewrite_dump(f, w);
size_t len = 0;
const unsigned char* bytes = cfree_writer_mem_bytes(w, &len);
diff --git a/test/parse/harness/parse_runner.c b/test/parse/harness/parse_runner.c
@@ -16,7 +16,10 @@
* The execmem boilerplate mirrors test/cg/harness/cg_runner.c — strict
* W^X dual mapping on Apple/Linux, single mapping elsewhere. */
-#include <cfree.h>
+#include <cfree/compile.h>
+#include <cfree/core.h>
+#include <cfree/jit.h>
+#include <cfree/link.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
@@ -88,18 +91,19 @@ typedef struct XmTok {
void* r;
size_t n;
} XmTok;
-static int xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve_single(size_t n, CfreeExecMemRegion* out) {
void* p =
mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (p == MAP_FAILED) return 1;
+ if (p == MAP_FAILED) return CFREE_NOMEM;
out->write = out->runtime = p;
out->size = n;
out->token = NULL;
- return 0;
+ return CFREE_OK;
}
-static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
+static CfreeStatus xm_reserve(void* u, size_t n, int p,
+ CfreeExecMemRegion* out) {
(void)u;
- if (!out || !n) return 1;
+ if (!out || !n) return CFREE_INVALID;
if (!(p & CFREE_PROT_EXEC)) return xm_reserve_single(n, out);
#if XM_DUAL_APPLE
{
@@ -108,24 +112,24 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
mach_vm_address_t r = 0;
vm_prot_t cur = 0, max = 0;
XmTok* tok;
- if (w == MAP_FAILED) return 1;
+ if (w == MAP_FAILED) return CFREE_NOMEM;
if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
VM_FLAGS_ANYWHERE, mach_task_self(),
(mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
VM_INHERIT_NONE) != KERN_SUCCESS) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap((void*)(uintptr_t)r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = (void*)(uintptr_t)r;
@@ -134,34 +138,34 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = (void*)(uintptr_t)r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#elif XM_DUAL_LINUX
{
int fd = (int)syscall(SYS_memfd_create, "cfree-jit-test", 0u);
void *w, *r;
XmTok* tok;
- if (fd < 0) return 1;
+ if (fd < 0) return CFREE_NOMEM;
if (ftruncate(fd, (off_t)n) != 0) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (w == MAP_FAILED) {
close(fd);
- return 1;
+ return CFREE_NOMEM;
}
r = mmap(NULL, n, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (r == MAP_FAILED) {
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok = (XmTok*)malloc(sizeof(*tok));
if (!tok) {
munmap(r, n);
munmap(w, n);
- return 1;
+ return CFREE_NOMEM;
}
tok->w = w;
tok->r = r;
@@ -170,15 +174,15 @@ static int xm_reserve(void* u, size_t n, int p, CfreeExecMemRegion* out) {
out->runtime = r;
out->size = n;
out->token = tok;
- return 0;
+ return CFREE_OK;
}
#else
return xm_reserve_single(n, out);
#endif
}
-static int xm_protect(void* u, void* a, size_t n, int p) {
+static CfreeStatus xm_protect(void* u, void* a, size_t n, int p) {
(void)u;
- return mprotect(a, n, xm_to_posix(p));
+ return mprotect(a, n, xm_to_posix(p)) == 0 ? CFREE_OK : CFREE_IO;
}
static void xm_release(void* u, CfreeExecMemRegion* region) {
(void)u;
@@ -249,16 +253,16 @@ static int read_file(const char* path, uint8_t** out, size_t* out_len) {
return 0;
}
-static int test_read_all(void* user, const char* path, CfreeFileData* out) {
+static CfreeStatus test_read_all(void* user, const char* path,
+ CfreeFileData* out) {
uint8_t* data = NULL;
size_t len = 0;
(void)user;
- if (!out || read_file(path, &data, &len) != 0)
- return 0;
+ if (!out || read_file(path, &data, &len) != 0) return CFREE_IO;
out->data = data;
out->size = len;
out->token = data;
- return 1;
+ return CFREE_OK;
}
static void test_release(void* user, CfreeFileData* d) {
@@ -273,13 +277,12 @@ static void test_release(void* user, CfreeFileData* d) {
static CfreeFileIO g_file_io = {test_read_all, test_release, NULL, NULL};
-static void env_init(CfreeEnv* env) {
- memset(env, 0, sizeof *env);
- env->heap = &g_heap;
- env->diag = &g_diag;
- env->file_io = &g_file_io;
- env->execmem = &g_execmem;
- env->now = -1;
+static void ctx_init(CfreeContext* ctx) {
+ memset(ctx, 0, sizeof *ctx);
+ ctx->heap = &g_heap;
+ ctx->diag = &g_diag;
+ ctx->file_io = &g_file_io;
+ ctx->now = -1;
}
static int opt_level_from_env(void) {
@@ -292,14 +295,15 @@ static int opt_level_from_env(void) {
exit(2);
}
-static void add_test_system_includes(CfreeCompileOptions* opts) {
+static void add_test_system_includes(CfreeCCompileOptions* opts) {
static const char* dirs[] = {"rt/include"};
- opts->pp.system_include_dirs = dirs;
- opts->pp.nsystem_include_dirs = 1;
+ opts->preprocess.system_include_dirs = dirs;
+ opts->preprocess.nsystem_include_dirs = 1;
}
-static int add_runtime_archive(CfreeLinkOptions* opts, uint8_t** rt_data_out) {
- static CfreeBytesInputArchive rt_archive;
+static int add_runtime_archive(CfreeJitLinkOptions* opts,
+ uint8_t** rt_data_out) {
+ static CfreeLinkArchiveInput rt_archive;
uint8_t* data = NULL;
size_t len = 0;
const char* arch = cfree_test_arch_name();
@@ -312,9 +316,9 @@ static int add_runtime_archive(CfreeLinkOptions* opts, uint8_t** rt_data_out) {
if (read_file(path, &data, &len) != 0)
return 0;
memset(&rt_archive, 0, sizeof rt_archive);
- rt_archive.input.name = path;
- rt_archive.input.data = data;
- rt_archive.input.len = len;
+ rt_archive.bytes.name = path;
+ rt_archive.bytes.data = data;
+ rt_archive.bytes.len = len;
opts->inputs.archives = &rt_archive;
opts->inputs.narchives = 1;
*rt_data_out = data;
@@ -327,11 +331,11 @@ static int mode_emit(const char* src_path, const char* out_path) {
uint8_t* src = NULL;
size_t src_len = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeCompileOptions opts;
- CfreeWriter* w;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeCCompileOptions opts;
+ CfreeWriter* w = NULL;
int rc = 0;
size_t len = 0;
const uint8_t* data;
@@ -342,9 +346,8 @@ static int mode_emit(const char* src_path, const char* out_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
@@ -354,14 +357,13 @@ static int mode_emit(const char* src_path, const char* out_path) {
in.name = src_path;
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_C;
memset(&opts, 0, sizeof opts);
- opts.opt_level = opt_level_from_env();
+ opts.code.opt_level = opt_level_from_env();
add_test_system_includes(&opts);
- w = cfree_writer_mem(&g_heap);
- if (cfree_compile_obj_emit(c, &opts, &in, w) != 0) {
+ (void)cfree_writer_mem(&g_heap, &w);
+ if (cfree_compile_c_obj_emit(c, &opts, &in, w) != CFREE_OK) {
cfree_writer_close(w);
cfree_compiler_free(c);
free(src);
@@ -405,12 +407,13 @@ static int mode_jit(const char* src_path) {
uint8_t* src = NULL;
size_t src_len = 0;
CfreeTarget tgt;
- CfreeEnv env;
- CfreeCompiler* c;
- CfreeBytesInput in;
- CfreeBytesInput obj_in;
- CfreeCompileOptions opts;
- CfreeLinkOptions lopts;
+ CfreeContext ctx;
+ CfreeCompiler* c = NULL;
+ CfreeBytes in;
+ CfreeBytes obj_in;
+ CfreeCCompileOptions opts;
+ CfreeJitLinkOptions lopts;
+ CfreeJitHost jhost;
CfreeWriter* obj_w = NULL;
size_t obj_len = 0;
const uint8_t* obj_data = NULL;
@@ -424,9 +427,8 @@ static int mode_jit(const char* src_path) {
return 2;
}
target_from_env(&tgt);
- env_init(&env);
- c = cfree_compiler_new(tgt, &env);
- if (!c) {
+ ctx_init(&ctx);
+ if (cfree_compiler_new(tgt, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
@@ -436,13 +438,12 @@ static int mode_jit(const char* src_path) {
in.name = src_path;
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_C;
memset(&opts, 0, sizeof opts);
- opts.opt_level = opt_level_from_env();
+ opts.code.opt_level = opt_level_from_env();
add_test_system_includes(&opts);
- obj_w = cfree_writer_mem(&g_heap);
- if (cfree_compile_obj_emit(c, &opts, &in, obj_w) != 0) {
+ (void)cfree_writer_mem(&g_heap, &obj_w);
+ if (cfree_compile_c_obj_emit(c, &opts, &in, obj_w) != CFREE_OK) {
cfree_writer_close(obj_w);
cfree_compiler_free(c);
free(src);
@@ -460,7 +461,10 @@ static int mode_jit(const char* src_path) {
lopts.inputs.entry = "test_main";
add_runtime_archive(&lopts, &rt_data);
- if (cfree_link_jit(c, &lopts, &jit) != 0 || !jit) {
+ memset(&jhost, 0, sizeof jhost);
+ jhost.execmem = &g_execmem;
+
+ if (cfree_link_jit(c, &lopts, &jhost, &jit) != CFREE_OK || !jit) {
cfree_writer_close(obj_w);
cfree_compiler_free(c);
free(rt_data);
diff --git a/test/wasm/harness/wasm_tool.c b/test/wasm/harness/wasm_tool.c
@@ -1,4 +1,4 @@
-#include <cfree.h>
+#include <cfree/core.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
@@ -103,28 +103,28 @@ int main(int argc, char** argv) {
free(src);
return 2;
}
- CfreeEnv env;
- memset(&env, 0, sizeof env);
- env.heap = &g_heap;
- env.diag = &g_diag;
- CfreeCompiler* c = cfree_compiler_new(target, &env);
- if (!c) {
+ CfreeContext ctx;
+ memset(&ctx, 0, sizeof ctx);
+ ctx.heap = &g_heap;
+ ctx.diag = &g_diag;
+ ctx.now = -1;
+ CfreeCompiler* c = NULL;
+ if (cfree_compiler_new(target, &ctx, &c) != CFREE_OK || !c) {
free(src);
return 2;
}
- CfreeWriter* w = cfree_writer_mem(&g_heap);
- if (!w) {
+ CfreeWriter* w = NULL;
+ if (cfree_writer_mem(&g_heap, &w) != CFREE_OK || !w) {
cfree_compiler_free(c);
free(src);
return 2;
}
- CfreeBytesInput in;
+ CfreeBytes in;
memset(&in, 0, sizeof in);
in.name = argv[2];
in.data = src;
in.len = src_len;
- in.lang = CFREE_LANG_WASM;
int rc = cfree_wasm_wat_to_wasm(c, &in, w);
size_t out_len = 0;
const uint8_t* out = cfree_writer_mem_bytes(w, &out_len);