commit 1b5a72b7bfb642b0a17e7e5cb8236930bd3a6fab
parent ca3d78eacf3e52586542094ebca90c079b9d9edd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 16:12:13 -0700
Reorganize public API bridge files
Diffstat:
20 files changed, 1938 insertions(+), 1950 deletions(-)
diff --git a/src/api/ar.c b/src/api/ar.c
@@ -1,435 +0,0 @@
-/* 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
- * ============================================================ */
-
-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'); }
-
-static void wh_ar_num(char* dst, int width, u64 v) {
- char tmp[20];
- int len = 0, i;
- if (v == 0) {
- tmp[len++] = '0';
- } else {
- u64 t = v;
- while (t) {
- tmp[len++] = '0' + (int)(t % 10);
- t /= 10;
- }
- }
- for (i = 0; i < len / 2; ++i) {
- char x = tmp[i];
- tmp[i] = tmp[len - 1 - i];
- tmp[len - 1 - i] = x;
- }
- for (i = 0; i < len && i < width; ++i) dst[i] = tmp[i];
- for (; i < width; ++i) dst[i] = ' ';
-}
-
-static CfreeStatus wh_be32(Writer* w, u32 v) {
- u8 b[4];
- wr_u32_be(b, v);
- return wh_bytes(w, b, 4);
-}
-
-static void ar_name_basename(const char* in, const char** name_out,
- size_t* len_out) {
- const char* name = in;
- const char* p;
- size_t namelen = 0;
- for (p = in; *p; ++p) {
- if (*p == '/') name = p + 1;
- }
- for (p = name; *p; ++p) ++namelen;
- *name_out = name;
- *len_out = namelen;
-}
-
-static int ar_name_needs_longtable(const char* name, size_t len) {
- size_t i;
- if (len > 15) return 1;
- for (i = 0; i < len; ++i)
- if (name[i] == '/') return 1;
- return 0;
-}
-
-static size_t ar_strlen(const char* s) {
- size_t n = 0;
- while (s[n]) ++n;
- return n;
-}
-
-static size_t ar_member_padded_size(size_t len) { return 60 + len + (len & 1); }
-
-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];
- for (j = 16; j < 28; ++j) hdr[j] = ' ';
- if (epoch)
- wh_ar_num(hdr + 16, 12, epoch);
- else
- hdr[16] = '0';
- for (j = 28; j < 34; ++j) hdr[j] = ' ';
- hdr[28] = '0';
- for (j = 34; j < 40; ++j) hdr[j] = ' ';
- hdr[34] = '0';
- for (j = 40; j < 48; ++j) hdr[j] = ' ';
- hdr[40] = '6';
- hdr[41] = '4';
- hdr[42] = '4';
- wh_ar_num(hdr + 48, 10, size);
- hdr[58] = '`';
- hdr[59] = '\n';
-}
-
-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;
- uint64_t epoch;
- int long_names;
- int symbol_index;
- const CfreeArMemberSymbols* msyms;
- uint64_t longtab_size = 0;
- 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 CFREE_INVALID;
- if (!members && nmembers) return CFREE_INVALID;
-
- if (!opts) opts = &default_opts;
- epoch = opts->epoch;
- long_names = opts->long_names;
- symbol_index = opts->symbol_index;
- msyms = opts->member_symbols;
-
- if (long_names) {
- for (i = 0; i < nmembers; ++i) {
- const char* name;
- size_t namelen;
- 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;
- }
- }
- } else {
- for (i = 0; i < nmembers; ++i) {
- if (!members[i].name) return CFREE_INVALID;
- }
- }
-
- if (symbol_index) {
- if (msyms) {
- for (i = 0; i < nmembers; ++i) {
- u32 k;
- 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 CFREE_INVALID;
- nsyms += 1;
- names_size += (uint64_t)ar_strlen(nm) + 1;
- }
- }
- }
- index_payload = (uint64_t)4 + (uint64_t)4 * (uint64_t)nsyms + names_size;
- index_total = 60 + index_payload + (index_payload & 1);
- }
- if (longtab_size) {
- longtab_total = 60 + longtab_size + (longtab_size & 1);
- }
-
- wh_bytes(out, magic, 8);
-
- if (symbol_index) {
- char hdr[60];
- char name_field[16];
- size_t j;
- uint64_t cur_offset;
-
- for (j = 0; j < 16; ++j) name_field[j] = ' ';
- name_field[0] = '/';
- ar_fill_header(hdr, name_field, epoch, index_payload);
- wh_bytes(out, hdr, 60);
-
- wh_be32(out, nsyms);
-
- cur_offset = (uint64_t)8 + index_total + longtab_total;
- if (msyms) {
- for (i = 0; i < nmembers; ++i) {
- u32 k;
- for (k = 0; k < msyms[i].count; ++k) {
- wh_be32(out, (u32)cur_offset);
- }
- cur_offset += ar_member_padded_size(members[i].len);
- }
- }
-
- if (msyms) {
- for (i = 0; i < nmembers; ++i) {
- u32 k;
- for (k = 0; k < msyms[i].count; ++k) {
- const char* nm = msyms[i].names[k];
- size_t nmlen = ar_strlen(nm);
- wh_bytes(out, nm, nmlen + 1);
- }
- }
- }
-
- if (index_payload & 1) wh_bytes(out, &pad, 1);
- }
-
- if (longtab_size) {
- char hdr[60];
- char name_field[16];
- size_t j;
- for (j = 0; j < 16; ++j) name_field[j] = ' ';
- name_field[0] = '/';
- name_field[1] = '/';
- ar_fill_header(hdr, name_field, 0, longtab_size);
- wh_bytes(out, hdr, 60);
- for (i = 0; i < nmembers; ++i) {
- const char* name;
- size_t namelen;
- ar_name_basename(members[i].name, &name, &namelen);
- if (ar_name_needs_longtable(name, namelen)) {
- wh_bytes(out, name, namelen);
- wh_bytes(out, "/\n", 2);
- }
- }
- if (longtab_size & 1) wh_bytes(out, &pad, 1);
- }
-
- {
- uint64_t longtab_off = 0;
- for (i = 0; i < nmembers; ++i) {
- const CfreeBytes* m = &members[i];
- const char* name;
- size_t namelen;
- char hdr[60];
- char name_field[16];
- size_t j;
-
- ar_name_basename(m->name, &name, &namelen);
-
- for (j = 0; j < 16; ++j) name_field[j] = ' ';
- if (long_names && ar_name_needs_longtable(name, namelen)) {
- name_field[0] = '/';
- wh_ar_num(name_field + 1, 15, longtab_off);
- longtab_off += (uint64_t)namelen + 2;
- } else {
- size_t emit = namelen > 15 ? 15 : namelen;
- for (j = 0; j < emit; ++j) name_field[j] = name[j];
- name_field[emit] = '/';
- }
-
- ar_fill_header(hdr, name_field, epoch, (uint64_t)m->len);
- wh_bytes(out, hdr, 60);
- if (m->data && m->len) wh_bytes(out, m->data, m->len);
- if (m->len & 1) wh_bytes(out, &pad, 1);
- }
- }
-
- return cfree_writer_status(out);
-}
-
-/* ============================================================
- * Read (iterator) and list
- * ============================================================ */
-
-#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];
-};
-
-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) {
- size_t k = (size_t)off + i;
- char ch;
- if (k >= it->longnames_len) break;
- ch = (char)it->longnames[k];
- if (ch == '/' || ch == '\n') break;
- it->namebuf[i] = ch;
- }
- it->namebuf[i] = '\0';
- return i;
-}
-
-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;
- int j;
- int namelen;
- char name_field[16];
-
- if (it->p + 60 > it->end) return CFREE_ITER_END;
-
- 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];
- 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 CFREE_ITER_END;
-
- if (name_field[0] == '/' && name_field[1] == '/') {
- it->longnames = it->p;
- it->longnames_len = (size_t)size;
- goto advance;
- }
- if (name_field[0] == '/' && name_field[1] == ' ') {
- goto advance;
- }
-
- if (name_field[0] == '/' && name_field[1] >= '0' && name_field[1] <= '9') {
- uint64_t off = 0;
- for (j = 1; j < 16; ++j) {
- char ch = name_field[j];
- if (ch < '0' || ch > '9') break;
- off = off * 10 + (uint64_t)(unsigned char)(ch - '0');
- }
- namelen = (int)ar_resolve_longname(it, off);
- } else if (name_field[0] == '#' && name_field[1] == '1' &&
- name_field[2] == '/') {
- uint64_t nlen = 0;
- size_t k;
- for (j = 3; j < 16; ++j) {
- char ch = name_field[j];
- if (ch < '0' || ch > '9') break;
- nlen = nlen * 10 + (uint64_t)(unsigned char)(ch - '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];
- if (ch == '\0') break;
- it->namebuf[namelen++] = ch;
- }
- 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] = '\0';
- }
-
- 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++;
-
- if (it->namebuf[0] == '_' && it->namebuf[1] == '_' &&
- it->namebuf[2] == '.') {
- continue;
- }
- if (namelen > 0) return CFREE_ITER_ITEM;
- continue;
-
- advance:
- it->p += (size_t)size;
- if ((size & 1) && it->p < it->end) it->p++;
- }
-}
-
-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 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';
-
- 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 cfree_writer_status(out);
-}
diff --git a/src/api/arch.c b/src/api/arch.c
@@ -0,0 +1,36 @@
+/* Public arch register name API. */
+
+#include "arch/arch.h"
+
+#include <cfree/arch.h>
+#include <stddef.h>
+
+const char* cfree_arch_register_name(CfreeArchKind arch, uint32_t dwarf_idx) {
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_name) return NULL;
+ return impl->register_name(dwarf_idx);
+}
+
+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) {
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_count) return 0;
+ return impl->register_count();
+}
+
+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/arch_regs.c b/src/api/arch_regs.c
@@ -1,36 +0,0 @@
-/* Public arch register name API. */
-
-#include <cfree/arch.h>
-#include <stddef.h>
-
-#include "arch/arch.h"
-
-const char* cfree_arch_register_name(CfreeArchKind arch, uint32_t dwarf_idx) {
- const ArchImpl* impl = arch_lookup(arch);
- if (!impl || !impl->register_name) return NULL;
- return impl->register_name(dwarf_idx);
-}
-
-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) {
- const ArchImpl* impl = arch_lookup(arch);
- if (!impl || !impl->register_count) return 0;
- return impl->register_count();
-}
-
-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/archive.c b/src/api/archive.c
@@ -0,0 +1,435 @@
+/* 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
+ * ============================================================ */
+
+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'); }
+
+static void wh_ar_num(char* dst, int width, u64 v) {
+ char tmp[20];
+ int len = 0, i;
+ if (v == 0) {
+ tmp[len++] = '0';
+ } else {
+ u64 t = v;
+ while (t) {
+ tmp[len++] = '0' + (int)(t % 10);
+ t /= 10;
+ }
+ }
+ for (i = 0; i < len / 2; ++i) {
+ char x = tmp[i];
+ tmp[i] = tmp[len - 1 - i];
+ tmp[len - 1 - i] = x;
+ }
+ for (i = 0; i < len && i < width; ++i) dst[i] = tmp[i];
+ for (; i < width; ++i) dst[i] = ' ';
+}
+
+static CfreeStatus wh_be32(Writer* w, u32 v) {
+ u8 b[4];
+ wr_u32_be(b, v);
+ return wh_bytes(w, b, 4);
+}
+
+static void ar_name_basename(const char* in, const char** name_out,
+ size_t* len_out) {
+ const char* name = in;
+ const char* p;
+ size_t namelen = 0;
+ for (p = in; *p; ++p) {
+ if (*p == '/') name = p + 1;
+ }
+ for (p = name; *p; ++p) ++namelen;
+ *name_out = name;
+ *len_out = namelen;
+}
+
+static int ar_name_needs_longtable(const char* name, size_t len) {
+ size_t i;
+ if (len > 15) return 1;
+ for (i = 0; i < len; ++i)
+ if (name[i] == '/') return 1;
+ return 0;
+}
+
+static size_t ar_strlen(const char* s) {
+ size_t n = 0;
+ while (s[n]) ++n;
+ return n;
+}
+
+static size_t ar_member_padded_size(size_t len) { return 60 + len + (len & 1); }
+
+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];
+ for (j = 16; j < 28; ++j) hdr[j] = ' ';
+ if (epoch)
+ wh_ar_num(hdr + 16, 12, epoch);
+ else
+ hdr[16] = '0';
+ for (j = 28; j < 34; ++j) hdr[j] = ' ';
+ hdr[28] = '0';
+ for (j = 34; j < 40; ++j) hdr[j] = ' ';
+ hdr[34] = '0';
+ for (j = 40; j < 48; ++j) hdr[j] = ' ';
+ hdr[40] = '6';
+ hdr[41] = '4';
+ hdr[42] = '4';
+ wh_ar_num(hdr + 48, 10, size);
+ hdr[58] = '`';
+ hdr[59] = '\n';
+}
+
+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;
+ uint64_t epoch;
+ int long_names;
+ int symbol_index;
+ const CfreeArMemberSymbols* msyms;
+ uint64_t longtab_size = 0;
+ 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 CFREE_INVALID;
+ if (!members && nmembers) return CFREE_INVALID;
+
+ if (!opts) opts = &default_opts;
+ epoch = opts->epoch;
+ long_names = opts->long_names;
+ symbol_index = opts->symbol_index;
+ msyms = opts->member_symbols;
+
+ if (long_names) {
+ for (i = 0; i < nmembers; ++i) {
+ const char* name;
+ size_t namelen;
+ 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;
+ }
+ }
+ } else {
+ for (i = 0; i < nmembers; ++i) {
+ if (!members[i].name) return CFREE_INVALID;
+ }
+ }
+
+ if (symbol_index) {
+ if (msyms) {
+ for (i = 0; i < nmembers; ++i) {
+ u32 k;
+ 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 CFREE_INVALID;
+ nsyms += 1;
+ names_size += (uint64_t)ar_strlen(nm) + 1;
+ }
+ }
+ }
+ index_payload = (uint64_t)4 + (uint64_t)4 * (uint64_t)nsyms + names_size;
+ index_total = 60 + index_payload + (index_payload & 1);
+ }
+ if (longtab_size) {
+ longtab_total = 60 + longtab_size + (longtab_size & 1);
+ }
+
+ wh_bytes(out, magic, 8);
+
+ if (symbol_index) {
+ char hdr[60];
+ char name_field[16];
+ size_t j;
+ uint64_t cur_offset;
+
+ for (j = 0; j < 16; ++j) name_field[j] = ' ';
+ name_field[0] = '/';
+ ar_fill_header(hdr, name_field, epoch, index_payload);
+ wh_bytes(out, hdr, 60);
+
+ wh_be32(out, nsyms);
+
+ cur_offset = (uint64_t)8 + index_total + longtab_total;
+ if (msyms) {
+ for (i = 0; i < nmembers; ++i) {
+ u32 k;
+ for (k = 0; k < msyms[i].count; ++k) {
+ wh_be32(out, (u32)cur_offset);
+ }
+ cur_offset += ar_member_padded_size(members[i].len);
+ }
+ }
+
+ if (msyms) {
+ for (i = 0; i < nmembers; ++i) {
+ u32 k;
+ for (k = 0; k < msyms[i].count; ++k) {
+ const char* nm = msyms[i].names[k];
+ size_t nmlen = ar_strlen(nm);
+ wh_bytes(out, nm, nmlen + 1);
+ }
+ }
+ }
+
+ if (index_payload & 1) wh_bytes(out, &pad, 1);
+ }
+
+ if (longtab_size) {
+ char hdr[60];
+ char name_field[16];
+ size_t j;
+ for (j = 0; j < 16; ++j) name_field[j] = ' ';
+ name_field[0] = '/';
+ name_field[1] = '/';
+ ar_fill_header(hdr, name_field, 0, longtab_size);
+ wh_bytes(out, hdr, 60);
+ for (i = 0; i < nmembers; ++i) {
+ const char* name;
+ size_t namelen;
+ ar_name_basename(members[i].name, &name, &namelen);
+ if (ar_name_needs_longtable(name, namelen)) {
+ wh_bytes(out, name, namelen);
+ wh_bytes(out, "/\n", 2);
+ }
+ }
+ if (longtab_size & 1) wh_bytes(out, &pad, 1);
+ }
+
+ {
+ uint64_t longtab_off = 0;
+ for (i = 0; i < nmembers; ++i) {
+ const CfreeBytes* m = &members[i];
+ const char* name;
+ size_t namelen;
+ char hdr[60];
+ char name_field[16];
+ size_t j;
+
+ ar_name_basename(m->name, &name, &namelen);
+
+ for (j = 0; j < 16; ++j) name_field[j] = ' ';
+ if (long_names && ar_name_needs_longtable(name, namelen)) {
+ name_field[0] = '/';
+ wh_ar_num(name_field + 1, 15, longtab_off);
+ longtab_off += (uint64_t)namelen + 2;
+ } else {
+ size_t emit = namelen > 15 ? 15 : namelen;
+ for (j = 0; j < emit; ++j) name_field[j] = name[j];
+ name_field[emit] = '/';
+ }
+
+ ar_fill_header(hdr, name_field, epoch, (uint64_t)m->len);
+ wh_bytes(out, hdr, 60);
+ if (m->data && m->len) wh_bytes(out, m->data, m->len);
+ if (m->len & 1) wh_bytes(out, &pad, 1);
+ }
+ }
+
+ return cfree_writer_status(out);
+}
+
+/* ============================================================
+ * Read (iterator) and list
+ * ============================================================ */
+
+#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];
+};
+
+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) {
+ size_t k = (size_t)off + i;
+ char ch;
+ if (k >= it->longnames_len) break;
+ ch = (char)it->longnames[k];
+ if (ch == '/' || ch == '\n') break;
+ it->namebuf[i] = ch;
+ }
+ it->namebuf[i] = '\0';
+ return i;
+}
+
+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;
+ int j;
+ int namelen;
+ char name_field[16];
+
+ if (it->p + 60 > it->end) return CFREE_ITER_END;
+
+ 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];
+ 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 CFREE_ITER_END;
+
+ if (name_field[0] == '/' && name_field[1] == '/') {
+ it->longnames = it->p;
+ it->longnames_len = (size_t)size;
+ goto advance;
+ }
+ if (name_field[0] == '/' && name_field[1] == ' ') {
+ goto advance;
+ }
+
+ if (name_field[0] == '/' && name_field[1] >= '0' && name_field[1] <= '9') {
+ uint64_t off = 0;
+ for (j = 1; j < 16; ++j) {
+ char ch = name_field[j];
+ if (ch < '0' || ch > '9') break;
+ off = off * 10 + (uint64_t)(unsigned char)(ch - '0');
+ }
+ namelen = (int)ar_resolve_longname(it, off);
+ } else if (name_field[0] == '#' && name_field[1] == '1' &&
+ name_field[2] == '/') {
+ uint64_t nlen = 0;
+ size_t k;
+ for (j = 3; j < 16; ++j) {
+ char ch = name_field[j];
+ if (ch < '0' || ch > '9') break;
+ nlen = nlen * 10 + (uint64_t)(unsigned char)(ch - '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];
+ if (ch == '\0') break;
+ it->namebuf[namelen++] = ch;
+ }
+ 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] = '\0';
+ }
+
+ 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++;
+
+ if (it->namebuf[0] == '_' && it->namebuf[1] == '_' &&
+ it->namebuf[2] == '.') {
+ continue;
+ }
+ if (namelen > 0) return CFREE_ITER_ITEM;
+ continue;
+
+ advance:
+ it->p += (size_t)size;
+ if ((size & 1) && it->p < it->end) it->p++;
+ }
+}
+
+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 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';
+
+ 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 cfree_writer_status(out);
+}
diff --git a/src/api/compile.c b/src/api/compile.c
@@ -0,0 +1,408 @@
+/* libcfree's top-level compile entry points. Status-returning shapes
+ * that drive the C, asm, and registered-frontend paths. */
+
+#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 "obj/obj.h"
+
+static SrcLoc no_loc(void) {
+ SrcLoc loc;
+ loc.file_id = 0;
+ loc.line = 0;
+ loc.col = 0;
+ return loc;
+}
+
+static _Noreturn void panic_bad_options(Compiler* c, const char* msg) {
+ compiler_panic(c, no_loc(), "bad cfree options: %s", msg);
+}
+
+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;
+}
+
+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 CFREE_OK;
+}
+
+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");
+ }
+}
+
+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 ((unsigned)input->lang < CFREE_LANG_COUNT) {
+ frontend = c->frontends[input->lang];
+ }
+ if (frontend) {
+ CfreeStatus st;
+ metrics_scope_begin(c, "compile.frontend");
+ 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");
+ metrics_count(c, "compile.obj_sections", obj_section_count(ob));
+ metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
+ return;
+ }
+
+ if (input->lang == CFREE_LANG_ASM) {
+ metrics_scope_begin(c, "compile.asm.lex_open");
+ 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");
+ 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_count(c, "compile.obj_sections", obj_section_count(ob));
+ metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
+ metrics_scope_begin(c, "compile.asm.mc_free");
+ mc_free(mc);
+ metrics_scope_end(c, "compile.asm.mc_free");
+ return;
+ }
+
+ compiler_panic(c, no_loc(), "no frontend registered for language: %u",
+ (u32)input->lang);
+}
+
+/* ============================================================
+ * 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");
+ }
+
+ 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;
+}
+
+CfreeStatus cfree_compile_c_obj(CfreeCompiler* c,
+ const CfreeCCompileOptions* opts,
+ const CfreeBytes* input,
+ CfreeObjBuilder** out) {
+ PanicSave saved;
+ 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 CFREE_ERR;
+ }
+ validate_bytes(c, input);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->len);
+ compile_c_into(c, opts, input, ob);
+ metrics_scope_end(c, "compile.tu");
+ *out = ob;
+ compiler_panic_restore(c, &saved);
+ return CFREE_OK;
+}
+
+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 CFREE_ERR;
+ }
+ validate_bytes(c, input);
+ ob = obj_new(c);
+ metrics_scope_begin(c, "compile.tu");
+ metrics_count(c, "compile.input_bytes", (u64)input->len);
+ 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 CFREE_OK;
+}
+
+/* ============================================================
+ * Asm
+ * ============================================================ */
+
+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;
+ 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 CFREE_ERR;
+ }
+ 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 CFREE_OK;
+}
+
+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 CFREE_ERR;
+ }
+ 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 CFREE_OK;
+}
+
+/* ============================================================
+ * Source (registered frontend)
+ * ============================================================ */
+
+CfreeStatus cfree_compile_source_obj(CfreeCompiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder** out) {
+ PanicSave saved;
+ 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 CFREE_ERR;
+ }
+ 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 CFREE_OK;
+}
+
+CfreeStatus cfree_compile_source_obj_emit(
+ CfreeCompiler* c, const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* 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 CFREE_ERR;
+ }
+ 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;
+}
+
+struct CfreeDepIter {
+ Compiler* c;
+ SourceDepIter* inner;
+};
+
+CfreeStatus cfree_dep_iter_new(CfreeCompiler* c, CfreeDepIter** out) {
+ Heap* h;
+ CfreeDepIter* it;
+ 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 CFREE_NOMEM;
+ it->c = c;
+ it->inner = source_depiter_new(c->sources);
+ if (!it->inner) {
+ h->free(h, it, sizeof(*it));
+ return CFREE_NOMEM;
+ }
+ *out = it;
+ return CFREE_OK;
+}
+
+CfreeIterResult cfree_dep_iter_next(CfreeDepIter* it, CfreeDepEdge* out) {
+ const SourceInclude* edge;
+ const SourceFile* includer;
+ const SourceFile* included;
+ if (!it || !out) return CFREE_ITER_ERROR;
+ edge = source_depiter_next(it->inner);
+ 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 =
+ includer ? pool_str(it->c->global, includer->name, NULL) : NULL;
+ out->included_name =
+ included ? pool_str(it->c->global, included->name, NULL) : NULL;
+ out->include_loc = edge->include_loc;
+ 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 CFREE_ITER_ITEM;
+}
+
+void cfree_dep_iter_free(CfreeDepIter* it) {
+ Heap* h;
+ if (!it) return;
+ h = it->c->ctx->heap;
+ if (it->inner) source_depiter_free(it->inner);
+ h->free(h, it, sizeof(*it));
+}
diff --git a/src/api/core.c b/src/api/core.c
@@ -0,0 +1,193 @@
+/* Public core API bridge. */
+
+#include "core/core.h"
+
+#include <cfree/core.h>
+#include <string.h>
+
+#include "core/heap.h"
+#include "core/pool.h"
+
+CfreeStatus cfree_compiler_new(CfreeTarget target, const CfreeContext* ctx,
+ CfreeCompiler** out) {
+ Heap* h;
+ Compiler* c;
+
+ 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 CFREE_NOMEM;
+ compiler_init(c, target, ctx);
+ *out = c;
+ return CFREE_OK;
+}
+
+void cfree_compiler_free(CfreeCompiler* c) {
+ Heap* h;
+ if (!c) return;
+ h = c->ctx->heap;
+ compiler_fini(c);
+ h->free(h, c, sizeof(*c));
+}
+
+CfreeTarget cfree_compiler_target(CfreeCompiler* c) {
+ CfreeTarget t;
+ memset(&t, 0, sizeof t);
+ if (!c) return t;
+ return c->target;
+}
+
+const CfreeContext* cfree_compiler_context(CfreeCompiler* c) {
+ return (c && c->ctx) ? c->ctx : NULL;
+}
+
+const char* cfree_compiler_file_name(CfreeCompiler* c, uint32_t file_id) {
+ const SourceFile* f;
+ if (!c) return NULL;
+ f = source_file(c->sources, file_id);
+ if (!f) return NULL;
+ return pool_str(c->global, f->name, NULL);
+}
+
+CfreeSym cfree_sym_intern(CfreeCompiler* c, const char* str) {
+ if (!c || !str) return 0;
+ 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;
+ char* buf;
+ CfreeSym out;
+ Heap* h;
+
+ if (!c || !source_name) return 0;
+ name = pool_str(c->global, (Sym)source_name, &len);
+ if (!name) return 0;
+ if (c->target.obj != CFREE_OBJ_MACHO) return source_name;
+
+ h = c->ctx->heap;
+ buf = (char*)h->alloc(h, len + 2u, 1);
+ if (!buf) return 0;
+ buf[0] = '_';
+ if (len) memcpy(buf + 1, name, len);
+ buf[len + 1u] = '\0';
+ out = pool_intern(c->global, buf, (u32)(len + 1u));
+ h->free(h, buf, len + 2u);
+ return out;
+}
+
+typedef struct MemWriter {
+ CfreeWriter base;
+ Heap* heap;
+ u8* data;
+ size_t cap;
+ size_t len;
+ size_t pos;
+ CfreeStatus status;
+} MemWriter;
+
+static CfreeStatus mw_grow(MemWriter* mw, size_t needed) {
+ size_t new_cap;
+ u8* p;
+
+ 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->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->status = CFREE_NOMEM;
+ return CFREE_NOMEM;
+ }
+ if (new_cap > mw->cap) memset(p + mw->cap, 0, new_cap - mw->cap);
+ mw->data = p;
+ mw->cap = new_cap;
+ return CFREE_OK;
+}
+
+static CfreeStatus mw_write(CfreeWriter* w, const void* data, size_t n) {
+ MemWriter* mw = (MemWriter*)w;
+ size_t end;
+ CfreeStatus st;
+
+ if (mw->status != CFREE_OK) return mw->status;
+ if (n == 0) return CFREE_OK;
+ end = mw->pos + n;
+ if (end < mw->pos) {
+ 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 CfreeStatus mw_seek(CfreeWriter* w, uint64_t off) {
+ MemWriter* mw = (MemWriter*)w;
+ 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 CfreeStatus mw_status(CfreeWriter* w) { return ((MemWriter*)w)->status; }
+
+static void mw_close(CfreeWriter* w) {
+ MemWriter* mw = (MemWriter*)w;
+ Heap* h = mw->heap;
+ if (mw->data) h->free(h, mw->data, mw->cap);
+ h->free(h, mw, sizeof(*mw));
+}
+
+CfreeStatus cfree_writer_mem(CfreeHeap* heap, CfreeWriter** out) {
+ MemWriter* mw;
+ if (!out) return CFREE_INVALID;
+ if (!heap) return CFREE_INVALID;
+ mw = (MemWriter*)heap->alloc(heap, sizeof(*mw), _Alignof(MemWriter));
+ if (!mw) return CFREE_NOMEM;
+ mw->base.write = mw_write;
+ mw->base.seek = mw_seek;
+ mw->base.tell = mw_tell;
+ 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->status = CFREE_OK;
+ *out = &mw->base;
+ return CFREE_OK;
+}
+
+const uint8_t* cfree_writer_mem_bytes(CfreeWriter* w, size_t* len_out) {
+ MemWriter* mw = (MemWriter*)w;
+ if (len_out) *len_out = mw ? mw->len : 0;
+ return mw ? mw->data : NULL;
+}
diff --git a/src/api/dep.c b/src/api/dep.c
@@ -1,59 +0,0 @@
-/* Header-dependency iteration. */
-
-#include <cfree/compile.h>
-
-#include "core/core.h"
-#include "core/heap.h"
-#include "core/pool.h"
-
-struct CfreeDepIter {
- Compiler* c;
- SourceDepIter* inner;
-};
-
-CfreeStatus cfree_dep_iter_new(CfreeCompiler* c, CfreeDepIter** out) {
- Heap* h;
- CfreeDepIter* it;
- 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 CFREE_NOMEM;
- it->c = c;
- it->inner = source_depiter_new(c->sources);
- if (!it->inner) {
- h->free(h, it, sizeof(*it));
- return CFREE_NOMEM;
- }
- *out = it;
- return CFREE_OK;
-}
-
-CfreeIterResult cfree_dep_iter_next(CfreeDepIter* it, CfreeDepEdge* out) {
- const SourceInclude* edge;
- const SourceFile* includer;
- const SourceFile* included;
- if (!it || !out) return CFREE_ITER_ERROR;
- edge = source_depiter_next(it->inner);
- 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 =
- includer ? pool_str(it->c->global, includer->name, NULL) : NULL;
- out->included_name =
- included ? pool_str(it->c->global, included->name, NULL) : NULL;
- out->include_loc = edge->include_loc;
- 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 CFREE_ITER_ITEM;
-}
-
-void cfree_dep_iter_free(CfreeDepIter* it) {
- Heap* h;
- if (!it) return;
- h = it->c->ctx->heap;
- if (it->inner) source_depiter_free(it->inner);
- h->free(h, it, sizeof(*it));
-}
diff --git a/src/api/frontend.c b/src/api/frontend.c
@@ -1,52 +1,9 @@
#include <cfree/frontend.h>
-#include <cfree/support/arena.h>
#include <setjmp.h>
#include <stdarg.h>
-#include <string.h>
-#include "core/arena.h"
#include "core/core.h"
#include "core/metrics.h"
-#include "core/pool.h"
-
-struct CfreeArena {
- Arena inner;
-};
-
-CfreeStatus cfree_arena_new(CfreeHeap* h, size_t block_size, CfreeArena** out) {
- CfreeArena* a;
- if (!out) return CFREE_INVALID;
- if (!h) return CFREE_INVALID;
- a = (CfreeArena*)h->alloc(h, sizeof(*a), _Alignof(CfreeArena));
- if (!a) return CFREE_NOMEM;
- arena_init(&a->inner, (Heap*)h, block_size);
- *out = a;
- return CFREE_OK;
-}
-
-void cfree_arena_free(CfreeArena* a) {
- CfreeHeap* h;
- if (!a) return;
- h = a->inner.heap;
- arena_fini(&a->inner);
- h->free(h, a, sizeof(*a));
-}
-
-void cfree_arena_reset(CfreeArena* a) {
- if (a) arena_reset(&a->inner);
-}
-
-void* cfree_arena_alloc(CfreeArena* a, size_t size, size_t align) {
- return a ? arena_alloc(&a->inner, size, align) : NULL;
-}
-
-void* cfree_arena_zalloc(CfreeArena* a, size_t size, size_t align) {
- return a ? arena_zalloc(&a->inner, size, align) : NULL;
-}
-
-char* cfree_arena_strdup(CfreeArena* a, const char* s, size_t len) {
- return a ? arena_strdup(&a->inner, s, len) : NULL;
-}
CfreeStatus cfree_frontend_run(CfreeCompiler* c, CfreeFrontendRunFn fn,
void* user) {
diff --git a/src/api/lifecycle.c b/src/api/lifecycle.c
@@ -1,158 +0,0 @@
-/* CfreeCompiler lifecycle. */
-
-#include <cfree/core.h>
-#include <cfree/source.h>
-#include <string.h>
-
-#include "core/core.h"
-#include "core/heap.h"
-#include "core/pool.h"
-
-CfreeStatus cfree_compiler_new(CfreeTarget target, const CfreeContext* ctx,
- CfreeCompiler** out) {
- Heap* h;
- Compiler* c;
-
- 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 CFREE_NOMEM;
- compiler_init(c, target, ctx);
- *out = c;
- return CFREE_OK;
-}
-
-void cfree_compiler_free(CfreeCompiler* c) {
- Heap* h;
- if (!c) return;
- h = c->ctx->heap;
- compiler_fini(c);
- h->free(h, c, sizeof(*c));
-}
-
-CfreeTarget cfree_compiler_target(CfreeCompiler* c) {
- CfreeTarget t;
- memset(&t, 0, sizeof t);
- if (!c) return t;
- return c->target;
-}
-
-const CfreeContext* cfree_compiler_context(CfreeCompiler* c) {
- return (c && c->ctx) ? c->ctx : NULL;
-}
-
-const char* cfree_compiler_file_name(CfreeCompiler* c, uint32_t file_id) {
- const SourceFile* f;
- if (!c) return NULL;
- f = source_file(c->sources, file_id);
- if (!f) return NULL;
- return pool_str(c->global, f->name, NULL);
-}
-
-CfreeSym cfree_sym_intern(CfreeCompiler* c, const char* str) {
- if (!c || !str) return 0;
- 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;
- char* buf;
- CfreeSym out;
- Heap* h;
-
- if (!c || !source_name) return 0;
- name = pool_str(c->global, (Sym)source_name, &len);
- if (!name) return 0;
- if (c->target.obj != CFREE_OBJ_MACHO) return source_name;
-
- h = c->ctx->heap;
- buf = (char*)h->alloc(h, len + 2u, 1);
- if (!buf) return 0;
- buf[0] = '_';
- if (len) memcpy(buf + 1, name, len);
- buf[len + 1u] = '\0';
- out = pool_intern(c->global, buf, (u32)(len + 1u));
- h->free(h, buf, len + 2u);
- return out;
-}
-
-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 CFREE_OK;
-}
-
-/* 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/link.c b/src/api/link.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/api/objbuilder.c b/src/api/objbuilder.c
@@ -1,214 +0,0 @@
-/* Public CfreeObjBuilder API: thin adapter over the internal obj_* surface. */
-
-#include <cfree/object.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/object_builder.c b/src/api/object_builder.c
@@ -0,0 +1,214 @@
+/* Public CfreeObjBuilder API: thin adapter over the internal obj_* surface. */
+
+#include <cfree/object.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/detect.c b/src/api/object_detect.c
diff --git a/src/api/object_file.c b/src/api/object_file.c
@@ -0,0 +1,375 @@
+/* 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/objfile.c b/src/api/objfile.c
@@ -1,376 +0,0 @@
-/* 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,351 +0,0 @@
-/* libcfree's top-level compile entry points. Status-returning shapes
- * that drive the C, asm, and registered-frontend paths. */
-
-#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 "obj/obj.h"
-
-static SrcLoc no_loc(void) {
- SrcLoc loc;
- loc.file_id = 0;
- loc.line = 0;
- loc.col = 0;
- return loc;
-}
-
-static _Noreturn void panic_bad_options(Compiler* c, const char* msg) {
- compiler_panic(c, no_loc(), "bad cfree options: %s", msg);
-}
-
-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");
- }
-}
-
-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 ((unsigned)input->lang < CFREE_LANG_COUNT) {
- frontend = c->frontends[input->lang];
- }
- if (frontend) {
- CfreeStatus st;
- metrics_scope_begin(c, "compile.frontend");
- 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");
- metrics_count(c, "compile.obj_sections", obj_section_count(ob));
- metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
- return;
- }
-
- if (input->lang == CFREE_LANG_ASM) {
- metrics_scope_begin(c, "compile.asm.lex_open");
- 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");
- 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_count(c, "compile.obj_sections", obj_section_count(ob));
- metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
- metrics_scope_begin(c, "compile.asm.mc_free");
- mc_free(mc);
- metrics_scope_end(c, "compile.asm.mc_free");
- return;
- }
-
- compiler_panic(c, no_loc(), "no frontend registered for language: %u",
- (u32)input->lang);
-}
-
-/* ============================================================
- * 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");
- }
-
- 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;
-}
-
-CfreeStatus cfree_compile_c_obj(CfreeCompiler* c,
- const CfreeCCompileOptions* opts,
- const CfreeBytes* input,
- CfreeObjBuilder** out) {
- PanicSave saved;
- 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 CFREE_ERR;
- }
- validate_bytes(c, input);
- ob = obj_new(c);
- metrics_scope_begin(c, "compile.tu");
- metrics_count(c, "compile.input_bytes", (u64)input->len);
- compile_c_into(c, opts, input, ob);
- metrics_scope_end(c, "compile.tu");
- *out = ob;
- compiler_panic_restore(c, &saved);
- return CFREE_OK;
-}
-
-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 CFREE_ERR;
- }
- validate_bytes(c, input);
- ob = obj_new(c);
- metrics_scope_begin(c, "compile.tu");
- metrics_count(c, "compile.input_bytes", (u64)input->len);
- 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 CFREE_OK;
-}
-
-/* ============================================================
- * Asm
- * ============================================================ */
-
-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;
- 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 CFREE_ERR;
- }
- 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 CFREE_OK;
-}
-
-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 CFREE_ERR;
- }
- 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 CFREE_OK;
-}
-
-/* ============================================================
- * Source (registered frontend)
- * ============================================================ */
-
-CfreeStatus cfree_compile_source_obj(CfreeCompiler* c,
- const CfreeFrontendCompileOptions* opts,
- const CfreeSourceInput* input,
- CfreeObjBuilder** out) {
- PanicSave saved;
- 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 CFREE_ERR;
- }
- 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 CFREE_OK;
-}
-
-CfreeStatus cfree_compile_source_obj_emit(CfreeCompiler* c,
- const CfreeFrontendCompileOptions* opts,
- const CfreeSourceInput* 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 CFREE_ERR;
- }
- 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/source.c b/src/api/source.c
@@ -0,0 +1,64 @@
+/* Public source registry API bridge. */
+
+#include <cfree/source.h>
+#include <string.h>
+
+#include "core/core.h"
+#include "core/pool.h"
+
+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/support_arena.c b/src/api/support_arena.c
@@ -0,0 +1,45 @@
+/* Public arena support API bridge. */
+
+#include <cfree/support/arena.h>
+
+#include "core/arena.h"
+#include "core/core.h"
+
+struct CfreeArena {
+ Arena inner;
+};
+
+CfreeStatus cfree_arena_new(CfreeHeap* h, size_t block_size, CfreeArena** out) {
+ CfreeArena* a;
+ if (!out) return CFREE_INVALID;
+ if (!h) return CFREE_INVALID;
+ a = (CfreeArena*)h->alloc(h, sizeof(*a), _Alignof(CfreeArena));
+ if (!a) return CFREE_NOMEM;
+ arena_init(&a->inner, (Heap*)h, block_size);
+ *out = a;
+ return CFREE_OK;
+}
+
+void cfree_arena_free(CfreeArena* a) {
+ CfreeHeap* h;
+ if (!a) return;
+ h = a->inner.heap;
+ arena_fini(&a->inner);
+ h->free(h, a, sizeof(*a));
+}
+
+void cfree_arena_reset(CfreeArena* a) {
+ if (a) arena_reset(&a->inner);
+}
+
+void* cfree_arena_alloc(CfreeArena* a, size_t size, size_t align) {
+ return a ? arena_alloc(&a->inner, size, align) : NULL;
+}
+
+void* cfree_arena_zalloc(CfreeArena* a, size_t size, size_t align) {
+ return a ? arena_zalloc(&a->inner, size, align) : NULL;
+}
+
+char* cfree_arena_strdup(CfreeArena* a, const char* s, size_t len) {
+ return a ? arena_strdup(&a->inner, s, len) : NULL;
+}
diff --git a/src/api/writer_mem.c b/src/api/writer_mem.c
@@ -1,110 +0,0 @@
-/* In-memory CfreeWriter. */
-
-#include <cfree/core.h>
-#include <string.h>
-
-#include "core/core.h"
-#include "core/heap.h"
-
-typedef struct MemWriter {
- CfreeWriter base;
- Heap* heap;
- u8* data;
- size_t cap;
- size_t len;
- size_t pos;
- CfreeStatus status;
-} MemWriter;
-
-static CfreeStatus mw_grow(MemWriter* mw, size_t needed) {
- size_t new_cap;
- u8* p;
-
- 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->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->status = CFREE_NOMEM;
- return CFREE_NOMEM;
- }
- if (new_cap > mw->cap) memset(p + mw->cap, 0, new_cap - mw->cap);
- mw->data = p;
- mw->cap = new_cap;
- return CFREE_OK;
-}
-
-static CfreeStatus mw_write(CfreeWriter* w, const void* data, size_t n) {
- MemWriter* mw = (MemWriter*)w;
- size_t end;
- CfreeStatus st;
-
- if (mw->status != CFREE_OK) return mw->status;
- if (n == 0) return CFREE_OK;
- end = mw->pos + n;
- if (end < mw->pos) {
- 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 CfreeStatus mw_seek(CfreeWriter* w, uint64_t off) {
- MemWriter* mw = (MemWriter*)w;
- 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 CfreeStatus mw_status(CfreeWriter* w) {
- return ((MemWriter*)w)->status;
-}
-
-static void mw_close(CfreeWriter* w) {
- MemWriter* mw = (MemWriter*)w;
- Heap* h = mw->heap;
- if (mw->data) h->free(h, mw->data, mw->cap);
- h->free(h, mw, sizeof(*mw));
-}
-
-CfreeStatus cfree_writer_mem(CfreeHeap* heap, CfreeWriter** out) {
- MemWriter* mw;
- if (!out) return CFREE_INVALID;
- if (!heap) return CFREE_INVALID;
- mw = (MemWriter*)heap->alloc(heap, sizeof(*mw), _Alignof(MemWriter));
- if (!mw) return CFREE_NOMEM;
- mw->base.write = mw_write;
- mw->base.seek = mw_seek;
- mw->base.tell = mw_tell;
- 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->status = CFREE_OK;
- *out = &mw->base;
- return CFREE_OK;
-}
-
-const uint8_t* cfree_writer_mem_bytes(CfreeWriter* w, size_t* len_out) {
- MemWriter* mw = (MemWriter*)w;
- if (len_out) *len_out = mw ? mw->len : 0;
- return mw ? mw->data : NULL;
-}
diff --git a/src/link/link_api.c b/src/link/link_api.c
@@ -1,168 +0,0 @@
-/* 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;
-}