commit 0bfde91143b108d562e7bc2754f9335c212745d5
parent 064cd29d7879a73a15ed06cbd9860f4ac4d59384
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 14 May 2026 19:32:31 -0700
Add append-only JIT snippets to dbg
Diffstat:
6 files changed, 1012 insertions(+), 6 deletions(-)
diff --git a/driver/dbg.c b/driver/dbg.c
@@ -95,6 +95,8 @@ void driver_help_dbg(void) {
" sl step to next source line (into calls)\n"
" n, next step to next source line (over calls)\n"
" finish run until current frame returns\n"
+ " jit [LANG|NAME] { ... } compile and append a frontend snippet\n"
+ " call SYMBOL [INT_OR_ADDR...] call function, returns uint64_t\n"
" jump ADDR set PC to ADDR (no resume)\n"
" bt, backtrace print stack trace with arguments\n"
" b LOC set breakpoint at LOC:\n"
@@ -248,6 +250,16 @@ static int dbg_compile_and_jit(DbgOpts* o, CfreePipeline* pipe,
driver_dlsym_resolver, NULL, out_jit);
}
+static void dbg_fill_compile_options(DbgOpts* o, CfreeCompileOptions* copts) {
+ {
+ CfreeCompileOptions z = {0};
+ *copts = z;
+ }
+ copts->opt_level = o->opt_level;
+ copts->debug_info = o->debug_info;
+ driver_cflags_fill_pp(&o->cf, &copts->pp);
+}
+
/* ============================================================
* Breakpoint table (driver-side)
* ============================================================
@@ -280,6 +292,8 @@ typedef struct Bp {
typedef struct DbgState {
DriverEnv* env;
+ CfreePipeline* pipe;
+ CfreeCompileOptions copts;
CfreeJit* jit;
CfreeJitSession* session;
const CfreeObjFile* view;
@@ -295,6 +309,7 @@ typedef struct DbgState {
int has_stop;
CfreeStopInfo last_stop;
+ uint64_t jit_counter;
} DbgState;
/* SIGINT trampoline. The handler in env.c calls our cb with this state;
@@ -786,6 +801,7 @@ static int dbg_read_value(DbgState*, const CfreeDwarfVarLoc*,
size_t stack_cap, uint8_t** buf_out,
size_t* alloc_out, size_t* got_out);
static void dbg_release_value_buf(DbgState*, uint8_t* buf, size_t alloc);
+static char* dbg_take_word(char* line, char** word_out);
/* ============================================================
* Backtrace
@@ -1391,6 +1407,228 @@ static void dbg_cmd_info_syms(DbgState* s, CfreeSymKind want,
if (!printed) driver_printf("(none)\n");
}
+static int dbg_refresh_dwarf(DbgState* s) {
+ if (s->dwarf) {
+ cfree_dwarf_close(s->dwarf);
+ s->dwarf = NULL;
+ }
+ s->view = cfree_jit_view(s->jit);
+ if (s->view) {
+ s->dwarf = cfree_dwarf_open(cfree_pipeline_compiler(s->pipe), s->view);
+ if (s->dwarf && s->session) {
+ cfree_jit_session_attach_dwarf(s->session, s->dwarf);
+ }
+ } else if (s->session) {
+ cfree_jit_session_attach_dwarf(s->session, NULL);
+ }
+ return 0;
+}
+
+static int dbg_buf_append(DbgState* s, char** buf, size_t* len, size_t* cap,
+ const char* src, size_t n) {
+ if (*len + n + 1u > *cap) {
+ size_t nc = *cap ? *cap * 2u : 1024u;
+ char* nb;
+ while (nc < *len + n + 1u) nc *= 2u;
+ nb = (char*)s->env->heap->realloc(s->env->heap, *buf, *cap, nc,
+ _Alignof(char));
+ if (!nb) return 1;
+ *buf = nb;
+ *cap = nc;
+ }
+ driver_memcpy(*buf + *len, src, n);
+ *len += n;
+ (*buf)[*len] = '\0';
+ return 0;
+}
+
+static int dbg_brace_delta(const char* p) {
+ int d = 0;
+ while (*p) {
+ if (*p == '{') ++d;
+ else if (*p == '}') --d;
+ ++p;
+ }
+ return d;
+}
+
+static CfreeLanguage dbg_jit_language_for_tag(const char* tag,
+ const char** name_out) {
+ if (!tag || !*tag || driver_streq(tag, "c")) {
+ if (name_out) *name_out = "<dbg-jit.c>";
+ return CFREE_LANG_C;
+ }
+ if (driver_streq(tag, "toy")) {
+ if (name_out) *name_out = "<dbg-jit.toy>";
+ return CFREE_LANG_TOY;
+ }
+ if (driver_streq(tag, "asm") || driver_streq(tag, "s")) {
+ if (name_out) *name_out = "<dbg-jit.s>";
+ return CFREE_LANG_ASM;
+ }
+ if (name_out) *name_out = tag;
+ return cfree_language_for_path(tag);
+}
+
+static void dbg_cmd_jit(DbgState* s, const char* rest) {
+ char* src = NULL;
+ size_t len = 0, cap = 0;
+ CfreeBytesInput in;
+ CfreeObjBuilder* ob = NULL;
+ const char* p = rest;
+ const char* input_name = "<dbg-jit.c>";
+ CfreeLanguage lang = CFREE_LANG_C;
+ int depth;
+
+ while (*p && dbg_isspace((unsigned char)*p)) ++p;
+ if (*p != '{') {
+ const char* tag = p;
+ size_t tag_n;
+ char tag_buf[64];
+ while (*p && !dbg_isspace((unsigned char)*p) && *p != '{') ++p;
+ tag_n = (size_t)(p - tag);
+ if (tag_n == 0 || tag_n >= sizeof(tag_buf)) {
+ driver_errf(DBG_TOOL, "usage: jit [c|toy|asm|name.ext] { ... }");
+ return;
+ }
+ driver_memcpy(tag_buf, tag, tag_n);
+ tag_buf[tag_n] = '\0';
+ lang = dbg_jit_language_for_tag(tag_buf, &input_name);
+ while (*p && dbg_isspace((unsigned char)*p)) ++p;
+ }
+ if (*p != '{') {
+ driver_errf(DBG_TOOL, "usage: jit [c|toy|asm|name.ext] { ... }");
+ return;
+ }
+ ++p;
+ depth = 1 + dbg_brace_delta(p);
+ {
+ const char* end = p + driver_strlen(p);
+ if (depth <= 0) {
+ while (end > p && end[-1] != '}') --end;
+ if (end > p) --end;
+ }
+ if (dbg_buf_append(s, &src, &len, &cap, p, (size_t)(end - p)) != 0)
+ goto oom;
+ if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
+ }
+ while (depth > 0) {
+ char line[LINE_CAP];
+ int n;
+ driver_printf(" > ");
+ driver_flush_stdout();
+ n = driver_read_line(line, sizeof(line));
+ if (n <= 0) {
+ driver_errf(DBG_TOOL, "unterminated jit block");
+ goto out;
+ }
+ depth += dbg_brace_delta(line);
+ if (depth <= 0) {
+ char* close = line;
+ while (*close && *close != '}') ++close;
+ if (dbg_buf_append(s, &src, &len, &cap, line,
+ (size_t)(close - line)) != 0)
+ goto oom;
+ if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
+ break;
+ }
+ if (dbg_buf_append(s, &src, &len, &cap, line, driver_strlen(line)) != 0)
+ goto oom;
+ if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
+ }
+
+ s->jit_counter++;
+ in.name = input_name;
+ in.data = (const uint8_t*)src;
+ in.len = len;
+ in.lang = lang;
+ if (cfree_pipeline_compile_obj(s->pipe, &s->copts, &in, &ob) != 0 || !ob) {
+ driver_errf(DBG_TOOL, "jit compile failed");
+ goto out;
+ }
+ if (cfree_jit_append_obj(s->jit, ob) != 0) {
+ driver_errf(DBG_TOOL, "jit append failed");
+ goto out;
+ }
+ dbg_refresh_dwarf(s);
+ driver_printf("JIT generation %llu\n",
+ (unsigned long long)cfree_jit_generation(s->jit));
+ goto out;
+
+oom:
+ driver_errf(DBG_TOOL, "out of memory");
+out:
+ if (src) driver_free(s->env, src, cap);
+}
+
+static void dbg_cmd_call(DbgState* s, const char* rest) {
+ char* tmp;
+ size_t tmp_size;
+ char* sym;
+ char* p;
+ uint64_t args[8];
+ uint32_t nargs = 0;
+ void* entry;
+ uint64_t ret = 0;
+ CfreeStopInfo stop;
+
+ tmp = dbg_dup(s->env, rest, driver_strlen(rest), &tmp_size);
+ if (!tmp) {
+ driver_errf(DBG_TOOL, "out of memory");
+ return;
+ }
+ p = dbg_take_word(tmp, &sym);
+ if (!*sym) {
+ driver_errf(DBG_TOOL, "usage: call SYMBOL [INT_OR_ADDR ...]");
+ goto out;
+ }
+ entry = cfree_jit_lookup(s->jit, sym);
+ if (!entry) {
+ driver_errf(DBG_TOOL, "symbol not found: %s", sym);
+ goto out;
+ }
+ while (*p) {
+ char* a;
+ uint64_t v;
+ size_t used;
+ p = dbg_take_word(p, &a);
+ if (!*a) break;
+ if (nargs == 8u) {
+ driver_errf(DBG_TOOL, "call supports at most 8 arguments");
+ goto out;
+ }
+ used = dbg_parse_uint(a, &v);
+ if (!used || a[used] != '\0') {
+ driver_errf(DBG_TOOL, "expected integer/address argument, got '%s'", a);
+ goto out;
+ }
+ args[nargs++] = v;
+ }
+ if (driver_install_sigint(dbg_on_sigint, s) != 0) {
+ driver_errf(DBG_TOOL, "failed to install SIGINT handler");
+ goto out;
+ }
+ if (cfree_jit_session_call_u64(s->session, entry, args, nargs, &ret, &stop) !=
+ 0) {
+ driver_restore_sigint();
+ driver_errf(DBG_TOOL, "call failed (debuggee must be idle or exited)");
+ goto out;
+ }
+ driver_restore_sigint();
+ s->last_stop = stop;
+ s->has_stop = (stop.kind != CFREE_STOP_EXIT);
+ if (stop.kind == CFREE_STOP_EXIT) {
+ driver_printf("= %llu (0x%llx)\n", (unsigned long long)ret,
+ (unsigned long long)ret);
+ } else {
+ s->has_stop = 1;
+ dbg_render_stop(s, &stop);
+ }
+
+out:
+ driver_free(s->env, tmp, tmp_size);
+}
+
/* ============================================================
* `x ADDR [count]`
* ============================================================
@@ -1681,6 +1919,8 @@ static void dbg_cmd_help(void) {
" sl step to next source line (into calls)\n"
" n, next step to next source line (over calls)\n"
" finish run until current frame returns\n"
+ " jit [LANG|NAME] { ... } compile and append a frontend snippet\n"
+ " call SYMBOL [INT_OR_ADDR...] call function, returns uint64_t\n"
" jump ADDR set PC to ADDR (no resume)\n"
" bt, backtrace print stack trace with arguments\n"
" b LOC set breakpoint at LOC:\n"
@@ -1769,6 +2009,18 @@ static int dbg_dispatch(DbgState* s, char* line) {
dbg_drive(s, RUN_STEP_OUT);
return 0;
}
+ if (driver_streq(cmd, "jit")) {
+ dbg_cmd_jit(s, rest);
+ return 0;
+ }
+ if (driver_streq(cmd, "call")) {
+ if (!s->session) {
+ driver_errf(DBG_TOOL, "no JIT session");
+ return 0;
+ }
+ dbg_cmd_call(s, rest);
+ return 0;
+ }
if (driver_streq(cmd, "bt") || driver_streq(cmd, "backtrace") ||
driver_streq(cmd, "where")) {
dbg_cmd_bt(s);
@@ -2035,6 +2287,8 @@ int driver_dbg(int argc, char** argv) {
}
st.env = &env;
+ st.pipe = pipe;
+ dbg_fill_compile_options(&o, &st.copts);
st.jit = jit;
st.prog_argc = (int)o.prog_argc;
st.prog_argv = o.prog_argv;
diff --git a/include/cfree.h b/include/cfree.h
@@ -470,6 +470,12 @@ const uint8_t* cfree_writer_mem_bytes(CfreeWriter*, size_t* len_out);
* `main`). Returns NULL on miss. */
void cfree_jit_free(CfreeJit*);
void* cfree_jit_lookup(CfreeJit*, const char* name);
+/* Experimental append-only JIT growth. Appends one finalized object into
+ * reserved JIT slack without moving existing code/data. Returns nonzero on
+ * duplicate strong definitions, unresolved references, capacity exhaustion,
+ * or relocation/protection failure. */
+int cfree_jit_append_obj(CfreeJit*, CfreeObjBuilder*);
+uint64_t cfree_jit_generation(CfreeJit*);
/* Run all fini_array destructors in reverse order. Call after the last
* use of JITed code, before cfree_jit_free. */
void cfree_jit_run_dtors(CfreeJit*);
@@ -580,6 +586,7 @@ typedef enum CfreeResumeMode {
* is shape-agnostic. New entry shapes extend the enum. */
typedef enum CfreeEntryKind {
CFREE_ENTRY_INT_ARGV, /* int(int, char**) */
+ CFREE_ENTRY_U64, /* uint64_t(uint64_t, ... up to 8 args) */
} CfreeEntryKind;
CfreeJitSession* cfree_jit_session_new(CfreeJit*);
@@ -599,6 +606,9 @@ int cfree_jit_session_attach_dwarf(CfreeJitSession*, CfreeDebugInfo*);
* populated. */
int cfree_jit_session_call(CfreeJitSession*, void* entry, CfreeEntryKind,
int argc, char** argv, CfreeStopInfo* stop_out);
+int cfree_jit_session_call_u64(CfreeJitSession*, void* entry,
+ const uint64_t* args, uint32_t nargs,
+ uint64_t* ret_out, CfreeStopInfo* stop_out);
/* Resume the parked worker. Blocks until the next stop. Returns 0 on
* success, nonzero if no worker is parked. */
diff --git a/src/api/pipeline.c b/src/api/pipeline.c
@@ -328,7 +328,7 @@ int cfree_link_jit(CfreeCompiler* c, const CfreeLinkOptions* opts,
image = link_resolve(linker); /* deferred-cleanup-registered */
*out_jit = cfree_jit_from_image(image); /* undefers + transfers ownership */
metrics_scope_end(c, "link_jit.all");
- link_free(linker);
+ /* cfree_jit_from_image keeps the Linker alive for append-only JIT growth. */
compiler_panic_restore(c, &saved);
return 0;
}
diff --git a/src/dbg/dbg.h b/src/dbg/dbg.h
@@ -148,6 +148,9 @@ struct CfreeJitSession {
int entry_argc;
char** entry_argv;
int entry_ret;
+ uint64_t entry_u64_args[8];
+ uint32_t entry_u64_nargs;
+ uint64_t entry_u64_ret;
/* current stop slot (filled by the fault handler / worker exit path) */
CfreeStopInfo stop;
diff --git a/src/dbg/session.c b/src/dbg/session.c
@@ -163,11 +163,79 @@ static void worker_main(void* arg) {
if (s->worker_should_exit) return;
if (s->state == DBG_STATE_RUNNING && s->entry) {
typedef int (*EntryIntArgv)(int, char**);
+ typedef uint64_t (*EntryU64_0)(void);
+ typedef uint64_t (*EntryU64_1)(uint64_t);
+ typedef uint64_t (*EntryU64_2)(uint64_t, uint64_t);
+ typedef uint64_t (*EntryU64_3)(uint64_t, uint64_t, uint64_t);
+ typedef uint64_t (*EntryU64_4)(uint64_t, uint64_t, uint64_t, uint64_t);
+ typedef uint64_t (*EntryU64_5)(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t);
+ typedef uint64_t (*EntryU64_6)(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t, uint64_t);
+ typedef uint64_t (*EntryU64_7)(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t, uint64_t, uint64_t);
+ typedef uint64_t (*EntryU64_8)(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t, uint64_t, uint64_t, uint64_t);
int ret = 0;
switch (s->entry_kind) {
case CFREE_ENTRY_INT_ARGV:
ret = ((EntryIntArgv)s->entry)(s->entry_argc, s->entry_argv);
break;
+ case CFREE_ENTRY_U64:
+ switch (s->entry_u64_nargs) {
+ case 0:
+ s->entry_u64_ret = ((EntryU64_0)s->entry)();
+ break;
+ case 1:
+ s->entry_u64_ret =
+ ((EntryU64_1)s->entry)(s->entry_u64_args[0]);
+ break;
+ case 2:
+ s->entry_u64_ret = ((EntryU64_2)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1]);
+ break;
+ case 3:
+ s->entry_u64_ret = ((EntryU64_3)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2]);
+ break;
+ case 4:
+ s->entry_u64_ret = ((EntryU64_4)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2], s->entry_u64_args[3]);
+ break;
+ case 5:
+ s->entry_u64_ret = ((EntryU64_5)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2], s->entry_u64_args[3],
+ s->entry_u64_args[4]);
+ break;
+ case 6:
+ s->entry_u64_ret = ((EntryU64_6)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2], s->entry_u64_args[3],
+ s->entry_u64_args[4], s->entry_u64_args[5]);
+ break;
+ case 7:
+ s->entry_u64_ret = ((EntryU64_7)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2], s->entry_u64_args[3],
+ s->entry_u64_args[4], s->entry_u64_args[5],
+ s->entry_u64_args[6]);
+ break;
+ case 8:
+ s->entry_u64_ret = ((EntryU64_8)s->entry)(
+ s->entry_u64_args[0], s->entry_u64_args[1],
+ s->entry_u64_args[2], s->entry_u64_args[3],
+ s->entry_u64_args[4], s->entry_u64_args[5],
+ s->entry_u64_args[6], s->entry_u64_args[7]);
+ break;
+ default:
+ s->entry_u64_ret = 0;
+ break;
+ }
+ ret = (int)s->entry_u64_ret;
+ break;
}
memset(&s->stop, 0, sizeof(s->stop));
s->stop.kind = CFREE_STOP_EXIT;
@@ -291,7 +359,7 @@ void cfree_jit_session_free(CfreeJitSession* s) {
int cfree_jit_session_call(CfreeJitSession* s, void* entry, CfreeEntryKind kind,
int argc, char** argv, CfreeStopInfo* stop_out) {
if (!s || !entry) return 1;
- if (s->state == DBG_STATE_RUNNING) return 1;
+ if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) return 1;
s->entry = entry;
s->entry_kind = kind;
s->entry_argc = argc;
@@ -308,6 +376,22 @@ int cfree_jit_session_call(CfreeJitSession* s, void* entry, CfreeEntryKind kind,
return 0;
}
+int cfree_jit_session_call_u64(CfreeJitSession* s, void* entry,
+ const uint64_t* args, uint32_t nargs,
+ uint64_t* ret_out, CfreeStopInfo* stop_out) {
+ uint32_t i;
+ int rc;
+ if (!s || !entry || nargs > 8u) return 1;
+ if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) return 1;
+ for (i = 0; i < nargs; ++i) s->entry_u64_args[i] = args ? args[i] : 0;
+ for (; i < 8u; ++i) s->entry_u64_args[i] = 0;
+ s->entry_u64_nargs = nargs;
+ s->entry_u64_ret = 0;
+ rc = cfree_jit_session_call(s, entry, CFREE_ENTRY_U64, 0, NULL, stop_out);
+ if (rc == 0 && ret_out) *ret_out = s->entry_u64_ret;
+ return rc;
+}
+
int cfree_jit_session_resume(CfreeJitSession* s, CfreeResumeMode mode,
CfreeStopInfo* stop_out) {
if (!s) return 1;
diff --git a/src/link/link_jit.c b/src/link/link_jit.c
@@ -72,6 +72,10 @@ struct CfreeJit {
* means "not yet built"; view_built distinguishes "tried and gave up"
* (multi-input v1, etc.) from "untried". */
CfreeObjFile* view;
+ Linker* linker; /* kept alive for append-time resolver/input context */
+ u64 append_cursor[SEG_NBUCKETS];
+ u64 append_limit[SEG_NBUCKETS];
+ u64 generation;
/* Mach-O TLV runtime state. Lazily allocated by jit_patch_tlv_descriptors
* when the image contains any in-image TLV descriptor. `tls_vtable` is
* borrowed from CfreeEnv (lives across the env's lifetime); `tls_ctx`
@@ -83,6 +87,11 @@ struct CfreeJit {
u8 pad[7];
};
+#define JIT_APPEND_RX_SLACK (64ull * 1024ull * 1024ull)
+#define JIT_APPEND_R_SLACK (16ull * 1024ull * 1024ull)
+#define JIT_APPEND_RW_SLACK (16ull * 1024ull * 1024ull)
+#define JIT_APPEND_TLS_SLACK (4ull * 1024ull * 1024ull)
+
/* AArch64 ELF ABI: TP points 16 bytes before the TLS image; TLSLE
* encodes (target_offset_in_image + 16). */
#define AARCH64_TCB_SIZE 16ull
@@ -299,6 +308,8 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) {
u64 page;
u64 image_base = (u64)-1;
u64 image_end = 0;
+ u64 append_start;
+ u64 append_total;
u64 master_size;
int needs_exec = 0;
int needs_input_materialize = 0;
@@ -328,7 +339,10 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) {
if (image_base & (page - 1u))
compiler_panic(c, no_loc(),
"cfree_jit_from_image: segment vaddr not page-aligned");
- master_size = image_end - image_base;
+ append_start = image_end;
+ append_total = JIT_APPEND_RX_SLACK + JIT_APPEND_R_SLACK +
+ JIT_APPEND_RW_SLACK + JIT_APPEND_TLS_SLACK;
+ master_size = (image_end - image_base) + append_total;
metrics_count(c, "jit.master_size", master_size);
metrics_count(c, "jit.nsegments", img->nsegments);
@@ -478,6 +492,12 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) {
"cfree_jit_from_image: execmem.protect failed");
}
}
+ if (append_total) {
+ void* slack_rt =
+ (u8*)master.runtime + (uintptr_t)(append_start - image_base);
+ (void)mem->protect(mem->user, slack_rt, (size_t)append_total,
+ CFREE_PROT_NONE);
+ }
metrics_scope_end(c, "jit.protect");
/* Flush only the segments that will be executed, against the
@@ -526,6 +546,17 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) {
jit->segs = segs;
jit->nsegs = img->nsegments;
jit->view = NULL;
+ jit->linker = img->linker;
+ jit->append_cursor[SEG_RX] = append_start;
+ jit->append_limit[SEG_RX] = jit->append_cursor[SEG_RX] + JIT_APPEND_RX_SLACK;
+ jit->append_cursor[SEG_R] = jit->append_limit[SEG_RX];
+ jit->append_limit[SEG_R] = jit->append_cursor[SEG_R] + JIT_APPEND_R_SLACK;
+ jit->append_cursor[SEG_RW] = jit->append_limit[SEG_R];
+ jit->append_limit[SEG_RW] = jit->append_cursor[SEG_RW] + JIT_APPEND_RW_SLACK;
+ jit->append_cursor[SEG_TLS] = jit->append_limit[SEG_RW];
+ jit->append_limit[SEG_TLS] =
+ jit->append_cursor[SEG_TLS] + JIT_APPEND_TLS_SLACK;
+ jit->generation = 0;
jit->view_built = 0u;
jit->tls_vtable = NULL;
jit->tls_ctx = NULL;
@@ -536,6 +567,10 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) {
compiler_undefer(c, img->deferred);
img->deferred = NULL;
}
+ if (jit->linker && jit->linker->deferred) {
+ compiler_undefer(c, jit->linker->deferred);
+ jit->linker->deferred = NULL;
+ }
/* Mach-O TLV descriptor pass: install our thunk into descriptor[+0],
* stash the per-image TLS ctx in [+8], and overwrite [+16] with the
@@ -595,6 +630,10 @@ void cfree_jit_free(CfreeJit* jit) {
/* link_image_free unfederes (no-op now) and releases storage. */
link_image_free(jit->image);
}
+ if (jit->linker) {
+ link_free(jit->linker);
+ jit->linker = NULL;
+ }
heap->free(heap, jit, sizeof(*jit));
}
@@ -615,6 +654,610 @@ void* cfree_jit_lookup(CfreeJit* jit, const char* name) {
return (void*)vaddr_to_runtime(jit->image, jit->segs, s->vaddr);
}
+uint64_t cfree_jit_generation(CfreeJit* jit) {
+ return jit ? jit->generation : 0;
+}
+
+typedef struct JitAppendSec {
+ ObjSecId obj_sec;
+ LinkSectionId link_sec;
+ LinkSegmentId link_seg;
+ SegBucket bucket;
+ u64 vaddr;
+ u64 size;
+ u64 file_size;
+ u32 align;
+ u16 flags;
+ u16 sem;
+ Sym name;
+} JitAppendSec;
+
+static int jit_bind_strength(u8 bind) {
+ switch (bind) {
+ case SB_GLOBAL:
+ return 3;
+ case SB_WEAK:
+ return 2;
+ case SB_LOCAL:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int jit_obj_sym_is_def(const ObjSym* s) {
+ return s && s->kind != SK_UNDEF &&
+ (s->kind == SK_ABS || s->kind == SK_COMMON || s->kind == SK_FILE ||
+ s->section_id != OBJ_SEC_NONE);
+}
+
+static int jit_obj_sym_is_spurious_undef(const ObjSym* s) {
+ return s && s->section_id == OBJ_SEC_NONE && s->kind != SK_ABS &&
+ s->kind != SK_COMMON && !s->referenced &&
+ (s->bind == SB_GLOBAL || s->bind == SB_WEAK);
+}
+
+static u8 jit_reloc_width_local(RelocKind k) {
+ switch (k) {
+ case R_ABS64:
+ case R_REL64:
+ case R_PC64:
+ case R_X64_TPOFF64:
+ return 8;
+ case R_AARCH64_ABS16:
+ case R_AARCH64_PREL16:
+ case R_RV_RVC_BRANCH:
+ case R_RV_RVC_JUMP:
+ return 2;
+ case R_RV_ADD8:
+ case R_RV_SUB8:
+ case R_RV_SUB6:
+ case R_RV_SET6:
+ case R_RV_SET8:
+ return 1;
+ default:
+ return 4;
+ }
+}
+
+static InputMap jit_input_map_alloc(CfreeJit* jit, ObjBuilder* ob) {
+ InputMap m;
+ ObjSymIter* it;
+ ObjSymEntry e;
+ u32 nsyms = 0;
+ Heap* h = jit->image->heap;
+ memset(&m, 0, sizeof(m));
+ it = obj_symiter_new(ob);
+ while (obj_symiter_next(it, &e)) ++nsyms;
+ obj_symiter_free(it);
+ m.nsym = nsyms + 1u;
+ m.sym = (LinkSymId*)h->alloc(h, sizeof(*m.sym) * m.nsym,
+ _Alignof(LinkSymId));
+ m.nsection = obj_section_count(ob);
+ m.section = (LinkSectionId*)h->alloc(h, sizeof(*m.section) * m.nsection,
+ _Alignof(LinkSectionId));
+ if (!m.sym || !m.section)
+ compiler_panic(jit->c, no_loc(), "cfree_jit_append_obj: oom on input map");
+ memset(m.sym, 0, sizeof(*m.sym) * m.nsym);
+ memset(m.section, 0, sizeof(*m.section) * m.nsection);
+ return m;
+}
+
+static void jit_invalidate_view(CfreeJit* jit) {
+ if (jit->view) {
+ cfree_obj_close(jit->view);
+ jit->view = NULL;
+ }
+ jit->view_built = 0u;
+}
+
+static void jit_apply_one_reloc(CfreeJit* jit, const LinkRelocApply* r) {
+ LinkImage* img = jit->image;
+ const LinkSymbol* tgt = LinkSyms_at(&img->syms, r->target - 1);
+ u64 S;
+ u64 P;
+ u8* P_bytes;
+ if (reloc_is_tlsle(r->kind)) {
+ S = (tgt->vaddr - img->tls_vaddr) + AARCH64_TCB_SIZE;
+ } else if (tgt->kind == SK_ABS) {
+ S = tgt->vaddr;
+ } else {
+ S = (u64)vaddr_to_runtime(img, jit->segs, tgt->vaddr);
+ }
+ P = (u64)vaddr_to_runtime(img, jit->segs, r->write_vaddr);
+ P_bytes = (u8*)vaddr_to_write(img, jit->segs, r->write_vaddr);
+ if (!P_bytes)
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: relocation site is unmapped");
+ if (tgt->bind == SB_WEAK && tgt->kind == SK_ABS && tgt->vaddr == 0) {
+ if (r->kind == R_AARCH64_ADR_PREL_PG_HI21 ||
+ r->kind == R_AARCH64_ADR_PREL_PG_HI21_NC) {
+ u32 instr = rd_u32_le(P_bytes);
+ u32 rd = instr & 0x1fu;
+ wr_u32_le(P_bytes, 0xd2800000u | rd);
+ return;
+ }
+ if (r->kind == R_AARCH64_ADD_ABS_LO12_NC) return;
+ }
+ if (r->kind == R_AARCH64_TLVP_LOAD_PAGEOFF12) {
+ u64 v = ((u64)S + (u64)r->addend) & 0xfffu;
+ u32 instr = rd_u32_le(P_bytes);
+ instr = 0x91000000u | (instr & 0x3ffu) | ((u32)v << 10);
+ wr_u32_le(P_bytes, instr);
+ return;
+ }
+ link_reloc_apply(jit->c, r->kind, P_bytes, S, r->addend, P);
+}
+
+static void jit_append_obj_inner(CfreeJit* jit, ObjBuilder* ob) {
+ LinkImage* img = jit->image;
+ Heap* h = img->heap;
+ const CfreeExecMem* mem = require_execmem(jit->c);
+ u64 page = jit_page_size(jit->c);
+ u32 old_nsections = img->nsections;
+ u32 old_nsegments = img->nsegments;
+ u32 old_nsegs = jit->nsegs;
+ u32 old_nmaps = img->ninput_maps;
+ u32 old_dbg_n = img->dbg_objs_n;
+ u32 old_nrelocs = LinkRelocs_count(&img->relocs);
+ u64 old_cursor[SEG_NBUCKETS];
+ InputMap m;
+ JitAppendSec* secs = NULL;
+ u32 nsecs = 0, sec_cap = 0;
+ u32 obj_sec_count;
+ u64 cursor[SEG_NBUCKETS];
+ LinkInputId new_input_id;
+ u32 new_input_idx;
+ ObjSymIter* it;
+ ObjSymEntry e;
+ u32 i;
+
+ for (i = 0; i < SEG_NBUCKETS; ++i) old_cursor[i] = jit->append_cursor[i];
+
+ if (!jit || !ob || !jit->linker || obj_compiler(ob) != jit->c)
+ compiler_panic(jit ? jit->c : NULL, no_loc(),
+ "cfree_jit_append_obj: object is not appendable");
+
+ /* Preflight duplicate strong definitions and unresolved references before
+ * mutating image-visible state. */
+ it = obj_symiter_new(ob);
+ while (obj_symiter_next(it, &e)) {
+ const ObjSym* s = e.sym;
+ if (jit_obj_sym_is_spurious_undef(s)) continue;
+ if (jit_obj_sym_is_def(s) && s->name != 0 &&
+ (s->bind == SB_GLOBAL || s->bind == SB_WEAK)) {
+ LinkSymId existing = symhash_get(&img->globals, s->name);
+ if (existing != LINK_SYM_NONE) {
+ LinkSymbol* prev = LinkSyms_at(&img->syms, existing - 1);
+ if (prev && prev->defined &&
+ jit_bind_strength((u8)s->bind) == jit_bind_strength(SB_GLOBAL) &&
+ jit_bind_strength(prev->bind) == jit_bind_strength(SB_GLOBAL)) {
+ size_t namelen;
+ const char* nm = pool_str(jit->c->global, s->name, &namelen);
+ obj_format_demangle_c(jit->c, &nm, &namelen);
+ obj_symiter_free(it);
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: duplicate global '%.*s'",
+ (int)namelen, nm);
+ }
+ }
+ } else if (!jit_obj_sym_is_def(s) && s->name != 0) {
+ LinkSymId hit = symhash_get(&img->globals, s->name);
+ int ok = 0;
+ if (hit != LINK_SYM_NONE) {
+ LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
+ if (def && def->defined) ok = 1;
+ }
+ if (!ok) {
+ ObjSymIter* it2 = obj_symiter_new(ob);
+ ObjSymEntry e2;
+ while (obj_symiter_next(it2, &e2)) {
+ const ObjSym* d = e2.sym;
+ if (d && d->name == s->name && jit_obj_sym_is_def(d) &&
+ (d->bind == SB_GLOBAL || d->bind == SB_WEAK)) {
+ ok = 1;
+ break;
+ }
+ }
+ obj_symiter_free(it2);
+ }
+ if (!ok && jit->linker->resolver) {
+ size_t namelen;
+ const char* nm = pool_str(jit->c->global, s->name, &namelen);
+ (void)namelen;
+ if (jit->linker->resolver(jit->linker->resolver_user, nm)) ok = 1;
+ }
+ if (!ok && s->bind == SB_WEAK) ok = 1;
+ if (!ok) {
+ size_t nlen;
+ const char* nm = pool_str(jit->c->global, s->name, &nlen);
+ if (nm && nlen == 15u && memcmp(nm, "__tlv_bootstrap", 15u) == 0)
+ ok = 1;
+ if (!ok) {
+ obj_format_demangle_c(jit->c, &nm, &nlen);
+ obj_symiter_free(it);
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: undefined reference to '%.*s'",
+ (int)nlen, nm);
+ }
+ }
+ }
+ }
+ obj_symiter_free(it);
+
+ m = jit_input_map_alloc(jit, ob);
+ obj_sec_count = obj_section_count(ob);
+ for (i = 0; i < SEG_NBUCKETS; ++i) cursor[i] = jit->append_cursor[i];
+
+ for (i = 1; i < obj_sec_count; ++i) {
+ const Section* s = obj_section_get(ob, (ObjSecId)i);
+ SegBucket b;
+ u64 size;
+ u64 vaddr;
+ if (!s || !link_section_kept(s)) continue;
+ size = (s->sem == SSEM_NOBITS) ? (u64)s->bss_size : (u64)s->bytes.total;
+ if (size == 0) continue;
+ b = link_bucket_for(s->flags);
+ vaddr = ALIGN_UP(cursor[b], (u64)(s->align ? s->align : 1u));
+ if (vaddr + size > jit->append_limit[b])
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: append slack exhausted");
+ if (nsecs == sec_cap) {
+ u32 ncap = sec_cap ? sec_cap * 2u : 8u;
+ JitAppendSec* ns =
+ (JitAppendSec*)h->realloc(h, secs, sizeof(*secs) * sec_cap,
+ sizeof(*secs) * ncap,
+ _Alignof(JitAppendSec));
+ if (!ns)
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: oom on section plan");
+ secs = ns;
+ sec_cap = ncap;
+ }
+ secs[nsecs].obj_sec = (ObjSecId)i;
+ secs[nsecs].link_sec = (LinkSectionId)(old_nsections + nsecs + 1u);
+ secs[nsecs].link_seg = (LinkSegmentId)(old_nsegments + nsecs + 1u);
+ secs[nsecs].bucket = b;
+ secs[nsecs].vaddr = vaddr;
+ secs[nsecs].size = size;
+ secs[nsecs].file_size = (s->sem == SSEM_NOBITS) ? 0 : size;
+ secs[nsecs].align = s->align ? s->align : 1u;
+ secs[nsecs].flags = s->flags;
+ secs[nsecs].sem = s->sem;
+ secs[nsecs].name = s->name;
+ m.section[i] = secs[nsecs].link_sec;
+ cursor[b] = vaddr + size;
+ ++nsecs;
+ }
+ for (i = 0; i < SEG_NBUCKETS; ++i) jit->append_cursor[i] = cursor[i];
+
+ it = obj_symiter_new(ob);
+ while (obj_symiter_next(it, &e)) {
+ const ObjSym* s = e.sym;
+ int is_def;
+ if (e.id >= m.nsym || jit_obj_sym_is_spurious_undef(s)) continue;
+ is_def = jit_obj_sym_is_def(s);
+ if (is_def && (s->bind == SB_GLOBAL || s->bind == SB_WEAK) &&
+ s->name != 0) {
+ LinkSymId existing = symhash_get(&img->globals, s->name);
+ if (existing != LINK_SYM_NONE) {
+ LinkSymbol* prev = LinkSyms_at(&img->syms, existing - 1);
+ if (prev && prev->defined &&
+ jit_bind_strength((u8)s->bind) == jit_bind_strength(SB_GLOBAL) &&
+ jit_bind_strength(prev->bind) == jit_bind_strength(SB_GLOBAL)) {
+ size_t namelen;
+ const char* nm = pool_str(jit->c->global, s->name, &namelen);
+ obj_format_demangle_c(jit->c, &nm, &namelen);
+ obj_symiter_free(it);
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: duplicate global '%.*s'",
+ (int)namelen, nm);
+ }
+ if (prev && prev->defined) {
+ m.sym[e.id] = existing;
+ continue;
+ }
+ }
+ }
+ m.sym[e.id] = (LinkSymId)(LinkSyms_count(&img->syms) + 1u);
+ {
+ LinkSymbol rec;
+ memset(&rec, 0, sizeof(rec));
+ rec.name = s->name;
+ rec.input_id = (LinkInputId)(LinkInputs_count(&jit->linker->inputs) + 1u);
+ rec.obj_sym = e.id;
+ rec.section_id = LINK_SEC_NONE;
+ rec.value = s->value;
+ rec.size = s->size;
+ rec.common_align = (s->kind == SK_COMMON) ? (u32)s->common_align : 0u;
+ rec.bind = (u8)s->bind;
+ rec.kind = (u8)s->kind;
+ rec.defined = (u8)is_def;
+ if (is_def && s->section_id != OBJ_SEC_NONE &&
+ s->section_id < m.nsection) {
+ rec.section_id = m.section[s->section_id];
+ if (rec.section_id != LINK_SEC_NONE) {
+ u32 sj;
+ for (sj = 0; sj < nsecs; ++sj) {
+ if (secs[sj].link_sec == rec.section_id) {
+ rec.vaddr = secs[sj].vaddr + s->value;
+ break;
+ }
+ }
+ }
+ } else if (s->kind == SK_ABS) {
+ rec.vaddr = s->value;
+ }
+ m.sym[e.id] = link_append_symbol(img, &rec);
+ if (is_def && (s->bind == SB_GLOBAL || s->bind == SB_WEAK) &&
+ s->name != 0) {
+ LinkSymId existing;
+ if (symhash_insert(&img->globals, s->name, m.sym[e.id], &existing)) {
+ } else {
+ m.sym[e.id] = existing;
+ }
+ }
+ }
+ }
+ obj_symiter_free(it);
+
+ /* Resolve newly appended undefs before any bytes become executable. */
+ for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
+ LinkSymbol* s = LinkSyms_at(&img->syms, i);
+ if (s->input_id != (LinkInputId)(LinkInputs_count(&jit->linker->inputs) + 1u))
+ continue;
+ if (s->defined) continue;
+ if (s->name != 0) {
+ LinkSymId hit = symhash_get(&img->globals, s->name);
+ if (hit != LINK_SYM_NONE && hit != s->id) {
+ LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
+ if (def->defined) {
+ s->section_id = def->section_id;
+ s->value = def->value;
+ s->vaddr = def->vaddr;
+ s->kind = def->kind;
+ s->bind = def->bind;
+ s->defined = 1;
+ continue;
+ }
+ }
+ }
+ if (jit->linker->resolver && s->name != 0) {
+ size_t namelen;
+ const char* nm = pool_str(jit->c->global, s->name, &namelen);
+ void* p = jit->linker->resolver(jit->linker->resolver_user, nm);
+ (void)namelen;
+ if (p) {
+ s->kind = SK_ABS;
+ s->vaddr = (u64)(uintptr_t)p;
+ s->defined = 1;
+ continue;
+ }
+ }
+ if (s->bind == SB_WEAK) {
+ s->kind = SK_ABS;
+ s->vaddr = 0;
+ s->defined = 1;
+ continue;
+ }
+ if (s->name != 0) {
+ size_t nlen;
+ const char* nm = pool_str(jit->c->global, s->name, &nlen);
+ if (nm && nlen == 15u && memcmp(nm, "__tlv_bootstrap", 15u) == 0) {
+ s->kind = SK_ABS;
+ s->vaddr = 0;
+ s->defined = 1;
+ continue;
+ }
+ obj_format_demangle_c(jit->c, &nm, &nlen);
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: undefined reference to '%.*s'",
+ (int)nlen, nm);
+ }
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: undefined anonymous symbol");
+ }
+
+ new_input_id = link_add_obj(jit->linker, ob);
+ new_input_idx = new_input_id - 1u;
+
+ {
+ InputMap* maps = (InputMap*)h->realloc(
+ h, img->input_maps, sizeof(*img->input_maps) * img->ninput_maps,
+ sizeof(*img->input_maps) * (new_input_idx + 1u), _Alignof(InputMap));
+ if (!maps)
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: oom growing input maps");
+ img->input_maps = maps;
+ while (img->ninput_maps < new_input_idx) {
+ memset(&img->input_maps[img->ninput_maps], 0, sizeof(InputMap));
+ img->ninput_maps++;
+ }
+ img->input_maps[new_input_idx] = m;
+ img->ninput_maps = new_input_idx + 1u;
+ }
+
+ if (nsecs) {
+ LinkSection* nsections = (LinkSection*)h->realloc(
+ h, img->sections, sizeof(*img->sections) * old_nsections,
+ sizeof(*img->sections) * (old_nsections + nsecs),
+ _Alignof(LinkSection));
+ LinkSegment* nsegments = (LinkSegment*)h->realloc(
+ h, img->segments, sizeof(*img->segments) * old_nsegments,
+ sizeof(*img->segments) * (old_nsegments + nsecs),
+ _Alignof(LinkSegment));
+ u8** nsegbytes = (u8**)h->realloc(
+ h, img->segment_bytes, sizeof(*img->segment_bytes) * old_nsegments,
+ sizeof(*img->segment_bytes) * (old_nsegments + nsecs), _Alignof(u8*));
+ size_t* nsegcaps = (size_t*)h->realloc(
+ h, img->segment_bytes_cap,
+ sizeof(*img->segment_bytes_cap) * old_nsegments,
+ sizeof(*img->segment_bytes_cap) * (old_nsegments + nsecs),
+ _Alignof(size_t));
+ CfreeExecMemRegion* njsegs = (CfreeExecMemRegion*)h->realloc(
+ h, jit->segs, sizeof(*jit->segs) * old_nsegs,
+ sizeof(*jit->segs) * (old_nsegs + nsecs),
+ _Alignof(CfreeExecMemRegion));
+ if (!nsections || !nsegments || !nsegbytes || !nsegcaps || !njsegs)
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: oom growing image tables");
+ img->sections = nsections;
+ img->segments = nsegments;
+ img->segment_bytes = nsegbytes;
+ img->segment_bytes_cap = nsegcaps;
+ jit->segs = njsegs;
+ }
+
+ for (i = 0; i < nsecs; ++i) {
+ JitAppendSec* ps = &secs[i];
+ LinkSection* ls = &img->sections[old_nsections + i];
+ LinkSegment* seg = &img->segments[old_nsegments + i];
+ CfreeExecMemRegion* js = &jit->segs[old_nsegs + i];
+ const Section* os = obj_section_get(ob, ps->obj_sec);
+ u64 page_lo = ps->vaddr & ~(page - 1u);
+ u64 page_hi = ALIGN_UP(ps->vaddr + ps->size, page);
+ memset(ls, 0, sizeof(*ls));
+ ls->id = ps->link_sec;
+ ls->input_id = new_input_id;
+ ls->obj_section_id = ps->obj_sec;
+ ls->segment_id = ps->link_seg;
+ ls->input_offset = 0;
+ ls->file_offset = ps->vaddr;
+ ls->vaddr = ps->vaddr;
+ ls->size = ps->size;
+ ls->flags = ps->flags;
+ ls->align = ps->align;
+ ls->name = ps->name;
+ ls->sem = ps->sem;
+
+ memset(seg, 0, sizeof(*seg));
+ seg->id = ps->link_seg;
+ seg->flags = SF_ALLOC;
+ if (ps->bucket == SEG_RX) seg->flags |= SF_EXEC;
+ if (ps->bucket == SEG_RW) seg->flags |= SF_WRITE;
+ if (ps->bucket == SEG_TLS) seg->flags |= SF_TLS;
+ seg->file_offset = ps->vaddr;
+ seg->vaddr = ps->vaddr;
+ seg->mem_size = ps->size;
+ seg->file_size = ps->file_size;
+ seg->align = (u32)page;
+ seg->nsections = 1;
+ img->segment_bytes[old_nsegments + i] = NULL;
+ img->segment_bytes_cap[old_nsegments + i] = 0;
+
+ memset(js, 0, sizeof(*js));
+ js->write = (u8*)jit->master.write + (uintptr_t)(page_lo - jit->image_base);
+ js->runtime =
+ (u8*)jit->master.runtime + (uintptr_t)(page_lo - jit->image_base);
+ js->size = (size_t)(page_hi - page_lo);
+ js->token = NULL;
+
+ if (os && os->sem != SSEM_NOBITS && os->bytes.total) {
+ u8* dst = (u8*)js->write + (uintptr_t)(ps->vaddr - page_lo);
+ buf_flatten(&os->bytes, dst);
+ }
+ img->nsections++;
+ img->nsegments++;
+ jit->nsegs++;
+ }
+
+ {
+ u32 total = obj_reloc_total(ob);
+ for (i = 0; i < total; ++i) {
+ const Reloc* r = obj_reloc_at(ob, i);
+ LinkRelocApply rec;
+ LinkSectionId ls_id;
+ const LinkSection* ls;
+ if (!r || r->section_id == OBJ_SEC_NONE || r->section_id >= m.nsection)
+ continue;
+ ls_id = m.section[r->section_id];
+ if (ls_id == LINK_SEC_NONE) continue;
+ if (r->sym == OBJ_SYM_NONE || r->sym >= m.nsym ||
+ m.sym[r->sym] == LINK_SYM_NONE)
+ continue;
+ ls = &img->sections[ls_id - 1];
+ memset(&rec, 0, sizeof(rec));
+ rec.input_id = new_input_id;
+ rec.section_id = r->section_id;
+ rec.link_section_id = ls_id;
+ rec.offset = r->offset;
+ rec.width = jit_reloc_width_local((RelocKind)r->kind);
+ rec.write_vaddr = ls->vaddr + r->offset;
+ rec.write_file_offset = rec.write_vaddr;
+ rec.kind = (RelocKind)r->kind;
+ rec.target = m.sym[r->sym];
+ rec.addend = r->addend;
+ *link_append_reloc_slot(img) = rec;
+ }
+ }
+
+ for (i = old_nrelocs; i < LinkRelocs_count(&img->relocs); ++i) {
+ const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i);
+ jit_apply_one_reloc(jit, r);
+ }
+
+ for (i = old_nsegs; i < jit->nsegs; ++i) {
+ const LinkSegment* seg = &img->segments[i];
+ if (mem->protect(mem->user, jit->segs[i].runtime, jit->segs[i].size,
+ perms_for(seg->flags)) != 0) {
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: execmem.protect failed");
+ }
+ }
+ if (mem->flush_icache) {
+ for (i = old_nsegs; i < jit->nsegs; ++i) {
+ const LinkSegment* seg = &img->segments[i];
+ if (seg->flags & SF_EXEC)
+ mem->flush_icache(mem->user, jit->segs[i].runtime, jit->segs[i].size);
+ }
+ }
+
+ {
+ ObjBuilder** nd = (ObjBuilder**)h->realloc(
+ h, img->dbg_objs, sizeof(*img->dbg_objs) * old_dbg_n,
+ sizeof(*img->dbg_objs) * (new_input_idx + 1u), _Alignof(ObjBuilder*));
+ u8* no = (u8*)h->realloc(h, img->dbg_objs_owned,
+ sizeof(*img->dbg_objs_owned) * old_dbg_n,
+ sizeof(*img->dbg_objs_owned) * (new_input_idx + 1u),
+ 1u);
+ if (!nd || !no)
+ compiler_panic(jit->c, no_loc(),
+ "cfree_jit_append_obj: oom growing debug inputs");
+ img->dbg_objs = nd;
+ img->dbg_objs_owned = no;
+ while (img->dbg_objs_n < new_input_idx) {
+ img->dbg_objs[img->dbg_objs_n] = NULL;
+ img->dbg_objs_owned[img->dbg_objs_n] = 0u;
+ img->dbg_objs_n++;
+ }
+ img->dbg_objs[new_input_idx] = ob;
+ img->dbg_objs_owned[new_input_idx] = 0u;
+ img->dbg_objs_n = new_input_idx + 1u;
+ }
+
+ jit_invalidate_view(jit);
+ jit->generation++;
+ if (secs) h->free(h, secs, sizeof(*secs) * sec_cap);
+ (void)old_cursor;
+ (void)old_nmaps;
+}
+
+int cfree_jit_append_obj(CfreeJit* jit, CfreeObjBuilder* ob) {
+ PanicSave saved;
+ Compiler* c;
+ if (!jit || !ob) return 1;
+ c = jit->c;
+ compiler_panic_save(c, &saved);
+ if (setjmp(c->panic)) {
+ compiler_run_cleanups(c);
+ compiler_panic_restore(c, &saved);
+ return 1;
+ }
+ jit_append_obj_inner(jit, ob);
+ compiler_panic_restore(c, &saved);
+ return 0;
+}
+
/* ---- inspector entries ---- */
/* True if `name` (NUL-terminated) is a debug section the DWARF consumer
@@ -931,6 +1574,9 @@ int cfree_jit_addr_to_sym(CfreeJit* jit, uint64_t addr, const char** name_out,
uint64_t* off_out) {
u32 n;
u32 i;
+ const LinkSymbol* best = NULL;
+ uint64_t best_base = 0;
+ uint64_t best_off = (uint64_t)-1;
if (name_out) *name_out = NULL;
if (off_out) *off_out = 0;
if (!jit) return 1;
@@ -943,11 +1589,20 @@ int cfree_jit_addr_to_sym(CfreeJit* jit, uint64_t addr, const char** name_out,
base = jit_sym_runtime_addr(jit, s);
if (!base) continue;
if (addr >= base && (s->size == 0 || addr < base + s->size)) {
- if (name_out) *name_out = jit_sym_name_cstr(jit, s);
- if (off_out) *off_out = addr - base;
- return 0;
+ uint64_t off = addr - base;
+ if (off < best_off ||
+ (off == best_off && best && best->size == 0 && s->size != 0)) {
+ best = s;
+ best_base = base;
+ best_off = off;
+ }
}
}
+ if (best) {
+ if (name_out) *name_out = jit_sym_name_cstr(jit, best);
+ if (off_out) *off_out = addr - best_base;
+ return 0;
+ }
return 1;
}