kit

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

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:
Adoc/api-migration.md | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/ar.c | 180++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mdriver/as.c | 51+++++++++++++++++++++++++++------------------------
Mdriver/cc.c | 677++++++++++++++++++++++++++++---------------------------------------------------
Mdriver/cflags.c | 4++--
Mdriver/cflags.h | 10++++++----
Mdriver/dbg.c | 349++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdriver/driver.h | 53++++++++++++++++++++++++++++++++++++-----------------
Mdriver/emu.c | 27+++++++++++++++------------
Mdriver/env.c | 288++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdriver/inputs.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mdriver/inputs.h | 29++++++++++++++++++-----------
Mdriver/ld.c | 64++++++++++++++++++++++++++++------------------------------------
Mdriver/objdump.c | 137+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mdriver/run.c | 65+++++++++++++++++++++++++++++++++--------------------------------
Mlang/c/c.c | 73++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mlang/c/c.h | 33+++++++++++++++++++++++++++------
Mlang/c/c_support.h | 6+++---
Mlang/c/lex/lex.c | 3++-
Mlang/c/pp/pp.c | 5+++--
Mlang/c/pp/pp_directive.c | 2+-
Mlang/c/pp/pp_priv.h | 2+-
Mlang/toy/compile.c | 27+++++++++++++++++----------
Mlang/toy/lexer.h | 2+-
Mlang/toy/toy.h | 9++++++---
Mlang/wasm/wasm.c | 42+++++++++++++++++++++++++-----------------
Mlang/wasm/wasm.h | 12++++++++----
Msrc/abi/abi.c | 5++---
Msrc/api/ar.c | 262+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/api/arch_regs.c | 27+++++++++++++++------------
Msrc/api/cg.c | 50+++++++++++++++++++++++++++-----------------------
Msrc/api/dep.c | 34+++++++++++++++-------------------
Msrc/api/detect.c | 46++++++++++++++++++++++------------------------
Msrc/api/disasm.c | 222+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/api/frontend.c | 81++++++++++++-------------------------------------------------------------------
Msrc/api/lifecycle.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Asrc/api/objbuilder.c | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/api/objfile.c | 376+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/api/pipeline.c | 1206++++++++++++++-----------------------------------------------------------------
Msrc/api/stubs.c | 77+++--------------------------------------------------------------------------
Msrc/api/writer_mem.c | 74++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/arch/aa64/disasm.c | 2+-
Msrc/arch/arch.h | 7+++++--
Msrc/asm/asm.c | 4++--
Msrc/asm/asm_helpers.h | 2+-
Msrc/asm/asm_lex.c | 7+++++--
Msrc/core/core.c | 37+++++++------------------------------
Msrc/core/core.h | 69+++++++++++++++++++++++++--------------------------------------------
Msrc/core/hashmap.h | 2+-
Msrc/core/metrics.h | 2+-
Msrc/core/source.c | 56++++++++++++++++++++++++++++----------------------------
Msrc/dbg/bp.c | 61++++++++++++++++++++++++++++++++-----------------------------
Msrc/dbg/dbg.h | 48+++++++++++++++++++++++++++++++-----------------
Msrc/dbg/displaced.c | 79++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/dbg/mem.c | 25+++++++++++--------------
Msrc/dbg/session.c | 213+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/dbg/step.c | 139++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/debug/debug.c | 2+-
Msrc/debug/dwarf_cfi.c | 21++++++++++++---------
Msrc/debug/dwarf_die.c | 3++-
Msrc/debug/dwarf_internal.h | 10++++++++--
Msrc/debug/dwarf_line.c | 80++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/debug/dwarf_loc.c | 4+++-
Msrc/debug/dwarf_open.c | 56++++++++++++++++++++++++++++++++++++++------------------
Msrc/debug/dwarf_query.c | 192++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/debug/dwarf_type.c | 54+++++++++++++++++++++++++++++++++---------------------
Msrc/emu/cpu.c | 6++----
Msrc/emu/decode.c | 2--
Msrc/emu/elf_load.c | 2--
Msrc/emu/emu.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/emu/emu.h | 22++++++++++++++++------
Msrc/emu/lift.c | 1-
Msrc/emu/runtime.c | 71++++++++++++++++++++++++++++++-----------------------------------------
Msrc/link/link.c | 30+++++++++++++++++++++---------
Msrc/link/link.h | 16++++++++++++----
Asrc/link/link_api.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/link/link_dyn.c | 3+--
Msrc/link/link_internal.h | 6++++++
Msrc/link/link_jit.c | 130++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/link/link_layout.c | 6++++--
Msrc/link/link_reloc_layout.c | 2+-
Msrc/link/link_resolve.c | 2+-
Msrc/link/link_script.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/obj/elf.h | 2+-
Msrc/obj/elf_emit.c | 2+-
Msrc/obj/macho.h | 2--
Msrc/obj/macho_emit.c | 2+-
Msrc/obj/obj.c | 2+-
Msrc/obj/obj.h | 4++--
Msrc/obj/obj_secnames.c | 4++--
Msrc/obj/obj_tls.c | 2+-
Mtest/api/cg_type_test.c | 93++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mtest/ar_test.c | 279+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/arch/aa64_inline_test.c | 8++++----
Mtest/asm/harness/asm_runner.c | 176+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mtest/debug/roundtrip_unit.c | 10+++++-----
Mtest/dwarf/dwarf_test.c | 72++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mtest/elf/cfree-roundtrip.c | 22++++++++++++----------
Mtest/elf/unit/align_4k.c | 12+++++++-----
Mtest/elf/unit/smoke.c | 18++++++++++--------
Mtest/lib/cfree_test_target.h | 2+-
Mtest/link/harness/jit_runner.c | 93++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mtest/link/harness/link_exe_runner.c | 51+++++++++++++++++++++++++++------------------------
Mtest/macho/cfree-roundtrip-macho.c | 22++++++++++++----------
Mtest/opt/opt_test.c | 28++++++++++++++++------------
Mtest/parse/harness/parse_runner.c | 132+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mtest/wasm/harness/wasm_tool.c | 22+++++++++++-----------
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, &param)) { + for (;;) { + CfreeIterResult r = cfree_dwarf_param_iter_next(pit, &param); + 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, - &region) != 0) { + &region) != 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);