commit de9bfacea7ed2f358f9838a35809d2a56cd1c990
parent cf332ff49fb4c2a596a7e677482531627462caca
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 9 Jun 2026 18:25:21 -0700
cleanup: parallel subsystem sweep (correctness, dead code, perf, dedup)
Correctness:
- frontend: __atomic_compare_exchange_n now uses c_abi_sizeof/alignof for the
pointer + bool-result frame slots (fixes rv32 stack corruption); recurse
find_record_member_path through arbitrary anon-struct nesting (was 2 levels)
- asm: decode_string free size now matches alloc size (heap-corruption fix);
asm_frontend_compile checks NULL + propagates parse errors (was always KIT_OK)
- emu: emu_mem_load64 reads the full 8 bytes via emu_mem_load_raw instead of
two load32s (no more split across a map boundary); EMU_MAX_NEEDED constant +
KIT_LOGW on DT_NEEDED overflow instead of silent truncation
- link: keep .preinit_array input sections (link_section_kept was dropping them)
- driver: build-exe now copies sysroot lib dirs into -L (build_apply_hosted_profile
matched to cc); -Werror honored at driver level; dep-iter reports write errors;
driver_basename strips '\'; objdump seg-perms uses a caller buffer
- interp: computed-goto dispatch table moved off function-static globals
- N4 asm expected-punct diagnostic prints the actual punctuator
Dead code removed:
- opt: OPT_ALLOC_SPLIT + live-range-split machinery, opt_regalloc, OptPassCtx,
OPT_ANALYSIS_CFG, x64 type_is_signed, RV_FRAME_SAVE_SIZE
- debug: dead loclist producer API, debug_remap_path decl, sibling_visited,
DwString; config_stubs orphans
- frontend: type_tag_get, hs_intersect
- dist: dead v2 DistManifest emit/parse; emu_addr_space_set_perm; wasm dead
tail-call helper; cg temp-locals no-op plumbing; kit_cg_abort alias
Perf: opt data-reloc rooting de-O(n^2) (section-indexed table); copy-cleanup
batched; debug str_index_of hashmap-backed; reused phase-1 alloc group info;
wasm per-function reg reset bounded by high-water mark.
Dedup: dist parse helpers (dist_parse.h); driver path-join/parse-u64/epoch/hex
helpers + load_bytes; strip/objcopy shared objedit.c; debug add_*_reloc; ABI
classify_void + int128-pair + align_up_u32; aa64 FP encoders via isa.h;
macho/elf byte buffers unified (obj/bytebuf.h); ELF DynSym* isolated from the
format-neutral header; abi_internal_type_info inlined; pool_intern_cstr.
API/diagnostics: kit_obj_symiter_new/dynsymiter_new take const; B5 link warns on
ignored shared/build-id opts instead of silent void-casts; O1opt IR dumps use
diag_emit not compiler_panic. _Float16 kept as the deliberate float alias; test
cc-emit-ir updated for the single-result IR dump format.
Diffstat:
113 files changed, 1891 insertions(+), 2777 deletions(-)
diff --git a/driver/cmd/ar.c b/driver/cmd/ar.c
@@ -123,18 +123,6 @@ void driver_help_ar(void) {
"usage\n")));
}
-/* Parse SOURCE_DATE_EPOCH into a u64; 0 (or unset/invalid) means "no epoch". */
-static uint64_t ar_epoch_from_env(void) {
- const char* s = driver_getenv("SOURCE_DATE_EPOCH");
- uint64_t v = 0;
- if (!s || !*s) return 0;
- for (; *s; ++s) {
- if (*s < '0' || *s > '9') return 0;
- v = v * 10 + (uint64_t)(*s - '0');
- }
- return v;
-}
-
/* Return 1 iff `name` matches any of the names in argv[start..argc) — or if
* there are no filters, in which case every member matches. */
static int ar_name_selected(KitSlice name, int argc, char** argv, int start) {
@@ -342,7 +330,7 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers,
void** sym_allocs = NULL;
size_t* sym_alloc_szs = NULL;
- opts.epoch = ar_epoch_from_env();
+ opts.epoch = driver_epoch_from_env();
opts.long_names = 1;
/* `r`: read existing archive (if any) and seed `members` with it. */
diff --git a/driver/cmd/build.c b/driver/cmd/build.c
@@ -1044,6 +1044,11 @@ static int build_apply_hosted_profile(BuildOptions* o) {
o->hosted.system_includes[i];
for (i = 0; i < o->hosted.ndefines; ++i)
o->groups[0].cf.defines[o->groups[0].cf.ndefines++] = o->hosted.defines[i];
+ /* Add hosted lib search dirs so user -l flags (-lm, -lpthread, etc.) can be
+ * resolved. The strings are owned by o->hosted and outlive lib_search_paths.
+ * Insert before any user -L dirs so sysroot libs take precedence. */
+ for (i = 0; i < o->hosted.nlib_search_dirs; ++i)
+ o->lib_search_paths[o->nlib_search_paths++] = o->hosted.lib_search_dirs[i];
for (i = 0; i < o->hosted.nbefore; ++i) {
if (o->no_startfiles) break;
if (build_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0)
@@ -2290,6 +2295,10 @@ static int build_main(int argc, char** argv, int kind, const char* tool) {
rc = build_run_per_source(&o, compiler, &ctx, &code, &diag);
}
+ if (rc == 0 &&
+ driver_diag_finish(&env, tool, o.warnings_are_errors, o.max_errors))
+ rc = 1;
+
done:
if (compiler) driver_compiler_free(compiler);
kit_target_free(target);
diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c
@@ -132,7 +132,6 @@ typedef struct CcOptions {
KitTargetSpec target; /* -target / host */
int target_set; /* did -target appear */
const char* output_path; /* -o */
- int output_path_set;
char* owned_output_path;
size_t owned_output_path_size;
const char* sysroot; /* --sysroot / -isysroot */
@@ -1160,7 +1159,12 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
KIT_SLICE_ARG(kit_slice_cstr(a)));
return 1;
}
- continue;
+ /* The preprocessor has no force-include hook yet (KitPreprocessOptions
+ * carries no prefix-include list), so honoring this would silently drop
+ * the file and miscompile. Fail loudly rather than ignore it. */
+ driver_errf(CC_TOOL, "-include is unimplemented: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
+ return 1;
}
if (driver_streq(a, "-fPIC") || driver_streq(a, "-fpic")) {
@@ -1291,12 +1295,10 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
return 1;
}
o->output_path = argv[i];
- o->output_path_set = 1;
continue;
}
if (driver_strneq(a, "--output=", 9)) {
o->output_path = a + 9;
- o->output_path_set = 1;
continue;
}
if (driver_streq(a, "--output")) {
@@ -1305,7 +1307,6 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
return 1;
}
o->output_path = argv[i];
- o->output_path_set = 1;
continue;
}
if (driver_streq(a, "-e")) {
@@ -1917,18 +1918,40 @@ static char* cc_dep_default_path(DriverEnv* env, const char* out_path,
}
}
+/* Collect the include-dependency edges into `list`. Returns 0 on success,
+ * CC_DEP_COLLECT_OOM on allocation failure (caller reports it generically), or
+ * CC_DEP_COLLECT_DIAGNOSED when the iterator faulted mid-scan -- in that case a
+ * specific diagnostic naming the offending dependency has already been emitted,
+ * so the caller must not overwrite it with a generic message. */
+#define CC_DEP_COLLECT_OOM 1
+#define CC_DEP_COLLECT_DIAGNOSED 2
+
static int cc_dep_collect(DriverEnv* env, KitCompiler* compiler,
int system_filter, CcDepList* list) {
KitDepIter* it = NULL;
- KitDepEdge e;
- if (kit_dep_iter_new(compiler, &it) != KIT_OK) return 1;
+ KitDepEdge e = {0};
+ if (kit_dep_iter_new(compiler, &it) != KIT_OK) return CC_DEP_COLLECT_OOM;
for (;;) {
KitIterResult r = kit_dep_iter_next(it, &e);
+ if (r == KIT_ITER_ERROR) {
+ /* Name the most recently scanned dependency so the failure is actionable
+ * instead of a silent short read of the dep list. `e` is zero-initialized
+ * and only ever written by kit_dep_iter_next, so it is never garbage: on
+ * an error before any edge it is empty (-> generic message), otherwise it
+ * holds the last edge we saw. */
+ if (e.included_name.len)
+ driver_errf(CC_TOOL, "failed while scanning dependency: %.*s",
+ KIT_SLICE_ARG(e.included_name));
+ else
+ driver_errf(CC_TOOL, "failed to scan include dependencies");
+ kit_dep_iter_free(it);
+ return CC_DEP_COLLECT_DIAGNOSED;
+ }
if (r != KIT_ITER_ITEM) break;
if (system_filter && e.from_system_path) continue;
if (cc_dep_list_push(env, list, e.included_name.s) != 0) {
kit_dep_iter_free(it);
- return 1;
+ return CC_DEP_COLLECT_OOM;
}
}
kit_dep_iter_free(it);
@@ -2005,10 +2028,14 @@ static int cc_dep_finish(DriverEnv* env, const KitContext* ctx,
uint32_t ntargets;
int rc = 1;
- if (cc_dep_collect(env, compiler, cc_dep_filters_system(o->dep_mode),
- &deps) != 0) {
- driver_errf(CC_TOOL, "out of memory");
- goto out;
+ {
+ int cr = cc_dep_collect(env, compiler, cc_dep_filters_system(o->dep_mode),
+ &deps);
+ if (cr == CC_DEP_COLLECT_OOM) {
+ driver_errf(CC_TOOL, "out of memory");
+ goto out;
+ }
+ if (cr != 0) goto out; /* CC_DEP_COLLECT_DIAGNOSED: already reported */
}
targets = o->dep_targets;
@@ -2800,6 +2827,10 @@ static int driver_cc_main(int argc, char** argv, int force_check) {
rc = cc_run_link_exe(&env, &co, &pp);
}
+ if (rc == 0 &&
+ driver_diag_finish(&env, CC_TOOL, co.warnings_are_errors, co.max_errors))
+ rc = 1;
+
cc_options_release(&co);
if (runtime_resolved) driver_runtime_support_fini(&env, &runtime);
driver_env_fini(&env);
diff --git a/driver/cmd/cmp.c b/driver/cmd/cmp.c
@@ -49,33 +49,6 @@ void driver_help_cmp(void) {
"trouble/usage\n")));
}
-/* Parse a decimal or 0x-hex non-negative integer. Returns 0 on success. */
-static int cmp_parse_u64(const char* s, uint64_t* out) {
- uint64_t v = 0;
- int base = 10;
- if (!s || !*s) return 1;
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
- base = 16;
- s += 2;
- if (!*s) return 1;
- }
- for (; *s; ++s) {
- unsigned d;
- char c = *s;
- if (c >= '0' && c <= '9')
- d = (unsigned)(c - '0');
- else if (base == 16 && c >= 'a' && c <= 'f')
- d = (unsigned)(c - 'a' + 10);
- else if (base == 16 && c >= 'A' && c <= 'F')
- d = (unsigned)(c - 'A' + 10);
- else
- return 1;
- v = v * (uint64_t)base + d;
- }
- *out = v;
- return 0;
-}
-
/* Load a named operand, or stdin when the name is "-". Returns 0 on success
* with data and len set. A stdin read fills stdin_buf/stdin_len (freed with
* driver_free); a file read fills ld (freed with driver_release_bytes). */
@@ -157,7 +130,7 @@ int driver_cmp(int argc, char** argv) {
continue;
}
if (driver_streq(a, "-n")) {
- if (i + 1 >= argc || cmp_parse_u64(argv[++i], &opts.max) != 0) {
+ if (i + 1 >= argc || driver_parse_u64(argv[++i], &opts.max) != 0) {
driver_errf(CMP_TOOL, "-n requires a non-negative count");
goto done;
}
@@ -174,7 +147,7 @@ int driver_cmp(int argc, char** argv) {
if (npos < 2) {
names[npos] = a;
} else if (npos < 4) {
- if (cmp_parse_u64(a, &skip[npos - 2]) != 0) {
+ if (driver_parse_u64(a, &skip[npos - 2]) != 0) {
driver_errf(CMP_TOOL, "invalid skip value: %s", a);
goto done;
}
diff --git a/driver/cmd/disas.c b/driver/cmd/disas.c
@@ -53,11 +53,6 @@ static int disas_is_hex(int c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
-static int disas_hexval(int c) {
- if (c >= '0' && c <= '9') return c - '0';
- if (c >= 'a' && c <= 'f') return c - 'a' + 10;
- return c - 'A' + 10;
-}
/* Decode a whitespace-tolerant hex string into freshly-allocated bytes.
* Returns 0 on success (caller frees via driver_free(env, *out, *outlen)). */
@@ -92,7 +87,7 @@ static int disas_parse_hex(DriverEnv* env, const char* s, uint8_t** out,
for (p = s; *p; ++p) {
int v;
if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') continue;
- v = disas_hexval((unsigned char)*p);
+ v = driver_hex_nibble((char)(unsigned char)*p);
if (hi < 0)
hi = v;
else {
@@ -191,33 +186,9 @@ int driver_disas(int argc, char** argv) {
driver_errf(DISAS_TOOL, "--base requires an address");
goto done;
}
- {
- const char* v = argv[++i];
- uint64_t val = 0;
- int base = 10;
- if (v[0] == '0' && (v[1] == 'x' || v[1] == 'X')) {
- base = 16;
- v += 2;
- }
- if (!*v) {
- driver_errf(DISAS_TOOL, "invalid --base address");
- goto done;
- }
- for (; *v; ++v) {
- int d;
- if (*v >= '0' && *v <= '9')
- d = *v - '0';
- else if (base == 16 && *v >= 'a' && *v <= 'f')
- d = *v - 'a' + 10;
- else if (base == 16 && *v >= 'A' && *v <= 'F')
- d = *v - 'A' + 10;
- else {
- driver_errf(DISAS_TOOL, "invalid --base address");
- goto done;
- }
- val = val * (uint64_t)base + (uint64_t)d;
- }
- o.base = val;
+ if (driver_parse_u64(argv[++i], &o.base) != 0) {
+ driver_errf(DISAS_TOOL, "invalid --base address");
+ goto done;
}
continue;
}
diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c
@@ -445,36 +445,6 @@ static int ld_note_library_request(LdOptions* o, const char* name) {
/* ---------- --build-id parsing ---------- */
-static int hex_nibble(char c) {
- if (c >= '0' && c <= '9') return c - '0';
- if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
- if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
- return -1;
-}
-
-/* Parse a -Ttext address: 0x<hex> or decimal. Returns 0 on success. */
-static int ld_parse_addr(const char* s, uint64_t* out) {
- uint64_t v = 0;
- const char* p;
- if (!s || !s[0]) return 1;
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
- p = s + 2;
- if (!*p) return 1;
- for (; *p; ++p) {
- int n = hex_nibble(*p);
- if (n < 0) return 1;
- v = (v << 4) | (uint64_t)n;
- }
- } else {
- for (p = s; *p; ++p) {
- if (*p < '0' || *p > '9') return 1;
- v = v * 10u + (uint64_t)(*p - '0');
- }
- }
- *out = v;
- return 0;
-}
-
/* Parse `--build-id=...` argument into options. Accepts "none", "sha256",
* "uuid", or "0x<even-hex>". Returns 0 on success, 1 on bad value. */
static int ld_parse_build_id(LdOptions* o, const char* val) {
@@ -510,8 +480,8 @@ static int ld_parse_build_id(LdOptions* o, const char* val) {
return 1;
}
for (i = 0; i < nbytes; ++i) {
- int hi = hex_nibble(hex[2 * i]);
- int lo = hex_nibble(hex[2 * i + 1]);
+ int hi = driver_hex_nibble(hex[2 * i]);
+ int lo = driver_hex_nibble(hex[2 * i + 1]);
if (hi < 0 || lo < 0) {
driver_errf(LD_TOOL, "--build-id: invalid hex digit");
driver_free(o->env, buf, nbytes);
@@ -873,7 +843,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
}
if (tval) {
uint64_t v;
- if (ld_parse_addr(tval, &v) != 0) {
+ if (driver_parse_u64(tval, &v) != 0) {
driver_errf(LD_TOOL, "-Ttext: invalid address: %s", tval);
return 1;
}
@@ -903,16 +873,18 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
o->support_dir = a + 14;
continue;
}
+ /* --sysroot is authoritative in the first pass (GNU ld treats it as a
+ * global search-prefix that must be known before any -l / "=" rewrite), so
+ * o->sysroot is already set. Here we only consume the tokens so they are
+ * not misread as input files; do not re-apply the value. */
if (driver_streq(a, "--sysroot")) {
if (++i >= argc) {
driver_errf(LD_TOOL, "--sysroot requires an argument");
return 1;
}
- o->sysroot = argv[i];
continue;
}
if (driver_strneq(a, "--sysroot=", 10)) {
- o->sysroot = a + 10;
continue;
}
diff --git a/driver/cmd/objcopy.c b/driver/cmd/objcopy.c
@@ -6,6 +6,7 @@
#include "driver.h"
#include "inputs.h"
+#include "objedit.h"
/* `kit objcopy` — copy + transform an object file. v1 scope is the
* high-traffic build-system subset called out in CTOOLCHAIN.md:
@@ -121,15 +122,6 @@ typedef struct CopyOpts {
const char* output;
} CopyOpts;
-static int name_in_list(KitSlice name, const char* const* list, uint32_t n) {
- uint32_t i;
- if (!name.len) return 0;
- for (i = 0; i < n; ++i) {
- if (list[i] && kit_slice_eq_cstr(name, list[i])) return 1;
- }
- return 0;
-}
-
static int push_str(DriverEnv* env, const char*** arr, uint32_t* n,
uint32_t* cap, const char* s) {
if (*n >= *cap) {
@@ -270,54 +262,9 @@ static int apply_strip_pass(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
if (!filter_syms) return 0;
/* Collect reloc-targeted sym ids, skipping relocs in debug sections. */
- {
- KitObjRelocIter* rit = NULL;
- if (kit_obj_reliter_new(of, &rit) != KIT_OK) {
- driver_errf(OBJCOPY_TOOL, "out of memory");
- return 1;
- }
- for (;;) {
- KitObjReloc r;
- KitIterResult ir = kit_obj_reliter_next(rit, &r);
- uint32_t k;
- int seen = 0;
- if (ir != KIT_ITER_ITEM) break;
- if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
- if (r.section != KIT_SECTION_NONE) {
- KitObjSecInfo hi;
- if (kit_obj_section(of, r.section, &hi) == KIT_OK &&
- hi.kind == KIT_SEC_DEBUG) {
- continue;
- }
- }
- for (k = 0; k < nneeded; ++k) {
- if (needed[k] == r.sym) {
- seen = 1;
- break;
- }
- }
- if (seen) continue;
- if (nneeded >= cap_needed) {
- uint32_t newcap = cap_needed ? cap_needed * 2u : 32u;
- KitObjSymbol* nb = (KitObjSymbol*)driver_alloc_zeroed(
- env, (size_t)newcap * sizeof(*nb));
- if (!nb) {
- kit_obj_reliter_free(rit);
- if (needed)
- driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
- driver_errf(OBJCOPY_TOOL, "out of memory");
- return 1;
- }
- if (needed) {
- memcpy(nb, needed, (size_t)nneeded * sizeof(*needed));
- driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
- }
- needed = nb;
- cap_needed = newcap;
- }
- needed[nneeded++] = r.sym;
- }
- kit_obj_reliter_free(rit);
+ if (driver_obj_collect_reloc_target_syms(env, OBJCOPY_TOOL, of, &needed,
+ &nneeded, &cap_needed) != 0) {
+ return 1;
}
/* Walk syms and drop unneeded ones. */
@@ -328,17 +275,10 @@ static int apply_strip_pass(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
for (;;) {
KitObjSymInfo si;
KitIterResult ir = kit_obj_symiter_next(sit, &si);
- uint32_t k;
- int in_needed = 0;
if (ir != KIT_ITER_ITEM) break;
if (si.kind == KIT_SK_UNDEF) continue;
- for (k = 0; k < nneeded; ++k) {
- if (needed[k] == si.id) {
- in_needed = 1;
- break;
- }
- }
- if (!in_needed) kit_obj_builder_remove_symbol(b, si.id);
+ if (!driver_obj_id_in_set(si.id, needed, nneeded))
+ kit_obj_builder_remove_symbol(b, si.id);
}
kit_obj_symiter_free(sit);
rc = 0;
@@ -358,7 +298,7 @@ static void apply_only_sections(KitObjFile* of, KitObjBuilder* b,
for (i = 0; i < n; ++i) {
KitObjSecInfo si;
if (kit_obj_section(of, i, &si) != KIT_OK) continue;
- if (!name_in_list(si.name, opts->only_sections, opts->nonly)) {
+ if (!driver_name_in_list(si.name, opts->only_sections, opts->nonly)) {
kit_obj_builder_remove_section(b, i);
}
}
diff --git a/driver/cmd/objdump.c b/driver/cmd/objdump.c
@@ -1222,13 +1222,14 @@ static void dump_dynrelocs(KitObjFile* f) {
driver_printf(any ? "\n" : "DYNAMIC RELOCATION RECORDS (none)\n\n");
}
-static const char* seg_perms_str(uint32_t perms) {
- static char b[4];
- b[0] = (perms & KIT_SEG_R) ? 'r' : '-';
- b[1] = (perms & KIT_SEG_W) ? 'w' : '-';
- b[2] = (perms & KIT_SEG_X) ? 'x' : '-';
- b[3] = '\0';
- return b;
+/* Format `perms` into the caller-supplied `buf[4]` and return it. The caller
+ * owns the storage, so there is no shared mutable state between calls. */
+static const char* seg_perms_str(uint32_t perms, char buf[4]) {
+ buf[0] = (perms & KIT_SEG_R) ? 'r' : '-';
+ buf[1] = (perms & KIT_SEG_W) ? 'w' : '-';
+ buf[2] = (perms & KIT_SEG_X) ? 'x' : '-';
+ buf[3] = '\0';
+ return buf;
}
/* align is a power of two; report it as 2**N like GNU objdump. */
@@ -1281,6 +1282,7 @@ static void dump_private(KitObjFile* f, const char* label) {
if (kit_obj_segiter_new(f, &sit) == KIT_OK) {
int any = 0;
while (kit_obj_segiter_next(sit, &seg) == KIT_ITER_ITEM) {
+ char perms[4];
any = 1;
driver_printf(
" %-12.*s off 0x%016llx vaddr 0x%016llx align 2**%u\n"
@@ -1288,7 +1290,7 @@ static void dump_private(KitObjFile* f, const char* label) {
KIT_SLICE_ARG(seg.name.len ? seg.name : KIT_SLICE_LIT("LOAD")),
(unsigned long long)seg.file_off, (unsigned long long)seg.vaddr,
u32_log2(seg.align), (unsigned long long)seg.file_size,
- (unsigned long long)seg.vsize, seg_perms_str(seg.perms));
+ (unsigned long long)seg.vsize, seg_perms_str(seg.perms, perms));
}
kit_obj_segiter_free(sit);
if (!any) driver_printf(" (none)\n");
diff --git a/driver/cmd/ranlib.c b/driver/cmd/ranlib.c
@@ -49,17 +49,6 @@ void driver_help_ranlib(void) {
"usage\n")));
}
-static uint64_t ranlib_epoch_from_env(void) {
- const char* s = driver_getenv("SOURCE_DATE_EPOCH");
- uint64_t v = 0;
- if (!s || !*s) return 0;
- for (; *s; ++s) {
- if (*s < '0' || *s > '9') return 0;
- v = v * 10 + (uint64_t)(*s - '0');
- }
- return v;
-}
-
int driver_ranlib(int argc, char** argv) {
DriverEnv env;
KitContext ctx;
@@ -130,7 +119,7 @@ int driver_ranlib(int argc, char** argv) {
KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
goto out;
}
- opts.epoch = ranlib_epoch_from_env();
+ opts.epoch = driver_epoch_from_env();
opts.long_names = 1;
opts.symbol_index = 1;
rc = kit_ar_write(out, NULL, 0, &opts) == KIT_OK ? 0 : 1;
@@ -211,7 +200,7 @@ int driver_ranlib(int argc, char** argv) {
KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
goto out;
}
- opts.epoch = ranlib_epoch_from_env();
+ opts.epoch = driver_epoch_from_env();
opts.long_names = 1;
opts.symbol_index = 1;
opts.member_symbols = msyms;
diff --git a/driver/cmd/run.c b/driver/cmd/run.c
@@ -1045,6 +1045,20 @@ int driver_run(int argc, char** argv) {
return rc;
}
+ /* Compile/JIT succeeded; enforce -Werror / note -fmax-errors before running
+ * the entry. On a -Werror trip we tear the JIT image down, never executing. */
+ if (driver_diag_finish(&env, RUN_TOOL, ro.warnings_are_errors,
+ ro.max_errors)) {
+ kit_interp_program_free(interp);
+ kit_jit_free(jit);
+ driver_compiler_free(compiler);
+ kit_target_free(target);
+ run_metrics_finish(metrics);
+ run_options_release(&ro);
+ driver_env_fini(&env);
+ return 1;
+ }
+
run_metrics_begin(metrics, "run.jit_lookup");
sym = kit_jit_lookup(jit, kit_slice_cstr(ro.entry));
run_metrics_end(metrics, "run.jit_lookup");
diff --git a/driver/cmd/strip.c b/driver/cmd/strip.c
@@ -6,6 +6,7 @@
#include "driver.h"
#include "inputs.h"
+#include "objedit.h"
/* `kit strip` — drop debug sections and / or unwanted symbols from a
* relocatable object or static archive, then write the result back. Scope
@@ -79,15 +80,6 @@ typedef struct StripOpts {
const char* input;
} StripOpts;
-static int name_in_list(KitSlice name, const char* const* list, uint32_t n) {
- uint32_t i;
- if (!name.len) return 0;
- for (i = 0; i < n; ++i) {
- if (list[i] && kit_slice_eq_cstr(name, list[i])) return 1;
- }
- return 0;
-}
-
static int push_name(DriverEnv* env, const char*** arr, uint32_t* n,
uint32_t* cap, const char* name) {
if (*n >= *cap) {
@@ -134,80 +126,6 @@ static int parse_name_arg(int* i, int argc, char** argv, const char* flag,
return 0;
}
-/* Collect the set of KitObjSymbol ids targeted by any reloc whose
- * containing section will survive emit — relocs inside the
- * about-to-be-removed KIT_SEC_DEBUG sections don't count. Otherwise a
- * symbol that's referenced only from DWARF (e.g. main's debug_info entry)
- * keeps every function symbol alive even though the on-disk relocs
- * holding it won't make it to the output. */
-static int collect_needed_syms(DriverEnv* env, KitObjFile* of,
- KitObjSymbol** needed_out, uint32_t* n_out,
- uint32_t* cap_out) {
- KitObjRelocIter* rit = NULL;
- KitObjSymbol* arr = NULL;
- uint32_t n = 0, cap = 0;
-
- if (kit_obj_reliter_new(of, &rit) != KIT_OK) {
- driver_errf(STRIP_TOOL, "out of memory");
- return 1;
- }
- for (;;) {
- KitObjReloc r;
- KitIterResult ir = kit_obj_reliter_next(rit, &r);
- uint32_t k;
- int seen = 0;
- if (ir != KIT_ITER_ITEM) break;
- if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
- /* Skip relocs hosted in a debug section — that section is being
- * dropped, so its relocs don't actually "need" their targets. */
- if (r.section != KIT_SECTION_NONE) {
- KitObjSecInfo hi;
- if (kit_obj_section(of, r.section, &hi) == KIT_OK &&
- hi.kind == KIT_SEC_DEBUG) {
- continue;
- }
- }
- for (k = 0; k < n; ++k) {
- if (arr[k] == r.sym) {
- seen = 1;
- break;
- }
- }
- if (seen) continue;
- if (n >= cap) {
- uint32_t newcap = cap ? cap * 2u : 32u;
- KitObjSymbol* nb =
- (KitObjSymbol*)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
- if (!nb) {
- kit_obj_reliter_free(rit);
- if (arr) driver_free(env, arr, (size_t)cap * sizeof(*arr));
- driver_errf(STRIP_TOOL, "out of memory");
- return 1;
- }
- if (arr) {
- memcpy(nb, arr, (size_t)n * sizeof(*arr));
- driver_free(env, arr, (size_t)cap * sizeof(*arr));
- }
- arr = nb;
- cap = newcap;
- }
- arr[n++] = r.sym;
- }
- kit_obj_reliter_free(rit);
- *needed_out = arr;
- *n_out = n;
- *cap_out = cap;
- return 0;
-}
-
-static int id_in_set(KitObjSymbol id, const KitObjSymbol* arr, uint32_t n) {
- uint32_t i;
- for (i = 0; i < n; ++i) {
- if (arr[i] == id) return 1;
- }
- return 0;
-}
-
/* The core strip pass: drop debug sections, then walk symbols and apply
* keep/strip lists and the operation policy. Mutations are issued
* against the builder; emit-time sweep cleans up cascades (orphan
@@ -233,7 +151,8 @@ static int strip_one_builder(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
/* Step 2: compute the needed-sym set. */
if (filter_syms) {
- if (collect_needed_syms(env, of, &needed, &nneeded, &cap_needed) != 0) {
+ if (driver_obj_collect_reloc_target_syms(env, STRIP_TOOL, of, &needed,
+ &nneeded, &cap_needed) != 0) {
return 1;
}
}
@@ -249,9 +168,11 @@ static int strip_one_builder(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
int drop = 0;
if (ir != KIT_ITER_ITEM) break;
/* --strip-symbol wins over --keep-symbol if both list the same name. */
- if (opts->nstrip && name_in_list(si.name, opts->strip, opts->nstrip)) {
+ if (opts->nstrip &&
+ driver_name_in_list(si.name, opts->strip, opts->nstrip)) {
drop = 1;
- } else if (opts->nkeep && name_in_list(si.name, opts->keep, opts->nkeep)) {
+ } else if (opts->nkeep &&
+ driver_name_in_list(si.name, opts->keep, opts->nkeep)) {
drop = 0;
} else if (filter_syms) {
/* Keep undefined externals so the .o stays linkable; keep symbols
@@ -261,7 +182,7 @@ static int strip_one_builder(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
* needed here. */
if (si.kind == KIT_SK_UNDEF) {
drop = 0;
- } else if (id_in_set(si.id, needed, nneeded)) {
+ } else if (driver_obj_id_in_set(si.id, needed, nneeded)) {
drop = 0;
} else {
drop = 1;
@@ -338,17 +259,6 @@ static int strip_object_bytes(DriverEnv* env, const KitContext* ctx,
return rc;
}
-static uint64_t strip_epoch_from_env(void) {
- const char* s = driver_getenv("SOURCE_DATE_EPOCH");
- uint64_t v = 0;
- if (!s || !*s) return 0;
- for (; *s; ++s) {
- if (*s < '0' || *s > '9') return 0;
- v = v * 10 + (uint64_t)(*s - '0');
- }
- return v;
-}
-
/* Strip every object member of an archive, write a fresh archive with
* a refreshed System-V symbol index. Non-object members pass through
* unchanged. */
@@ -487,7 +397,7 @@ static int strip_archive(DriverEnv* env, const KitContext* ctx,
KIT_SLICE_ARG(kit_slice_cstr(output_path)));
goto done;
}
- opts_ar.epoch = strip_epoch_from_env();
+ opts_ar.epoch = driver_epoch_from_env();
opts_ar.long_names = 1;
opts_ar.symbol_index = 1;
opts_ar.member_symbols = msyms;
diff --git a/driver/cmd/xxd.c b/driver/cmd/xxd.c
@@ -126,38 +126,6 @@ static int xxd_is_hex(int c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
-static int xxd_hexval(int c) {
- if (c >= '0' && c <= '9') return c - '0';
- if (c >= 'a' && c <= 'f') return c - 'a' + 10;
- return c - 'A' + 10;
-}
-
-/* Parse a decimal or 0x-hex non-negative integer. Returns 0 on success. */
-static int xxd_parse_u64(const char* s, uint64_t* out) {
- uint64_t v = 0;
- int base = 10;
- if (!s || !*s) return 1;
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
- base = 16;
- s += 2;
- if (!*s) return 1;
- }
- for (; *s; ++s) {
- unsigned d;
- char c = *s;
- if (c >= '0' && c <= '9')
- d = (unsigned)(c - '0');
- else if (base == 16 && (c >= 'a' && c <= 'f'))
- d = (unsigned)(c - 'a' + 10);
- else if (base == 16 && (c >= 'A' && c <= 'F'))
- d = (unsigned)(c - 'A' + 10);
- else
- return 1;
- v = v * (uint64_t)base + d;
- }
- *out = v;
- return 0;
-}
/* Derive a C identifier from a file path: basename, non-alnum -> '_', a leading
* digit gets an '_' prefix. Writes into out (cap bytes). */
@@ -266,9 +234,9 @@ static void xxd_reverse(Xb* b, const uint8_t* data, size_t len, int plain) {
}
if (xxd_is_hex(ch)) {
if (hi < 0) {
- hi = xxd_hexval(ch);
+ hi = driver_hex_nibble((char)ch);
} else {
- xb_c(b, (char)((hi << 4) | xxd_hexval(ch)));
+ xb_c(b, (char)((hi << 4) | driver_hex_nibble((char)ch)));
hi = -1;
}
continue;
@@ -335,7 +303,7 @@ int driver_xxd(int argc, char** argv) {
if (a[0] == '-' && a[1] == 'c') {
const char* v = xxd_optval(a, argc, argv, &i);
uint64_t n;
- if (!v || xxd_parse_u64(v, &n) != 0 || n == 0) {
+ if (!v || driver_parse_u64(v, &n) != 0 || n == 0) {
driver_errf(XXD_TOOL, "-c requires a positive integer");
goto done;
}
@@ -345,7 +313,7 @@ int driver_xxd(int argc, char** argv) {
if (a[0] == '-' && a[1] == 'g') {
const char* v = xxd_optval(a, argc, argv, &i);
uint64_t n;
- if (!v || xxd_parse_u64(v, &n) != 0 || n == 0) {
+ if (!v || driver_parse_u64(v, &n) != 0 || n == 0) {
driver_errf(XXD_TOOL, "-g requires a positive integer");
goto done;
}
@@ -354,7 +322,7 @@ int driver_xxd(int argc, char** argv) {
}
if (a[0] == '-' && a[1] == 's') {
const char* v = xxd_optval(a, argc, argv, &i);
- if (!v || xxd_parse_u64(v, &o.seek) != 0) {
+ if (!v || driver_parse_u64(v, &o.seek) != 0) {
driver_errf(XXD_TOOL, "-s requires a byte offset");
goto done;
}
@@ -362,7 +330,7 @@ int driver_xxd(int argc, char** argv) {
}
if (a[0] == '-' && a[1] == 'l') {
const char* v = xxd_optval(a, argc, argv, &i);
- if (!v || xxd_parse_u64(v, &o.limit) != 0) {
+ if (!v || driver_parse_u64(v, &o.limit) != 0) {
driver_errf(XXD_TOOL, "-l requires a length");
goto done;
}
diff --git a/driver/driver.h b/driver/driver.h
@@ -15,23 +15,6 @@
*
* Preprocessor-only mode is `cc -E` — there is no separate `cpp` tool. */
-typedef enum DriverTool {
- DRIVER_TOOL_CC,
- DRIVER_TOOL_CHECK,
- DRIVER_TOOL_CPP,
- DRIVER_TOOL_AS,
- DRIVER_TOOL_LD,
- DRIVER_TOOL_AR,
- DRIVER_TOOL_RANLIB,
- DRIVER_TOOL_OBJDUMP,
- DRIVER_TOOL_DBG,
- DRIVER_TOOL_RUN,
- DRIVER_TOOL_EMU,
- DRIVER_TOOL_NM,
- DRIVER_TOOL_SIZE,
- DRIVER_TOOL_ADDR2LINE,
-} DriverTool;
-
/* Multi-call entry: dispatches by argv[0] basename (or argv[1] fallback). */
int driver_main(int argc, char** argv);
@@ -177,6 +160,36 @@ int driver_arch_from_name(const char* name, KitArchKind* arch_out,
* adding a frontend extension reaches every tool at once. */
int driver_path_is_source(const char* path);
+/* ----------------------------------------------------------------------
+ * Shared scalar parsing helpers
+ *
+ * Small, OS-neutral parsers the byte/binutils tools used to each open-code.
+ * Implemented in driver/env/common.c.
+ * ---------------------------------------------------------------------- */
+
+/* Parse SOURCE_DATE_EPOCH into a u64 seconds count. Returns 0 when the variable
+ * is unset, empty, or not all decimal digits ("no epoch"). */
+uint64_t driver_epoch_from_env(void);
+
+/* Parse a non-negative integer that is either decimal or `0x`/`0X`-prefixed
+ * hex. Rejects empty input, a bare `0x` with no digits, and any non-digit.
+ * Returns 0 on success (value in *out), nonzero on a malformed string. */
+int driver_parse_u64(const char* s, uint64_t* out);
+
+/* Map one hex digit (0-9, a-f, A-F) to its 0..15 value, or -1 if `c` is not a
+ * hex digit. */
+int driver_hex_nibble(char c);
+
+/* Join two path components into a freshly heap-allocated, NUL-terminated string
+ * `<a>/<b>`. A '/' separator is inserted iff `a` is non-empty and does not
+ * already end in '/' or '\\' (so Windows-style separators are not doubled). An
+ * empty `a` yields just `<b>` (relative to the current directory); a NULL/empty
+ * `b` yields a copy of `a`. When `out_size` is non-NULL it receives the
+ * allocation size (length + 1) for a later driver_free. Returns NULL only on
+ * allocation failure. */
+char* driver_path_join(DriverEnv* env, const char* a, const char* b,
+ size_t* out_size);
+
typedef struct DriverTargetFeatures {
DriverEnv* env;
KitTargetFeature* features;
diff --git a/driver/env.h b/driver/env.h
@@ -59,6 +59,16 @@ KitStatus driver_compiler_new(const KitTarget*, const KitContext*,
KitCompiler** out);
void driver_compiler_free(KitCompiler*);
+/* Driver-level post-compile diagnostic gate, enforcing the warning policies the
+ * C frontend does not yet honor from KitDiagnosticOptions. Call once after a
+ * compile/link run completes. When `warnings_are_errors` is set and libkit's
+ * diag sink recorded any warnings, emits an error and returns nonzero so the
+ * caller can fail the tool (-Werror). When `max_errors` is nonzero, emits a
+ * single note that -fmax-errors is unimplemented (the frontend does not bound
+ * the error count). Returns 0 when the run should be considered successful. */
+int driver_diag_finish(DriverEnv* env, const char* tool,
+ int warnings_are_errors, uint32_t max_errors);
+
/* Default target used by tools that don't expose a target-selection flag
* yet. v1: native-looking host target (chosen at compile time). */
KitTargetSpec driver_host_target(void);
diff --git a/driver/env/common.c b/driver/env/common.c
@@ -55,12 +55,12 @@ static const char* diag_label(KitDiagKind k) {
return "diag";
}
-/* Tracks the compiler currently driving libkit calls so the stderr
- * diag sink can resolve loc.file_id to the source's spelling (path or
- * memory-input label). NULL falls back to the numeric `<file:%u>` form. */
-static KitCompiler* g_diag_active_compiler;
-
-void driver_diag_set_compiler(KitCompiler* c) { g_diag_active_compiler = c; }
+/* The compiler currently driving libkit calls is stashed in the stderr sink's
+ * `user` field so the sink can resolve loc.file_id to the source's spelling
+ * (path or memory-input label). NULL falls back to the numeric `<file:%u>`
+ * form. Held on the sink rather than in process-global state so the pointer
+ * travels with the sink the emit callback is invoked on. */
+void driver_diag_set_compiler(KitCompiler* c) { g_diag_stderr.user = c; }
KitStatus driver_compiler_new(const KitTarget* t, const KitContext* ctx,
KitCompiler** out) {
@@ -77,15 +77,15 @@ KitStatus driver_compiler_new(const KitTarget* t, const KitContext* ctx,
void driver_compiler_free(KitCompiler* c) {
if (!c) return;
- if (g_diag_active_compiler == c) driver_diag_set_compiler(NULL);
+ if (g_diag_stderr.user == c) driver_diag_set_compiler(NULL);
kit_compiler_free(c);
}
static void diag_stderr_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
const char* fmt, va_list ap) {
- (void)s;
+ KitCompiler* compiler = s ? (KitCompiler*)s->user : NULL;
if (loc.file_id || loc.line) {
- KitSlice name = kit_compiler_file_name(g_diag_active_compiler, loc.file_id);
+ KitSlice name = kit_compiler_file_name(compiler, loc.file_id);
if (name.len) {
fprintf(stderr, "%.*s:%u:%u: %.*s: ", KIT_SLICE_ARG(name), loc.line,
loc.col, KIT_SLICE_ARG(kit_slice_cstr(diag_label(k))));
@@ -107,6 +107,31 @@ KitDiagSink g_diag_stderr = {
0,
};
+/* Driver-level post-compile diagnostic gate. The C frontend currently ignores
+ * KitDiagnosticOptions, so the two warning policies are enforced here, over the
+ * counts libkit maintains on the diag sink:
+ * -Werror : a successful compile with any emitted warning becomes a
+ * failure (the frontend kept compiling; we fail the tool).
+ * -fmax-errors : not yet honored mid-compile by the frontend, so rather than
+ * silently accept it we emit a single explanatory note.
+ * Returns nonzero when the run should be treated as failed. */
+int driver_diag_finish(DriverEnv* env, const char* tool,
+ int warnings_are_errors, uint32_t max_errors) {
+ KitDiagSink* sink = env ? env->diag : NULL;
+ uint32_t warnings = sink ? sink->warnings : 0u;
+ if (max_errors) {
+ driver_errf(tool,
+ "note: -fmax-errors is unimplemented; it does not bound the "
+ "error count");
+ }
+ if (warnings_are_errors && warnings) {
+ driver_errf(tool, "%u warning%s treated as error%s (-Werror)", warnings,
+ warnings == 1u ? "" : "s", warnings == 1u ? "" : "s");
+ return 1;
+ }
+ return 0;
+}
+
/* ---------------- alloc helpers ---------------- */
void* driver_alloc(DriverEnv* e, size_t n) {
@@ -127,31 +152,52 @@ void driver_memcpy(void* dst, const void* src, size_t n) {
memcpy(dst, src, n);
}
+/* ---------------- file load/release (OS-neutral) ---------------- */
+
+/* Pure file_io vtable bookkeeping: no per-host behavior, so it lives here
+ * rather than being duplicated in each env/<host>.c. */
+
+int driver_load_bytes(const KitFileIO* io, const char* tool, const char* path,
+ DriverLoad* out, KitSlice* in) {
+ out->loaded = 0;
+ out->fd.data = NULL;
+ out->fd.size = 0;
+ out->fd.token = NULL;
+ if (!io || !io->read_all) {
+ driver_errf(tool, "host file I/O unavailable");
+ return 1;
+ }
+ if (io->read_all(io->user, path, &out->fd) != KIT_OK) {
+ driver_errf(tool, "failed to read: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(path)));
+ return 1;
+ }
+ out->loaded = 1;
+ in->data = out->fd.data;
+ in->len = out->fd.size;
+ return 0;
+}
+
+void driver_release_bytes(const KitFileIO* io, DriverLoad* lf) {
+ if (!lf || !lf->loaded) return;
+ if (io && io->release) io->release(io->user, &lf->fd);
+ lf->loaded = 0;
+}
+
/* ---------------- hosted dir lists ---------------- */
-/* Mechanical only: join `a` (+ optional `/sub`), dup into the list. No per-OS
- * policy lives here -- producers (driver/lib/hosted.c expansion and the
- * per-host driver_default_hosted_dirs probes) supply the paths. */
+/* Mechanical only: join `a` (+ optional `/sub`) via driver_path_join and dup
+ * into the list. No per-OS policy lives here -- producers (driver/lib/hosted.c
+ * expansion and the per-host driver_default_hosted_dirs probes) supply the
+ * paths. */
static int hd_store(DriverEnv* env, char** arr, size_t* sizes, uint32_t* n,
uint32_t cap, const char* a, const char* b) {
- size_t alen, blen, slash, bytes, off;
+ size_t bytes;
char* out;
if (!a || !a[0]) return 0; /* empty base -> successful no-op */
if (*n >= cap) return 1; /* loud overflow -- never a silent drop */
- alen = driver_strlen(a);
- blen = b ? driver_strlen(b) : 0;
- slash = (blen && a[alen - 1] != '/') ? 1u : 0u;
- bytes = alen + slash + blen + 1u;
- out = driver_alloc(env, bytes);
+ out = driver_path_join(env, a, b, &bytes);
if (!out) return 1;
- driver_memcpy(out, a, alen);
- off = alen;
- if (slash) out[off++] = '/';
- if (blen) {
- driver_memcpy(out + off, b, blen);
- off += blen;
- }
- out[off] = '\0';
arr[*n] = out;
sizes[*n] = bytes;
(*n)++;
@@ -221,7 +267,9 @@ const char* driver_strchr(const char* s, int c) {
const char* driver_basename(const char* path) {
size_t i = kit_slice_cstr(path).len;
while (i > 0) {
- if (path[i - 1] == '/') return path + i;
+ /* Accept both separators so a Windows argv[0] like "C:\\bin\\kit.exe"
+ * strips to its basename rather than returning the full path. */
+ if (path[i - 1] == '/' || path[i - 1] == '\\') return path + i;
--i;
}
return path;
@@ -233,6 +281,71 @@ int driver_has_suffix(const char* s, const char* suffix) {
return ls >= lf && memcmp(s + ls - lf, suffix, lf) == 0;
}
+/* ---------------- scalar parsing helpers ---------------- */
+
+int driver_hex_nibble(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
+ if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
+ return -1;
+}
+
+uint64_t driver_epoch_from_env(void) {
+ const char* s = driver_getenv("SOURCE_DATE_EPOCH");
+ uint64_t v = 0;
+ if (!s || !*s) return 0;
+ for (; *s; ++s) {
+ if (*s < '0' || *s > '9') return 0;
+ v = v * 10 + (uint64_t)(*s - '0');
+ }
+ return v;
+}
+
+int driver_parse_u64(const char* s, uint64_t* out) {
+ uint64_t v = 0;
+ int base = 10;
+ if (!s || !*s) return 1;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ base = 16;
+ s += 2;
+ if (!*s) return 1;
+ }
+ for (; *s; ++s) {
+ int d = driver_hex_nibble(*s);
+ if (d < 0 || d >= base) return 1;
+ v = v * (uint64_t)base + (uint64_t)d;
+ }
+ *out = v;
+ return 0;
+}
+
+char* driver_path_join(DriverEnv* env, const char* a, const char* b,
+ size_t* out_size) {
+ size_t al = a ? driver_strlen(a) : 0u;
+ size_t bl = b ? driver_strlen(b) : 0u;
+ /* Insert a '/' only when there is a `b` to separate from `a` and `a` does
+ * not already end in a separator -- a NULL/empty `b` yields a plain copy of
+ * `a` with no trailing slash. */
+ size_t slash =
+ (bl && al > 0 && a[al - 1u] != '/' && a[al - 1u] != '\\') ? 1u : 0u;
+ size_t bytes = al + slash + bl + 1u;
+ char* out = (char*)driver_alloc(env, bytes);
+ size_t off = 0;
+ if (!out) return NULL;
+ if (al) {
+ driver_memcpy(out, a, al);
+ off = al;
+ }
+ if (slash) out[off++] = '/';
+ if (bl) {
+ driver_memcpy(out + off, b, bl);
+ off += bl;
+ }
+ out[off] = '\0';
+ if (out_size) *out_size = bytes;
+ return out;
+}
+
/* ---------------- printf/errf/logf ---------------- */
void driver_errf(const char* tool, const char* fmt, ...) {
diff --git a/driver/env/posix.c b/driver/env/posix.c
@@ -584,19 +584,6 @@ int driver_path_lexists(const char* path) {
return lstat(path, &sb) == 0;
}
-static char* driver_join_path(DriverEnv* env, const char* a, const char* b) {
- size_t al = kit_slice_cstr(a).len;
- size_t bl = kit_slice_cstr(b).len;
- int slash = al > 0 && a[al - 1u] != '/';
- char* out = (char*)driver_alloc(env, al + (slash ? 1u : 0u) + bl + 1u);
- if (!out) return NULL;
- memcpy(out, a, al);
- if (slash) out[al++] = '/';
- memcpy(out + al, b, bl);
- out[al + bl] = '\0';
- return out;
-}
-
static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
const char* rel, DriverWalkFileFn cb,
void* user) {
@@ -612,10 +599,10 @@ static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
struct stat sb;
int child_rc = 0;
if (driver_streq(name, ".") || driver_streq(name, "..")) continue;
- child = driver_join_path(env, dir, name);
+ child = driver_path_join(env, dir, name, NULL);
if (!child) goto out;
- child_rel = rel && rel[0] ? driver_join_path(env, rel, name)
- : driver_join_path(env, "", name);
+ child_rel = rel && rel[0] ? driver_path_join(env, rel, name, NULL)
+ : driver_path_join(env, "", name, NULL);
if (!child_rel) {
driver_free(env, child, kit_slice_cstr(child).len + 1u);
goto out;
@@ -681,32 +668,7 @@ int driver_random_bytes(uint8_t* out, size_t n) {
/* ---------------- load helpers ---------------- */
-int driver_load_bytes(const KitFileIO* io, const char* tool, const char* path,
- DriverLoad* out, KitSlice* in) {
- out->loaded = 0;
- out->fd.data = NULL;
- out->fd.size = 0;
- out->fd.token = NULL;
- if (!io || !io->read_all) {
- driver_errf(tool, "host file I/O unavailable");
- return 1;
- }
- if (io->read_all(io->user, path, &out->fd) != KIT_OK) {
- driver_errf(tool, "failed to read: %.*s",
- KIT_SLICE_ARG(kit_slice_cstr(path)));
- return 1;
- }
- out->loaded = 1;
- in->data = out->fd.data;
- in->len = out->fd.size;
- return 0;
-}
-
-void driver_release_bytes(const KitFileIO* io, DriverLoad* lf) {
- if (!lf || !lf->loaded) return;
- if (io && io->release) io->release(io->user, &lf->fd);
- lf->loaded = 0;
-}
+/* driver_load_bytes / driver_release_bytes are OS-neutral; see env/common.c. */
/* ---------------- stdin / edit_temp / read_line ---------------- */
diff --git a/driver/env/windows.c b/driver/env/windows.c
@@ -590,8 +590,6 @@ struct DriverDirHandle {
uint64_t count;
};
-static char* driver_join_path(DriverEnv* env, const char* a, const char* b);
-
DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) {
char* pattern;
wchar_t* wpattern;
@@ -603,7 +601,7 @@ DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) {
uint64_t count = 0;
if (!env || !path) return NULL;
- pattern = driver_join_path(env, path, "*");
+ pattern = driver_path_join(env, path, "*", NULL);
if (!pattern) return NULL;
wpattern = widen(pattern);
driver_free(env, pattern, kit_slice_cstr(pattern).len + 1u);
@@ -908,19 +906,6 @@ int driver_path_lexists(const char* path) {
return attr != INVALID_FILE_ATTRIBUTES;
}
-static char* driver_join_path(DriverEnv* env, const char* a, const char* b) {
- size_t al = kit_slice_cstr(a).len;
- size_t bl = kit_slice_cstr(b).len;
- int slash = al > 0 && a[al - 1u] != '/' && a[al - 1u] != '\\';
- char* out = (char*)driver_alloc(env, al + (slash ? 1u : 0u) + bl + 1u);
- if (!out) return NULL;
- memcpy(out, a, al);
- if (slash) out[al++] = '/';
- memcpy(out + al, b, bl);
- out[al + bl] = '\0';
- return out;
-}
-
static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
const char* rel, DriverWalkFileFn cb,
void* user) {
@@ -931,7 +916,7 @@ static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
DWORD last;
int rc = 1;
- pattern = driver_join_path(env, dir, "*");
+ pattern = driver_path_join(env, dir, "*", NULL);
if (!pattern) return 1;
wpattern = widen(pattern);
driver_free(env, pattern, kit_slice_cstr(pattern).len + 1u);
@@ -954,9 +939,9 @@ static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
free(name);
goto loop_next;
}
- child = driver_join_path(env, dir, name);
- child_rel = rel && rel[0] ? driver_join_path(env, rel, name)
- : driver_join_path(env, "", name);
+ child = driver_path_join(env, dir, name, NULL);
+ child_rel = rel && rel[0] ? driver_path_join(env, rel, name, NULL)
+ : driver_path_join(env, "", name, NULL);
if (!child || !child_rel) goto loop_fail;
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
@@ -1039,32 +1024,7 @@ int driver_random_bytes(uint8_t* out, size_t n) {
* load helpers
* ============================================================ */
-int driver_load_bytes(const KitFileIO* io, const char* tool, const char* path,
- DriverLoad* out, KitSlice* in) {
- out->loaded = 0;
- out->fd.data = NULL;
- out->fd.size = 0;
- out->fd.token = NULL;
- if (!io || !io->read_all) {
- driver_errf(tool, "host file I/O unavailable");
- return 1;
- }
- if (io->read_all(io->user, path, &out->fd) != KIT_OK) {
- driver_errf(tool, "failed to read: %.*s",
- KIT_SLICE_ARG(kit_slice_cstr(path)));
- return 1;
- }
- out->loaded = 1;
- in->data = out->fd.data;
- in->len = out->fd.size;
- return 0;
-}
-
-void driver_release_bytes(const KitFileIO* io, DriverLoad* lf) {
- if (!lf || !lf->loaded) return;
- if (io && io->release) io->release(io->user, &lf->fd);
- lf->loaded = 0;
-}
+/* driver_load_bytes / driver_release_bytes are OS-neutral; see env/common.c. */
/* ============================================================
* stdin / edit_temp / read_line
diff --git a/driver/lib/hosted.c b/driver/lib/hosted.c
@@ -4,29 +4,6 @@
#include <stdint.h>
#include <string.h>
-static char* hosted_join2(DriverEnv* env, const char* a, const char* b,
- size_t* out_size) {
- size_t alen = driver_strlen(a);
- size_t blen = driver_strlen(b);
- size_t slash = (alen > 0 && a[alen - 1] != '/') ? 1u : 0u;
- size_t bytes = alen + slash + blen + 1u;
- char* out = driver_alloc(env, bytes);
- size_t off = 0;
- if (!out) return NULL;
- if (alen) {
- driver_memcpy(out + off, a, alen);
- off += alen;
- }
- if (slash) out[off++] = '/';
- if (blen) {
- driver_memcpy(out + off, b, blen);
- off += blen;
- }
- out[off] = '\0';
- if (out_size) *out_size = bytes;
- return out;
-}
-
static int hosted_add_input(DriverHostedInput* items, uint32_t* n, uint32_t cap,
uint8_t kind, char* path, size_t path_size) {
DriverHostedInput* it;
@@ -50,7 +27,7 @@ static char* hosted_find_in_libdirs(DriverEnv* env,
uint32_t i;
for (i = 0; i < dirs->nlibdirs; ++i) {
size_t size = 0;
- char* path = hosted_join2(env, dirs->libdirs[i], file, &size);
+ char* path = driver_path_join(env, dirs->libdirs[i], file, &size);
if (!path) return NULL;
if (driver_path_exists(path)) {
*out_size = size;
@@ -541,6 +518,14 @@ static int hosted_resolve_windows_mingw(const DriverHostedRequest* req,
hosted_add_required_search(
plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs,
"libkernel32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 ||
+ /* Deliberate second pass over the mingw/ucrt runtime core (a manual
+ * --start-group/--end-group): these archives are mutually recursive --
+ * libucrt pulls libmingwex/libmoldname members which in turn pull more
+ * libucrt members, and winpthread/mingw32 close the cycle. Because
+ * archives are scanned in order with lazy member pulls, one pass leaves
+ * late-discovered undefs unresolved; re-listing the core set lets the
+ * linker pull the members the first pass missed. This is NOT a verbatim
+ * duplicate of the list above (the system import libs are not repeated). */
hosted_add_required_search(
plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs,
"libmingw32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 ||
diff --git a/driver/lib/lib_resolve.c b/driver/lib/lib_resolve.c
@@ -4,45 +4,38 @@
#include <stdint.h>
/* Compose `<dir>/<prefix><name><suffix>` into a fresh heap buffer.
- * Inserts a separating '/' iff `dir` does not already end in one.
* Empty `dir` is treated as the current directory: the path becomes
* `<prefix><name><suffix>`. `prefix` is "lib" or "" (Windows MSVC-
* style libs ship without the prefix); `suffix` is e.g. ".a" or ".so"
- * — both caller-owned, NUL-terminated. */
+ * — both caller-owned, NUL-terminated. The leaf (`prefix+name+suffix`) is
+ * built first, then joined onto `dir` via the shared driver_path_join so the
+ * separator decision (including Windows '\\') lives in one place. */
static char* compose_path(DriverEnv* env, const char* dir, const char* prefix,
const char* name, const char* suffix,
size_t* out_size) {
- size_t dlen = driver_strlen(dir);
size_t plen = driver_strlen(prefix);
size_t nlen = driver_strlen(name);
size_t slen = driver_strlen(suffix);
- size_t need_slash = (dlen > 0 && dir[dlen - 1] != '/') ? 1 : 0;
- /* "<dir>" + "/"? + "<prefix>" + "<name>" + "<suffix>" + NUL */
- size_t bytes = dlen + need_slash + plen + nlen + slen + 1;
- char* buf = driver_alloc(env, bytes);
+ size_t leaf_bytes = plen + nlen + slen + 1;
+ char* leaf = driver_alloc(env, leaf_bytes);
+ char* buf;
size_t off = 0;
- if (!buf) return NULL;
- if (dlen) {
- driver_memcpy(buf + off, dir, dlen);
- off += dlen;
- }
- if (need_slash) {
- buf[off++] = '/';
- }
+ if (!leaf) return NULL;
if (plen) {
- driver_memcpy(buf + off, prefix, plen);
+ driver_memcpy(leaf + off, prefix, plen);
off += plen;
}
if (nlen) {
- driver_memcpy(buf + off, name, nlen);
+ driver_memcpy(leaf + off, name, nlen);
off += nlen;
}
if (slen) {
- driver_memcpy(buf + off, suffix, slen);
+ driver_memcpy(leaf + off, suffix, slen);
off += slen;
}
- buf[off] = '\0';
- *out_size = bytes;
+ leaf[off] = '\0';
+ buf = driver_path_join(env, dir, leaf, out_size);
+ driver_free(env, leaf, leaf_bytes);
return buf;
}
diff --git a/driver/lib/link_flags.c b/driver/lib/link_flags.c
@@ -94,22 +94,6 @@ static void lf_free_build_id(DriverLinkFlags* lf) {
}
}
-static int lf_hex_val(char c, unsigned* out) {
- if (c >= '0' && c <= '9') {
- *out = (unsigned)(c - '0');
- return 0;
- }
- if (c >= 'a' && c <= 'f') {
- *out = (unsigned)(c - 'a' + 10);
- return 0;
- }
- if (c >= 'A' && c <= 'F') {
- *out = (unsigned)(c - 'A' + 10);
- return 0;
- }
- return 1;
-}
-
static int lf_parse_hex_bytes(DriverLinkFlags* lf, const char* s, size_t n,
uint8_t** out_bytes, uint32_t* out_len,
size_t* out_size) {
@@ -119,8 +103,9 @@ static int lf_parse_hex_bytes(DriverLinkFlags* lf, const char* s, size_t n,
bs = (uint8_t*)driver_alloc(lf->env, n / 2u);
if (!bs) return 1;
for (i = 0; i < n; i += 2u) {
- unsigned hi, lo;
- if (lf_hex_val(s[i], &hi) != 0 || lf_hex_val(s[i + 1u], &lo) != 0) {
+ int hi = driver_hex_nibble(s[i]);
+ int lo = driver_hex_nibble(s[i + 1u]);
+ if (hi < 0 || lo < 0) {
driver_free(lf->env, bs, n / 2u);
return 1;
}
diff --git a/driver/lib/objedit.c b/driver/lib/objedit.c
@@ -0,0 +1,72 @@
+#include "objedit.h"
+
+#include <string.h>
+
+int driver_name_in_list(KitSlice name, const char* const* list, uint32_t n) {
+ uint32_t i;
+ if (!name.len) return 0;
+ for (i = 0; i < n; ++i) {
+ if (list[i] && kit_slice_eq_cstr(name, list[i])) return 1;
+ }
+ return 0;
+}
+
+int driver_obj_id_in_set(KitObjSymbol id, const KitObjSymbol* arr, uint32_t n) {
+ uint32_t i;
+ for (i = 0; i < n; ++i) {
+ if (arr[i] == id) return 1;
+ }
+ return 0;
+}
+
+int driver_obj_collect_reloc_target_syms(DriverEnv* env, const char* tool,
+ KitObjFile* of, KitObjSymbol** out,
+ uint32_t* n_out, uint32_t* cap_out) {
+ KitObjRelocIter* rit = NULL;
+ KitObjSymbol* arr = NULL;
+ uint32_t n = 0, cap = 0;
+
+ if (kit_obj_reliter_new(of, &rit) != KIT_OK) {
+ driver_errf(tool, "out of memory");
+ return 1;
+ }
+ for (;;) {
+ KitObjReloc r;
+ KitIterResult ir = kit_obj_reliter_next(rit, &r);
+ if (ir != KIT_ITER_ITEM) break;
+ if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
+ /* Skip relocs hosted in a debug section -- that section is being
+ * dropped, so its relocs don't actually "need" their targets. */
+ if (r.section != KIT_SECTION_NONE) {
+ KitObjSecInfo hi;
+ if (kit_obj_section(of, r.section, &hi) == KIT_OK &&
+ hi.kind == KIT_SEC_DEBUG) {
+ continue;
+ }
+ }
+ if (driver_obj_id_in_set(r.sym, arr, n)) continue;
+ if (n >= cap) {
+ uint32_t newcap = cap ? cap * 2u : 32u;
+ KitObjSymbol* nb =
+ (KitObjSymbol*)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
+ if (!nb) {
+ kit_obj_reliter_free(rit);
+ if (arr) driver_free(env, arr, (size_t)cap * sizeof(*arr));
+ driver_errf(tool, "out of memory");
+ return 1;
+ }
+ if (arr) {
+ memcpy(nb, arr, (size_t)n * sizeof(*arr));
+ driver_free(env, arr, (size_t)cap * sizeof(*arr));
+ }
+ arr = nb;
+ cap = newcap;
+ }
+ arr[n++] = r.sym;
+ }
+ kit_obj_reliter_free(rit);
+ *out = arr;
+ *n_out = n;
+ *cap_out = cap;
+ return 0;
+}
diff --git a/driver/lib/objedit.h b/driver/lib/objedit.h
@@ -0,0 +1,33 @@
+#ifndef KIT_DRIVER_LIB_OBJEDIT_H
+#define KIT_DRIVER_LIB_OBJEDIT_H
+
+#include <kit/core.h>
+#include <kit/object.h>
+#include <stdint.h>
+
+#include "driver.h"
+
+/* Object-editing helpers shared by the `strip` and `objcopy` tools, which both
+ * drop debug sections and then prune symbols not reached by a surviving reloc.
+ * Kept here so the reloc-target scan and the small membership predicates live
+ * in one place rather than being copied between the two drivers. */
+
+/* Whether `name` exactly matches one of the `n` NUL-terminated entries in
+ * `list`. An empty `name` never matches. */
+int driver_name_in_list(KitSlice name, const char* const* list, uint32_t n);
+
+/* Whether symbol id `id` appears in `arr[0..n)`. */
+int driver_obj_id_in_set(KitObjSymbol id, const KitObjSymbol* arr, uint32_t n);
+
+/* Collect (deduplicated) the symbol ids targeted by any relocation whose
+ * containing section will survive emit -- relocs hosted in KIT_SEC_DEBUG
+ * sections are skipped, since those sections are about to be dropped and so do
+ * not actually keep their targets alive. On success *out owns a freshly
+ * allocated array of *n ids with capacity *cap (free with driver_free over
+ * *cap * sizeof(KitObjSymbol)); on failure emits an error tagged `tool` and
+ * returns nonzero. Returns 0 on success. */
+int driver_obj_collect_reloc_target_syms(DriverEnv* env, const char* tool,
+ KitObjFile* of, KitObjSymbol** out,
+ uint32_t* n, uint32_t* cap);
+
+#endif
diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c
@@ -210,25 +210,9 @@ static char* rt_dup(DriverEnv* env, const char* s, size_t* out_size) {
return p;
}
-static char* rt_join(DriverEnv* env, const char* a, const char* b,
- size_t* out_size) {
- size_t la = driver_strlen(a);
- size_t lb = driver_strlen(b);
- int slash = la > 0 && a[la - 1u] != '/';
- size_t n = la + (slash ? 1u : 0u) + lb + 1u;
- char* p = (char*)driver_alloc(env, n);
- if (!p) return NULL;
- driver_memcpy(p, a, la);
- if (slash) p[la++] = '/';
- driver_memcpy(p + la, b, lb);
- p[la + lb] = '\0';
- if (out_size) *out_size = n;
- return p;
-}
-
static char* rt_join_slot(DriverEnv* env, char** slot, size_t* size_slot,
const char* a, const char* b) {
- char* p = rt_join(env, a, b, size_slot);
+ char* p = driver_path_join(env, a, b, size_slot);
*slot = p;
return p;
}
@@ -261,7 +245,8 @@ static int rt_set_layout(DriverEnv* env, DriverRuntimeSupport* out,
const char* support_root, const char* rt_root) {
out->support_root = rt_dup(env, support_root, &out->support_root_size);
out->rt_root = rt_dup(env, rt_root, &out->rt_root_size);
- out->include_dir = rt_join(env, rt_root, "include", &out->include_dir_size);
+ out->include_dir =
+ driver_path_join(env, rt_root, "include", &out->include_dir_size);
if (!out->support_root || !out->rt_root || !out->include_dir) {
driver_runtime_support_fini(env, out);
return 1;
@@ -275,7 +260,7 @@ static int rt_try_support_root(DriverEnv* env, const char* root,
size_t rt_root_size = 0;
int ok;
- rt_root = rt_join(env, root, "rt", &rt_root_size);
+ rt_root = driver_path_join(env, root, "rt", &rt_root_size);
if (!rt_root) return 1;
ok = rt_has_layout(rt_root) && rt_set_layout(env, out, root, rt_root) == 0;
driver_free(env, rt_root, rt_root_size);
@@ -317,7 +302,7 @@ static int rt_try_argv0_support(DriverEnv* env, const char* argv0,
driver_memcpy(dir, argv0, dir_len);
dir[dir_len] = '\0';
- support = rt_join(env, dir, "support", &support_size);
+ support = driver_path_join(env, dir, "support", &support_size);
driver_free(env, dir, dir_size);
if (!support) return 1;
rc = rt_try_support_root(env, support, out);
@@ -348,7 +333,7 @@ static int rt_try_argv0_checkout_root(DriverEnv* env, const char* argv0,
driver_memcpy(dir, argv0, dir_len);
dir[dir_len] = '\0';
- parent = rt_join(env, dir, "..", &parent_size);
+ parent = driver_path_join(env, dir, "..", &parent_size);
driver_free(env, dir, dir_size);
if (!parent) return 1;
rc = rt_try_support_root(env, parent, out);
@@ -607,9 +592,9 @@ static int rt_compile_source(DriverEnv* env,
int rc = 1;
*obj_writer = NULL;
- lib_path = rt_join(env, support->rt_root, "lib", &lib_path_size);
+ lib_path = driver_path_join(env, support->rt_root, "lib", &lib_path_size);
if (!lib_path) goto out;
- src_path = rt_join(env, lib_path, rel, &src_path_size);
+ src_path = driver_path_join(env, lib_path, rel, &src_path_size);
if (!src_path) goto out;
if (driver_load_bytes(ctx.file_io, tool, src_path, &src_load, &input) != 0)
goto out;
@@ -818,9 +803,10 @@ static int rt_is_archive_stale(DriverEnv* env,
size_t src_path_size = 0;
int64_t src_mtime;
int stale = 0;
- lib_path = rt_join(env, support->rt_root, "lib", &lib_path_size);
+ lib_path = driver_path_join(env, support->rt_root, "lib", &lib_path_size);
if (!lib_path) return 1;
- src_path = rt_join(env, lib_path, variant->sources[i], &src_path_size);
+ src_path =
+ driver_path_join(env, lib_path, variant->sources[i], &src_path_size);
driver_free(env, lib_path, lib_path_size);
if (!src_path) return 1;
if (driver_path_mtime_ns(src_path, &src_mtime) != 0 ||
@@ -839,9 +825,9 @@ static char* rt_archive_dir_for_root(DriverEnv* env, const char* root,
char* dir = NULL;
size_t build_rt_size = 0;
size_t dir_size = 0;
- build_rt = rt_join(env, root, "build/rt", &build_rt_size);
+ build_rt = driver_path_join(env, root, "build/rt", &build_rt_size);
if (!build_rt) return NULL;
- dir = rt_join(env, build_rt, variant->key, &dir_size);
+ dir = driver_path_join(env, build_rt, variant->key, &dir_size);
driver_free(env, build_rt, build_rt_size);
if (out_size) *out_size = dir ? dir_size : 0;
return dir;
@@ -855,7 +841,8 @@ static int rt_archive_try_dir(DriverEnv* env, const char* tool,
char* archive_path;
size_t archive_path_size = 0;
- archive_path = rt_join(env, cache_dir, "libkit_rt.a", &archive_path_size);
+ archive_path =
+ driver_path_join(env, cache_dir, "libkit_rt.a", &archive_path_size);
if (!archive_path) {
driver_errf(tool, "out of memory");
return 1;
@@ -915,7 +902,8 @@ int driver_runtime_ensure_archive(DriverEnv* env, const char* tool,
}
/* User cache location, outside any support/install tree. */
if (env->cache_dir) {
- cache_dir = rt_join(env, env->cache_dir, variant->key, &cache_dir_size);
+ cache_dir =
+ driver_path_join(env, env->cache_dir, variant->key, &cache_dir_size);
if (!cache_dir) {
driver_free(env, in_tree_dir, in_tree_dir_size);
driver_errf(diag_tool, "out of memory");
diff --git a/driver/lib/target.c b/driver/lib/target.c
@@ -406,6 +406,13 @@ int driver_target_from_triple(const char* triple, KitTargetSpec* out) {
}
}
if (!os_set) {
+ /* No recognized OS token. This deliberately also covers bare-metal and
+ * vendor-only triples that carry no OS at all -- e.g. "x86_64",
+ * "aarch64-unknown", "riscv64-unknown-elf" -- which must keep resolving to
+ * a freestanding ELF/WASM target. We cannot tell an unrecognized OS token
+ * (e.g. "x86_64-pc-frobnicate") apart from those without a vendor/env token
+ * table, so erroring here would regress valid bare-metal triples. Left as a
+ * silent freestanding default by design; see findings note for B15. */
t.os = KIT_OS_FREESTANDING;
t.obj = (t.arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF;
}
diff --git a/include/kit/cg.h b/include/kit/cg.h
@@ -467,8 +467,9 @@ KIT_API KitStatus kit_cg_begin(KitCg*, KitObjBuilder* out,
KIT_API KitStatus kit_cg_begin_unit(KitCg*, const KitCgUnitOptions*);
KIT_API KitStatus kit_cg_end_unit(KitCg*);
KIT_API KitStatus kit_cg_finish(KitCg*, const KitCgFinishOptions*);
+/* Release the session, abandoning any in-progress object/unit state. Used on
+ * both the success-detach and error-abort paths. */
KIT_API KitStatus kit_cg_detach(KitCg*);
-KIT_API KitStatus kit_cg_abort(KitCg*);
KIT_API void kit_cg_free(KitCg*);
/* Sticky source location. Function, scope, local, param, instruction, and
diff --git a/include/kit/object.h b/include/kit/object.h
@@ -355,7 +355,7 @@ KIT_API KitStatus kit_obj_section_format_flags(const KitObjFile*,
KIT_API KitStatus kit_obj_symbol_by_name(const KitObjFile*, KitSlice name,
KitObjSymInfo* out);
-KIT_API KitStatus kit_obj_symiter_new(KitObjFile*, KitObjSymIter** out);
+KIT_API KitStatus kit_obj_symiter_new(const KitObjFile*, KitObjSymIter** out);
KIT_API KitIterResult kit_obj_symiter_next(KitObjSymIter*, KitObjSymInfo* out);
KIT_API void kit_obj_symiter_free(KitObjSymIter*);
@@ -401,7 +401,7 @@ KIT_API void kit_obj_rpathiter_free(KitObjRpathIter*);
/* Dynamic symbol table (.dynsym / dyld export trie / PE export table).
* Reuses the KitObjSymInfo shape and the KitObjSymIter handle — drive
* it with kit_obj_symiter_next / _free. Empty on relocatable objects. */
-KIT_API KitStatus kit_obj_dynsymiter_new(KitObjFile*, KitObjSymIter** out);
+KIT_API KitStatus kit_obj_dynsymiter_new(const KitObjFile*, KitObjSymIter** out);
/* Dynamic relocations (.rela.dyn / .rela.plt / dyld binds / PE base relocs).
* Reuses KitObjReloc and the KitObjRelocIter handle — drive it with
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -1956,8 +1956,8 @@ static int try_parse_builtin_call(Parser* p) {
FrameSlotDesc fsd;
memset(&fsd, 0, sizeof fsd);
fsd.type = eptr_ty;
- fsd.size = 8;
- fsd.align = 8;
+ fsd.size = c_abi_sizeof(p->abi, eptr_ty);
+ fsd.align = c_abi_alignof(p->abi, eptr_ty);
fsd.kind = FS_LOCAL;
FrameSlot eslot = pcg_local(p, &fsd);
pcg_push_local_typed(p, eslot, eptr_ty);
@@ -1990,8 +1990,8 @@ static int try_parse_builtin_call(Parser* p) {
FrameSlotDesc okd;
memset(&okd, 0, sizeof okd);
okd.type = ok_ty;
- okd.size = 4;
- okd.align = 4;
+ okd.size = c_abi_sizeof(p->abi, ok_ty);
+ okd.align = c_abi_alignof(p->abi, ok_ty);
okd.kind = FS_LOCAL;
FrameSlot okslot = pcg_local(p, &okd);
pcg_push_local_typed(p, okslot, ok_ty);
@@ -2192,9 +2192,15 @@ static void parse_primary(Parser* p) {
perr(p, "expected expression");
}
+/* Resolve `mname` inside `rec_ty`, recursing through anonymous struct/union
+ * members to arbitrary depth (like offsetof_find_member / find_field). On
+ * success accumulates the cumulative byte offset into *out_off, records the
+ * matched member type, and — for a bit-field leaf — the bit-field metadata
+ * pulled from the final ABIFieldLayout. */
static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
- const Type** out_ty, u32 path[2],
- u32* out_depth, const Field** out_field) {
+ const Type** out_ty, i64* out_off,
+ u16* out_bf_off, u16* out_bf_w,
+ u32* out_bf_ss) {
const ABIRecordLayout* L;
rec_ty = type_unqual(p->pool, rec_ty);
if (!rec_ty || (rec_ty->kind != TY_STRUCT && rec_ty->kind != TY_UNION))
@@ -2204,68 +2210,35 @@ static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
for (u16 i = 0; i < rec_ty->rec.nfields; ++i) {
const Field* f = &rec_ty->rec.fields[i];
if (f->name == mname && mname != 0) {
+ const ABIFieldLayout* fl = &L->fields[i];
*out_ty = f->type;
- path[0] = i;
- *out_depth = 1;
- if (out_field) *out_field = f;
+ *out_off += (i64)fl->offset;
+ if (f->flags & FIELD_BITFIELD) {
+ *out_bf_off = fl->bit_offset;
+ *out_bf_w = fl->bit_width;
+ *out_bf_ss = fl->storage_size;
+ }
return 1;
}
- {
- const Type* fty = type_unqual(p->pool, f->type);
- if (!((f->flags & FIELD_ANON) &&
- (fty->kind == TY_STRUCT || fty->kind == TY_UNION))) {
- continue;
- }
- const ABIRecordLayout* IL = c_abi_record_layout(p->abi, p->pool, fty);
- if (!IL) continue;
- for (u16 j = 0; j < fty->rec.nfields; ++j) {
- const Field* ff = &fty->rec.fields[j];
- if (ff->name == mname && mname != 0) {
- *out_ty = ff->type;
- path[0] = i;
- path[1] = j;
- *out_depth = 2;
- if (out_field) *out_field = ff;
- return 1;
- }
- }
+ }
+ for (u16 i = 0; i < rec_ty->rec.nfields; ++i) {
+ const Field* f = &rec_ty->rec.fields[i];
+ const Type* fty = type_unqual(p->pool, f->type);
+ i64 nested_off;
+ if (!((f->flags & FIELD_ANON) &&
+ (fty->kind == TY_STRUCT || fty->kind == TY_UNION))) {
+ continue;
+ }
+ nested_off = *out_off + (i64)L->fields[i].offset;
+ if (find_record_member_path(p, fty, mname, out_ty, &nested_off, out_bf_off,
+ out_bf_w, out_bf_ss)) {
+ *out_off = nested_off;
+ return 1;
}
}
return 0;
}
-static void cg_record_member_path(Parser* p, const Type* member_ty,
- const u32* path, u32 depth,
- const Field* field) {
- /* Walk the path locally to compute the cumulative byte offset; pull
- * bit-field metadata from the final ABIFieldLayout when applicable. The
- * field/index/addr_offset CG ops are gone — pcg_lv_member folds the offset
- * (and any bit-field meta) onto the TOS lvalue's aux for the next memop. */
- const Type* cur_ty = pcg_top_type(p);
- i64 total_offset = 0;
- u16 bf_off = 0;
- u16 bf_w = 0;
- u32 bf_ss = 0;
- cur_ty = type_unqual(p->pool, cur_ty);
- for (u32 i = 0; i < depth; ++i) {
- const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, cur_ty);
- const ABIFieldLayout* fl;
- const Field* f;
- if (!L) break;
- fl = &L->fields[path[i]];
- f = &cur_ty->rec.fields[path[i]];
- total_offset += (i64)fl->offset;
- if (i + 1u == depth && (f->flags & FIELD_BITFIELD)) {
- bf_off = fl->bit_offset;
- bf_w = fl->bit_width;
- bf_ss = fl->storage_size;
- }
- cur_ty = type_unqual(p->pool, f->type);
- }
- (void)field;
- pcg_lv_member(p, total_offset, member_ty, bf_off, bf_w, bf_ss);
-}
-
static void parse_postfix(Parser* p) {
VLABound* vla_bounds;
p->last_pushed_vla_slot = FRAME_SLOT_NONE;
@@ -2394,9 +2367,9 @@ static void parse_postfix(Parser* p) {
const Type* lt = pcg_top_type(p);
Sym mname;
const Type* mty = NULL;
- const Field* mf = NULL;
- u32 path[2];
- u32 depth = 0;
+ i64 off = 0;
+ u16 bf_off = 0, bf_w = 0;
+ u32 bf_ss = 0;
advance(p); /* '.' */
if (!lt || (lt->kind != TY_STRUCT && lt->kind != TY_UNION)) {
perr(p,
@@ -2408,9 +2381,10 @@ static void parse_postfix(Parser* p) {
mname = p->cur.v.ident;
advance(p);
lt = type_unqual(p->pool, lt);
- if (!find_record_member_path(p, lt, mname, &mty, path, &depth, &mf))
+ if (!find_record_member_path(p, lt, mname, &mty, &off, &bf_off, &bf_w,
+ &bf_ss))
perr(p, "no such member");
- cg_record_member_path(p, mty, path, depth, mf);
+ pcg_lv_member(p, off, mty, bf_off, bf_w, bf_ss);
continue;
}
if (is_punct(&t, P_ARROW)) {
@@ -2418,9 +2392,9 @@ static void parse_postfix(Parser* p) {
const Type* rec_ty;
Sym mname;
const Type* mty = NULL;
- const Field* mf = NULL;
- u32 path[2];
- u32 depth = 0;
+ i64 off = 0;
+ u16 bf_off = 0, bf_w = 0;
+ u32 bf_ss = 0;
advance(p); /* `->` */
to_rvalue(p);
lt0 = pcg_top_type(p);
@@ -2436,10 +2410,11 @@ static void parse_postfix(Parser* p) {
}
mname = p->cur.v.ident;
advance(p);
- if (!find_record_member_path(p, rec_ty, mname, &mty, path, &depth, &mf))
+ if (!find_record_member_path(p, rec_ty, mname, &mty, &off, &bf_off, &bf_w,
+ &bf_ss))
perr(p, "no such member");
pcg_deref(p, rec_ty);
- cg_record_member_path(p, mty, path, depth, mf);
+ pcg_lv_member(p, off, mty, bf_off, bf_w, bf_ss);
continue;
}
break;
@@ -2470,12 +2445,7 @@ void parse_unary(Parser* p) {
fsd.kind = FS_LOCAL;
fsd.flags = FSF_NONE;
slot = pcg_local(p, &fsd);
- if (lit_ty && (lit_ty->kind == TY_ARRAY || lit_ty->kind == TY_STRUCT ||
- lit_ty->kind == TY_UNION)) {
- init_at(p, slot, lit_ty, 0, lit_ty);
- } else {
- init_at(p, slot, lit_ty, 0, lit_ty);
- }
+ init_at(p, slot, lit_ty, 0, lit_ty);
pcg_push_local_typed(p, slot, lit_ty);
return;
}
@@ -3326,8 +3296,6 @@ static void parse_lor(Parser* p) {
}
}
-static const Type* common_fp_type(Parser* p, const Type* a, const Type* b);
-
static void parse_ternary(Parser* p) {
parse_lor(p);
if (!is_punct(&p->cur, '?')) return;
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -691,6 +691,10 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
advance(p);
seen = 1;
} else if (is_kw(p, &t, KW_FLOAT) || is_kw(p, &t, KW_FLOAT16)) {
+ /* _Float16 is intentionally aliased to 32-bit float (a deliberate
+ * approximation; see test/parse/cases/float16_01_decl, which asserts
+ * sizeof(_Float16) == sizeof(float)). Real 16-bit semantics would be a
+ * separate feature. */
acc.saw_float = 1;
acc.saw_explicit_type = 1;
advance(p);
diff --git a/lang/c/type/type.c b/lang/c/type/type.c
@@ -207,13 +207,6 @@ TagId type_tag_new(Pool* p, TagDeclKind kind, Sym spelling, SrcLoc loc) {
return (TagId)(c->next_tag++);
}
-const TagDecl* type_tag_get(Pool* p, TagId id) {
- (void)p;
- (void)id;
- /* TagDecl table is parser-territory; not modeled in v1. */
- return NULL;
-}
-
TypeRecordBuilder* type_record_begin(Pool* p, TypeKind kind, TagId tag_id,
Sym tag) {
TypeRecordOpts opts;
diff --git a/lang/c/type/type.h b/lang/c/type/type.h
@@ -135,7 +135,6 @@ const Type* type_qualified(Pool*, const Type*, u16 qual);
* size/alignment, and bitfield storage are target ABI facts. */
typedef struct TypeRecordBuilder TypeRecordBuilder;
TagId type_tag_new(Pool*, TagDeclKind, Sym spelling, SrcLoc);
-const TagDecl* type_tag_get(Pool*, TagId);
TypeRecordBuilder* type_record_begin(Pool*, TypeKind kind, TagId,
Sym tag); /* TY_STRUCT or TY_UNION */
diff --git a/lang/cpp/pp/pp_directive.c b/lang/cpp/pp/pp_directive.c
@@ -5,6 +5,7 @@
static void destringize(Pp* pp, const Tok* str_tok, char* out, size_t cap,
size_t* out_len);
+static void pp_warn(Pp* pp, SrcLoc loc, const char* fmt, ...);
/* ============================================================
* If-stack
@@ -959,7 +960,7 @@ static int pragma_num_u32(Pp* pp, const Tok* t, u32* out) {
return 1;
}
-static void handle_pragma_pack(Pp* pp, const Tok* line, u32 n) {
+static void handle_pragma_pack(Pp* pp, const Tok* line, u32 n, SrcLoc loc) {
u32 i = 0;
if (n < 3 || line[0].kind != TOK_IDENT) return;
{
@@ -982,6 +983,10 @@ static void handle_pragma_pack(Pp* pp, const Tok* line, u32 n) {
if (pp->pack_stack_n <
(u32)(sizeof pp->pack_stack / sizeof pp->pack_stack[0])) {
pp->pack_stack[pp->pack_stack_n++] = pp->pack_align;
+ } else {
+ pp_warn(pp, loc,
+ "#pragma pack(push): pack stack overflow (max %u); push dropped",
+ (u32)(sizeof pp->pack_stack / sizeof pp->pack_stack[0]));
}
++i;
if (i < n && line[i].kind == TOK_PUNCT && line[i].v.punct == ',') {
@@ -1005,7 +1010,7 @@ static void handle_pragma_pack(Pp* pp, const Tok* line, u32 n) {
static void do_pragma(Pp* pp, const Tok* line, u32 n, SrcLoc loc) {
/* Forward unrecognised pragmas to the output. STDC pragmas pass
* through too; we don't act on them yet. */
- handle_pragma_pack(pp, line, n);
+ handle_pragma_pack(pp, line, n, loc);
emit_pragma_line(pp, line, n, loc);
}
diff --git a/lang/cpp/pp/pp_expand.c b/lang/cpp/pp/pp_expand.c
@@ -86,33 +86,6 @@ HidesetId hs_add(Pp* pp, HidesetId id, Sym s) {
return hs_register(pp, buf, n + 1);
}
-/* Used by token-paste in stage 5; declared early so the rest of the file
- * doesn't grow forward decls. */
-__attribute__((unused)) static HidesetId hs_intersect(Pp* pp, HidesetId a,
- HidesetId b) {
- Sym buf[64];
- Hideset *ha, *hb;
- u32 i, j, k;
- if (a == HS_EMPTY || b == HS_EMPTY) return HS_EMPTY;
- if (a == b) return a;
- ha = pp->hsets[a];
- hb = pp->hsets[b];
- /* Both sorted; standard merge intersection. */
- i = j = k = 0;
- while (i < ha->n && j < hb->n) {
- if (ha->names[i] == hb->names[j]) {
- buf[k++] = ha->names[i];
- ++i;
- ++j;
- } else if (ha->names[i] < hb->names[j]) {
- ++i;
- } else {
- ++j;
- }
- }
- return hs_register(pp, buf, k);
-}
-
/* ============================================================
* Macro table
* ============================================================ */
diff --git a/mk/driver_srcs.mk b/mk/driver_srcs.mk
@@ -68,6 +68,7 @@ DRIVER_SRCS += $(call need-any,CC CHECK BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib
DRIVER_SRCS += $(call need-any,CC CHECK BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib/link_flags.c)
DRIVER_SRCS += $(call need-any,BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib/archive_engine.c)
DRIVER_SRCS += $(call need-any,AR RANLIB STRIP DBG RUN BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib/inputs.c)
+DRIVER_SRCS += $(call need-any,STRIP OBJCOPY,driver/lib/objedit.c)
DRIVER_SRCS += $(call need-any,RUN,driver/lib/wasm_run.c)
DRIVER_SRCS += $(call need-any,CC CHECK BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib/compile_engine.c)
DRIVER_SRCS += $(call need-any,CAS PKG,driver/lib/dist_host.c)
diff --git a/src/abi/abi.c b/src/abi/abi.c
@@ -87,10 +87,6 @@ ABITypeInfo abi_cg_type_info(TargetABI* a, KitCgTypeId id) {
}
}
-ABITypeInfo abi_internal_type_info(TargetABI* a, KitCgTypeId id) {
- return abi_cg_type_info(a, id);
-}
-
u32 abi_cg_sizeof(TargetABI* a, KitCgTypeId id) {
return abi_cg_type_info(a, id).size;
}
@@ -153,6 +149,39 @@ const ABIRecordLayout* abi_cg_record_layout(TargetABI* a, KitCgTypeId id) {
return L;
}
+/* ---- shared classifier primitives ----
+ *
+ * The per-ABI classifiers (abi_sysv_x64.c, abi_win64_x64.c, abi_aapcs64.c,
+ * abi_rv64.c) share these byte-identical building blocks; the ABI-specific
+ * scalar/aggregate rules stay in their own TUs. */
+
+/* A void / zero-size argument is ignored (no parts, no register/stack slot). */
+void abi_classify_void(ABIArgInfo* out) {
+ memset(out, 0, sizeof *out);
+ out->kind = ABI_ARG_IGNORE;
+}
+
+/* A 16-byte integer scalar (__int128 / __uint128) passed/returned as two
+ * INTEGER eightbytes: low half in the lower-numbered register, high in the
+ * next. Used by the SysV-x64, Win64 (mingw), and AAPCS64 classifiers, which
+ * all agree on this shape. */
+void abi_classify_int128_pair(TargetABI* a, ABIArgInfo* out) {
+ ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
+ memset(parts, 0, sizeof(ABIArgPart) * 2);
+ for (u32 i = 0; i < 2; ++i) {
+ parts[i].cls = ABI_CLASS_INT;
+ parts[i].loc = ABI_LOC_REG;
+ parts[i].size = 8;
+ parts[i].align = 8;
+ parts[i].src_offset = i * 8;
+ }
+ out->kind = ABI_ARG_DIRECT;
+ out->flags = ABI_AF_NONE;
+ out->parts = parts;
+ out->nparts = 2;
+ out->indirect_align = 0;
+}
+
/* ---- function classification (vtabled) ---- */
const ABIFuncInfo* abi_cg_func_info(TargetABI* a, KitCgTypeId fn_type) {
@@ -178,12 +207,13 @@ u32 abi_stack_probe_interval(TargetABI* a) {
ABITypeInfo abi_va_list_info(TargetABI* a) { return a->vt->va_list_info; }
ABIVaListInfo abi_va_list_layout(TargetABI* a) {
+ /* va_list_info is the single source of truth for the va_list ABITypeInfo;
+ * the layout's .type is always derived from it so the two cannot drift. */
ABIVaListInfo out = a->vt->va_list_layout;
- if (out.kind == ABI_VA_LIST_OPAQUE) {
- out.type = a->vt->va_list_info;
- if (out.type.scalar_kind == ABI_SC_PTR && out.type.size == 8u)
- out.kind = ABI_VA_LIST_POINTER;
- }
+ out.type = a->vt->va_list_info;
+ if (out.kind == ABI_VA_LIST_OPAQUE && out.type.scalar_kind == ABI_SC_PTR &&
+ out.type.size == 8u)
+ out.kind = ABI_VA_LIST_POINTER;
return out;
}
diff --git a/src/abi/abi_aapcs64.c b/src/abi/abi_aapcs64.c
@@ -19,22 +19,9 @@
#include "core/core.h"
static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) {
- ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
- memset(parts, 0, sizeof(ABIArgPart) * 2);
- for (u32 i = 0; i < 2; ++i) {
- parts[i].cls = ABI_CLASS_INT;
- parts[i].loc = ABI_LOC_REG;
- parts[i].size = 8;
- parts[i].align = 8;
- parts[i].src_offset = i * 8;
- }
- out->kind = ABI_ARG_DIRECT;
- out->flags = ABI_AF_NONE;
- out->parts = parts;
- out->nparts = 2;
- out->indirect_align = 0;
+ abi_classify_int128_pair(a, out);
return;
}
out->kind = ABI_ARG_DIRECT;
@@ -53,16 +40,11 @@ static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
out->nparts = 1;
}
-static void classify_void(ABIArgInfo* out) {
- memset(out, 0, sizeof *out);
- out->kind = ABI_ARG_IGNORE;
-}
-
static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.size == 0) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
/* AAPCS64: aggregates ≤ 16 bytes pass in up to 2 GPRs (or HFA in FP regs;
@@ -100,7 +82,7 @@ static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
const CgType* ty = cg_type_get(a->c, t);
if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
switch (ty->kind) {
@@ -148,8 +130,7 @@ ABIFuncInfo* aapcs64_compute_func_info(TargetABI* a, KitCgTypeId fn) {
const ABIVtable aapcs64_vtable = {
.compute_func_info = aapcs64_compute_func_info,
.va_list_info = {32, 8, ABI_SC_VOID, 0, 0, 0},
- .va_list_layout = {.type = {32, 8, ABI_SC_VOID, 0, 0, 0},
- .kind = ABI_VA_LIST_AAPCS64,
+ .va_list_layout = {.kind = ABI_VA_LIST_AAPCS64,
.stack_offset = 0,
.gr_top_offset = 8,
.vr_top_offset = 16,
diff --git a/src/abi/abi_aapcs64_windows.c b/src/abi/abi_aapcs64_windows.c
@@ -71,8 +71,7 @@ const ABIVtable aapcs64_windows_vtable = {
* in src/arch/aa64/native.c. */
.stack_probe_interval = 4096,
.va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .kind = ABI_VA_LIST_POINTER,
+ .va_list_layout = {.kind = ABI_VA_LIST_POINTER,
.gp_reg_count = 8,
.fp_reg_count = 0,
.gp_slot_size = 8,
diff --git a/src/abi/abi_apple_arm64.c b/src/abi/abi_apple_arm64.c
@@ -43,6 +43,5 @@ static ABIFuncInfo* apple_arm64_compute_func_info(TargetABI* a,
const ABIVtable apple_arm64_vtable = {
.compute_func_info = apple_arm64_compute_func_info,
.va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .kind = ABI_VA_LIST_POINTER},
+ .va_list_layout = {.kind = ABI_VA_LIST_POINTER},
};
diff --git a/src/abi/abi_apple_x64.c b/src/abi/abi_apple_x64.c
@@ -17,8 +17,7 @@ static ABIFuncInfo* apple_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) {
const ABIVtable apple_x64_vtable = {
.compute_func_info = apple_x64_compute_func_info,
.va_list_info = {24, 8, ABI_SC_VOID, 0, 0, 0},
- .va_list_layout = {.type = {24, 8, ABI_SC_VOID, 0, 0, 0},
- .kind = ABI_VA_LIST_SYSV_X64,
+ .va_list_layout = {.kind = ABI_VA_LIST_SYSV_X64,
.stack_offset = 8,
.gr_top_offset = 16,
.gr_offs_offset = 0,
diff --git a/src/abi/abi_internal.h b/src/abi/abi_internal.h
@@ -17,6 +17,9 @@ typedef struct ABIVtable {
* lowered by generic CG as multiple addressable machine-word lanes, or 0
* when the target ABI treats it as one scalar value. */
u32 (*scalar_split_lane_size)(TargetABI*, KitCgTypeId);
+ /* The single source of truth for the va_list ABITypeInfo. abi_va_list_layout
+ * always derives ABIVaListInfo.type from this, so vtables leave
+ * va_list_layout.type zero-initialized rather than restating it. */
ABITypeInfo va_list_info;
ABIVaListInfo va_list_layout;
/* Stack-probe granularity. 0 = the target OS auto-grows the stack on any
@@ -68,7 +71,10 @@ struct TargetABI {
RecordLayoutCacheEntry* rec_cache;
};
-/* Shared helpers exposed to per-ABI TUs. */
-ABITypeInfo abi_internal_type_info(TargetABI*, KitCgTypeId);
+/* Shared classifier primitives implemented in abi.c. The byte-identical
+ * building blocks the per-ABI classifiers (sysv_x64 / win64_x64 / aapcs64 /
+ * rv64) reuse; ABI-specific scalar/aggregate rules stay in those TUs. */
+void abi_classify_void(ABIArgInfo* out);
+void abi_classify_int128_pair(TargetABI* a, ABIArgInfo* out);
#endif
diff --git a/src/abi/abi_rv64.c b/src/abi/abi_rv64.c
@@ -112,7 +112,7 @@ static u32 riscv_collect_leaves(TargetABI* a, KitCgTypeId tid, u32 base_off,
}
if (t->kind == KIT_CG_TYPE_ARRAY) {
if (t->array.count == 0) return written; /* zero-length array: skip */
- ABITypeInfo elem = abi_internal_type_info(a, t->array.elem);
+ ABITypeInfo elem = abi_cg_type_info(a, t->array.elem);
if (elem.size == 0) return written;
for (u64 i = 0; i < t->array.count; ++i) {
u32 off = base_off + (u32)(i * elem.size);
@@ -122,7 +122,7 @@ static u32 riscv_collect_leaves(TargetABI* a, KitCgTypeId tid, u32 base_off,
return written;
}
/* Scalar leaf (including pointer). */
- ABITypeInfo ti = abi_internal_type_info(a, tid);
+ ABITypeInfo ti = abi_cg_type_info(a, tid);
if (ti.size == 0) return written;
if (written >= cap) return written + 1u;
out[written].offset = base_off;
@@ -133,7 +133,7 @@ static u32 riscv_collect_leaves(TargetABI* a, KitCgTypeId tid, u32 base_off,
static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
RiscvAbiDesc d = riscv_abi_desc(a);
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
/* A scalar twice the GPR width that lives in the integer/long-double space
* (or a soft-float double) is carried as an aligned pair of GPRs. On rv64
* this is the 16-byte long double / __int128 pair; on rv32 it is the 8-byte
@@ -180,7 +180,7 @@ static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
static u32 riscv32_scalar_split_lane_size(TargetABI* a, KitCgTypeId t) {
RiscvAbiDesc d = riscv_abi_desc(a);
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
int fp_part;
if (d.gpr_bytes != 4u) return 0;
fp_part = (ti.scalar_kind == ABI_SC_FLOAT) &&
@@ -191,11 +191,6 @@ static u32 riscv32_scalar_split_lane_size(TargetABI* a, KitCgTypeId t) {
return 0;
}
-static void classify_void(ABIArgInfo* out) {
- memset(out, 0, sizeof *out);
- out->kind = ABI_ARG_IGNORE;
-}
-
/* Try the psABI floating-point aggregate refinements. Returns 1 if `out`
* was populated, 0 to fall back to the generic GPR-pair packing. */
static int riscv_classify_fp_aggregate(TargetABI* a, KitCgTypeId t,
@@ -241,9 +236,9 @@ static int riscv_classify_fp_aggregate(TargetABI* a, KitCgTypeId t,
static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
RiscvAbiDesc d = riscv_abi_desc(a);
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.size == 0) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
if (ti.size <= d.aggregate_gpr_bytes) {
@@ -282,7 +277,7 @@ static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
const CgType* ty = cg_type_get(a->c, t);
if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
switch (ty->kind) {
@@ -332,8 +327,7 @@ const ABIVtable rv64_vtable = {
* varargs are passed in GPRs, so there is no separate FP save area. The
* gp_reg_count/gp_slot_size fields let native_frame_va_save_bytes size that
* area from the ABI rather than a backend constant. */
- .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .kind = ABI_VA_LIST_POINTER,
+ .va_list_layout = {.kind = ABI_VA_LIST_POINTER,
.gp_reg_count = 8,
.fp_reg_count = 0,
.gp_slot_size = 8,
@@ -347,8 +341,7 @@ const ABIVtable rv32_vtable = {
/* ILP32* va_list is a plain 4-byte pointer; the variadic register-save
* area is the 8 integer arg registers (a0..a7) spilled contiguously =
* 32 bytes. FP varargs are passed in GPRs, so there is no FP save area. */
- .va_list_layout = {.type = {4, 4, ABI_SC_PTR, 0, 0, 0},
- .kind = ABI_VA_LIST_POINTER,
+ .va_list_layout = {.kind = ABI_VA_LIST_POINTER,
.gp_reg_count = 8,
.fp_reg_count = 0,
.gp_slot_size = 4,
diff --git a/src/abi/abi_sysv_x64.c b/src/abi/abi_sysv_x64.c
@@ -11,31 +11,13 @@
#include "core/arena.h"
#include "core/core.h"
-static void classify_void(ABIArgInfo* out) {
- memset(out, 0, sizeof *out);
- out->kind = ABI_ARG_IGNORE;
-}
-
static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
/* __int128 / __uint128: SysV psABI classifies as two INTEGER eightbytes
* (rdi+rsi etc. for args; rax+rdx for return). */
if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) {
- ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
- memset(parts, 0, sizeof(ABIArgPart) * 2);
- for (u32 i = 0; i < 2; ++i) {
- parts[i].cls = ABI_CLASS_INT;
- parts[i].loc = ABI_LOC_REG;
- parts[i].size = 8;
- parts[i].align = 8;
- parts[i].src_offset = i * 8;
- }
- out->kind = ABI_ARG_DIRECT;
- out->flags = ABI_AF_NONE;
- out->parts = parts;
- out->nparts = 2;
- out->indirect_align = 0;
+ abi_classify_int128_pair(a, out);
return;
}
/* long double: 80-bit x87 (padded to 16B with 16B alignment). SysV class
@@ -107,7 +89,7 @@ static int classify_range(TargetABI* a, KitCgTypeId t, u32 base,
if (ty->kind == KIT_CG_TYPE_ENUM) {
return classify_range(a, ty->enum_.base, base, cls);
}
- ti = abi_internal_type_info(a, t);
+ ti = abi_cg_type_info(a, t);
switch (ty->kind) {
case KIT_CG_TYPE_BOOL:
case KIT_CG_TYPE_INT:
@@ -118,7 +100,7 @@ static int classify_range(TargetABI* a, KitCgTypeId t, u32 base,
return mark_eightbytes(cls, base, ti.size, SYSV_SSE);
return 0;
case KIT_CG_TYPE_ARRAY: {
- ABITypeInfo ei = abi_internal_type_info(a, ty->array.elem);
+ ABITypeInfo ei = abi_cg_type_info(a, ty->array.elem);
for (u64 i = 0; i < ty->array.count; ++i) {
if (i > UINT32_MAX || ei.size > UINT32_MAX ||
base > UINT32_MAX - (u32)(i * ei.size))
@@ -134,7 +116,7 @@ static int classify_range(TargetABI* a, KitCgTypeId t, u32 base,
for (u32 i = 0; i < ty->record.nfields; ++i) {
const CgTypeField* f = &ty->record.fields[i];
const ABIFieldLayout* fl = &L->fields[i];
- ABITypeInfo fi = abi_internal_type_info(a, f->type);
+ ABITypeInfo fi = abi_cg_type_info(a, f->type);
if ((f->flags & KIT_CG_FIELD_BITFIELD) != 0) {
if (fl->bit_width == 0) continue;
if (!mark_eightbytes(cls, base + fl->offset, fl->storage_size,
@@ -157,9 +139,9 @@ static int classify_range(TargetABI* a, KitCgTypeId t, u32 base,
static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.size == 0) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
if (ti.size <= 16) {
@@ -203,7 +185,7 @@ static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
const CgType* ty = cg_type_get(a->c, t);
if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
switch (ty->kind) {
@@ -299,8 +281,7 @@ static ABIFuncInfo* sysv_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) {
const ABIVtable sysv_x64_vtable = {
.compute_func_info = sysv_x64_compute_func_info,
.va_list_info = {24, 8, ABI_SC_VOID, 0, 0, 0},
- .va_list_layout = {.type = {24, 8, ABI_SC_VOID, 0, 0, 0},
- .kind = ABI_VA_LIST_SYSV_X64,
+ .va_list_layout = {.kind = ABI_VA_LIST_SYSV_X64,
.stack_offset = 8,
.gr_top_offset = 16,
.gr_offs_offset = 0,
diff --git a/src/abi/abi_win64_x64.c b/src/abi/abi_win64_x64.c
@@ -25,14 +25,9 @@
#include "core/arena.h"
#include "core/core.h"
-static void classify_void(ABIArgInfo* out) {
- memset(out, 0, sizeof *out);
- out->kind = ABI_ARG_IGNORE;
-}
-
static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
(void)is_return;
/* __int128 / __uint128: mingw/GCC convention emits two INTEGER
* eightbytes (rcx+rdx for args, rax+rdx for return) -- same shape
@@ -40,20 +35,7 @@ static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
* aggregates, but mingw is kit's interop target on Windows and
* mingw matches SysV here. */
if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) {
- ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
- memset(parts, 0, sizeof(ABIArgPart) * 2);
- for (u32 i = 0; i < 2; ++i) {
- parts[i].cls = ABI_CLASS_INT;
- parts[i].loc = ABI_LOC_REG;
- parts[i].size = 8;
- parts[i].align = 8;
- parts[i].src_offset = i * 8;
- }
- out->kind = ABI_ARG_DIRECT;
- out->flags = ABI_AF_NONE;
- out->parts = parts;
- out->nparts = 2;
- out->indirect_align = 0;
+ abi_classify_int128_pair(a, out);
return;
}
/* long double on Win64 is 64-bit double (both MSVC and mingw, unless
@@ -97,9 +79,9 @@ static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.size == 0) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
/* Win64: aggregates pass by value only when the size is exactly one
@@ -132,7 +114,7 @@ static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
const CgType* ty = cg_type_get(a->c, t);
if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
- classify_void(out);
+ abi_classify_void(out);
return;
}
switch (ty->kind) {
@@ -180,6 +162,5 @@ const ABIVtable win64_x64_vtable = {
* __chkstk call on this (frame_size > one page). See x64_build_prologue. */
.stack_probe_interval = 4096,
.va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0},
- .kind = ABI_VA_LIST_POINTER},
+ .va_list_layout = {.kind = ABI_VA_LIST_POINTER},
};
diff --git a/src/api/compile.c b/src/api/compile.c
@@ -495,7 +495,7 @@ KitStatus kit_compile_session_compile_cg(KitCompileSession* s,
unit_open = 0;
kit_frontend_commit(s->frontend);
} else if (unit_open) {
- (void)kit_cg_abort(cg);
+ (void)kit_cg_detach(cg);
}
return st;
}
@@ -571,21 +571,38 @@ static KitStatus asm_frontend_compile(KitFrontendState* frontend,
Compiler* c;
AsmLexer* lex;
MCEmitter* mc;
+ KitDiagSink* diag;
+ u32 errors0;
(void)opts;
if (!fe || !fe->c || !input || !out) return KIT_INVALID;
c = fe->c;
+ diag = c->ctx ? c->ctx->diag : NULL;
+ errors0 = diag ? diag->errors : 0;
metrics_scope_begin(c, "compile.asm.lex_open");
lex = asm_lex_open_mem(c, input->name.s, input->bytes.s, input->bytes.len);
metrics_scope_end(c, "compile.asm.lex_open");
metrics_scope_begin(c, "compile.asm.mc_new");
mc = mc_new(c, (ObjBuilder*)out);
metrics_scope_end(c, "compile.asm.mc_new");
+ if (!lex || !mc) {
+ /* Allocation failed before we could parse anything. Release whichever
+ * half we did get and report out-of-memory rather than a silent KIT_OK. */
+ mc_free(mc);
+ asm_lex_close(lex);
+ return KIT_NOMEM;
+ }
metrics_scope_begin(c, "compile.asm.parse");
asm_parse(c, lex, mc);
metrics_scope_end(c, "compile.asm.parse");
metrics_scope_begin(c, "compile.asm.mc_free");
mc_free(mc);
+ asm_lex_close(lex);
metrics_scope_end(c, "compile.asm.mc_free");
+ /* asm_parse reports hard errors via panic (longjmp to the compile-obj
+ * setjmp frame, which yields KIT_ERR), so reaching here means the parse
+ * completed; but propagate any soft diagnostics the sink recorded instead
+ * of unconditionally claiming success. */
+ if (diag && diag->errors > errors0) return KIT_ERR;
return KIT_OK;
}
diff --git a/src/api/config_stubs.c b/src/api/config_stubs.c
@@ -287,20 +287,6 @@ void debug_emit_row(Debug* d, ObjSecId text_section_id, u32 text_offset,
(void)loc;
}
-u32 debug_loclist_new(Debug* d) {
- (void)d;
- return 0;
-}
-
-void debug_loclist_add(Debug* d, u32 id, u32 begin_pc, u32 end_pc,
- DebugVarLoc loc) {
- (void)d;
- (void)id;
- (void)begin_pc;
- (void)end_pc;
- (void)loc;
-}
-
void debug_emit(Debug* d) { (void)d; }
KitStatus kit_dwarf_open(const KitContext* ctx, const KitObjFile* obj,
diff --git a/src/api/link.c b/src/api/link.c
@@ -22,6 +22,7 @@
#include "cg/internal.h"
#include "cg/ir_recorder.h"
#include "core/core.h"
+#include "core/diag.h"
#include "link/link_internal.h"
KitJit* kit_jit_from_image(LinkImage*);
@@ -66,6 +67,19 @@ static KitStatus link_session_remember_publish_obj(KitLinkSession* s,
return KIT_OK;
}
+/* These KitLinkSessionOptions fields have no plumbing into the Linker yet:
+ * the shared-library DT_* knobs (soname/rpaths/runpaths/exports/
+ * allow_undefined) and the build-id selector (build_id_mode/bytes/len; the
+ * ELF writer currently emits a fixed image-hash note regardless of mode).
+ * Until they are wired through, warn rather than silently honor the default
+ * instead of the caller's request. */
+static void link_warn_ignored_opt(Compiler* c, const char* name) {
+ DiagSink* diag = (c && c->ctx) ? c->ctx->diag : NULL;
+ if (!diag) return;
+ diag_emit(diag, DIAG_WARN, SRCLOC_NONE,
+ "link: option '%s' is not yet supported and is ignored", name);
+}
+
KitStatus kit_link_session_new(KitCompiler* c,
const KitLinkSessionOptions* opts,
KitLinkSession** out) {
@@ -113,14 +127,15 @@ KitStatus kit_link_session_new(KitCompiler* c,
link_set_gc_sections(l, opts->gc_sections);
link_set_strip_debug(l, opts->strip_debug);
link_set_pie(l, 1);
- (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;
+ if (opts->soname.s && opts->soname.len)
+ link_warn_ignored_opt(s->c, "-soname");
+ if (opts->rpaths && opts->nrpaths) link_warn_ignored_opt(s->c, "-rpath");
+ if (opts->runpaths && opts->nrunpaths)
+ link_warn_ignored_opt(s->c, "-rpath (DT_RUNPATH)");
+ if (opts->exports && opts->nexports)
+ link_warn_ignored_opt(s->c, "--export-symbol");
+ if (opts->allow_undefined)
+ link_warn_ignored_opt(s->c, "--allow-shlib-undefined");
break;
case KIT_LINK_OUTPUT_RELOCATABLE:
break;
@@ -149,6 +164,13 @@ KitStatus kit_link_session_new(KitCompiler* c,
opts->linker_script->entry.len)) {
link_set_entry(l, KIT_SLICE_LIT("WinMainCRTStartup"));
}
+ /* TODO(build-id): the ELF writer (src/obj/elf/link.c) emits a fixed
+ * image-hash build-id note unconditionally for non-scripted layouts; the
+ * caller's mode selection is not threaded through, so a note is emitted
+ * even for KIT_BUILDID_NONE and uuid/user-bytes modes get the default hash.
+ * Honoring the mode means carrying it into LinkImage + the ELF/Mach-O
+ * writers (and is a no-op for the common default), so it is left as a known
+ * gap rather than warned about on the default path. */
(void)opts->build_id_mode;
(void)opts->build_id_bytes;
(void)opts->build_id_len;
diff --git a/src/api/object_file.c b/src/api/object_file.c
@@ -267,7 +267,7 @@ KitStatus kit_obj_symbol_by_name(const KitObjFile* f, KitSlice name,
}
struct KitObjSymIter {
- KitObjFile* file;
+ const KitObjFile* file;
ObjSymIter* inner; /* .symtab walk; NULL when iterating the dynamic table */
u32 dyn_idx; /* next index into obj_image dynsyms (dynamic mode) */
int dynamic;
@@ -276,7 +276,8 @@ struct KitObjSymIter {
/* Shared by kit_obj_symiter_new (.symtab) and kit_obj_dynsymiter_new
* (.dynsym). When dynamic, the inner ObjSymIter is unused and we walk the
* image's dynamic symbol table by index. */
-static KitStatus symiter_make(KitObjFile* f, int dynamic, KitObjSymIter** out) {
+static KitStatus symiter_make(const KitObjFile* f, int dynamic,
+ KitObjSymIter** out) {
Heap* h;
KitObjSymIter* it;
if (!f || !out) return KIT_INVALID;
@@ -298,11 +299,11 @@ static KitStatus symiter_make(KitObjFile* f, int dynamic, KitObjSymIter** out) {
return KIT_OK;
}
-KitStatus kit_obj_symiter_new(KitObjFile* f, KitObjSymIter** out) {
+KitStatus kit_obj_symiter_new(const KitObjFile* f, KitObjSymIter** out) {
return symiter_make(f, 0, out);
}
-KitStatus kit_obj_dynsymiter_new(KitObjFile* f, KitObjSymIter** out) {
+KitStatus kit_obj_dynsymiter_new(const KitObjFile* f, KitObjSymIter** out) {
return symiter_make(f, 1, out);
}
diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c
@@ -129,8 +129,6 @@ enum {
* physically the same address, expressed via aa_fp_off_tail_out_arg.
* ========================================================================== */
-static u32 align_up_u32(u32 v, u32 align);
-
typedef struct AAFrameLayout {
u32 slot_bytes; /* sum of aa_frame_slot reservations (callee-saves + locals
* + spills + sret/variadic) */
@@ -306,7 +304,7 @@ static inline i32 aa_fp_off_home_slot(u32 i) {
return (i32)(AA_FRAME_SAVE_SIZE + i * 8u);
}
-static void aa_panic(AANativeTarget* a, const char* msg) {
+static _Noreturn void aa_panic(AANativeTarget* a, const char* msg) {
compiler_panic(a->base.c, a->loc, "aarch64 native target: %s", msg);
}
@@ -332,11 +330,6 @@ static void aa_patch32(ObjBuilder* obj, ObjSecId sec, u32 off, u32 word) {
obj_patch(obj, sec, off, b, sizeof b);
}
-static u32 align_up_u32(u32 v, u32 align) {
- u32 mask = align ? align - 1u : 0u;
- return (v + mask) & ~mask;
-}
-
static u32 type_size32(NativeTarget* t, KitCgTypeId type) {
u64 n = type ? cg_type_size(t->c, type) : 8u;
if (n == 0) n = 8u;
@@ -507,24 +500,24 @@ static __attribute__((unused)) u32 aa_mrs_tpidr_el0(u32 rt) {
return 0xd53bd040u | (rt & 0x1fu);
}
+/* The scalar-FP / bit-packing encoders below delegate to the single-source
+ * isa.h encoders so encode/decode stay in lockstep. ftype 0=single, 1=double;
+ * the historical aa_* signatures (and call sites) are preserved as thin
+ * wrappers. */
static u32 aa_fp_bin(u32 op, u32 is_double, u32 rd, u32 rn, u32 rm) {
- return (is_double ? 0x1e600000u : 0x1e200000u) | op | ((rm & 0x1fu) << 16) |
- ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_fp_dp2(is_double, op, rd, rn, rm);
}
static u32 aa_fcmp(u32 is_double, u32 rn, u32 rm) {
- return (is_double ? 0x1e602000u : 0x1e202000u) | ((rm & 0x1fu) << 16) |
- ((rn & 0x1fu) << 5);
+ return aa64_fcmp_reg(is_double, rn, rm);
}
static u32 aa_fneg(u32 is_double, u32 rd, u32 rn) {
- return (is_double ? 0x1e614000u : 0x1e214000u) | ((rn & 0x1fu) << 5) |
- (rd & 0x1fu);
+ return aa64_fp_dp1(is_double, AA64_FP_DP1_FNEG, rd, rn);
}
static u32 aa_fmov_fp(u32 is_double, u32 rd, u32 rn) {
- return (is_double ? 0x1e604000u : 0x1e204000u) | ((rn & 0x1fu) << 5) |
- (rd & 0x1fu);
+ return aa64_fp_dp1(is_double, AA64_FP_DP1_FMOV, rd, rn);
}
/* MOV Vd.16B, Vn.16B (alias of ORR Vd.16B, Vn.16B, Vn.16B): a full 128-bit
@@ -535,65 +528,65 @@ static u32 aa_mov_vec16(u32 rd, u32 rn) {
(rd & 0x1fu);
}
+/* The FP<->int conversion family: sf selects the GPR width (1=64-bit), ftype
+ * the FP width (1=double). Their roles flip between the convert-from-int
+ * (s/ucvtf: sf=src GPR, ftype=dst FP) and convert-to-int (fcvtz*: sf=dst GPR,
+ * ftype=src FP) directions, but the encoding is the same shape. */
static u32 aa_scvtf(u32 is_double_dst, u32 is64_src, u32 fd, u32 rn) {
- return (is64_src ? 0x9e220000u : 0x1e220000u) |
- (is_double_dst ? 0x00400000u : 0) | ((rn & 0x1fu) << 5) | (fd & 0x1fu);
+ return aa64_fp_int_cvt(is64_src, is_double_dst, AA64_FP_ICVT_SCVTF, fd, rn);
}
static u32 aa_ucvtf(u32 is_double_dst, u32 is64_src, u32 fd, u32 rn) {
- return (is64_src ? 0x9e230000u : 0x1e230000u) |
- (is_double_dst ? 0x00400000u : 0) | ((rn & 0x1fu) << 5) | (fd & 0x1fu);
+ return aa64_fp_int_cvt(is64_src, is_double_dst, AA64_FP_ICVT_UCVTF, fd, rn);
}
static u32 aa_fcvtzs(u32 is64_dst, u32 is_double_src, u32 rd, u32 fn) {
- return (is64_dst ? 0x9e380000u : 0x1e380000u) |
- (is_double_src ? 0x00400000u : 0) | ((fn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_fp_int_cvt(is64_dst, is_double_src, AA64_FP_ICVT_FCVTZS, rd, fn);
}
static u32 aa_fcvtzu(u32 is64_dst, u32 is_double_src, u32 rd, u32 fn) {
- return (is64_dst ? 0x9e390000u : 0x1e390000u) |
- (is_double_src ? 0x00400000u : 0) | ((fn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_fp_int_cvt(is64_dst, is_double_src, AA64_FP_ICVT_FCVTZU, rd, fn);
}
static u32 aa_fcvt_d_s(u32 rd, u32 rn) {
- return 0x1e22c000u | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_fcvt_prec(/*src=single*/ 0, /*dst=double*/ 1, rd, rn);
}
static u32 aa_fcvt_s_d(u32 rd, u32 rn) {
- return 0x1e624000u | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_fcvt_prec(/*src=double*/ 1, /*dst=single*/ 0, rd, rn);
}
+/* fmov between GPR and FP reg: the FP ftype tracks the operand width (64-bit
+ * GPR <-> double, 32-bit GPR <-> single), so sf and ftype move together. */
static u32 aa_fmov_gpr_to_fp(u32 is64, u32 fd, u32 rn) {
- return (is64 ? 0x9e670000u : 0x1e270000u) | ((rn & 0x1fu) << 5) |
- (fd & 0x1fu);
+ return aa64_fp_int_cvt(is64, is64, AA64_FP_ICVT_FMOV_TO_FP, fd, rn);
}
static u32 aa_fmov_fp_to_gpr(u32 is64, u32 rd, u32 fn) {
- return (is64 ? 0x9e660000u : 0x1e260000u) | ((fn & 0x1fu) << 5) |
- (rd & 0x1fu);
+ return aa64_fp_int_cvt(is64, is64, AA64_FP_ICVT_FMOV_TO_GPR, rd, fn);
}
static u32 aa_clz(u32 sf, u32 rd, u32 rn) {
- return (sf ? 0xdac01000u : 0x5ac01000u) | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_dp1(sf, AA64_DP1_CLZ, rd, rn);
}
static u32 aa_rbit(u32 sf, u32 rd, u32 rn) {
- return (sf ? 0xdac00000u : 0x5ac00000u) | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_dp1(sf, AA64_DP1_RBIT, rd, rn);
}
+/* REV reverses all bytes of the operand, so the 32-bit form is REV(32) and the
+ * 64-bit form is REV(64) — the opcode2 follows sf rather than being constant. */
static u32 aa_rev(u32 sf, u32 rd, u32 rn) {
- return (sf ? 0xdac00c00u : 0x5ac00800u) | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_dp1(sf, sf ? AA64_DP1_REV64 : AA64_DP1_REV32, rd, rn);
}
static u32 aa_sbfm(u32 sf, u32 rd, u32 rn, u32 immr, u32 imms) {
- return (sf ? 0x93400000u : 0x13000000u) | ((immr & 0x3fu) << 16) |
- ((imms & 0x3fu) << 10) | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_bitfield(sf, /*SBFM*/ 0u, immr, imms, rd, rn);
}
static __attribute__((unused)) u32 aa_ubfm(u32 sf, u32 rd, u32 rn, u32 immr,
u32 imms) {
- return (sf ? 0xd3400000u : 0x53000000u) | ((immr & 0x3fu) << 16) |
- ((imms & 0x3fu) << 10) | ((rn & 0x1fu) << 5) | (rd & 0x1fu);
+ return aa64_bitfield(sf, /*UBFM*/ 2u, immr, imms, rd, rn);
}
static __attribute__((unused)) u32 aa_ldaxr(u32 size, u32 rt, u32 rn) {
diff --git a/src/arch/arch.h b/src/arch/arch.h
@@ -102,7 +102,7 @@ typedef struct ArchDecodeOps {
ArchInsnFormatter* (*formatter_new)(Compiler*);
KitStatus (*format)(ArchInsnFormatter*, const KitDecodedInsn*, KitInsn* out);
- void (*formatter_free)(ArchInsnFormatter*);
+ void (*formatter_destroy)(ArchInsnFormatter*);
} ArchDecodeOps;
typedef struct ArchEmuOps {
diff --git a/src/arch/native_target.h b/src/arch/native_target.h
@@ -614,6 +614,13 @@ static inline int native_loc_is_fp(NativeLoc loc) {
/* Scalar size/align, clamped to a usable register-sized default. Shared by the
* backends whose scalars are at most pointer-width (x64, rv64); aa64 keeps its
* own size query because it asserts on over-wide scalars. */
+/* Round v up to the next multiple of align (a power of two, or 0 → no-op).
+ * Shared by every native backend's frame-layout math. */
+static inline u32 align_up_u32(u32 v, u32 align) {
+ u32 mask = align ? align - 1u : 0u;
+ return (v + mask) & ~mask;
+}
+
static inline u32 native_type_size(NativeTarget* t, KitCgTypeId type) {
u64 n = type ? cg_type_size(t->c, type) : 8u;
if (n == 0) n = 8u;
diff --git a/src/arch/riscv/disasm.c b/src/arch/riscv/disasm.c
@@ -470,5 +470,5 @@ const ArchDecodeOps rv64_decode_ops = {
.decode_block = rv64_decode_block,
.formatter_new = rv64_formatter_new,
.format = rv64_format_insn,
- .formatter_free = rv64_formatter_destroy,
+ .formatter_destroy = rv64_formatter_destroy,
};
diff --git a/src/arch/riscv/native.c b/src/arch/riscv/native.c
@@ -52,7 +52,6 @@ enum {
* fp, each up to 4 words for a far s0-relative offset) on top of the header,
* sret, and variadic spills. Size the build buffer for the worst case. */
RV_KNOWN_PROLOGUE_WORDS = 192u,
- RV_FRAME_SAVE_SIZE = 16u,
};
/* s1..s11 (11) + fs0..fs11 (12); separate int/fp collect arrays use this cap.
@@ -94,11 +93,6 @@ static int fits_i32(i64 v) {
return v >= (i64)(i32)0x80000000 && v <= (i64)(i32)0x7fffffff;
}
-static u32 align_up_u32(u32 v, u32 align) {
- u32 mask = align ? align - 1u : 0u;
- return (v + mask) & ~mask;
-}
-
static i64 floor_div_4096(i64 v) {
if (v >= 0) return v / 4096;
return -((-v + 4095) / 4096);
@@ -610,7 +604,12 @@ static int rv_imm_legal(NativeTarget* t, NativeImmUse use, u32 op,
return 0;
}
case NATIVE_IMM_CMP:
- return imm == 0; /* compares need both ends in registers (SLT/branch) */
+ /* Only the compare-against-zero case is folded; a non-zero RHS is
+ * materialized into a register first. This is a deliberate codegen
+ * simplification, not an architectural constraint — SLTI/SLTIU do
+ * accept a 12-bit signed immediate, but the comparison lowering does
+ * not special-case immediate operands beyond zero. */
+ return imm == 0;
case NATIVE_IMM_ADDR_OFFSET:
return fits_i12(imm);
}
diff --git a/src/arch/wasm/abi.c b/src/arch/wasm/abi.c
@@ -28,7 +28,7 @@ static void classify_void(ABIArgInfo* out) {
}
static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
ABIArgPart* parts;
if (ti.size > 8) {
@@ -76,7 +76,7 @@ static int try_classify_singleton(TargetABI* a, KitCgTypeId t,
if (!field) return 0;
if (field->kind == KIT_CG_TYPE_RECORD || field->kind == KIT_CG_TYPE_ARRAY)
return 0;
- ti = abi_internal_type_info(a, field_ty);
+ ti = abi_cg_type_info(a, field_ty);
if (ti.size != (u32)ty->size) return 0;
classify_scalar(a, field_ty, out);
return 1;
@@ -84,7 +84,7 @@ static int try_classify_singleton(TargetABI* a, KitCgTypeId t,
static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
int is_return) {
- ABITypeInfo ti = abi_internal_type_info(a, t);
+ ABITypeInfo ti = abi_cg_type_info(a, t);
if (ti.size == 0) {
classify_void(out);
return;
diff --git a/src/arch/wasm/emit.c b/src/arch/wasm/emit.c
@@ -203,6 +203,7 @@ static u32 reg_local(WTarget* t, Reg r, KitCgTypeId ty, RegClass cls) {
t->reg_to_local[r] = add_wasm_local(t, vt);
t->reg_type[r] = ty;
t->reg_cls[r] = (u8)cls;
+ if (r + 1u > t->reg_hwm) t->reg_hwm = r + 1u;
}
return t->reg_to_local[r];
}
@@ -603,8 +604,11 @@ void wasm_func_begin(CGTarget* tg, const CGFuncDesc* d) {
t->va_arg_tmp_addr_local = 0xffffffffu;
t->nparams_cg = 0;
t->nbyval_copies = 0;
- /* Wipe reg map. */
- for (u32 i = 0; i < t->reg_cap; ++i) t->reg_to_local[i] = 0xffffffffu;
+ /* Wipe reg map. Only [0, reg_hwm) can hold a non-sentinel binding from a
+ * prior function — reg_cap grows monotonically but slots beyond the
+ * high-water mark are sentinel-initialized at grow time and never bound, so
+ * wiping the full reg_cap each function is wasted work on large TUs. */
+ for (u32 i = 0; i < t->reg_hwm; ++i) t->reg_to_local[i] = 0xffffffffu;
idx = sym_to_wasm_func(t, d->sym, &f);
t->cur_func_idx = idx;
@@ -1077,21 +1081,6 @@ static void promote_import_func(WTarget* t, ObjSymId sym, WasmFunc* f,
}
}
-const char* wasm_tail_call_unrealizable_reason(CGTarget* tg,
- const CGCallDesc* d) {
- (void)tg;
- /* Variadic tail calls are not realizable on wasm: varargs are packed into a
- * buffer carved from this function's linear-memory frame, which return_call
- * tears down before the callee reads it. sret is realizable — the tail
- * forwards the function's own incoming sret pointer (see wasm_call). wasm
- * function parameters are wasm locals, so there is no caller stack-arg area
- * to overflow. */
- if (d->abi && d->abi->variadic)
- return "wasm cannot tail-call a variadic function (its vararg buffer "
- "lives in the frame a sibling call tears down)";
- return NULL;
-}
-
void wasm_call(CGTarget* tg, const CGCallDesc* d) {
WTarget* t = (WTarget*)tg;
if (t->dead) return;
@@ -1104,9 +1093,9 @@ void wasm_call(CGTarget* tg, const CGCallDesc* d) {
int callee_variadic = (d->abi && d->abi->variadic) ? 1 : 0;
int is_tail = (d->flags & CG_CALL_TAIL) ? 1 : 0;
if (is_tail) {
- /* Realizability is decided by CG via wasm_tail_call_unrealizable_reason
- * before CG_CALL_TAIL is set: variadic tails are rejected there, and sret
- * tails forward the incoming sret pointer (handled in the WIR emit). */
+ /* Realizability is decided by CG via wasm_ir_tail_call_unrealizable_reason
+ * (target.c) before CG_CALL_TAIL is set: variadic tails are rejected there,
+ * and sret tails forward the incoming sret pointer (handled in WIR emit). */
ensure_module(t);
t->module->features |= WASM_FEATURE_TAIL_CALLS;
}
diff --git a/src/arch/wasm/internal.h b/src/arch/wasm/internal.h
@@ -292,11 +292,14 @@ typedef struct WTarget {
struct WasmFunc* cur_func;
/* SSA Reg -> Wasm local index (0..nparams=params, then locals). 0xffffffffu
- * means "not assigned yet". */
+ * means "not assigned yet". reg_cap grows monotonically across the TU;
+ * reg_hwm tracks one past the highest Reg actually bound in any function so
+ * far, so func_begin only needs to wipe [0, reg_hwm) rather than reg_cap. */
u32* reg_to_local;
KitCgTypeId* reg_type;
u8* reg_cls;
u32 reg_cap;
+ u32 reg_hwm;
/* WIR record list for the current function. */
WIR* wir;
diff --git a/src/arch/x64/emit.h b/src/arch/x64/emit.h
@@ -68,10 +68,6 @@ static inline u32 type_byte_size(KitCgTypeId t) {
if (t == CG_BUILTIN_ID(KIT_CG_BUILTIN_F128)) return 16;
return 8;
}
-static inline int type_is_signed(KitCgTypeId t) {
- (void)t;
- return 0;
-}
static inline void x64_abi_direct_reg_need(const ABIArgInfo* ai, u32* need_int,
u32* need_fp) {
diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c
@@ -142,11 +142,6 @@ static X64NativeSlot* x64_slot_get(X64NativeTarget* a, NativeFrameSlot fs) {
return native_frame_slot_at(&a->frame, fs);
}
-static u32 align_up_u32(u32 v, u32 align) {
- u32 mask = align ? align - 1u : 0u;
- return (v + mask) & ~mask;
-}
-
/* ============================ type helpers ============================ */
/* Scalar size/align/mem/class/loc constructors are shared in native_target.h
diff --git a/src/asm/asm.c b/src/asm/asm.c
@@ -504,9 +504,16 @@ int asm_driver_eat_punct(AsmDriver* d, u32 p) {
}
void asm_driver_expect_punct(AsmDriver* d, u32 p, const char* what) {
- if (!asm_driver_eat_punct(d, p))
- d_panicf(d, "asm: expected '%.*s' (%.*s)", SLICE_ARG(SLICE_LIT("punct")),
+ if (!asm_driver_eat_punct(d, p)) {
+ /* Single-char punctuators (incl. '#') carry their ASCII value in `p`;
+ * print it directly. Multi-char puncts fall back to the description. */
+ char ch = (char)p;
+ if (p >= 0x20 && p < 0x7f)
+ d_panicf(d, "asm: expected '%c' (%.*s)", ch,
+ SLICE_ARG(slice_from_cstr(what)));
+ d_panicf(d, "asm: expected punctuator (%.*s)",
SLICE_ARG(slice_from_cstr(what)));
+ }
}
i64 asm_driver_parse_const(AsmDriver* d) {
@@ -530,7 +537,11 @@ int asm_driver_tok_is_punct(AsmTok t, u32 p) {
/* ---- string-literal decoding ---- */
-static void decode_string(AsmDriver* d, Sym spelling, u8** out, u32* nout) {
+/* Decode a string literal's spelling into raw bytes. *out is the heap buffer
+ * (allocated with size *cap_out, which the caller must pass back to free —
+ * the decoded length *nout <= *cap_out once escapes collapse). */
+static void decode_string(AsmDriver* d, Sym spelling, u8** out, u32* nout,
+ size_t* cap_out) {
size_t n = 0;
const char* p = asm_str(d, spelling, &n);
/* Skip any encoding prefix (L/u/u8/U). */
@@ -540,8 +551,8 @@ static void decode_string(AsmDriver* d, Sym spelling, u8** out, u32* nout) {
}
if (n < 2 || p[0] != '"' || p[n - 1] != '"')
d_panicf(d, "asm: malformed string literal");
- size_t cap = n;
- u8* buf = (u8*)d->heap->alloc(d->heap, cap ? cap : 1, 1);
+ size_t cap = n ? n : 1;
+ u8* buf = (u8*)d->heap->alloc(d->heap, cap, 1);
u32 k = 0;
for (size_t i = 1; i + 1 < n; ++i) {
char c = p[i];
@@ -627,6 +638,7 @@ static void decode_string(AsmDriver* d, Sym spelling, u8** out, u32* nout) {
}
*out = buf;
*nout = k;
+ *cap_out = cap;
}
/* ---- directives ---- */
@@ -1100,11 +1112,12 @@ static void do_directive(AsmDriver* d, Sym name) {
(void)d_next(d);
u8* buf = NULL;
u32 n = 0;
- decode_string(d, t.spelling, &buf, &n);
+ size_t cap = 0;
+ decode_string(d, t.spelling, &buf, &n, &cap);
(void)asm_driver_cur_section(d);
d->mc->emit_bytes(d->mc, buf, n);
if (term) emit_le(d, 0, 1);
- d->heap->free(d->heap, buf, n);
+ d->heap->free(d->heap, buf, cap);
if (!asm_driver_eat_comma(d)) break;
}
d_skip_to_eol(d);
@@ -1289,7 +1302,7 @@ static void relax_local_branches(AsmDriver* d) {
for (i = 0; i < total; ++i) {
const Reloc* r = obj_reloc_at(d->ob, i);
const ObjSym* tgt;
- Section* sec;
+ const Section* sec;
u8 insn[4];
if (!r || r->removed) continue;
if (!is_relaxable_branch_kind(r->kind)) continue;
@@ -1298,14 +1311,21 @@ static void relax_local_branches(AsmDriver* d) {
if (tgt->section_id != r->section_id) continue; /* cross-section / undef */
if (tgt->bind != SB_LOCAL) continue; /* preemptible; keep */
if (tgt->kind == SK_FUNC) continue; /* call/tail-call; keep */
- sec = (Section*)obj_section_get(d->ob, r->section_id);
+ /* Read-only access to the in-progress section is fine via the const
+ * accessor; the byte write goes back through obj_patch so we don't reach
+ * into Section internals. */
+ sec = obj_section_get(d->ob, r->section_id);
if (!sec) continue;
if ((u64)r->offset + 4 > sec->bytes.total) continue;
buf_read(&sec->bytes, r->offset, insn, 4);
/* Section-relative S and P make the base cancel: disp = S + A - P. */
link_reloc_apply(d->c, (RelocKind)r->kind, insn, tgt->value, r->addend,
r->offset);
- buf_patch(&sec->bytes, r->offset, insn, 4);
+ obj_patch(d->ob, r->section_id, r->offset, insn, 4);
+ /* No public per-reloc tombstone setter exists on ObjBuilder; the const
+ * cast mutates the still-private in-progress object we own outright (we
+ * built every reloc in this builder). Tolerated until obj exposes an
+ * obj_reloc_remove() mutator alongside obj_section/symbol_remove. */
((Reloc*)r)->removed = 1;
}
}
diff --git a/src/asm/asm_lex.c b/src/asm/asm_lex.c
@@ -149,11 +149,6 @@ void asm_lex_close(AsmLexer* l) {
SrcLoc asm_lex_loc(const AsmLexer* l) { return asm_lex_here(l); }
u32 asm_lex_file_id(const AsmLexer* l) { return l->file_id; }
-const AsmLitInfo* asm_lex_lit(const AsmLexer* l, AsmLitId id) {
- (void)l;
- (void)id;
- return NULL;
-}
/* Intern bytes [start, end) with line splices (\<newline>) removed, so token
* spellings reflect post-phase-2 logical text. */
diff --git a/src/asm/asm_lex.h b/src/asm/asm_lex.h
@@ -58,38 +58,11 @@ typedef enum AsmPunct {
ASM_P_HASH_HASH,
} AsmPunct;
-typedef u32 AsmLitId;
-#define ASM_LIT_NONE 0u
-
-typedef enum AsmLitKind {
- ASM_LIT_INT,
- ASM_LIT_FLOAT,
- ASM_LIT_STRING,
- ASM_LIT_CHAR,
-} AsmLitKind;
-
-typedef enum AsmLitEnc {
- ASM_LENC_ORDINARY,
- ASM_LENC_UTF8,
- ASM_LENC_WIDE,
- ASM_LENC_UTF16,
- ASM_LENC_UTF32,
-} AsmLitEnc;
-
-typedef struct AsmLitInfo {
- u8 kind;
- u8 enc;
- u16 flags;
- Sym spelling;
- BytesId bytes;
-} AsmLitInfo;
-
typedef struct AsmTok {
u16 kind;
u16 flags;
SrcLoc loc;
Sym spelling;
- AsmLitId lit;
union {
Sym ident;
Sym str;
@@ -106,6 +79,5 @@ void asm_lex_close(AsmLexer*);
AsmTok asm_lex_next(AsmLexer*);
SrcLoc asm_lex_loc(const AsmLexer*);
u32 asm_lex_file_id(const AsmLexer*);
-const AsmLitInfo* asm_lex_lit(const AsmLexer*, AsmLitId);
#endif
diff --git a/src/cg/arith.c b/src/cg/arith.c
@@ -268,8 +268,6 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
agg.size = 16;
agg.align = 16;
g->target->copy_bytes(g->target, dst_addr, src_addr, agg);
- api_release_temp_local(g, dst_addr.v.local);
- api_release_temp_local(g, src_addr.v.local);
} else if (v.op.kind == OPK_LOCAL) {
g->target->store(g->target, dst_lv, v.op,
api_mem_for_lvalue(g, &dst_lv, sty));
@@ -1046,7 +1044,6 @@ int api_try_i128_convert(KitCg* g, ConvKind ck, KitCgTypeId sty,
if (dw == 0) return 0; /* i128->float unsupported here */
addr = api_i128_addr(g, v);
lo = api_i128_load_lane(g, addr, lo_off);
- api_release_temp_local(g, addr.v.local);
api_release(g, v);
if (dw >= 64) {
api_push(g, api_make_sv(lo, dty));
diff --git a/src/cg/call.c b/src/cg/call.c
@@ -86,12 +86,6 @@ CGLocal api_alloc_call_result(KitCg* g, KitCgTypeId ret_ty) {
return api_alloc_temp_local(g, ret_ty);
}
-void api_release_call_args(KitCg* g, CGLocal* args, u32 nargs) {
- for (u32 i = 0; i < nargs; ++i) {
- if (args[i] != CG_LOCAL_NONE) api_release_temp_local(g, args[i]);
- }
-}
-
void api_push_call_result(KitCg* g, CGLocal result, KitCgTypeId ret_ty) {
Operand op = api_op_local(result, ret_ty);
/* An aggregate result is a PLACE (it is addressed/copied, never a scalar
@@ -136,17 +130,11 @@ static int api_tail_decide(KitCg* g, const CGCallDesc* desc,
return 0;
}
-static void api_finish_call(KitCg* g, CGCallDesc* desc, CGLocal* args,
- u32 nargs, Operand callee_op, ApiSValue* callee,
- int want_tail, int emit_tail) {
- if (emit_tail) api_temp_locals_finish(g);
+static void api_finish_call(KitCg* g, CGCallDesc* desc, int want_tail,
+ int emit_tail) {
if (!emit_tail) api_call_clobber_boundary(g, desc);
g->target->call(g->target, desc);
- api_release_call_args(g, args, nargs);
- if (callee && callee->op.kind != OPK_GLOBAL) {
- api_release_temp_local(g, callee_op.v.local);
- }
/* Push the single result (if any) onto the stack. */
if (desc->result != CG_LOCAL_NONE) {
KitCgTypeId rty = cg_type_func_result_id(g->c, desc->fn_type);
@@ -215,8 +203,7 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
desc.result = api_alloc_call_result(g, result_type);
(void)T;
- api_finish_call(g, &desc, args, nargs, callee_op, &callee, want_tail,
- emit_tail);
+ api_finish_call(g, &desc, want_tail, emit_tail);
}
void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
@@ -264,7 +251,7 @@ void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
result_type = emit_tail ? KIT_CG_TYPE_NONE : cg_type_func_result_id(g->c, fty);
if (result_type != KIT_CG_TYPE_NONE)
desc.result = api_alloc_call_result(g, result_type);
- api_finish_call(g, &desc, args, nargs, callee_op, NULL, want_tail, emit_tail);
+ api_finish_call(g, &desc, want_tail, emit_tail);
}
void kit_cg_call_symbol(KitCg* g, KitCgSym sym, uint32_t nargs,
diff --git a/src/cg/control.c b/src/cg/control.c
@@ -1031,7 +1031,6 @@ void kit_cg_field(KitCg* g, uint32_t field_index) {
result = api_op_local(fr, rec_ptr_ty);
T->binop(T, BO_IADD, result, base_addr,
api_op_imm((i64)field_offset, rec_ptr_ty));
- api_release_temp_local(g, base_addr.v.local);
}
api_push(
g, api_make_lv(api_op_indirect(result.v.local, 0, field_ty), field_ty));
diff --git a/src/cg/fold.c b/src/cg/fold.c
@@ -165,14 +165,7 @@ CmpOp api_invert_cmp(CmpOp op) {
}
void api_release_cmp(KitCg* g, ApiSValue* sv) {
- if (sv->delayed.cmp.a_owned) api_release_operand_local(g, sv->delayed.cmp.a);
- if (sv->delayed.cmp.b_owned &&
- (sv->delayed.cmp.b.kind != OPK_LOCAL ||
- sv->delayed.cmp.a.kind != OPK_LOCAL ||
- sv->delayed.cmp.b.v.local != sv->delayed.cmp.a.v.local ||
- !sv->delayed.cmp.a_owned)) {
- api_release_operand_local(g, sv->delayed.cmp.b);
- }
+ (void)g;
memset(&sv->delayed.cmp.a, 0, sizeof sv->delayed.cmp.a);
memset(&sv->delayed.cmp.b, 0, sizeof sv->delayed.cmp.b);
sv->delayed.cmp.a_owned = 0;
@@ -183,14 +176,6 @@ void api_release_cmp(KitCg* g, ApiSValue* sv) {
void api_materialize_cmp_to(KitCg* g, ApiSValue* sv, Operand dst) {
g->target->cmp(g->target, sv->delayed.cmp.op, dst, sv->delayed.cmp.a,
sv->delayed.cmp.b);
- if (sv->delayed.cmp.a_owned && sv->delayed.cmp.a.kind == OPK_LOCAL &&
- sv->delayed.cmp.a.v.local != dst.v.local) {
- api_release_operand_local(g, sv->delayed.cmp.a);
- }
- if (sv->delayed.cmp.b_owned && sv->delayed.cmp.b.kind == OPK_LOCAL &&
- sv->delayed.cmp.b.v.local != dst.v.local) {
- api_release_operand_local(g, sv->delayed.cmp.b);
- }
memset(&sv->delayed.cmp.a, 0, sizeof sv->delayed.cmp.a);
memset(&sv->delayed.cmp.b, 0, sizeof sv->delayed.cmp.b);
sv->delayed.cmp.a_owned = 0;
@@ -245,15 +230,7 @@ ApiSValue api_make_arith_binop(BinOp op, Operand a, Operand b, KitCgTypeId ty,
}
void api_release_arith(KitCg* g, ApiSValue* sv) {
- if (sv->delayed.arith.a_owned)
- api_release_operand_local(g, sv->delayed.arith.a);
- if (sv->delayed.arith.b_owned &&
- (sv->delayed.arith.b.kind != OPK_LOCAL ||
- sv->delayed.arith.a.kind != OPK_LOCAL ||
- sv->delayed.arith.b.v.local != sv->delayed.arith.a.v.local ||
- !sv->delayed.arith.a_owned)) {
- api_release_operand_local(g, sv->delayed.arith.b);
- }
+ (void)g;
memset(&sv->delayed.arith.a, 0, sizeof sv->delayed.arith.a);
memset(&sv->delayed.arith.b, 0, sizeof sv->delayed.arith.b);
sv->delayed.arith.a_owned = 0;
@@ -269,14 +246,6 @@ void api_materialize_arith_to(KitCg* g, ApiSValue* sv, Operand dst) {
g->target->binop(g->target, sv->delayed.arith.bin_op, dst,
sv->delayed.arith.a, sv->delayed.arith.b);
}
- if (sv->delayed.arith.a_owned && sv->delayed.arith.a.kind == OPK_LOCAL &&
- sv->delayed.arith.a.v.local != dst.v.local) {
- api_release_operand_local(g, sv->delayed.arith.a);
- }
- if (sv->delayed.arith.b_owned && sv->delayed.arith.b.kind == OPK_LOCAL &&
- sv->delayed.arith.b.v.local != dst.v.local) {
- api_release_operand_local(g, sv->delayed.arith.b);
- }
memset(&sv->delayed.arith.a, 0, sizeof sv->delayed.arith.a);
memset(&sv->delayed.arith.b, 0, sizeof sv->delayed.arith.b);
sv->delayed.arith.a_owned = 0;
diff --git a/src/cg/internal.h b/src/cg/internal.h
@@ -258,7 +258,6 @@ void kit_cg_atomic_fence(KitCg* g, KitCgMemOrder order);
CGLocal* api_alloc_call_args(KitCg* g, u32 nargs);
void api_pack_call_arg(KitCg* g, CGLocal* out, KitCgTypeId fty, u32 idx);
CGLocal api_alloc_call_result(KitCg* g, KitCgTypeId ret_ty);
-void api_release_call_args(KitCg* g, CGLocal* args, u32 nargs);
void api_push_call_result(KitCg* g, CGLocal result, KitCgTypeId ret_ty);
void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
KitCgCallAttrs attrs);
@@ -412,10 +411,7 @@ ApiSValue api_pop(KitCg* g);
CGLocal api_local_of_sv(const ApiSValue* sv);
void api_set_owned_local(ApiSValue* sv, CGLocal r);
KitCgTypeId api_owned_local_type(KitCg* g, const ApiSValue* sv);
-void api_temp_locals_begin(KitCg* g);
-void api_temp_locals_finish(KitCg* g);
CGLocal api_alloc_temp_local(KitCg* g, KitCgTypeId ty);
-void api_release_temp_local(KitCg* g, CGLocal r);
MemAccess api_mem_for_lvalue(KitCg* g, const Operand* lv, KitCgTypeId ty);
MemAccess api_mem_from_access(KitCg* g, const Operand* lv,
KitCgMemAccess access);
@@ -426,7 +422,6 @@ void api_require_scalar_mem_type(KitCg* g, const char* who, KitCgTypeId ty);
void api_require_pointer_value(KitCg* g, const char* who, KitCgTypeId ty);
void api_validate_memory_value(KitCg* g, const char* who, KitCgTypeId access_ty,
KitCgTypeId value_ty);
-void api_release_operand_local(KitCg* g, Operand op);
int api_sv_owns_operand_local(const ApiSValue* sv, const Operand* op);
void api_ensure_local(KitCg* g, ApiSValue* sv);
Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty);
diff --git a/src/cg/memory.c b/src/cg/memory.c
@@ -236,7 +236,6 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
CgTarget* T;
KitCgTypeId ty;
KitCgTypeId access_ty;
- CGLocal owned_base = CG_LOCAL_NONE;
Operand mem_op;
CGLocal dst_r;
Operand dst;
@@ -304,7 +303,6 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
KitCgTypeId pty = cg_type_ptr_to(g->c, api_sv_type(&base));
Operand addr = api_lvalue_addr(g, &base, pty);
mem_op = api_op_indirect(addr.v.local, 0, access_ty);
- owned_base = addr.v.local;
} else {
mem_op = place_operand_for_access(base.op, access_ty);
}
@@ -324,7 +322,6 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
}
api_release(g, &base);
- if (owned_base != CG_LOCAL_NONE) api_release_temp_local(g, owned_base);
api_push(g, api_make_sv(dst, access_ty));
}
@@ -390,7 +387,6 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
KitCgTypeId ty;
KitCgTypeId access_ty;
Operand src;
- CGLocal owned_base = CG_LOCAL_NONE;
Operand mem_op;
int is_lvalue;
int is_bitfield;
@@ -418,8 +414,6 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
cg_type_is_aggregate(g->c, api_sv_type(&rv)))) {
KitCgTypeId ptr_ty;
Operand dst_addr, src_addr;
- int dst_addr_owned;
- int src_addr_owned;
int src_ptr_rvalue;
AggregateAccess agg;
u32 src_size;
@@ -452,20 +446,15 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
}
ptr_ty = cg_type_ptr_to(g->c, ty);
dst_addr = api_lvalue_addr(g, &base, ptr_ty);
- dst_addr_owned = 1;
if (src_ptr_rvalue) {
src_addr = api_force_local(g, &rv, api_sv_type(&rv));
- src_addr_owned = 0;
} else {
src_addr = api_lvalue_addr(g, &rv, ptr_ty);
- src_addr_owned = 1;
}
memset(&agg, 0, sizeof agg);
agg.size = access_size;
agg.align = access.align ? access.align : abi_cg_alignof(g->c->abi, ty);
T->copy_bytes(T, dst_addr, src_addr, agg);
- if (dst_addr_owned) api_release_temp_local(g, dst_addr.v.local);
- if (src_addr_owned) api_release_temp_local(g, src_addr.v.local);
api_release(g, &base);
api_release(g, &rv);
return;
@@ -526,7 +515,6 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
KitCgTypeId pty = cg_type_ptr_to(g->c, api_sv_type(&base));
Operand addr = api_lvalue_addr(g, &base, pty);
mem_op = api_op_indirect(addr.v.local, 0, access_ty);
- owned_base = addr.v.local;
} else {
mem_op = place_operand_for_access(base.op, access_ty);
}
@@ -555,7 +543,6 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
api_release(g, &base);
api_release(g, &rv);
- if (owned_base != CG_LOCAL_NONE) api_release_temp_local(g, owned_base);
}
/* ============================================================
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -238,13 +238,11 @@ KitStatus kit_cg_detach(KitCg* g) {
return KIT_OK;
}
-KitStatus kit_cg_abort(KitCg* g) { return kit_cg_detach(g); }
-
void kit_cg_free(KitCg* g) {
Heap* h;
if (!g) return;
h = g->c->ctx->heap;
- (void)kit_cg_abort(g);
+ (void)kit_cg_detach(g);
h->free(h, g, sizeof *g);
}
@@ -408,7 +406,6 @@ void kit_cg_func_begin_attrs(KitCg* g, KitCgSym cg_sym,
if (dt != DEBUG_TYPE_NONE) debug_func_begin(g->debug, sym, dt, g->cur_loc);
}
T->func_begin(T, &g->fn_desc);
- api_temp_locals_begin(g);
}
void kit_cg_func_begin(KitCg* g, KitCgSym cg_sym) {
@@ -469,7 +466,6 @@ static void api_debug_emit_source_locals(KitCg* g) {
void kit_cg_func_end(KitCg* g) {
if (!g) return;
- api_temp_locals_finish(g);
g->target->func_end(g->target);
api_debug_emit_source_locals(g);
if (g->debug) debug_func_end(g->debug);
diff --git a/src/cg/value.c b/src/cg/value.c
@@ -241,10 +241,6 @@ KitCgTypeId api_owned_local_type(KitCg* g, const ApiSValue* sv) {
/* ---- temporary local allocation ---- */
-void api_temp_locals_begin(KitCg* g) { (void)g; }
-
-void api_temp_locals_finish(KitCg* g) { (void)g; }
-
CGLocal api_alloc_temp_local(KitCg* g, KitCgTypeId ty) {
CGLocalDesc d;
CGLocal local;
@@ -268,11 +264,6 @@ CGLocal api_alloc_temp_local(KitCg* g, KitCgTypeId ty) {
return local;
}
-void api_release_temp_local(KitCg* g, CGLocal r) {
- (void)g;
- (void)r;
-}
-
MemAccess api_mem_for_lvalue(KitCg* g, const Operand* lv, KitCgTypeId ty) {
MemAccess m;
memset(&m, 0, sizeof m);
@@ -376,10 +367,6 @@ void api_validate_memory_value(KitCg* g, const char* who, KitCgTypeId access_ty,
}
}
-void api_release_operand_local(KitCg* g, Operand op) {
- if (op.kind == OPK_LOCAL) api_release_temp_local(g, op.v.local);
-}
-
int api_sv_owns_operand_local(const ApiSValue* sv, const Operand* op) {
return sv->res == RES_LOCAL && op->kind == OPK_LOCAL &&
sv->op.kind == OPK_LOCAL && sv->op.v.local == op->v.local;
@@ -441,9 +428,6 @@ Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
T->load_imm(T, dst, v->op.v.imm);
} else if (api_is_lvalue_sv(v)) {
T->load(T, dst, v->op, api_mem_for_lvalue(g, &v->op, ty));
- if (v->op.kind == OPK_INDIRECT) {
- api_release_temp_local(g, v->op.v.ind.base);
- }
} else if (v->op.kind == OPK_GLOBAL) {
T->addr_of(T, dst, v->op);
} else {
@@ -464,8 +448,6 @@ void api_release(KitCg* g, ApiSValue* sv) {
api_release_cmp(g, sv);
} else if (sv->kind == SV_ARITH) {
api_release_arith(g, sv);
- } else if (sv->res == RES_LOCAL) {
- api_release_temp_local(g, (CGLocal)api_local_of_sv(sv));
}
sv->res = RES_INHERENT;
}
diff --git a/src/cg/wide.c b/src/cg/wide.c
@@ -64,7 +64,6 @@ void api_store_f128_bytes(KitCg* g, CGLocal local, KitCgTypeId ty,
g->target->store(
g->target, api_op_indirect(ar, 8, i64_ty),
api_op_imm((i64)api_u64_from_target_bytes(g, bytes + 8), i64_ty), ma);
- api_release_temp_local(g, ar);
}
void api_encode_binary128_from_double(KitCg* g, double value, u8 out[16]) {
@@ -178,7 +177,6 @@ ApiSValue api_make_wide8_const_bits(KitCg* g, u64 bits, KitCgTypeId ty) {
api_op_imm((i64)(i32)(u32)(bits & 0xffffffffu), i32_ty), ma);
g->target->store(g->target, api_op_indirect(ar, api_wide8_hi_off(g), i32_ty),
api_op_imm((i64)(i32)(u32)(bits >> 32), i32_ty), ma);
- api_release_temp_local(g, ar);
return api_make_sv(api_op_local(local, ty), ty);
}
@@ -274,8 +272,6 @@ ApiSValue api_wide16_materialize_lvalue(KitCg* g, ApiSValue* v,
agg.size = 16;
agg.align = 16;
g->target->copy_bytes(g->target, dst_addr, src_addr, agg);
- api_release_temp_local(g, dst_addr.v.local);
- api_release_temp_local(g, src_addr.v.local);
return api_make_lv(dst_lv, ty);
}
if (v->op.kind == OPK_LOCAL) {
diff --git a/src/debug/debug.c b/src/debug/debug.c
@@ -80,14 +80,6 @@ void debug_free(Debug* d) {
d->heap->free(d->heap, d->types, sizeof(*d->types) * d->types_cap);
if (d->files)
d->heap->free(d->heap, d->files, sizeof(*d->files) * d->files_cap);
- if (d->loclists) {
- for (i = 0; i < d->nloclists; ++i) {
- DebugLocList* l = &d->loclists[i];
- if (l->entries)
- d->heap->free(d->heap, l->entries, sizeof(*l->entries) * l->cap);
- }
- d->heap->free(d->heap, d->loclists, sizeof(*d->loclists) * d->loclists_cap);
- }
U32ToU32_fini(&d->src_to_file);
d->heap->free(d->heap, d, sizeof(*d));
}
@@ -513,29 +505,3 @@ void debug_line(Debug* d, ObjSecId text_section_id, u32 text_offset, SrcLoc loc,
row->loc = loc;
row->is_stmt = (u8)(is_stmt ? 1 : 0);
}
-
-/* ---- loclists (Phase 5 placeholder) ---- */
-
-u32 debug_loclist_new(Debug* d) {
- DebugLocList* l;
- if (VEC_GROW(d->heap, d->loclists, d->loclists_cap, d->nloclists + 1))
- debug_oom(d, "loclists");
- l = &d->loclists[d->nloclists];
- memset(l, 0, sizeof(*l));
- d->nloclists++;
- return d->nloclists;
-}
-
-void debug_loclist_add(Debug* d, u32 id, u32 begin_pc, u32 end_pc,
- DebugVarLoc vloc) {
- DebugLocList* l;
- DebugLocListEntry* e;
- if (id == 0 || id > d->nloclists) return;
- l = &d->loclists[id - 1];
- if (VEC_GROW(d->heap, l->entries, l->cap, l->nentries + 1))
- debug_oom(d, "loclist entries");
- e = &l->entries[l->nentries++];
- e->begin_pc = begin_pc;
- e->end_pc = end_pc;
- e->loc = vloc;
-}
diff --git a/src/debug/debug.h b/src/debug/debug.h
@@ -108,7 +108,6 @@ typedef enum DebugVarLocKind {
DVL_FRAME,
DVL_REG,
DVL_GLOBAL,
- DVL_LOCLIST, /* time-varying location, see debug_loclist_* */
} DebugVarLocKind;
typedef struct DebugVarLoc {
@@ -118,7 +117,6 @@ typedef struct DebugVarLoc {
i32 frame_ofs;
Reg reg;
ObjSymId global;
- u32 loclist_id;
} v;
} DebugVarLoc;
@@ -139,10 +137,6 @@ void debug_set_pending_loc(Debug*, SrcLoc);
* (section, offset, loc) triples. */
void debug_emit_row(Debug*, ObjSecId text_section_id, u32 text_offset, SrcLoc);
-/* location lists — for opt'd code where a variable moves between locations */
-u32 debug_loclist_new(Debug*);
-void debug_loclist_add(Debug*, u32 id, u32 begin_pc, u32 end_pc, DebugVarLoc);
-
/* Emit the accumulated debug info as DWARF sections into the ObjBuilder.
* Must be called after all code sections are finalized but before the
* file emitters run. */
diff --git a/src/debug/debug_emit.c b/src/debug/debug_emit.c
@@ -33,6 +33,7 @@ void abbrev_fini_heap(DebugAbbrevPool* p, Heap* h);
typedef struct StrTab {
Buf buf;
SymToU32 by_sym; /* Sym → byte offset within buf */
+ SymToU32 idx_by_sym; /* Sym → insertion index (== DW_FORM_strx value) */
/* Insertion order — used to populate .debug_str_offsets. */
Sym* syms;
u32 nsyms;
@@ -42,6 +43,7 @@ typedef struct StrTab {
static void str_init(StrTab* s, Heap* h) {
buf_init(&s->buf, h);
SymToU32_init(&s->by_sym, h);
+ SymToU32_init(&s->idx_by_sym, h);
s->syms = NULL;
s->nsyms = 0;
s->syms_cap = 0;
@@ -50,6 +52,7 @@ static void str_init(StrTab* s, Heap* h) {
static void str_fini(StrTab* s, Heap* h) {
buf_fini(&s->buf);
SymToU32_fini(&s->by_sym);
+ SymToU32_fini(&s->idx_by_sym);
if (s->syms) h->free(h, s->syms, sizeof(Sym) * s->syms_cap);
s->syms = NULL;
s->nsyms = 0;
@@ -77,16 +80,14 @@ static u32 str_intern(StrTab* s, Heap* h, Pool* pool, Sym sym) {
}
SymToU32_set(&s->by_sym, sym, ofs);
if (VEC_GROW(h, s->syms, s->syms_cap, s->nsyms + 1)) return ofs;
+ SymToU32_set(&s->idx_by_sym, sym, s->nsyms);
s->syms[s->nsyms++] = sym;
return ofs;
}
static u32 str_index_of(StrTab* s, Sym sym) {
- u32 i;
- for (i = 0; i < s->nsyms; ++i) {
- if (s->syms[i] == sym) return i;
- }
- return 0;
+ u32* idx = SymToU32_get(&s->idx_by_sym, sym);
+ return idx ? *idx : 0;
}
/* ---------------------------------------------------------------- */
@@ -214,44 +215,14 @@ static void add_fixup(EmitCtx* e, u32 buf_offset, DebugTypeId target) {
fx->target = target;
}
-static void add_info_reloc(EmitCtx* e, u32 buf_offset, ObjSymId sym) {
+/* Append a symbol-relative address reloc to one of the per-section AddrReloc
+ * arrays (info/line/aranges/rng). The arrays share an identical element
+ * layout and growth policy; only the backing array + count/cap differ. */
+static void add_addr_reloc(EmitCtx* e, AddrReloc** arr, u32* n, u32* cap,
+ u32 buf_offset, ObjSymId sym) {
AddrReloc* r;
- if (VEC_GROW(e->heap, e->info_relocs, e->info_relocs_cap,
- e->ninfo_relocs + 1))
- return;
- r = &e->info_relocs[e->ninfo_relocs++];
- r->buf_offset = buf_offset;
- r->sym = sym;
- r->section = OBJ_SEC_NONE;
-}
-
-static void add_line_reloc(EmitCtx* e, u32 buf_offset, ObjSymId sym) {
- AddrReloc* r;
- if (VEC_GROW(e->heap, e->line_relocs, e->line_relocs_cap,
- e->nline_relocs + 1))
- return;
- r = &e->line_relocs[e->nline_relocs++];
- r->buf_offset = buf_offset;
- r->sym = sym;
- r->section = OBJ_SEC_NONE;
-}
-
-static void add_aranges_reloc(EmitCtx* e, u32 buf_offset, ObjSymId sym) {
- AddrReloc* r;
- if (VEC_GROW(e->heap, e->aranges_relocs, e->aranges_relocs_cap,
- e->naranges_relocs + 1))
- return;
- r = &e->aranges_relocs[e->naranges_relocs++];
- r->buf_offset = buf_offset;
- r->sym = sym;
- r->section = OBJ_SEC_NONE;
-}
-
-static void add_rng_reloc(EmitCtx* e, u32 buf_offset, ObjSymId sym) {
- AddrReloc* r;
- if (VEC_GROW(e->heap, e->rng_relocs, e->nrng_relocs_cap, e->nrng_relocs + 1))
- return;
- r = &e->rng_relocs[e->nrng_relocs++];
+ if (VEC_GROW(e->heap, *arr, *cap, *n + 1)) return;
+ r = &(*arr)[(*n)++];
r->buf_offset = buf_offset;
r->sym = sym;
r->section = OBJ_SEC_NONE;
@@ -601,9 +572,6 @@ static void emit_var_loc_exprloc(EmitCtx* e, Buf* b, DebugVarLoc loc) {
(void)loc.v.global;
break;
}
- case DVL_LOCLIST:
- /* Phase 5: emit as DW_FORM_loclistx. Phase 1: empty expr. */
- break;
}
form_uleb(b, n);
buf_write(b, expr, n);
@@ -668,7 +636,8 @@ static void emit_subprogram_die(EmitCtx* e, DebugFunc* f) {
u8 zero8[8] = {0};
buf_write(&e->info_body, zero8, e->d->c->target.ptr_size);
}
- add_info_reloc(e, reloc_off, f->sym);
+ add_addr_reloc(e, &e->info_relocs, &e->ninfo_relocs, &e->info_relocs_cap,
+ reloc_off, f->sym);
fn_size = f->has_pc_range ? (f->end_ofs - f->begin_ofs) : 0;
form_u32(&e->info_body, fn_size);
{
@@ -862,7 +831,8 @@ static void emit_section_line(EmitCtx* e) {
u32 buf_ofs = buf_pos(&prog);
u8 zeros[8] = {0};
buf_write(&prog, zeros, addr_size);
- add_line_reloc(e, buf_ofs, f->sym);
+ add_addr_reloc(e, &e->line_relocs, &e->nline_relocs, &e->line_relocs_cap,
+ buf_ofs, f->sym);
}
for (j = 0; j < f->nrows; ++j) {
LineRow* r = &f->rows[j];
@@ -1096,7 +1066,8 @@ static void emit_section_aranges(EmitCtx* e) {
u32 reloc_at = buf_pos(&b);
u8 zeros[8] = {0};
buf_write(&b, zeros, addr_size);
- add_aranges_reloc(e, reloc_at, f->sym);
+ add_addr_reloc(e, &e->aranges_relocs, &e->naranges_relocs,
+ &e->aranges_relocs_cap, reloc_at, f->sym);
}
{
u32 fn_size = f->end_ofs - f->begin_ofs;
@@ -1156,7 +1127,8 @@ static void emit_section_rnglists(EmitCtx* e) {
u32 reloc_at = buf_pos(&b);
u8 zeros[8] = {0};
buf_write(&b, zeros, addr_size);
- add_rng_reloc(e, reloc_at, f->sym);
+ add_addr_reloc(e, &e->rng_relocs, &e->nrng_relocs, &e->nrng_relocs_cap,
+ reloc_at, f->sym);
}
form_uleb(&b, f->end_ofs - f->begin_ofs);
}
diff --git a/src/debug/debug_internal.h b/src/debug/debug_internal.h
@@ -42,12 +42,11 @@ typedef struct DebugEnumVal {
} DebugEnumVal;
typedef struct DebugType {
- u8 kind; /* DebugTypeKind */
- u8 is_union; /* DTK_RECORD only */
- u8 variadic; /* DTK_FUNC only */
- u8 sibling_visited; /* internal: layout pass */
- u8 base_encoding; /* DebugBaseEncoding (only for DTK_BASE) */
- u8 pad[3];
+ u8 kind; /* DebugTypeKind */
+ u8 is_union; /* DTK_RECORD only */
+ u8 variadic; /* DTK_FUNC only */
+ u8 base_encoding; /* DebugBaseEncoding (only for DTK_BASE) */
+ u8 pad[4];
Sym name; /* base / typedef / record / enum tag */
u32 byte_size; /* base / record */
u32 align; /* record */
@@ -167,20 +166,6 @@ HASHMAP_DEFINE(SymToU32, Sym, u32, hash_u32);
HASHMAP_DEFINE(U32ToU32, u32, u32, hash_u32);
HASHMAP_DEFINE(PtrToU32, u64, u32, hash_u64);
-/* Loclist entry (Phase 5 placeholder; we register the storage but do not
- * yet emit .debug_loclists). */
-typedef struct DebugLocListEntry {
- u32 begin_pc;
- u32 end_pc;
- DebugVarLoc loc;
-} DebugLocListEntry;
-
-typedef struct DebugLocList {
- DebugLocListEntry* entries;
- u32 nentries;
- u32 cap;
-} DebugLocList;
-
/* Abbrev pool — see debug_abbrev.c for encoding. */
typedef struct DebugAbbrevAttr {
u16 attr;
@@ -233,11 +218,6 @@ struct Debug {
/* Line rows pending: latest set_loc */
SrcLoc pending_loc;
- /* Loclists */
- DebugLocList* loclists;
- u32 nloclists;
- u32 loclists_cap;
-
/* Pre-built type ids for void/builtin reuse — c_debug uses these. */
DebugTypeId void_type;
};
@@ -262,7 +242,4 @@ u32 abbrev_intern(DebugAbbrevPool*, Heap*, u16 tag, u8 has_children,
/* Encode the entire pool to bytes in `buf`. */
void abbrev_encode(const DebugAbbrevPool*, Buf*);
-/* Internal helpers exposed for debug_emit.c */
-const char* debug_remap_path(Debug*, Sym original, size_t* len_out);
-
#endif
diff --git a/src/debug/dwarf_die.c b/src/debug/dwarf_die.c
@@ -423,9 +423,12 @@ void dw_build_globals(KitDebugInfo* d) {
}
}
-/* Public accessor for the type module: read attrs given die. */
-void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p) {
- u32 off = die->attrs_off;
+/* Public accessor for the type module: decode a DIE's attrs into `p` and
+ * advance *off past them (so the caller lands on the first child DIE). The
+ * single read_pack pass both decodes and advances — callers no longer need a
+ * second dw_skip_form loop to step over the attribute stream. */
+void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p,
+ u32* off) {
pack_init(p);
- read_pack(d, cu, die, p, &off);
+ read_pack(d, cu, die, p, off);
}
diff --git a/src/debug/dwarf_internal.h b/src/debug/dwarf_internal.h
@@ -208,10 +208,6 @@ typedef struct DwSubprog {
/* ---- The main consumer state ---- */
-typedef struct DwString {
- Sym sym; /* interned in compiler->global pool */
-} DwString;
-
struct KitDebugInfo {
const KitContext* ctx;
Heap* h;
@@ -408,7 +404,8 @@ typedef struct DieAttrPack {
u8 inlined;
} DieAttrPack;
-void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p);
+void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p,
+ u32* off);
/* Subprograms */
void dw_build_subs(KitDebugInfo* d);
diff --git a/src/debug/dwarf_type.c b/src/debug/dwarf_type.c
@@ -71,15 +71,7 @@ static void walk_struct_fields(KitDebugInfo* d, DwCu* cu, u32* off,
if (!dw_read_die(d, cu, off, &die)) break;
if (die.abbrev->tag == DW_TAG_member) {
DieAttrPack p;
- dw_die_pack(d, cu, &die, &p);
- /* skip past die's attrs */
- {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
- }
+ dw_die_pack(d, cu, &die, &p, off); /* decodes attrs and advances off */
if (nfields == cap) {
u32 ncap = cap ? cap * 2 : 4;
DwField* na =
@@ -122,14 +114,7 @@ static void walk_enum_values(KitDebugInfo* d, DwCu* cu, u32* off,
if (!dw_read_die(d, cu, off, &die)) break;
if (die.abbrev->tag == DW_TAG_enumerator) {
DieAttrPack p;
- dw_die_pack(d, cu, &die, &p);
- {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
- }
+ dw_die_pack(d, cu, &die, &p, off); /* decodes attrs and advances off */
if (nev == cap) {
u32 ncap = cap ? cap * 2 : 4;
DwEnumVal* na =
@@ -166,14 +151,7 @@ static void walk_array_subrange(KitDebugInfo* d, DwCu* cu, u32* off,
if (!dw_read_die(d, cu, off, &die)) break;
if (die.abbrev->tag == DW_TAG_subrange_type) {
DieAttrPack p;
- dw_die_pack(d, cu, &die, &p);
- {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
- }
+ dw_die_pack(d, cu, &die, &p, off); /* decodes attrs and advances off */
if (p.has_array_count) t->element_count = p.array_count;
if (die.abbrev->has_children) {
for (;;) {
@@ -208,7 +186,8 @@ KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset) {
off = die_offset;
if (!dw_read_die(d, cu, &off, &die)) return dw_void_type(d);
if (!die.abbrev) return dw_void_type(d);
- dw_die_pack(d, cu, &die, &p);
+ /* Decode attrs and advance `off` to the first child DIE. */
+ dw_die_pack(d, cu, &die, &p, &off);
/* Allocate before recursing — break cycles by interning early. */
t = type_alloc(d);
if (!t) return dw_void_type(d);
@@ -260,13 +239,8 @@ KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset) {
p.type_die_offset)
: dw_void_type(d);
if (die.abbrev->has_children) {
+ /* `off` already sits at the first child (attrs decoded above). */
u32 cur = off;
- /* Skip attrs (already read into p). */
- u32 ii;
- for (ii = 0; ii < die.abbrev->nattrs; ++ii) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[ii];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &cur);
- }
walk_array_subrange(d, cu, &cur, t);
}
if (t->inner && t->element_count)
@@ -279,11 +253,6 @@ KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset) {
t->byte_size = p.byte_size;
if (die.abbrev->has_children) {
u32 cur = off;
- u32 ii;
- for (ii = 0; ii < die.abbrev->nattrs; ++ii) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[ii];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &cur);
- }
walk_struct_fields(d, cu, &cur, t);
}
break;
@@ -293,11 +262,6 @@ KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset) {
t->byte_size = p.byte_size;
if (die.abbrev->has_children) {
u32 cur = off;
- u32 ii;
- for (ii = 0; ii < die.abbrev->nattrs; ++ii) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[ii];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &cur);
- }
walk_struct_fields(d, cu, &cur, t);
}
break;
@@ -310,11 +274,6 @@ KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset) {
: dw_void_type(d);
if (die.abbrev->has_children) {
u32 cur = off;
- u32 ii;
- for (ii = 0; ii < die.abbrev->nattrs; ++ii) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[ii];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &cur);
- }
walk_enum_values(d, cu, &cur, t);
}
break;
diff --git a/src/dist/blob.c b/src/dist/blob.c
@@ -17,6 +17,10 @@ static void hash_u64(DistBlake2b* h, uint64_t v) {
void dist_blob_id(uint8_t out[DIST_BLAKE2B_LEN], const uint8_t* data,
size_t len) {
+ /* The flat blob id is plain BLAKE2b of the bytes: it doubles as the CAS
+ * content id and the package format's tree-object hash (pkg_hash), so it
+ * must stay un-prefixed. (Domain separation from tree ids was considered
+ * but would require coordinated changes across the whole .kpkg/CAS format.) */
dist_blake2b(out, data, len);
}
@@ -51,7 +55,7 @@ void dist_blob_empty_root(uint8_t out[DIST_BLAKE2B_LEN]) {
int dist_blob_root(uint8_t out[DIST_BLAKE2B_LEN], const uint8_t* data,
size_t len, size_t chunk_size) {
- uint8_t level[DIST_MAX_FILES][DIST_BLAKE2B_LEN];
+ uint8_t level[DIST_BLOB_MAX_CHUNKS][DIST_BLAKE2B_LEN];
size_t leaves, i;
if (chunk_size == 0) return DIST_ERR;
if (len && !data) return DIST_ERR;
@@ -60,7 +64,7 @@ int dist_blob_root(uint8_t out[DIST_BLAKE2B_LEN], const uint8_t* data,
return DIST_OK;
}
leaves = (len + chunk_size - 1u) / chunk_size;
- if (leaves > DIST_MAX_FILES) return DIST_ERR;
+ if (leaves > DIST_BLOB_MAX_CHUNKS) return DIST_ERR;
for (i = 0; i < leaves; ++i) {
size_t off = i * chunk_size;
size_t n = len - off;
diff --git a/src/dist/dist.h b/src/dist/dist.h
@@ -28,6 +28,11 @@
#define DIST_MAX_FILES 256u
#define DIST_MAX_OUTPUTS 16u
+/* Maximum number of Merkle leaves (chunks) a single blob may have when its
+ * root is computed in-memory. Distinct from DIST_MAX_FILES (a package member
+ * count) even though the two currently share a value. */
+#define DIST_BLOB_MAX_CHUNKS 256u
+
/* String field caps inside in-memory manifest structs. */
#define DIST_NAME_MAX 128u
#define DIST_VERSION_MAX 64u
@@ -36,6 +41,7 @@
#define DIST_TRIPLE_MAX 64u
#define DIST_KIND_MAX 16u
#define DIST_PCONSTRAINT_MAX 64u
+#define DIST_URL_MAX 1024u /* URL / URL-template fields (not ustar names) */
/* Result convention: 0 = ok, non-zero = error. */
#define DIST_OK 0
diff --git a/src/dist/dist_parse.h b/src/dist/dist_parse.h
@@ -0,0 +1,77 @@
+#ifndef KIT_DIST_DIST_PARSE_H
+#define KIT_DIST_DIST_PARSE_H
+
+#include <kit/core.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dist.h"
+
+/* Shared `key = value` text-manifest parse/emit helpers used by the tree,
+ * package-manifest, and kpkg-descriptor codecs. These were once duplicated
+ * (with drift) across manifest.c/tree.c/kpkg.c; the single copy here
+ * reconciles them to the strictest/most-informative variant of each. Provided
+ * as `static inline` so no extra translation unit (and no Makefile
+ * registration) is required. */
+
+/* Max length of a single emitted "key = value\n" line. */
+#define DIST_KV_LINE_MAX 1024u
+
+/* Skip leading spaces/tabs, returning a pointer into `s`. */
+static inline char* dist_trim_lead(char* s) {
+ while (*s == ' ' || *s == '\t') ++s;
+ return s;
+}
+
+/* Strip trailing spaces/tabs/CR/LF in place. */
+static inline void dist_trim_trail(char* s) {
+ size_t n = strlen(s);
+ while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' ||
+ s[n - 1] == '\n'))
+ s[--n] = '\0';
+}
+
+/* Record `msg` into the caller's error buffer (if any) and return DIST_ERR. */
+static inline int dist_set_err(char* err, size_t cap, const char* msg) {
+ if (err && cap) snprintf(err, cap, "%s", msg);
+ return DIST_ERR;
+}
+
+/* Copy NUL-terminated `src` into `dst` of capacity `cap`, failing (with an
+ * error message) if it would not fit. */
+static inline int dist_copy_field(char* dst, size_t cap, const char* src,
+ char* err, size_t errcap) {
+ if (strlen(src) >= cap)
+ return dist_set_err(err, errcap, "field value too long");
+ snprintf(dst, cap, "%s", src);
+ return DIST_OK;
+}
+
+/* Strict base-10 parse of `s` into `*out`: rejects empty input, any non-digit,
+ * and values that would overflow uint64_t. */
+static inline int dist_parse_u64(const char* s, uint64_t* out) {
+ uint64_t v = 0;
+ if (!s || !*s || !out) return DIST_ERR;
+ for (; *s; ++s) {
+ unsigned d;
+ if (*s < '0' || *s > '9') return DIST_ERR;
+ d = (unsigned)(*s - '0');
+ if (v > (UINT64_MAX - (uint64_t)d) / 10u) return DIST_ERR;
+ v = v * 10u + (uint64_t)d;
+ }
+ *out = v;
+ return DIST_OK;
+}
+
+/* Emit a single "key = value\n" line to `out`. */
+static inline int dist_emit_kv(KitWriter* out, const char* key,
+ const char* val) {
+ char line[DIST_KV_LINE_MAX];
+ snprintf(line, sizeof line, "%s = %s\n", key, val);
+ return kit_writer_write(out, line, strlen(line)) == KIT_OK ? DIST_OK
+ : DIST_ERR;
+}
+
+#endif
diff --git a/src/dist/kpkg.c b/src/dist/kpkg.c
@@ -5,8 +5,7 @@
#include <string.h>
#include "blake2b.h"
-
-#define DESC_LINE_MAX 1024u
+#include "dist_parse.h"
static void put_u32le(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)v;
@@ -36,54 +35,16 @@ static int emit(KitWriter* out, const char* s) {
return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR;
}
-static int emit_kv(KitWriter* out, const char* key, const char* val) {
- char line[DESC_LINE_MAX];
- snprintf(line, sizeof line, "%s = %s\n", key, val);
- return emit(out, line);
-}
-
static int emit_u64(KitWriter* out, const char* key, uint64_t v) {
char num[24];
snprintf(num, sizeof num, "%llu", (unsigned long long)v);
- return emit_kv(out, key, num);
+ return dist_emit_kv(out, key, num);
}
static int emit_hex(KitWriter* out, const char* key, const uint8_t* h) {
char hex[2 * DIST_BLAKE2B_LEN + 1];
dist_hex_encode(hex, h, DIST_BLAKE2B_LEN);
- return emit_kv(out, key, hex);
-}
-
-static char* trim_lead(char* s) {
- while (*s == ' ' || *s == '\t') ++s;
- return s;
-}
-
-static void trim_trail(char* s) {
- size_t n = strlen(s);
- while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' ||
- s[n - 1] == '\n'))
- s[--n] = '\0';
-}
-
-static int set_err(char* err, size_t cap, const char* msg) {
- if (err && cap) snprintf(err, cap, "%s", msg);
- return DIST_ERR;
-}
-
-static int parse_u64_strict(const char* s, uint64_t* out) {
- uint64_t v = 0;
- if (!*s) return DIST_ERR;
- while (*s) {
- uint64_t digit;
- if (*s < '0' || *s > '9') return DIST_ERR;
- digit = (uint64_t)(*s - '0');
- if (v > (UINT64_MAX - digit) / 10u) return DIST_ERR;
- v = v * 10u + digit;
- ++s;
- }
- *out = v;
- return DIST_OK;
+ return dist_emit_kv(out, key, hex);
}
static int parse_hex32(uint8_t out[DIST_BLAKE2B_LEN], const char* val) {
@@ -91,13 +52,6 @@ static int parse_hex32(uint8_t out[DIST_BLAKE2B_LEN], const char* val) {
return dist_hex_decode(out, val, DIST_BLAKE2B_LEN);
}
-static int copy_field(char* out, size_t cap, const char* val) {
- size_t n = strlen(val);
- if (n >= cap) return DIST_ERR;
- memcpy(out, val, n + 1u);
- return DIST_OK;
-}
-
static int seen_once(uint32_t* seen, uint32_t bit) {
if (*seen & bit) return DIST_ERR;
*seen |= bit;
@@ -208,10 +162,12 @@ int dist_kpkg3_descriptor_emit(KitWriter* out, const DistKpkg3Descriptor* d) {
size_t i;
if (emit(out, "kit-encoding 3\n") != DIST_OK) return DIST_ERR;
if (emit_hex(out, "package-id", d->package_id) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "format", "kpkg") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "hash", DIST_KPKG3_HASH) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "tree", DIST_KPKG3_TREE_FORMAT) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "blob", DIST_KPKG3_BLOB_FORMAT) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "format", "kpkg") != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "hash", DIST_KPKG3_HASH) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "tree", DIST_KPKG3_TREE_FORMAT) != DIST_OK)
+ return DIST_ERR;
+ if (dist_emit_kv(out, "blob", DIST_KPKG3_BLOB_FORMAT) != DIST_OK)
+ return DIST_ERR;
if (emit_u64(out, "chunk-size", d->chunk_size) != DIST_OK) return DIST_ERR;
if (emit_u64(out, "alignment", d->alignment) != DIST_OK) return DIST_ERR;
if (emit_u64(out, "tree-offset", d->tree_offset) != DIST_OK) return DIST_ERR;
@@ -222,7 +178,8 @@ int dist_kpkg3_descriptor_emit(KitWriter* out, const DistKpkg3Descriptor* d) {
if (emit_u64(out, "index-size", d->index_size) != DIST_OK) return DIST_ERR;
if (emit_u64(out, "index-bytes", d->index_bytes) != DIST_OK) return DIST_ERR;
if (emit_hex(out, "index-root", d->index_root) != DIST_OK) return DIST_ERR;
- if (d->index_url[0] && emit_kv(out, "index-url", d->index_url) != DIST_OK)
+ if (d->index_url[0] &&
+ dist_emit_kv(out, "index-url", d->index_url) != DIST_OK)
return DIST_ERR;
if (emit_u64(out, "content-offset", d->content_offset) != DIST_OK)
return DIST_ERR;
@@ -241,19 +198,20 @@ int dist_kpkg3_descriptor_emit(KitWriter* out, const DistKpkg3Descriptor* d) {
}
if (emit_hex(out, "blake2b", d->trees[i].blake2b) != DIST_OK)
return DIST_ERR;
- if (d->trees[i].url[0] && emit_kv(out, "url", d->trees[i].url) != DIST_OK)
+ if (d->trees[i].url[0] &&
+ dist_emit_kv(out, "url", d->trees[i].url) != DIST_OK)
return DIST_ERR;
}
for (i = 0; i < d->n_chunk_sources; ++i) {
if (emit(out, "\n[chunk-source]\n") != DIST_OK) return DIST_ERR;
if (d->chunk_sources[i].kind == DIST_KPKG3_CHUNK_SOURCE_EMBEDDED) {
- if (emit_kv(out, "kind", "embedded") != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "kind", "embedded") != DIST_OK) return DIST_ERR;
} else if (d->chunk_sources[i].kind ==
DIST_KPKG3_CHUNK_SOURCE_URL_TEMPLATE) {
if (!d->chunk_sources[i].tmpl[0]) return DIST_ERR;
- if (emit_kv(out, "kind", "url-template") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "template", d->chunk_sources[i].tmpl) != DIST_OK)
+ if (dist_emit_kv(out, "kind", "url-template") != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "template", d->chunk_sources[i].tmpl) != DIST_OK)
return DIST_ERR;
} else {
return DIST_ERR;
@@ -286,7 +244,16 @@ typedef enum Kpkg3DescriptorSection {
#define KPKG3_TOP_CONTENT_SIZE (1u << 15)
#define KPKG3_TOP_CONTENT_ROOT (1u << 16)
#define KPKG3_TOP_INDEX_URL (1u << 17)
-#define KPKG3_TOP_REQUIRED ((1u << 17) - 1u)
+/* Explicit OR of every required top-level field (index-url is optional, so it
+ * is intentionally absent). Listing the bits by name means adding a new
+ * optional field cannot silently get pulled into the required set. */
+#define KPKG3_TOP_REQUIRED \
+ (KPKG3_TOP_PACKAGE_ID | KPKG3_TOP_FORMAT | KPKG3_TOP_HASH | KPKG3_TOP_TREE | \
+ KPKG3_TOP_BLOB | KPKG3_TOP_CHUNK_SIZE | KPKG3_TOP_ALIGNMENT | \
+ KPKG3_TOP_TREE_OFFSET | KPKG3_TOP_TREE_SIZE | KPKG3_TOP_TREE_ROOT | \
+ KPKG3_TOP_INDEX_OFFSET | KPKG3_TOP_INDEX_SIZE | KPKG3_TOP_INDEX_BYTES | \
+ KPKG3_TOP_INDEX_ROOT | KPKG3_TOP_CONTENT_OFFSET | KPKG3_TOP_CONTENT_SIZE | \
+ KPKG3_TOP_CONTENT_ROOT)
#define KPKG3_TREE_SEEN_TREE (1u << 0)
#define KPKG3_TREE_SEEN_OFFSET (1u << 1)
@@ -306,22 +273,22 @@ static int finish_kpkg3_section(Kpkg3DescriptorSection section, uint32_t seen,
int has_size = (seen & KPKG3_TREE_SEEN_SIZE) != 0;
if ((seen & KPKG3_TREE_SEEN_TREE) == 0 ||
(seen & KPKG3_TREE_SEEN_BLAKE2B) == 0)
- return set_err(err, errcap, "missing tree-object field");
+ return dist_set_err(err, errcap, "missing tree-object field");
if (has_offset != has_size)
- return set_err(err, errcap, "partial tree-object embedded range");
+ return dist_set_err(err, errcap, "partial tree-object embedded range");
tree->embedded = has_offset;
} else if (section == KPKG3_DESC_CHUNK_SOURCE) {
DistKpkg3ChunkSource* source = &d->chunk_sources[d->n_chunk_sources - 1u];
if ((seen & KPKG3_CHUNK_SEEN_KIND) == 0)
- return set_err(err, errcap, "missing chunk-source kind");
+ return dist_set_err(err, errcap, "missing chunk-source kind");
if (source->kind == DIST_KPKG3_CHUNK_SOURCE_URL_TEMPLATE) {
if ((seen & KPKG3_CHUNK_SEEN_TEMPLATE) == 0)
- return set_err(err, errcap, "missing chunk-source template");
+ return dist_set_err(err, errcap, "missing chunk-source template");
} else if (source->kind == DIST_KPKG3_CHUNK_SOURCE_EMBEDDED) {
if ((seen & KPKG3_CHUNK_SEEN_TEMPLATE) != 0)
- return set_err(err, errcap, "embedded chunk-source has template");
+ return dist_set_err(err, errcap, "embedded chunk-source has template");
} else {
- return set_err(err, errcap, "bad chunk-source kind");
+ return dist_set_err(err, errcap, "bad chunk-source kind");
}
}
return DIST_OK;
@@ -333,77 +300,78 @@ static int parse_kpkg3_top_key(DistKpkg3Descriptor* d, uint32_t* seen,
if (strcmp(key, "package-id") == 0) {
if (seen_once(seen, KPKG3_TOP_PACKAGE_ID) != DIST_OK ||
parse_hex32(d->package_id, val) != DIST_OK)
- return set_err(err, errcap, "bad package-id");
+ return dist_set_err(err, errcap, "bad package-id");
} else if (strcmp(key, "format") == 0) {
if (seen_once(seen, KPKG3_TOP_FORMAT) != DIST_OK ||
strcmp(val, "kpkg") != 0)
- return set_err(err, errcap, "bad format");
+ return dist_set_err(err, errcap, "bad format");
} else if (strcmp(key, "hash") == 0) {
if (seen_once(seen, KPKG3_TOP_HASH) != DIST_OK ||
strcmp(val, DIST_KPKG3_HASH) != 0)
- return set_err(err, errcap, "bad hash algorithm");
+ return dist_set_err(err, errcap, "bad hash algorithm");
} else if (strcmp(key, "tree") == 0) {
if (seen_once(seen, KPKG3_TOP_TREE) != DIST_OK ||
strcmp(val, DIST_KPKG3_TREE_FORMAT) != 0)
- return set_err(err, errcap, "bad tree format");
+ return dist_set_err(err, errcap, "bad tree format");
} else if (strcmp(key, "blob") == 0) {
if (seen_once(seen, KPKG3_TOP_BLOB) != DIST_OK ||
strcmp(val, DIST_KPKG3_BLOB_FORMAT) != 0)
- return set_err(err, errcap, "bad blob format");
+ return dist_set_err(err, errcap, "bad blob format");
} else if (strcmp(key, "chunk-size") == 0) {
if (seen_once(seen, KPKG3_TOP_CHUNK_SIZE) != DIST_OK ||
- parse_u64_strict(val, &d->chunk_size) != DIST_OK || d->chunk_size == 0)
- return set_err(err, errcap, "bad chunk-size");
+ dist_parse_u64(val, &d->chunk_size) != DIST_OK || d->chunk_size == 0)
+ return dist_set_err(err, errcap, "bad chunk-size");
} else if (strcmp(key, "alignment") == 0) {
if (seen_once(seen, KPKG3_TOP_ALIGNMENT) != DIST_OK ||
- parse_u64_strict(val, &d->alignment) != DIST_OK || d->alignment == 0)
- return set_err(err, errcap, "bad alignment");
+ dist_parse_u64(val, &d->alignment) != DIST_OK || d->alignment == 0)
+ return dist_set_err(err, errcap, "bad alignment");
} else if (strcmp(key, "tree-offset") == 0) {
if (seen_once(seen, KPKG3_TOP_TREE_OFFSET) != DIST_OK ||
- parse_u64_strict(val, &d->tree_offset) != DIST_OK)
- return set_err(err, errcap, "bad tree-offset");
+ dist_parse_u64(val, &d->tree_offset) != DIST_OK)
+ return dist_set_err(err, errcap, "bad tree-offset");
} else if (strcmp(key, "tree-size") == 0) {
if (seen_once(seen, KPKG3_TOP_TREE_SIZE) != DIST_OK ||
- parse_u64_strict(val, &d->tree_size) != DIST_OK)
- return set_err(err, errcap, "bad tree-size");
+ dist_parse_u64(val, &d->tree_size) != DIST_OK)
+ return dist_set_err(err, errcap, "bad tree-size");
} else if (strcmp(key, "tree-root") == 0) {
if (seen_once(seen, KPKG3_TOP_TREE_ROOT) != DIST_OK ||
parse_hex32(d->tree_root, val) != DIST_OK)
- return set_err(err, errcap, "bad tree-root");
+ return dist_set_err(err, errcap, "bad tree-root");
} else if (strcmp(key, "index-offset") == 0) {
if (seen_once(seen, KPKG3_TOP_INDEX_OFFSET) != DIST_OK ||
- parse_u64_strict(val, &d->index_offset) != DIST_OK)
- return set_err(err, errcap, "bad index-offset");
+ dist_parse_u64(val, &d->index_offset) != DIST_OK)
+ return dist_set_err(err, errcap, "bad index-offset");
} else if (strcmp(key, "index-size") == 0) {
if (seen_once(seen, KPKG3_TOP_INDEX_SIZE) != DIST_OK ||
- parse_u64_strict(val, &d->index_size) != DIST_OK)
- return set_err(err, errcap, "bad index-size");
+ dist_parse_u64(val, &d->index_size) != DIST_OK)
+ return dist_set_err(err, errcap, "bad index-size");
} else if (strcmp(key, "index-bytes") == 0) {
if (seen_once(seen, KPKG3_TOP_INDEX_BYTES) != DIST_OK ||
- parse_u64_strict(val, &d->index_bytes) != DIST_OK)
- return set_err(err, errcap, "bad index-bytes");
+ dist_parse_u64(val, &d->index_bytes) != DIST_OK)
+ return dist_set_err(err, errcap, "bad index-bytes");
} else if (strcmp(key, "index-root") == 0) {
if (seen_once(seen, KPKG3_TOP_INDEX_ROOT) != DIST_OK ||
parse_hex32(d->index_root, val) != DIST_OK)
- return set_err(err, errcap, "bad index-root");
+ return dist_set_err(err, errcap, "bad index-root");
} else if (strcmp(key, "index-url") == 0) {
if (seen_once(seen, KPKG3_TOP_INDEX_URL) != DIST_OK ||
- copy_field(d->index_url, sizeof d->index_url, val) != DIST_OK)
- return set_err(err, errcap, "bad index-url");
+ dist_copy_field(d->index_url, sizeof d->index_url, val, err, errcap) !=
+ DIST_OK)
+ return dist_set_err(err, errcap, "bad index-url");
} else if (strcmp(key, "content-offset") == 0) {
if (seen_once(seen, KPKG3_TOP_CONTENT_OFFSET) != DIST_OK ||
- parse_u64_strict(val, &d->content_offset) != DIST_OK)
- return set_err(err, errcap, "bad content-offset");
+ dist_parse_u64(val, &d->content_offset) != DIST_OK)
+ return dist_set_err(err, errcap, "bad content-offset");
} else if (strcmp(key, "content-size") == 0) {
if (seen_once(seen, KPKG3_TOP_CONTENT_SIZE) != DIST_OK ||
- parse_u64_strict(val, &d->content_size) != DIST_OK)
- return set_err(err, errcap, "bad content-size");
+ dist_parse_u64(val, &d->content_size) != DIST_OK)
+ return dist_set_err(err, errcap, "bad content-size");
} else if (strcmp(key, "content-root") == 0) {
if (seen_once(seen, KPKG3_TOP_CONTENT_ROOT) != DIST_OK ||
parse_hex32(d->content_root, val) != DIST_OK)
- return set_err(err, errcap, "bad content-root");
+ return dist_set_err(err, errcap, "bad content-root");
} else {
- return set_err(err, errcap, "unknown encoding descriptor key");
+ return dist_set_err(err, errcap, "unknown encoding descriptor key");
}
return DIST_OK;
}
@@ -414,25 +382,26 @@ static int parse_kpkg3_tree_key(DistKpkg3TreeObject* tree, uint32_t* seen,
if (strcmp(key, "tree") == 0) {
if (seen_once(seen, KPKG3_TREE_SEEN_TREE) != DIST_OK ||
parse_hex32(tree->tree, val) != DIST_OK)
- return set_err(err, errcap, "bad tree-object tree");
+ return dist_set_err(err, errcap, "bad tree-object tree");
} else if (strcmp(key, "offset") == 0) {
if (seen_once(seen, KPKG3_TREE_SEEN_OFFSET) != DIST_OK ||
- parse_u64_strict(val, &tree->offset) != DIST_OK)
- return set_err(err, errcap, "bad tree-object offset");
+ dist_parse_u64(val, &tree->offset) != DIST_OK)
+ return dist_set_err(err, errcap, "bad tree-object offset");
} else if (strcmp(key, "size") == 0) {
if (seen_once(seen, KPKG3_TREE_SEEN_SIZE) != DIST_OK ||
- parse_u64_strict(val, &tree->size) != DIST_OK)
- return set_err(err, errcap, "bad tree-object size");
+ dist_parse_u64(val, &tree->size) != DIST_OK)
+ return dist_set_err(err, errcap, "bad tree-object size");
} else if (strcmp(key, "blake2b") == 0) {
if (seen_once(seen, KPKG3_TREE_SEEN_BLAKE2B) != DIST_OK ||
parse_hex32(tree->blake2b, val) != DIST_OK)
- return set_err(err, errcap, "bad tree-object blake2b");
+ return dist_set_err(err, errcap, "bad tree-object blake2b");
} else if (strcmp(key, "url") == 0) {
if (seen_once(seen, KPKG3_TREE_SEEN_URL) != DIST_OK ||
- copy_field(tree->url, sizeof tree->url, val) != DIST_OK)
- return set_err(err, errcap, "bad tree-object url");
+ dist_copy_field(tree->url, sizeof tree->url, val, err, errcap) !=
+ DIST_OK)
+ return dist_set_err(err, errcap, "bad tree-object url");
} else {
- return set_err(err, errcap, "unknown tree-object key");
+ return dist_set_err(err, errcap, "unknown tree-object key");
}
return DIST_OK;
}
@@ -442,20 +411,21 @@ static int parse_kpkg3_chunk_key(DistKpkg3ChunkSource* source, uint32_t* seen,
size_t errcap) {
if (strcmp(key, "kind") == 0) {
if (seen_once(seen, KPKG3_CHUNK_SEEN_KIND) != DIST_OK)
- return set_err(err, errcap, "bad chunk-source kind");
+ return dist_set_err(err, errcap, "bad chunk-source kind");
if (strcmp(val, "embedded") == 0) {
source->kind = DIST_KPKG3_CHUNK_SOURCE_EMBEDDED;
} else if (strcmp(val, "url-template") == 0) {
source->kind = DIST_KPKG3_CHUNK_SOURCE_URL_TEMPLATE;
} else {
- return set_err(err, errcap, "bad chunk-source kind");
+ return dist_set_err(err, errcap, "bad chunk-source kind");
}
} else if (strcmp(key, "template") == 0) {
if (seen_once(seen, KPKG3_CHUNK_SEEN_TEMPLATE) != DIST_OK ||
- copy_field(source->tmpl, sizeof source->tmpl, val) != DIST_OK)
- return set_err(err, errcap, "bad chunk-source template");
+ dist_copy_field(source->tmpl, sizeof source->tmpl, val, err, errcap) !=
+ DIST_OK)
+ return dist_set_err(err, errcap, "bad chunk-source template");
} else {
- return set_err(err, errcap, "unknown chunk-source key");
+ return dist_set_err(err, errcap, "unknown chunk-source key");
}
return DIST_OK;
}
@@ -469,22 +439,23 @@ int dist_kpkg3_descriptor_parse(const uint8_t* data, size_t len,
Kpkg3DescriptorSection section = KPKG3_DESC_TOP;
memset(d, 0, sizeof *d);
while (pos < len) {
- char buf[DESC_LINE_MAX], *t, *eq, *key, *val;
+ char buf[DIST_KV_LINE_MAX], *t, *eq, *key, *val;
size_t end = pos, n;
while (end < len && data[end] != '\n') ++end;
n = end - pos;
- if (n >= sizeof buf) return set_err(err, errcap, "line too long");
+ if (n >= sizeof buf) return dist_set_err(err, errcap, "line too long");
memcpy(buf, data + pos, n);
buf[n] = '\0';
pos = (end < len) ? end + 1u : end;
- trim_trail(buf);
+ dist_trim_trail(buf);
if (first) {
first = 0;
if (strcmp(buf, "kit-encoding 3") != 0)
- return set_err(err, errcap, "bad encoding descriptor magic/version");
+ return dist_set_err(err, errcap,
+ "bad encoding descriptor magic/version");
continue;
}
- t = trim_lead(buf);
+ t = dist_trim_lead(buf);
if (*t == '\0' || *t == '#') continue;
if (*t == '[') {
if (finish_kpkg3_section(section, section_seen, d, err, errcap) !=
@@ -493,28 +464,28 @@ int dist_kpkg3_descriptor_parse(const uint8_t* data, size_t len,
section_seen = 0;
if (strcmp(t, "[tree-object]") == 0) {
if (d->n_trees == DIST_MAX_OUTPUTS)
- return set_err(err, errcap, "too many tree-object sections");
+ return dist_set_err(err, errcap, "too many tree-object sections");
memset(&d->trees[d->n_trees], 0, sizeof d->trees[d->n_trees]);
++d->n_trees;
section = KPKG3_DESC_TREE_OBJECT;
} else if (strcmp(t, "[chunk-source]") == 0) {
if (d->n_chunk_sources == DIST_MAX_OUTPUTS)
- return set_err(err, errcap, "too many chunk-source sections");
+ return dist_set_err(err, errcap, "too many chunk-source sections");
memset(&d->chunk_sources[d->n_chunk_sources], 0,
sizeof d->chunk_sources[d->n_chunk_sources]);
++d->n_chunk_sources;
section = KPKG3_DESC_CHUNK_SOURCE;
} else {
- return set_err(err, errcap, "unknown encoding descriptor section");
+ return dist_set_err(err, errcap, "unknown encoding descriptor section");
}
continue;
}
eq = strchr(t, '=');
- if (!eq) return set_err(err, errcap, "expected key = value");
+ if (!eq) return dist_set_err(err, errcap, "expected key = value");
*eq = '\0';
key = t;
- trim_trail(key);
- val = trim_lead(eq + 1);
+ dist_trim_trail(key);
+ val = dist_trim_lead(eq + 1);
if (section == KPKG3_DESC_TOP) {
if (parse_kpkg3_top_key(d, &top_seen, key, val, err, errcap) != DIST_OK)
return DIST_ERR;
@@ -529,13 +500,14 @@ int dist_kpkg3_descriptor_parse(const uint8_t* data, size_t len,
return DIST_ERR;
}
}
- if (first) return set_err(err, errcap, "empty encoding descriptor");
+ if (first) return dist_set_err(err, errcap, "empty encoding descriptor");
if (finish_kpkg3_section(section, section_seen, d, err, errcap) != DIST_OK)
return DIST_ERR;
if ((top_seen & KPKG3_TOP_REQUIRED) != KPKG3_TOP_REQUIRED)
- return set_err(err, errcap, "missing required encoding descriptor field");
+ return dist_set_err(err, errcap,
+ "missing required encoding descriptor field");
if (d->index_size != 0 && d->index_size != d->index_bytes)
- return set_err(err, errcap, "embedded index size mismatch");
+ return dist_set_err(err, errcap, "embedded index size mismatch");
return DIST_OK;
}
diff --git a/src/dist/kpkg.h b/src/dist/kpkg.h
@@ -50,7 +50,7 @@ typedef struct DistKpkg3TreeObject {
uint64_t offset, size;
int embedded;
uint8_t blake2b[DIST_BLAKE2B_LEN];
- char url[DIST_PATH_MAX + 1];
+ char url[DIST_URL_MAX];
} DistKpkg3TreeObject;
typedef enum DistKpkg3ChunkSourceKind {
@@ -60,7 +60,7 @@ typedef enum DistKpkg3ChunkSourceKind {
typedef struct DistKpkg3ChunkSource {
uint32_t kind;
- char tmpl[DIST_PATH_MAX + 1];
+ char tmpl[DIST_URL_MAX];
} DistKpkg3ChunkSource;
typedef struct DistKpkg3Descriptor {
@@ -70,7 +70,7 @@ typedef struct DistKpkg3Descriptor {
uint8_t tree_root[DIST_BLAKE2B_LEN];
uint64_t index_offset, index_size, index_bytes;
uint8_t index_root[DIST_BLAKE2B_LEN];
- char index_url[DIST_PATH_MAX + 1];
+ char index_url[DIST_URL_MAX];
uint64_t content_offset, content_size;
uint8_t content_root[DIST_BLAKE2B_LEN];
DistKpkg3TreeObject trees[DIST_MAX_OUTPUTS];
diff --git a/src/dist/manifest.c b/src/dist/manifest.c
@@ -1,109 +1,25 @@
#include "manifest.h"
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#define DIST_LINE_MAX 1024u
-
-#define F_NAME 0x01u
-#define F_VERSION 0x02u
-#define F_HASH 0x04u
-#define F_ID 0x08u
-#define F_PATH 0x10u
-#define F_KIND 0x20u
-#define F_BLAKE2B 0x40u
-#define F_ROOT 0x80u
-#define F_SIZE 0x100u
-
-typedef enum { SEC_TOP, SEC_ART, SEC_DEP } Section;
+#include "dist_parse.h"
static int emit(KitWriter* out, const char* s) {
return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR;
}
-static int emit_kv(KitWriter* out, const char* key, const char* val) {
- char line[DIST_LINE_MAX];
- snprintf(line, sizeof line, "%s = %s\n", key, val);
- return emit(out, line);
-}
-
static int emit_hex(KitWriter* out, const char* key, const uint8_t* h,
size_t n) {
char hex[2 * DIST_BLAKE2B_LEN + 1];
dist_hex_encode(hex, h, n);
- return emit_kv(out, key, hex);
+ return dist_emit_kv(out, key, hex);
}
static int emit_u64(KitWriter* out, const char* key, uint64_t v) {
char num[24];
snprintf(num, sizeof num, "%llu", (unsigned long long)v);
- return emit_kv(out, key, num);
-}
-
-int dist_manifest_emit(const DistManifest* m, KitWriter* out) {
- size_t i;
- if (emit(out, DIST_MANIFEST_MAGIC "\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
- if (m->description[0] &&
- emit_kv(out, "description", m->description) != DIST_OK)
- return DIST_ERR;
- if (emit_kv(out, "hash", DIST_MANIFEST_HASH) != DIST_OK) return DIST_ERR;
-
- for (i = 0; i < m->n_artifacts; ++i) {
- const DistArtifact* a = &m->artifacts[i];
- if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR;
- if (emit_u64(out, "id", a->id) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "path", a->path) != DIST_OK) return DIST_ERR;
- if (a->target[0] && emit_kv(out, "target", a->target) != DIST_OK)
- return DIST_ERR;
- if (emit_kv(out, "kind", a->kind) != DIST_OK) return DIST_ERR;
- if (emit_u64(out, "size", a->size) != DIST_OK) return DIST_ERR;
- if (emit_hex(out, "blake2b", a->blake2b, DIST_BLAKE2B_LEN) != DIST_OK)
- return DIST_ERR;
- if (emit_hex(out, "root", a->root, DIST_BLAKE2B_LEN) != DIST_OK)
- return DIST_ERR;
- if (a->entry && emit_kv(out, "entry", "true") != DIST_OK) return DIST_ERR;
- }
-
- for (i = 0; i < m->n_deps; ++i) {
- const DistDependency* d = &m->deps[i];
- if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "name", d->name) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "version", d->version) != DIST_OK) return DIST_ERR;
- if (d->has_blake2b &&
- emit_hex(out, "blake2b", d->blake2b, DIST_BLAKE2B_LEN) != DIST_OK)
- return DIST_ERR;
- if (d->has_keyid &&
- emit_hex(out, "key", d->keyid, DIST_KEYID_LEN) != DIST_OK)
- return DIST_ERR;
- }
- return DIST_OK;
-}
-
-static char* trim_lead(char* s) {
- while (*s == ' ' || *s == '\t') ++s;
- return s;
-}
-
-static void trim_trail(char* s) {
- size_t n = strlen(s);
- while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' ||
- s[n - 1] == '\n'))
- s[--n] = '\0';
-}
-
-static int set_err(char* err, size_t cap, const char* msg) {
- if (err && cap) snprintf(err, cap, "%s", msg);
- return DIST_ERR;
-}
-
-static int copy_field(char* dst, size_t cap, const char* src, char* err,
- size_t errcap) {
- if (strlen(src) >= cap) return set_err(err, errcap, "field value too long");
- snprintf(dst, cap, "%s", src);
- return DIST_OK;
+ return dist_emit_kv(out, key, num);
}
static int kind_valid(const char* k) {
@@ -115,7 +31,7 @@ static int kind_valid(const char* k) {
int dist_manifest_path_valid(const char* p) {
size_t start = 0, i;
- if (!p[0] || p[0] == '/') return 0;
+ if (!p || !p[0] || p[0] == '/') return 0;
for (i = 0;; ++i) {
char c = p[i];
if (c == '\\' || c == ':') return 0;
@@ -130,192 +46,6 @@ int dist_manifest_path_valid(const char* p) {
}
}
-static int parse_u64(const char* s, uint64_t* out) {
- char* end = NULL;
- unsigned long long v;
- if (!*s) return DIST_ERR;
- v = strtoull(s, &end, 10);
- if (!end || *end != '\0') return DIST_ERR;
- *out = (uint64_t)v;
- return DIST_OK;
-}
-
-static int finalize(Section sec, uint32_t seen, char* err, size_t errcap) {
- if (sec == SEC_TOP) {
- if ((seen & (F_NAME | F_VERSION | F_HASH)) != (F_NAME | F_VERSION | F_HASH))
- return set_err(err, errcap, "missing required top-level field");
- } else if (sec == SEC_ART) {
- if ((seen & (F_ID | F_PATH | F_KIND | F_BLAKE2B | F_ROOT | F_SIZE)) !=
- (F_ID | F_PATH | F_KIND | F_BLAKE2B | F_ROOT | F_SIZE))
- return set_err(err, errcap, "missing required [artifact] field");
- } else {
- if ((seen & (F_NAME | F_VERSION)) != (F_NAME | F_VERSION))
- return set_err(err, errcap, "missing required [dependency] field");
- }
- return DIST_OK;
-}
-
-int dist_manifest_parse(const uint8_t* data, size_t len, DistManifest* m,
- char* err, size_t errcap) {
- size_t pos = 0;
- int first = 1;
- Section sec = SEC_TOP;
- uint32_t seen = 0;
- DistArtifact* art = NULL;
- DistDependency* dep = NULL;
-
- memset(m, 0, sizeof *m);
-
- while (pos < len) {
- char buf[DIST_LINE_MAX];
- size_t end = pos;
- size_t n;
- char *t, *key, *val, *eq;
-
- while (end < len && data[end] != '\n') ++end;
- n = end - pos;
- if (n >= sizeof buf) return set_err(err, errcap, "line too long");
- memcpy(buf, data + pos, n);
- buf[n] = '\0';
- pos = (end < len) ? end + 1 : end;
- trim_trail(buf);
-
- if (first) {
- first = 0;
- if (strcmp(buf, DIST_MANIFEST_MAGIC) != 0)
- return set_err(err, errcap, "bad manifest magic/version");
- continue;
- }
-
- t = trim_lead(buf);
- if (*t == '\0' || *t == '#') continue;
-
- if (*t == '[') {
- if (finalize(sec, seen, err, errcap) != DIST_OK) return DIST_ERR;
- seen = 0;
- if (strcmp(t, "[artifact]") == 0) {
- if (m->n_artifacts >= DIST_MAX_ARTIFACTS)
- return set_err(err, errcap, "too many artifacts");
- sec = SEC_ART;
- art = &m->artifacts[m->n_artifacts++];
- } else if (strcmp(t, "[dependency]") == 0) {
- if (m->n_deps >= DIST_MAX_DEPS)
- return set_err(err, errcap, "too many dependencies");
- sec = SEC_DEP;
- dep = &m->deps[m->n_deps++];
- } else {
- return set_err(err, errcap, "unknown section");
- }
- continue;
- }
-
- eq = strchr(t, '=');
- if (!eq) return set_err(err, errcap, "expected key = value");
- *eq = '\0';
- key = t;
- trim_trail(key);
- val = trim_lead(eq + 1);
-
- if (sec == SEC_TOP) {
- if (strcmp(key, "name") == 0) {
- if (copy_field(m->name, sizeof m->name, val, err, errcap))
- return DIST_ERR;
- seen |= F_NAME;
- } else if (strcmp(key, "version") == 0) {
- if (copy_field(m->version, sizeof m->version, val, err, errcap))
- return DIST_ERR;
- seen |= F_VERSION;
- } else if (strcmp(key, "description") == 0) {
- if (copy_field(m->description, sizeof m->description, val, err, errcap))
- return DIST_ERR;
- } else if (strcmp(key, "hash") == 0) {
- if (strcmp(val, DIST_MANIFEST_HASH) != 0)
- return set_err(err, errcap, "unsupported hash algorithm");
- seen |= F_HASH;
- } else {
- return set_err(err, errcap, "unknown top-level key");
- }
- } else if (sec == SEC_ART) {
- if (strcmp(key, "id") == 0) {
- if (parse_u64(val, &art->id) != DIST_OK)
- return set_err(err, errcap, "bad artifact id");
- seen |= F_ID;
- } else if (strcmp(key, "path") == 0) {
- if (!dist_manifest_path_valid(val))
- return set_err(err, errcap, "unsafe artifact path");
- if (copy_field(art->path, sizeof art->path, val, err, errcap))
- return DIST_ERR;
- seen |= F_PATH;
- } else if (strcmp(key, "target") == 0) {
- if (copy_field(art->target, sizeof art->target, val, err, errcap))
- return DIST_ERR;
- } else if (strcmp(key, "kind") == 0) {
- if (!kind_valid(val))
- return set_err(err, errcap, "unknown artifact kind");
- if (copy_field(art->kind, sizeof art->kind, val, err, errcap))
- return DIST_ERR;
- seen |= F_KIND;
- } else if (strcmp(key, "size") == 0) {
- if (parse_u64(val, &art->size) != DIST_OK)
- return set_err(err, errcap, "bad artifact size");
- seen |= F_SIZE;
- } else if (strcmp(key, "blake2b") == 0) {
- if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
- dist_hex_decode(art->blake2b, val, DIST_BLAKE2B_LEN) != DIST_OK)
- return set_err(err, errcap, "bad artifact blake2b");
- seen |= F_BLAKE2B;
- } else if (strcmp(key, "root") == 0) {
- if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
- dist_hex_decode(art->root, val, DIST_BLAKE2B_LEN) != DIST_OK)
- return set_err(err, errcap, "bad artifact root");
- seen |= F_ROOT;
- } else if (strcmp(key, "entry") == 0) {
- art->entry = (strcmp(val, "true") == 0);
- if (!art->entry && strcmp(val, "false") != 0)
- return set_err(err, errcap, "bad entry value");
- } else {
- return set_err(err, errcap, "unknown [artifact] key");
- }
- } else {
- if (strcmp(key, "name") == 0) {
- if (copy_field(dep->name, sizeof dep->name, val, err, errcap))
- return DIST_ERR;
- seen |= F_NAME;
- } else if (strcmp(key, "version") == 0) {
- if (copy_field(dep->version, sizeof dep->version, val, err, errcap))
- return DIST_ERR;
- seen |= F_VERSION;
- } else if (strcmp(key, "blake2b") == 0) {
- if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
- dist_hex_decode(dep->blake2b, val, DIST_BLAKE2B_LEN) != DIST_OK)
- return set_err(err, errcap, "bad dependency blake2b");
- dep->has_blake2b = 1;
- } else if (strcmp(key, "key") == 0) {
- if (strlen(val) != 2 * DIST_KEYID_LEN ||
- dist_hex_decode(dep->keyid, val, DIST_KEYID_LEN) != DIST_OK)
- return set_err(err, errcap, "bad dependency key id");
- dep->has_keyid = 1;
- } else {
- return set_err(err, errcap, "unknown [dependency] key");
- }
- }
- }
-
- if (finalize(sec, seen, err, errcap) != DIST_OK) return DIST_ERR;
- {
- size_t i, j;
- for (i = 0; i < m->n_artifacts; ++i) {
- for (j = i + 1u; j < m->n_artifacts; ++j) {
- if (m->artifacts[i].id == m->artifacts[j].id)
- return set_err(err, errcap, "duplicate artifact id");
- if (strcmp(m->artifacts[i].path, m->artifacts[j].path) == 0)
- return set_err(err, errcap, "duplicate artifact path");
- }
- }
- }
- return DIST_OK;
-}
-
#define P3_F_NAME 0x00000001u
#define P3_F_VERSION 0x00000002u
#define P3_F_DESCRIPTION 0x00000004u
@@ -361,25 +91,11 @@ static int parse_bool3(const char* s, int* out) {
return DIST_ERR;
}
-static int parse_u64_dec3(const char* s, uint64_t* out) {
- uint64_t v = 0;
- if (!*s) return DIST_ERR;
- for (; *s; ++s) {
- unsigned digit;
- if (*s < '0' || *s > '9') return DIST_ERR;
- digit = (unsigned)(*s - '0');
- if (v > (UINT64_MAX - (uint64_t)digit) / 10u) return DIST_ERR;
- v = v * 10u + (uint64_t)digit;
- }
- *out = v;
- return DIST_OK;
-}
-
static int decode_hash3(uint8_t out[DIST_BLAKE2B_LEN], const char* val,
const char* err_msg, char* err, size_t errcap) {
if (strlen(val) != 2u * DIST_BLAKE2B_LEN ||
dist_hex_decode(out, val, DIST_BLAKE2B_LEN) != DIST_OK)
- return set_err(err, errcap, err_msg);
+ return dist_set_err(err, errcap, err_msg);
return DIST_OK;
}
@@ -387,13 +103,13 @@ static int decode_keyid3(uint8_t out[DIST_KEYID_LEN], const char* val,
char* err, size_t errcap) {
if (strlen(val) != 2u * DIST_KEYID_LEN ||
dist_hex_decode(out, val, DIST_KEYID_LEN) != DIST_OK)
- return set_err(err, errcap, "bad dependency key id");
+ return dist_set_err(err, errcap, "bad dependency key id");
return DIST_OK;
}
static int seen_once3(uint32_t* seen, uint32_t bit, const char* msg, char* err,
size_t errcap) {
- if (*seen & bit) return set_err(err, errcap, msg);
+ if (*seen & bit) return dist_set_err(err, errcap, msg);
*seen |= bit;
return DIST_OK;
}
@@ -412,18 +128,18 @@ static int finalize_package_section3(PackageSection sec, uint32_t seen,
if ((seen & (P3_F_NAME | P3_F_VERSION | P3_F_HASH | P3_F_TREE_FORMAT |
P3_F_BLOB_FORMAT)) != (P3_F_NAME | P3_F_VERSION | P3_F_HASH |
P3_F_TREE_FORMAT | P3_F_BLOB_FORMAT))
- return set_err(err, errcap, "missing required top-level field");
+ return dist_set_err(err, errcap, "missing required top-level field");
} else if (sec == P3_SEC_OUTPUT) {
if ((seen & (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID)) !=
(P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID))
- return set_err(err, errcap, "missing required [output] field");
+ return dist_set_err(err, errcap, "missing required [output] field");
} else if (sec == P3_SEC_ARTIFACT) {
if ((seen & (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND)) !=
(P3_F_OUTPUT | P3_F_PATH | P3_F_KIND))
- return set_err(err, errcap, "missing required [artifact] field");
+ return dist_set_err(err, errcap, "missing required [artifact] field");
} else {
if ((seen & (P3_F_NAME | P3_F_VERSION)) != (P3_F_NAME | P3_F_VERSION))
- return set_err(err, errcap, "missing required [dependency] field");
+ return dist_set_err(err, errcap, "missing required [dependency] field");
}
return DIST_OK;
}
@@ -435,48 +151,48 @@ int dist_package_manifest_validate(const DistPackageManifest* m, char* err,
if (!field_text_valid(m->name, 1) || !field_text_valid(m->version, 1) ||
!field_text_valid(m->description, 0))
- return set_err(err, errcap, "bad package string field");
- if (m->n_outputs == 0) return set_err(err, errcap, "missing [output]");
+ return dist_set_err(err, errcap, "bad package string field");
+ if (m->n_outputs == 0) return dist_set_err(err, errcap, "missing [output]");
if (m->n_outputs > DIST_MAX_OUTPUTS)
- return set_err(err, errcap, "too many outputs");
+ return dist_set_err(err, errcap, "too many outputs");
if (m->n_artifacts > DIST_MAX_ARTIFACTS)
- return set_err(err, errcap, "too many artifacts");
+ return dist_set_err(err, errcap, "too many artifacts");
if (m->n_deps > DIST_MAX_DEPS)
- return set_err(err, errcap, "too many dependencies");
+ return dist_set_err(err, errcap, "too many dependencies");
for (i = 0; i < m->n_outputs; ++i) {
const DistPackageOutput* out = &m->outputs[i];
if (!field_text_valid(out->name, 1) || !field_text_valid(out->target, 0))
- return set_err(err, errcap, "bad output string field");
+ return dist_set_err(err, errcap, "bad output string field");
if (out->is_default) ++default_outputs;
for (j = i + 1u; j < m->n_outputs; ++j) {
if (out->id == m->outputs[j].id)
- return set_err(err, errcap, "duplicate output id");
+ return dist_set_err(err, errcap, "duplicate output id");
}
}
if (default_outputs > 1u)
- return set_err(err, errcap, "duplicate default output");
+ return dist_set_err(err, errcap, "duplicate default output");
for (i = 0; i < m->n_artifacts; ++i) {
const DistPackageArtifact* art = &m->artifacts[i];
if (find_output3(m, art->output_id) < 0)
- return set_err(err, errcap, "artifact references unknown output");
+ return dist_set_err(err, errcap, "artifact references unknown output");
if (!field_text_valid(art->path, 1) || !dist_manifest_path_valid(art->path))
- return set_err(err, errcap, "unsafe artifact path");
+ return dist_set_err(err, errcap, "unsafe artifact path");
if (!kind_valid(art->kind))
- return set_err(err, errcap, "unknown artifact kind");
+ return dist_set_err(err, errcap, "unknown artifact kind");
for (j = i + 1u; j < m->n_artifacts; ++j) {
const DistPackageArtifact* other = &m->artifacts[j];
if (art->output_id == other->output_id &&
strcmp(art->path, other->path) == 0)
- return set_err(err, errcap, "duplicate artifact path");
+ return dist_set_err(err, errcap, "duplicate artifact path");
}
}
for (i = 0; i < m->n_deps; ++i) {
const DistPackageDependency* dep = &m->deps[i];
if (!field_text_valid(dep->name, 1) || !field_text_valid(dep->version, 1))
- return set_err(err, errcap, "bad dependency string field");
+ return dist_set_err(err, errcap, "bad dependency string field");
}
return DIST_OK;
@@ -490,28 +206,28 @@ int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out) {
return DIST_ERR;
if (emit(out, DIST_PACKAGE3_MAGIC "\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
if (m->description[0] &&
- emit_kv(out, "description", m->description) != DIST_OK)
+ dist_emit_kv(out, "description", m->description) != DIST_OK)
return DIST_ERR;
- if (emit_kv(out, "hash", DIST_PACKAGE3_HASH) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "tree", DIST_PACKAGE3_TREE_FORMAT) != DIST_OK)
+ if (dist_emit_kv(out, "hash", DIST_PACKAGE3_HASH) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "tree", DIST_PACKAGE3_TREE_FORMAT) != DIST_OK)
return DIST_ERR;
- if (emit_kv(out, "blob", DIST_PACKAGE3_BLOB_FORMAT) != DIST_OK)
+ if (dist_emit_kv(out, "blob", DIST_PACKAGE3_BLOB_FORMAT) != DIST_OK)
return DIST_ERR;
for (i = 0; i < m->n_outputs; ++i) {
const DistPackageOutput* pkg_out = &m->outputs[i];
if (emit(out, "\n[output]\n") != DIST_OK) return DIST_ERR;
if (emit_u64(out, "id", pkg_out->id) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "name", pkg_out->name) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "name", pkg_out->name) != DIST_OK) return DIST_ERR;
if (emit_hex(out, "tree", pkg_out->tree, DIST_BLAKE2B_LEN) != DIST_OK)
return DIST_ERR;
if (pkg_out->target[0] &&
- emit_kv(out, "target", pkg_out->target) != DIST_OK)
+ dist_emit_kv(out, "target", pkg_out->target) != DIST_OK)
return DIST_ERR;
- if (pkg_out->is_default && emit_kv(out, "default", "true") != DIST_OK)
+ if (pkg_out->is_default && dist_emit_kv(out, "default", "true") != DIST_OK)
return DIST_ERR;
}
@@ -519,16 +235,17 @@ int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out) {
const DistPackageArtifact* art = &m->artifacts[i];
if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR;
if (emit_u64(out, "output", art->output_id) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "path", art->path) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "kind", art->kind) != DIST_OK) return DIST_ERR;
- if (art->entry && emit_kv(out, "entry", "true") != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "path", art->path) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "kind", art->kind) != DIST_OK) return DIST_ERR;
+ if (art->entry && dist_emit_kv(out, "entry", "true") != DIST_OK)
+ return DIST_ERR;
}
for (i = 0; i < m->n_deps; ++i) {
const DistPackageDependency* dep = &m->deps[i];
if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "name", dep->name) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "version", dep->version) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "name", dep->name) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "version", dep->version) != DIST_OK) return DIST_ERR;
if (dep->has_package &&
emit_hex(out, "package", dep->package, DIST_BLAKE2B_LEN) != DIST_OK)
return DIST_ERR;
@@ -554,27 +271,27 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
memset(m, 0, sizeof *m);
while (pos < len) {
- char buf[DIST_LINE_MAX];
+ char buf[DIST_KV_LINE_MAX];
size_t end = pos;
size_t n;
char *t, *key, *val, *eq;
while (end < len && data[end] != '\n') ++end;
n = end - pos;
- if (n >= sizeof buf) return set_err(err, errcap, "line too long");
+ if (n >= sizeof buf) return dist_set_err(err, errcap, "line too long");
memcpy(buf, data + pos, n);
buf[n] = '\0';
pos = (end < len) ? end + 1 : end;
- trim_trail(buf);
+ dist_trim_trail(buf);
if (first) {
first = 0;
if (strcmp(buf, DIST_PACKAGE3_MAGIC) != 0)
- return set_err(err, errcap, "bad package manifest magic/version");
+ return dist_set_err(err, errcap, "bad package manifest magic/version");
continue;
}
- t = trim_lead(buf);
+ t = dist_trim_lead(buf);
if (*t == '\0' || *t == '#') continue;
if (*t == '[') {
@@ -583,31 +300,31 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
seen = 0;
if (strcmp(t, "[output]") == 0) {
if (m->n_outputs >= DIST_MAX_OUTPUTS)
- return set_err(err, errcap, "too many outputs");
+ return dist_set_err(err, errcap, "too many outputs");
sec = P3_SEC_OUTPUT;
pkg_out = &m->outputs[m->n_outputs++];
} else if (strcmp(t, "[artifact]") == 0) {
if (m->n_artifacts >= DIST_MAX_ARTIFACTS)
- return set_err(err, errcap, "too many artifacts");
+ return dist_set_err(err, errcap, "too many artifacts");
sec = P3_SEC_ARTIFACT;
art = &m->artifacts[m->n_artifacts++];
} else if (strcmp(t, "[dependency]") == 0) {
if (m->n_deps >= DIST_MAX_DEPS)
- return set_err(err, errcap, "too many dependencies");
+ return dist_set_err(err, errcap, "too many dependencies");
sec = P3_SEC_DEPENDENCY;
dep = &m->deps[m->n_deps++];
} else {
- return set_err(err, errcap, "unknown section");
+ return dist_set_err(err, errcap, "unknown section");
}
continue;
}
eq = strchr(t, '=');
- if (!eq) return set_err(err, errcap, "expected key = value");
+ if (!eq) return dist_set_err(err, errcap, "expected key = value");
*eq = '\0';
key = t;
- trim_trail(key);
- val = trim_lead(eq + 1);
+ dist_trim_trail(key);
+ val = dist_trim_lead(eq + 1);
if (sec == P3_SEC_TOP) {
if (strcmp(key, "name") == 0) {
@@ -615,60 +332,62 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 1))
- return set_err(err, errcap, "bad package name");
- if (copy_field(m->name, sizeof m->name, val, err, errcap))
+ return dist_set_err(err, errcap, "bad package name");
+ if (dist_copy_field(m->name, sizeof m->name, val, err, errcap))
return DIST_ERR;
} else if (strcmp(key, "version") == 0) {
if (seen_once3(&seen, P3_F_VERSION, "duplicate top-level key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 1))
- return set_err(err, errcap, "bad package version");
- if (copy_field(m->version, sizeof m->version, val, err, errcap))
+ return dist_set_err(err, errcap, "bad package version");
+ if (dist_copy_field(m->version, sizeof m->version, val, err, errcap))
return DIST_ERR;
} else if (strcmp(key, "description") == 0) {
if (seen_once3(&seen, P3_F_DESCRIPTION, "duplicate top-level key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 0))
- return set_err(err, errcap, "bad package description");
- if (copy_field(m->description, sizeof m->description, val, err, errcap))
+ return dist_set_err(err, errcap, "bad package description");
+ if (dist_copy_field(m->description, sizeof m->description, val, err,
+ errcap))
return DIST_ERR;
} else if (strcmp(key, "hash") == 0) {
if (seen_once3(&seen, P3_F_HASH, "duplicate top-level key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (strcmp(val, DIST_PACKAGE3_HASH) != 0)
- return set_err(err, errcap, "unsupported hash algorithm");
+ return dist_set_err(err, errcap, "unsupported hash algorithm");
} else if (strcmp(key, "tree") == 0) {
if (seen_once3(&seen, P3_F_TREE_FORMAT, "duplicate top-level key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (strcmp(val, DIST_PACKAGE3_TREE_FORMAT) != 0)
- return set_err(err, errcap, "unsupported tree format");
+ return dist_set_err(err, errcap, "unsupported tree format");
} else if (strcmp(key, "blob") == 0) {
if (seen_once3(&seen, P3_F_BLOB_FORMAT, "duplicate top-level key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (strcmp(val, DIST_PACKAGE3_BLOB_FORMAT) != 0)
- return set_err(err, errcap, "unsupported blob format");
+ return dist_set_err(err, errcap, "unsupported blob format");
} else {
- return set_err(err, errcap, "unknown top-level key");
+ return dist_set_err(err, errcap, "unknown top-level key");
}
} else if (sec == P3_SEC_OUTPUT) {
if (strcmp(key, "id") == 0) {
if (seen_once3(&seen, P3_F_ID, "duplicate [output] key", err, errcap) !=
DIST_OK)
return DIST_ERR;
- if (parse_u64_dec3(val, &pkg_out->id) != DIST_OK)
- return set_err(err, errcap, "bad output id");
+ if (dist_parse_u64(val, &pkg_out->id) != DIST_OK)
+ return dist_set_err(err, errcap, "bad output id");
} else if (strcmp(key, "name") == 0) {
if (seen_once3(&seen, P3_F_OUTPUT_NAME, "duplicate [output] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 1))
- return set_err(err, errcap, "bad output name");
- if (copy_field(pkg_out->name, sizeof pkg_out->name, val, err, errcap))
+ return dist_set_err(err, errcap, "bad output name");
+ if (dist_copy_field(pkg_out->name, sizeof pkg_out->name, val, err,
+ errcap))
return DIST_ERR;
} else if (strcmp(key, "tree") == 0) {
if (seen_once3(&seen, P3_F_TREE_ID, "duplicate [output] key", err,
@@ -682,50 +401,50 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 0))
- return set_err(err, errcap, "bad output target");
- if (copy_field(pkg_out->target, sizeof pkg_out->target, val, err,
- errcap))
+ return dist_set_err(err, errcap, "bad output target");
+ if (dist_copy_field(pkg_out->target, sizeof pkg_out->target, val, err,
+ errcap))
return DIST_ERR;
} else if (strcmp(key, "default") == 0) {
if (seen_once3(&seen, P3_F_DEFAULT, "duplicate [output] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (parse_bool3(val, &pkg_out->is_default) != DIST_OK)
- return set_err(err, errcap, "bad default value");
+ return dist_set_err(err, errcap, "bad default value");
} else {
- return set_err(err, errcap, "unknown [output] key");
+ return dist_set_err(err, errcap, "unknown [output] key");
}
} else if (sec == P3_SEC_ARTIFACT) {
if (strcmp(key, "output") == 0) {
if (seen_once3(&seen, P3_F_OUTPUT, "duplicate [artifact] key", err,
errcap) != DIST_OK)
return DIST_ERR;
- if (parse_u64_dec3(val, &art->output_id) != DIST_OK)
- return set_err(err, errcap, "bad artifact output id");
+ if (dist_parse_u64(val, &art->output_id) != DIST_OK)
+ return dist_set_err(err, errcap, "bad artifact output id");
} else if (strcmp(key, "path") == 0) {
if (seen_once3(&seen, P3_F_PATH, "duplicate [artifact] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!dist_manifest_path_valid(val))
- return set_err(err, errcap, "unsafe artifact path");
- if (copy_field(art->path, sizeof art->path, val, err, errcap))
+ return dist_set_err(err, errcap, "unsafe artifact path");
+ if (dist_copy_field(art->path, sizeof art->path, val, err, errcap))
return DIST_ERR;
} else if (strcmp(key, "kind") == 0) {
if (seen_once3(&seen, P3_F_KIND, "duplicate [artifact] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!kind_valid(val))
- return set_err(err, errcap, "unknown artifact kind");
- if (copy_field(art->kind, sizeof art->kind, val, err, errcap))
+ return dist_set_err(err, errcap, "unknown artifact kind");
+ if (dist_copy_field(art->kind, sizeof art->kind, val, err, errcap))
return DIST_ERR;
} else if (strcmp(key, "entry") == 0) {
if (seen_once3(&seen, P3_F_ENTRY, "duplicate [artifact] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (parse_bool3(val, &art->entry) != DIST_OK)
- return set_err(err, errcap, "bad entry value");
+ return dist_set_err(err, errcap, "bad entry value");
} else {
- return set_err(err, errcap, "unknown [artifact] key");
+ return dist_set_err(err, errcap, "unknown [artifact] key");
}
} else {
if (strcmp(key, "name") == 0) {
@@ -733,16 +452,17 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 1))
- return set_err(err, errcap, "bad dependency name");
- if (copy_field(dep->name, sizeof dep->name, val, err, errcap))
+ return dist_set_err(err, errcap, "bad dependency name");
+ if (dist_copy_field(dep->name, sizeof dep->name, val, err, errcap))
return DIST_ERR;
} else if (strcmp(key, "version") == 0) {
if (seen_once3(&seen, P3_F_VERSION, "duplicate [dependency] key", err,
errcap) != DIST_OK)
return DIST_ERR;
if (!field_text_valid(val, 1))
- return set_err(err, errcap, "bad dependency version");
- if (copy_field(dep->version, sizeof dep->version, val, err, errcap))
+ return dist_set_err(err, errcap, "bad dependency version");
+ if (dist_copy_field(dep->version, sizeof dep->version, val, err,
+ errcap))
return DIST_ERR;
} else if (strcmp(key, "package") == 0) {
if (seen_once3(&seen, P3_F_PACKAGE, "duplicate [dependency] key", err,
@@ -760,12 +480,13 @@ int dist_package_manifest_parse(const uint8_t* data, size_t len,
return DIST_ERR;
dep->has_keyid = 1;
} else {
- return set_err(err, errcap, "unknown [dependency] key");
+ return dist_set_err(err, errcap, "unknown [dependency] key");
}
}
}
- if (first) return set_err(err, errcap, "bad package manifest magic/version");
+ if (first)
+ return dist_set_err(err, errcap, "bad package manifest magic/version");
if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK)
return DIST_ERR;
return dist_package_manifest_validate(m, err, errcap);
diff --git a/src/dist/manifest.h b/src/dist/manifest.h
@@ -7,47 +7,15 @@
#include "dist.h"
-/* The signed logical package object for distribution v2. The physical
- * encodings (.tar.gz and .kpkg) both carry these literal bytes and the same
- * detached minisign signature. */
+/* The signed logical package object for distribution. The physical encodings
+ * (.tar.gz and .kpkg) both carry these literal bytes and the same detached
+ * minisign signature. */
-#define DIST_MANIFEST_MAGIC "kit-package 2"
-#define DIST_MANIFEST_HASH "blake2b-merkle-v1"
#define DIST_PACKAGE3_MAGIC "kit-package 3"
#define DIST_PACKAGE3_HASH "blake2b-256"
#define DIST_PACKAGE3_TREE_FORMAT "kit-tree-v1"
#define DIST_PACKAGE3_BLOB_FORMAT "kit-blob-v1"
-typedef struct DistArtifact {
- uint64_t id;
- char path[DIST_PATH_MAX + 1];
- char target[DIST_TRIPLE_MAX]; /* "" = target-independent */
- char kind[DIST_KIND_MAX];
- uint8_t blake2b[DIST_BLAKE2B_LEN];
- uint8_t root[DIST_BLAKE2B_LEN];
- uint64_t size;
- int entry;
-} DistArtifact;
-
-typedef struct DistDependency {
- char name[DIST_NAME_MAX];
- char version[DIST_PCONSTRAINT_MAX];
- uint8_t blake2b[DIST_BLAKE2B_LEN];
- int has_blake2b;
- uint8_t keyid[DIST_KEYID_LEN];
- int has_keyid;
-} DistDependency;
-
-typedef struct DistManifest {
- char name[DIST_NAME_MAX];
- char version[DIST_VERSION_MAX];
- char description[DIST_DESC_MAX]; /* "" = absent */
- DistArtifact artifacts[DIST_MAX_ARTIFACTS];
- size_t n_artifacts;
- DistDependency deps[DIST_MAX_DEPS];
- size_t n_deps;
-} DistManifest;
-
typedef struct DistPackageOutput {
uint64_t id;
char name[DIST_NAME_MAX];
@@ -84,13 +52,8 @@ typedef struct DistPackageManifest {
size_t n_deps;
} DistPackageManifest;
-int dist_manifest_emit(const DistManifest* m, KitWriter* out);
-
int dist_manifest_path_valid(const char* path);
-int dist_manifest_parse(const uint8_t* data, size_t len, DistManifest* m,
- char* err, size_t errcap);
-
int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out);
int dist_package_manifest_parse(const uint8_t* data, size_t len,
DistPackageManifest* m, char* err,
diff --git a/src/dist/tree.c b/src/dist/tree.c
@@ -5,8 +5,7 @@
#include <string.h>
#include "blake2b.h"
-
-#define TREE_LINE_MAX 1024u
+#include "dist_parse.h"
#define F_HASH 0x01u
#define F_TOP_BLOB 0x02u
@@ -18,11 +17,6 @@
typedef enum TreeSection { TREE_SEC_TOP, TREE_SEC_FILE } TreeSection;
-static int set_err(char* err, size_t cap, const char* msg) {
- if (err && cap) snprintf(err, cap, "%s", msg);
- return DIST_ERR;
-}
-
int dist_tree_mode_parse(const char* s, uint8_t* out) {
if (!s || !out) return DIST_ERR;
if (strcmp(s, "-") == 0) {
@@ -67,17 +61,17 @@ static int entry_cmp(const void* ap, const void* bp) {
int dist_tree_sort_validate(DistTree* tree, char* err, size_t errcap) {
size_t i;
- if (!tree) return set_err(err, errcap, "missing tree");
+ if (!tree) return dist_set_err(err, errcap, "missing tree");
if (tree->n_entries && !tree->entries)
- return set_err(err, errcap, "missing tree entries");
+ return dist_set_err(err, errcap, "missing tree entries");
qsort(tree->entries, tree->n_entries, sizeof tree->entries[0], entry_cmp);
for (i = 0; i < tree->n_entries; ++i) {
if (!dist_tree_path_valid(tree->entries[i].path))
- return set_err(err, errcap, "unsafe tree path");
+ return dist_set_err(err, errcap, "unsafe tree path");
if (!dist_tree_mode_name(tree->entries[i].mode))
- return set_err(err, errcap, "bad tree mode");
+ return dist_set_err(err, errcap, "bad tree mode");
if (i > 0 && strcmp(tree->entries[i - 1u].path, tree->entries[i].path) == 0)
- return set_err(err, errcap, "duplicate tree path");
+ return dist_set_err(err, errcap, "duplicate tree path");
}
return DIST_OK;
}
@@ -99,37 +93,32 @@ static int emit(KitWriter* out, const char* s) {
return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR;
}
-static int emit_kv(KitWriter* out, const char* key, const char* val) {
- char line[TREE_LINE_MAX];
- snprintf(line, sizeof line, "%s = %s\n", key, val);
- return emit(out, line);
-}
-
static int emit_u64(KitWriter* out, const char* key, uint64_t v) {
char num[24];
snprintf(num, sizeof num, "%llu", (unsigned long long)v);
- return emit_kv(out, key, num);
+ return dist_emit_kv(out, key, num);
}
static int emit_hex(KitWriter* out, const char* key, const uint8_t* h) {
char hex[2 * DIST_BLAKE2B_LEN + 1];
dist_hex_encode(hex, h, DIST_BLAKE2B_LEN);
- return emit_kv(out, key, hex);
+ return dist_emit_kv(out, key, hex);
}
int dist_tree_emit(const DistTree* tree, KitWriter* out) {
size_t i;
if (!out || tree_validate_canonical(tree) != DIST_OK) return DIST_ERR;
if (emit(out, DIST_TREE_MAGIC "\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "hash", DIST_TREE_HASH) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "blob", DIST_TREE_BLOB_FORMAT) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "hash", DIST_TREE_HASH) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "blob", DIST_TREE_BLOB_FORMAT) != DIST_OK)
+ return DIST_ERR;
for (i = 0; i < tree->n_entries; ++i) {
const DistTreeEntry* e = &tree->entries[i];
const char* mode = dist_tree_mode_name(e->mode);
if (!mode) return DIST_ERR;
if (emit(out, "\n[file]\n") != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "path", e->path) != DIST_OK) return DIST_ERR;
- if (emit_kv(out, "mode", mode) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "path", e->path) != DIST_OK) return DIST_ERR;
+ if (dist_emit_kv(out, "mode", mode) != DIST_OK) return DIST_ERR;
if (emit_u64(out, "size", e->size) != DIST_OK) return DIST_ERR;
if (emit_hex(out, "blob", e->blob) != DIST_OK) return DIST_ERR;
if (emit_hex(out, "root", e->root) != DIST_OK) return DIST_ERR;
@@ -137,47 +126,13 @@ int dist_tree_emit(const DistTree* tree, KitWriter* out) {
return DIST_OK;
}
-static char* trim_lead(char* s) {
- while (*s == ' ' || *s == '\t') ++s;
- return s;
-}
-
-static void trim_trail(char* s) {
- size_t n = strlen(s);
- while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' ||
- s[n - 1] == '\n'))
- s[--n] = '\0';
-}
-
-static int copy_field(char* dst, size_t cap, const char* src, char* err,
- size_t errcap) {
- if (strlen(src) >= cap) return set_err(err, errcap, "field value too long");
- snprintf(dst, cap, "%s", src);
- return DIST_OK;
-}
-
-static int parse_u64(const char* s, uint64_t* out) {
- uint64_t v = 0;
- size_t i;
- if (!s || !*s || !out) return DIST_ERR;
- for (i = 0; s[i]; ++i) {
- unsigned d;
- if (s[i] < '0' || s[i] > '9') return DIST_ERR;
- d = (unsigned)(s[i] - '0');
- if (v > (UINT64_MAX - d) / 10u) return DIST_ERR;
- v = v * 10u + d;
- }
- *out = v;
- return DIST_OK;
-}
-
static int hex_hash(const char* val, uint8_t out[DIST_BLAKE2B_LEN]) {
if (strlen(val) != 2u * DIST_BLAKE2B_LEN) return DIST_ERR;
return dist_hex_decode(out, val, DIST_BLAKE2B_LEN);
}
static int check_dup(uint32_t seen, uint32_t flag, char* err, size_t errcap) {
- if (seen & flag) return set_err(err, errcap, "duplicate tree field");
+ if (seen & flag) return dist_set_err(err, errcap, "duplicate tree field");
return DIST_OK;
}
@@ -186,19 +141,20 @@ static int finalize_file(const DistTree* tree, uint32_t seen, char* err,
const DistTreeEntry* e;
if ((seen & (F_PATH | F_MODE | F_SIZE | F_FILE_BLOB | F_ROOT)) !=
(F_PATH | F_MODE | F_SIZE | F_FILE_BLOB | F_ROOT))
- return set_err(err, errcap, "missing required [file] field");
+ return dist_set_err(err, errcap, "missing required [file] field");
if (!tree || tree->n_entries == 0)
- return set_err(err, errcap, "missing file");
+ return dist_set_err(err, errcap, "missing file");
e = &tree->entries[tree->n_entries - 1u];
if (!dist_tree_path_valid(e->path))
- return set_err(err, errcap, "unsafe tree path");
+ return dist_set_err(err, errcap, "unsafe tree path");
if (!dist_tree_mode_name(e->mode))
- return set_err(err, errcap, "bad tree mode");
+ return dist_set_err(err, errcap, "bad tree mode");
if (tree->n_entries > 1u) {
const char* prev = tree->entries[tree->n_entries - 2u].path;
int cmp = strcmp(prev, e->path);
- if (cmp == 0) return set_err(err, errcap, "duplicate tree path");
- if (cmp > 0) return set_err(err, errcap, "non-canonical tree ordering");
+ if (cmp == 0) return dist_set_err(err, errcap, "duplicate tree path");
+ if (cmp > 0)
+ return dist_set_err(err, errcap, "non-canonical tree ordering");
}
return DIST_OK;
}
@@ -211,48 +167,48 @@ int dist_tree_parse(const uint8_t* data, size_t len, DistTree* out, char* err,
uint32_t top_seen = 0;
uint32_t file_seen = 0;
- if (!data || !out) return set_err(err, errcap, "missing tree manifest");
+ if (!data || !out) return dist_set_err(err, errcap, "missing tree manifest");
if (out->cap_entries && !out->entries)
- return set_err(err, errcap, "missing tree entries");
+ return dist_set_err(err, errcap, "missing tree entries");
out->n_entries = 0;
while (pos < len) {
- char buf[TREE_LINE_MAX];
+ char buf[DIST_KV_LINE_MAX];
size_t end = pos;
size_t n, i;
char *t, *key, *val, *eq;
while (end < len && data[end] != '\n') ++end;
n = end - pos;
- if (n >= sizeof buf) return set_err(err, errcap, "line too long");
+ if (n >= sizeof buf) return dist_set_err(err, errcap, "line too long");
for (i = pos; i < end; ++i)
if (data[i] == 0)
- return set_err(err, errcap, "NUL byte in tree manifest");
+ return dist_set_err(err, errcap, "NUL byte in tree manifest");
memcpy(buf, data + pos, n);
buf[n] = '\0';
pos = (end < len) ? end + 1u : end;
- trim_trail(buf);
+ dist_trim_trail(buf);
if (first) {
first = 0;
if (strcmp(buf, DIST_TREE_MAGIC) != 0)
- return set_err(err, errcap, "bad tree magic/version");
+ return dist_set_err(err, errcap, "bad tree magic/version");
continue;
}
- t = trim_lead(buf);
+ t = dist_trim_lead(buf);
if (*t == '\0' || *t == '#') continue;
if (*t == '[') {
if (strcmp(t, "[file]") != 0)
- return set_err(err, errcap, "unknown tree section");
+ return dist_set_err(err, errcap, "unknown tree section");
if ((top_seen & (F_HASH | F_TOP_BLOB)) != (F_HASH | F_TOP_BLOB))
- return set_err(err, errcap, "missing required top-level field");
+ return dist_set_err(err, errcap, "missing required top-level field");
if (sec == TREE_SEC_FILE &&
finalize_file(out, file_seen, err, errcap) != DIST_OK)
return DIST_ERR;
if (out->n_entries >= out->cap_entries)
- return set_err(err, errcap, "too many tree files");
+ return dist_set_err(err, errcap, "too many tree files");
memset(&out->entries[out->n_entries], 0, sizeof out->entries[0]);
++out->n_entries;
sec = TREE_SEC_FILE;
@@ -261,27 +217,27 @@ int dist_tree_parse(const uint8_t* data, size_t len, DistTree* out, char* err,
}
eq = strchr(t, '=');
- if (!eq) return set_err(err, errcap, "expected key = value");
+ if (!eq) return dist_set_err(err, errcap, "expected key = value");
*eq = '\0';
key = t;
- trim_trail(key);
- val = trim_lead(eq + 1);
+ dist_trim_trail(key);
+ val = dist_trim_lead(eq + 1);
if (sec == TREE_SEC_TOP) {
if (strcmp(key, "hash") == 0) {
if (check_dup(top_seen, F_HASH, err, errcap) != DIST_OK)
return DIST_ERR;
if (strcmp(val, DIST_TREE_HASH) != 0)
- return set_err(err, errcap, "unsupported tree hash");
+ return dist_set_err(err, errcap, "unsupported tree hash");
top_seen |= F_HASH;
} else if (strcmp(key, "blob") == 0) {
if (check_dup(top_seen, F_TOP_BLOB, err, errcap) != DIST_OK)
return DIST_ERR;
if (strcmp(val, DIST_TREE_BLOB_FORMAT) != 0)
- return set_err(err, errcap, "unsupported tree blob format");
+ return dist_set_err(err, errcap, "unsupported tree blob format");
top_seen |= F_TOP_BLOB;
} else {
- return set_err(err, errcap, "unknown top-level tree key");
+ return dist_set_err(err, errcap, "unknown top-level tree key");
}
} else {
DistTreeEntry* e = &out->entries[out->n_entries - 1u];
@@ -289,43 +245,44 @@ int dist_tree_parse(const uint8_t* data, size_t len, DistTree* out, char* err,
if (check_dup(file_seen, F_PATH, err, errcap) != DIST_OK)
return DIST_ERR;
if (!dist_tree_path_valid(val))
- return set_err(err, errcap, "unsafe tree path");
- if (copy_field(e->path, sizeof e->path, val, err, errcap) != DIST_OK)
+ return dist_set_err(err, errcap, "unsafe tree path");
+ if (dist_copy_field(e->path, sizeof e->path, val, err, errcap) !=
+ DIST_OK)
return DIST_ERR;
file_seen |= F_PATH;
} else if (strcmp(key, "mode") == 0) {
if (check_dup(file_seen, F_MODE, err, errcap) != DIST_OK)
return DIST_ERR;
if (dist_tree_mode_parse(val, &e->mode) != DIST_OK)
- return set_err(err, errcap, "bad tree mode");
+ return dist_set_err(err, errcap, "bad tree mode");
file_seen |= F_MODE;
} else if (strcmp(key, "size") == 0) {
if (check_dup(file_seen, F_SIZE, err, errcap) != DIST_OK)
return DIST_ERR;
- if (parse_u64(val, &e->size) != DIST_OK)
- return set_err(err, errcap, "bad tree size");
+ if (dist_parse_u64(val, &e->size) != DIST_OK)
+ return dist_set_err(err, errcap, "bad tree size");
file_seen |= F_SIZE;
} else if (strcmp(key, "blob") == 0) {
if (check_dup(file_seen, F_FILE_BLOB, err, errcap) != DIST_OK)
return DIST_ERR;
if (hex_hash(val, e->blob) != DIST_OK)
- return set_err(err, errcap, "bad tree blob hash");
+ return dist_set_err(err, errcap, "bad tree blob hash");
file_seen |= F_FILE_BLOB;
} else if (strcmp(key, "root") == 0) {
if (check_dup(file_seen, F_ROOT, err, errcap) != DIST_OK)
return DIST_ERR;
if (hex_hash(val, e->root) != DIST_OK)
- return set_err(err, errcap, "bad tree blob root");
+ return dist_set_err(err, errcap, "bad tree blob root");
file_seen |= F_ROOT;
} else {
- return set_err(err, errcap, "unknown [file] tree key");
+ return dist_set_err(err, errcap, "unknown [file] tree key");
}
}
}
- if (first) return set_err(err, errcap, "bad tree magic/version");
+ if (first) return dist_set_err(err, errcap, "bad tree magic/version");
if ((top_seen & (F_HASH | F_TOP_BLOB)) != (F_HASH | F_TOP_BLOB))
- return set_err(err, errcap, "missing required top-level field");
+ return dist_set_err(err, errcap, "missing required top-level field");
if (sec == TREE_SEC_FILE &&
finalize_file(out, file_seen, err, errcap) != DIST_OK)
return DIST_ERR;
@@ -334,6 +291,9 @@ int dist_tree_parse(const uint8_t* data, size_t len, DistTree* out, char* err,
void dist_tree_id(uint8_t out[DIST_BLAKE2B_LEN], const uint8_t* manifest,
size_t len) {
+ /* Plain BLAKE2b of the canonical tree bytes: this id is the package
+ * format's tree-object hash (verified via pkg_hash), so it must not be
+ * domain-prefixed. See dist_blob_id. */
dist_blake2b(out, manifest, len);
}
diff --git a/src/emu/emu.h b/src/emu/emu.h
@@ -125,8 +125,12 @@ typedef struct EmuImportBinding {
KitEmuImportSignature signature;
} EmuImportBinding;
+/* Max DT_NEEDED entries recorded per object; overflow is silently truncated by
+ * the ELF emu loader (src/obj/elf/emu_load.c). */
+#define EMU_MAX_NEEDED 16
+
typedef struct EmuObjectImports {
- KitSlice needed[16];
+ KitSlice needed[EMU_MAX_NEEDED];
u32 nneeded;
} EmuObjectImports;
@@ -393,7 +397,6 @@ KitStatus emu_addr_space_find_gap(EmuAddrSpace*, u64 nbytes, u64 align,
KitStatus emu_addr_space_set_brk(EmuAddrSpace*, u64 requested, u64* actual_out);
KitStatus emu_addr_space_copy_in(EmuAddrSpace*, u64 va, const void* src,
u64 nbytes);
-KitStatus emu_addr_space_set_perm(EmuAddrSpace*, u64 va, u64 nbytes, u8 perms);
u8* emu_addr_space_ptr(EmuAddrSpace*, u64 va, u64 nbytes, u8 need_perms);
u64 emu_addr_space_contig_len(EmuAddrSpace*, u64 va, u8 need_perms);
const EmuMemFault* emu_addr_space_last_fault(const EmuAddrSpace*);
diff --git a/src/emu/image.c b/src/emu/image.c
@@ -330,11 +330,6 @@ KitStatus emu_addr_space_copy_in(EmuAddrSpace* as, u64 va, const void* src,
return KIT_OK;
}
-KitStatus emu_addr_space_set_perm(EmuAddrSpace* as, u64 va, u64 nbytes,
- u8 perms) {
- return emu_addr_space_protect(as, va, nbytes, perms);
-}
-
u8* emu_addr_space_ptr(EmuAddrSpace* as, u64 va, u64 nbytes, u8 need_perms) {
EmuMap* m;
u64 off, start_page, end_page, i;
diff --git a/src/emu/runtime.c b/src/emu/runtime.c
@@ -220,7 +220,7 @@ static u64 emu_mem_load_checked(EmuThread* t, u64 addr, u64 nbytes, u8 access,
return 0;
}
-/* Bounds-checked fast (unchecked-resume) load/store of `nbytes` (1..4)
+/* Bounds-checked fast (unchecked-resume) load/store of `nbytes` (1..8)
* little-endian bytes through the CPUState guest-AS window. A load miss
* trap-faults the CPU and yields 0; a store miss routes through the OS fault
* delivery and returns its resume PC. These back the fixed-width shims. */
@@ -258,11 +258,7 @@ u32 emu_mem_load32(EmuThread* t, u64 addr) {
return (u32)emu_mem_load_raw(t, addr, 4u);
}
u64 emu_mem_load64(EmuThread* t, u64 addr) {
- /* Two-half compose preserves the legacy per-map-boundary translation: each
- * half is bounds-checked through its own va_to_host_perm window. */
- u32 lo = emu_mem_load32(t, addr);
- u32 hi = emu_mem_load32(t, addr + 4u);
- return (u64)lo | ((u64)hi << 32);
+ return emu_mem_load_raw(t, addr, 8u);
}
u64 emu_mem_load8_checked(EmuThread* t, u64 addr, u64 fault_pc, u64 next_pc,
diff --git a/src/interp/engine.c b/src/interp/engine.c
@@ -216,6 +216,11 @@ static int interp_intrinsic(InterpStack* st, InterpFunc* fn, u64* regs,
* follow strict stack discipline (CALL bumps the top, RET rewinds it), so a
* generous fixed reservation suffices; overflow traps cleanly as a stack
* overflow rather than corrupting memory. */
+/* TODO(perf): these 16 MiB are allocated and freed per kit_interp_call /
+ * kit_interp_call_args (each spins up a fresh InterpStack). For call-heavy
+ * embeddings, reuse the arenas across calls via a per-program stack pool rather
+ * than shrinking the reservation — the size must stay generous because the
+ * arenas are non-relocating (escaping pointers; see above). */
#define INTERP_REGS_RESERVE (8u * 1024u * 1024u)
#define INTERP_MEM_RESERVE (8u * 1024u * 1024u)
@@ -921,22 +926,22 @@ KitInterpStatus interp_run_stack(InterpStack* st, int64_t* out_ret) {
/* Per-function lazy threading: copy each opcode's handler into its record on
* first entry to the function (RELOAD runs whenever the top frame changes).
*/
-#define RELOAD() \
- do { \
- fr = &st->frames[st->nframes - 1u]; \
- fn = fr->fn; \
- regs = (u64*)(st->regs_arena + fr->regs_off); \
- mem_off = fr->mem_off; \
- ip = fr->ip; \
- if (!fn->threaded) { \
- u32 ti_; \
- for (ti_ = 0; ti_ < fn->ncode; ++ti_) { \
- u32 o_ = fn->code[ti_].op; \
- fn->code[ti_].handler = \
- g_dt[o_ < (u32)IOP__COUNT ? o_ : (u32)IOP_TRAP]; \
- } \
- fn->threaded = 1; \
- } \
+#define RELOAD() \
+ do { \
+ fr = &st->frames[st->nframes - 1u]; \
+ fn = fr->fn; \
+ regs = (u64*)(st->regs_arena + fr->regs_off); \
+ mem_off = fr->mem_off; \
+ ip = fr->ip; \
+ if (!fn->threaded) { \
+ u32 ti_; \
+ for (ti_ = 0; ti_ < fn->ncode; ++ti_) { \
+ u32 o_ = fn->code[ti_].op; \
+ fn->code[ti_].handler = \
+ st->dt[o_ < (u32)IOP__COUNT ? o_ : (u32)IOP_TRAP]; \
+ } \
+ fn->threaded = 1; \
+ } \
} while (0)
#else
#define RELOAD() \
@@ -950,13 +955,11 @@ KitInterpStatus interp_run_stack(InterpStack* st, int64_t* out_ret) {
#endif
#if INTERP_DISPATCH_THREADED
- static void* g_dt[IOP__COUNT];
- static int g_dt_ready = 0;
- if (!g_dt_ready) {
-#define DT_ENTRY(name) g_dt[name] = &&L_##name;
+ if (!st->dt_ready) {
+#define DT_ENTRY(name) st->dt[name] = &&L_##name;
INTERP_OPS(DT_ENTRY)
#undef DT_ENTRY
- g_dt_ready = 1;
+ st->dt_ready = 1;
}
#endif
diff --git a/src/interp/interp.h b/src/interp/interp.h
@@ -182,6 +182,14 @@ struct KitInterpStack {
u8 status; /* KitInterpStatus */
u8 mem_fault; /* set by mem_read/write/copy on a translation miss */
const char* trap_reason;
+ /* Computed-goto dispatch table (threaded build only): per-context, populated
+ * lazily on first interp_run_stack entry. Lives here rather than as a
+ * function-local static so there is no global state / first-call race. The
+ * label addresses are identical across contexts, so the per-function handler
+ * cache (InterpFunc.threaded) stays consistent regardless of which stack
+ * threaded a given function. */
+ void* dt[IOP__COUNT];
+ u8 dt_ready;
};
typedef struct KitInterpStack InterpStack;
diff --git a/src/link/link.c b/src/link/link.c
@@ -143,7 +143,7 @@ LinkInputId link_add_obj_bytes(Linker* l, const char* name, const u8* data,
in = inputs_push(l, &id);
in->order = l->next_input_order++;
in->obj = ob; /* re-uses the ObjBuilder slot for ownership */
- in->name = name ? pool_intern_slice(l->c->global, slice_from_cstr(name)) : 0;
+ in->name = name ? pool_intern_cstr(l->c->global, name) : 0;
{
Sym soname = 0;
if (impl->classify_obj_input &&
@@ -185,7 +185,7 @@ LinkInputId link_add_dso_bytes(Linker* l, const char* name, const u8* data,
in->kind = LINK_INPUT_DSO_BYTES;
in->order = l->next_input_order++;
in->obj = ob;
- in->name = name ? pool_intern_slice(l->c->global, slice_from_cstr(name)) : 0;
+ in->name = name ? pool_intern_cstr(l->c->global, name) : 0;
/* DT_SONAME wins; fall back to the file's basename if the DSO has
* no SONAME (matches GNU ld's behaviour for hand-rolled libraries
* that forgot to set DT_SONAME). */
@@ -196,7 +196,7 @@ LinkInputId link_add_dso_bytes(Linker* l, const char* name, const u8* data,
const char* p;
for (p = name; *p; ++p)
if (*p == '/') base = p + 1;
- in->soname = pool_intern_slice(l->c->global, slice_from_cstr(base));
+ in->soname = pool_intern_cstr(l->c->global, base);
} else {
in->soname = 0;
}
@@ -239,7 +239,7 @@ LinkInputId link_add_archive_bytes(Linker* l, const char* name, const u8* data,
ar = LinkArchives_push(&l->archives, NULL);
if (!ar)
compiler_panic(l->c, SRCLOC_NONE, "link: out of memory growing archives");
- ar->name = name ? pool_intern_slice(l->c->global, slice_from_cstr(name)) : 0;
+ ar->name = name ? pool_intern_cstr(l->c->global, name) : 0;
ar->order = l->next_input_order++;
ar->whole_archive = whole_archive;
ar->link_mode = link_mode;
diff --git a/src/link/link_internal.h b/src/link/link_internal.h
@@ -387,130 +387,12 @@ SEGVEC_DEFINE(LinkRelocs, LinkRelocApply, 7); /* 128 entries per segment */
/* ---- Dynamic-link synthesis state (Phase 4) ----
*
- * Owned by LinkImage when emit_pie is set. Holds the synthesized
- * .interp / .dynsym / .dynstr / .gnu.hash / .rela.dyn / .rela.plt /
- * .plt / .got.plt / .dynamic content plus the section ids the emit
- * pass needs to fill PT_DYNAMIC and the .dynamic body.
- *
- * Phase 4 builds the dynsym/dynstr/gnu.hash content and the JUMP_SLOT
- * .rela.plt records (one per imported function, against its synthetic
- * .got.plt slot). The .plt body is allocated but not emitted (Phase 5).
- * Phase 6 populates .rela.dyn with R_AARCH64_RELATIVE records for any
- * internal absolute reloc seen during reloc-apply.
- *
- * Layout invariants this struct enforces:
- * - dynsym entry 0 is the reserved STN_UNDEF slot (zero-filled).
- * - dynsym entries 1..nimport_func+nimport_data are imports, in the
- * order PLT-functions first, then GOT-data.
- * - PLT slots and JUMP_SLOT entries match the import_func order 1:1.
- * - .got.plt has 3 reserved leading u64 slots (per AArch64 psABI:
- * slot 0 = &.dynamic, slot 1 = link_map cookie, slot 2 =
- * _dl_runtime_resolve), then one slot per imported function.
- */
-
-typedef struct DynSymRec {
- u32 st_name; /* offset into .dynstr */
- u8 st_info;
- u8 st_other;
- u16 st_shndx;
- u64 st_value;
- u64 st_size;
-} DynSymRec;
-
-typedef struct DynRela {
- u64 r_offset; /* image-relative vaddr of the patch site */
- u64 r_info; /* ELF64_R_INFO(dynsym_index, elf_reloc_type) */
- i64 r_addend;
-} DynRela;
-
-typedef struct LinkDynState {
- /* PT_INTERP / .interp. interp_path is interned in compiler->global. */
- Sym interp_path;
- LinkSectionId sec_interp;
-
- /* .dynsym */
- LinkSectionId sec_dynsym;
- DynSymRec* dynsym;
- u32 ndynsym; /* incl. slot-0 STN_UNDEF */
- u32 first_global; /* sh_info value: index of first non-local entry */
-
- /* .dynstr */
- LinkSectionId sec_dynstr;
- u8* dynstr;
- u32 dynstr_len;
-
- /* .gnu.hash */
- LinkSectionId sec_gnu_hash;
- u8* gnu_hash;
- u32 gnu_hash_len;
-
- /* GNU symbol versioning. Emitted only when at least one imported symbol
- * binds to a versioned DSO export (nverneed > 0); otherwise all three are
- * zero and no version sections / DT_VER* entries are produced (musl/static
- * links are unchanged). .gnu.version is one u16 per .dynsym entry;
- * .gnu.version_r holds Verneed/Vernaux requirements keyed by DT_NEEDED
- * soname. Both carry only .dynstr offsets + version indices (no vaddrs), so
- * the bytes are final at layout time and copied verbatim during emit. */
- LinkSectionId sec_gnu_version;
- u8* versym; /* ndynsym * 2 bytes */
- u32 versym_len;
- LinkSectionId sec_gnu_version_r;
- u8* verneed; /* nverneed Verneed records + their Vernaux */
- u32 verneed_len;
- u32 nverneed; /* DT_VERNEEDNUM */
-
- /* .rela.dyn — R_AARCH64_GLOB_DAT (imports against .got slots) and
- * R_AARCH64_RELATIVE (PIE internal abs64 fixups, populated during
- * Phase 6 emit). Pre-sized at layout time; the RELATIVE tail is
- * filled in during emit. */
- LinkSectionId sec_rela_dyn;
- DynRela* rela_dyn;
- u32 nrela_dyn; /* number of records currently populated */
- u32 cap_rela_dyn; /* allocation capacity (records, not bytes) */
-
- /* .rela.plt — R_AARCH64_JUMP_SLOT, one per imported function. */
- LinkSectionId sec_rela_plt;
- DynRela* rela_plt;
- u32 nrela_plt;
-
- /* .plt — 32-byte PLT0 stub + 16 bytes per imported function. Body
- * is allocated (zero-initialized) but not emitted in Phase 4. */
- LinkSectionId sec_plt;
- u32 nplt; /* number of imported functions */
- u64 plt_vaddr; /* image-relative .plt base */
- u64 plt_size;
-
- /* .got.plt — 24 reserved bytes + 8 per PLT slot. */
- LinkSectionId sec_got_plt;
- u64 got_plt_vaddr;
- u64 got_plt_size;
-
- /* .dynamic — PT_DYNAMIC body. Built at layout time; its size is
- * fixed once we know the DT_NEEDED count. */
- LinkSectionId sec_dynamic;
- u64 dynamic_vaddr;
- u64 dynamic_size;
- u32 ndyn_entries;
-
- /* DT_NEEDED list (interned soname Syms, in input order). */
- Sym* needed;
- u32 nneeded;
-
- /* Per-import dynsym index, indexed by LinkSymId. 0 means "not
- * imported / not in dynsym". Used by GLOB_DAT / JUMP_SLOT emit. */
- u32* sym_dynidx; /* size = sym_dynidx_size */
- u32 sym_dynidx_size;
-
- /* Per-import PLT entry vaddr, indexed by LinkSymId (Phase 5). Set
- * for every imported function: vaddr of its 16-byte PLT stub inside
- * `.plt`. 0 means "no PLT stub" (symbol is data-only or not
- * imported). apply_all_relocs reads this when redirecting a
- * CALL26/JUMP26 against an imported function — S becomes the PLT
- * entry vaddr instead of the symbol's (zero) vaddr. The vaddrs
- * stored here track the post-shift values (shift_image_addresses
- * bumps them along with .plt's segment vaddr). */
- u64* sym_plt_vaddr; /* size = sym_dynidx_size */
-} LinkDynState;
+ * The ELF linker's dynamic-link working state (.dynsym / .dynstr / .gnu.hash
+ * / .rela.* / .plt / .got.plt / .dynamic) and its wire-format records live in
+ * the ELF-only header src/obj/elf/link_dyn.h. LinkImage holds it as an opaque
+ * `LinkDynState* dyn` so the COFF/Mach-O linkers never see the ELF field
+ * names; only the ELF linker dereferences it. */
+typedef struct LinkDynState LinkDynState;
struct LinkImage {
Compiler* c;
diff --git a/src/link/link_jit.c b/src/link/link_jit.c
@@ -1310,7 +1310,7 @@ static ViewSec* view_sec_for(KitJit* jit, ViewSec* tab, u32* ntab,
ViewSec** tab_out) {
Heap* h = (Heap*)jit->c->ctx->heap;
Pool* view_pool = obj_compiler(view_ob)->global;
- Sym vn = pool_intern_slice(view_pool, slice_from_cstr(name));
+ Sym vn = pool_intern_cstr(view_pool, name);
u32 i;
for (i = 0; i < *ntab; ++i) {
if (tab[i].view_name == vn) {
diff --git a/src/link/link_layout.c b/src/link/link_layout.c
@@ -50,7 +50,9 @@ int link_section_kept(const Section* s) {
if (!(s->flags & SF_ALLOC)) return 0;
if (s->sem == SSEM_PROGBITS || s->sem == SSEM_NOBITS || s->sem == SSEM_NOTE)
return 1;
- if (s->sem == SSEM_INIT_ARRAY || s->sem == SSEM_FINI_ARRAY) return 1;
+ if (s->sem == SSEM_INIT_ARRAY || s->sem == SSEM_FINI_ARRAY ||
+ s->sem == SSEM_PREINIT_ARRAY)
+ return 1;
return 0;
}
diff --git a/src/obj/bytebuf.h b/src/obj/bytebuf.h
@@ -0,0 +1,111 @@
+#ifndef KIT_OBJ_BYTEBUF_H
+#define KIT_OBJ_BYTEBUF_H
+
+/* Heap-backed growable byte buffer — the canonical staging buffer for object
+ * writers that assemble a blob (a linkedit table, a .dynstr, a chained-fixups
+ * stream) before handing it to a Writer. The ELF and Mach-O linkers both grew
+ * their own near-identical copies; this is the shared one.
+ *
+ * Offsets returned by the append family are byte offsets into the buffer at
+ * the time of the call, which callers patch up later via the data/len fields.
+ * Allocation failure is fatal inside VEC_GROW (it panics), so callers do not
+ * need to check append return values for OOM. */
+
+#include <string.h>
+
+#include "core/core.h"
+#include "core/bytes.h"
+#include "core/util.h"
+#include "core/vec.h"
+
+typedef struct ObjByteBuf {
+ Heap* heap;
+ u8* data;
+ u32 len;
+ u32 cap;
+} ObjByteBuf;
+
+static inline void objbb_init(ObjByteBuf* b, Heap* h) {
+ b->heap = h;
+ b->data = NULL;
+ b->len = 0;
+ b->cap = 0;
+}
+
+static inline void objbb_fini(ObjByteBuf* b) {
+ if (b->data) b->heap->free(b->heap, b->data, b->cap);
+ b->data = NULL;
+ b->cap = b->len = 0;
+}
+
+static inline void objbb_reserve(ObjByteBuf* b, u32 need) {
+ if (need <= b->cap) return;
+ (void)VEC_GROW(b->heap, b->data, b->cap, need);
+}
+
+/* Zero-fill up to the next `a`-aligned length; returns the new (aligned)
+ * length so callers can record an aligned offset in one step. */
+static inline u32 objbb_align(ObjByteBuf* b, u32 a) {
+ u32 n = (u32)ALIGN_UP((u64)b->len, (u64)a);
+ if (n > b->len) {
+ objbb_reserve(b, n);
+ memset(b->data + b->len, 0, n - b->len);
+ b->len = n;
+ }
+ return b->len;
+}
+
+static inline u32 objbb_append(ObjByteBuf* b, const void* src, u32 n) {
+ u32 off = b->len;
+ objbb_reserve(b, b->len + n);
+ if (n) memcpy(b->data + b->len, src, n);
+ b->len += n;
+ return off;
+}
+
+static inline u32 objbb_u8(ObjByteBuf* b, u8 v) { return objbb_append(b, &v, 1); }
+
+static inline u32 objbb_u16(ObjByteBuf* b, u16 v) {
+ u8 t[2];
+ wr_u16_le(t, v);
+ return objbb_append(b, t, 2);
+}
+
+static inline u32 objbb_u32(ObjByteBuf* b, u32 v) {
+ u8 t[4];
+ wr_u32_le(t, v);
+ return objbb_append(b, t, 4);
+}
+
+static inline u32 objbb_u64(ObjByteBuf* b, u64 v) {
+ u8 t[8];
+ wr_u64_le(t, v);
+ return objbb_append(b, t, 8);
+}
+
+/* Append n bytes plus a NUL terminator (no dedup). */
+static inline u32 objbb_str(ObjByteBuf* b, const char* s, u32 n) {
+ u32 off = b->len;
+ objbb_reserve(b, b->len + n + 1u);
+ if (n) memcpy(b->data + b->len, s, n);
+ b->data[b->len + n] = 0;
+ b->len += n + 1u;
+ return off;
+}
+
+/* Append a NUL-terminated string with linear dedup over what we've appended
+ * so far. Strtabs (.dynstr) are small, so the scan stays cheap. An empty
+ * string maps to offset 0 (the leading NUL the caller is expected to have
+ * placed). */
+static inline u32 objbb_append_str(ObjByteBuf* b, const char* s, u32 n) {
+ if (n == 0) return 0;
+ if (b->len > n) {
+ u32 i;
+ for (i = 0; i + n < b->len; ++i) {
+ if (b->data[i + n] == 0 && memcmp(b->data + i, s, n) == 0) return i;
+ }
+ }
+ return objbb_str(b, s, n);
+}
+
+#endif
diff --git a/src/obj/coff/link.c b/src/obj/coff/link.c
@@ -225,7 +225,7 @@ typedef struct CoffTlsLayout {
} CoffTlsLayout;
static LinkSymId coff_find_sym(LinkImage* img, const char* name) {
- Sym sym = pool_intern_slice(img->c->global, slice_from_cstr(name));
+ Sym sym = pool_intern_cstr(img->c->global, name);
u32 n = LinkSyms_count(&img->syms);
u32 i;
for (i = 0; i < n; ++i) {
diff --git a/src/obj/elf/emu_load.c b/src/obj/elf/emu_load.c
@@ -132,7 +132,7 @@ static KitStatus parse_object_dynamic(EmuLoadedImage* img,
u8* dyn;
u64 dynamic_size;
u64 strtab = 0, strsz = 0;
- u64 needed_offs[16];
+ u64 needed_offs[EMU_MAX_NEEDED];
u32 nneeded_offs = 0;
u64 j;
EmuElfDynInfo* dinfo = elf_dyn(obj);
@@ -148,8 +148,12 @@ static KitStatus parse_object_dynamic(EmuLoadedImage* img,
if (tag == DT_NULL) break;
switch (tag) {
case DT_NEEDED:
- if (nneeded_offs < sizeof(needed_offs) / sizeof(needed_offs[0]))
+ if (nneeded_offs < EMU_MAX_NEEDED)
needed_offs[nneeded_offs++] = val;
+ else
+ KIT_LOGW("emu: >%d DT_NEEDED entries; extra shared-library deps "
+ "ignored",
+ EMU_MAX_NEEDED);
break;
case DT_STRTAB:
strtab = ptr;
@@ -222,8 +226,7 @@ static KitStatus parse_object_dynamic(EmuLoadedImage* img,
}
for (j = 0; j < nneeded_offs; ++j) {
u64 val = needed_offs[j];
- if (strtab && obj->imports.nneeded < sizeof(obj->imports.needed) /
- sizeof(obj->imports.needed[0])) {
+ if (strtab && obj->imports.nneeded < EMU_MAX_NEEDED) {
u8* s =
emu_addr_space_ptr(&img->addr_space, strtab + val, 1, EMU_MEM_READ);
if (s)
diff --git a/src/obj/elf/link.c b/src/obj/elf/link.c
@@ -57,6 +57,7 @@
#include "link/link_arch.h"
#include "link/link_internal.h"
#include "obj/elf/elf.h"
+#include "obj/elf/link_dyn.h"
#include "obj/format.h"
/* ---- ELF64 wire structs (subset) ---- */
diff --git a/src/obj/elf/link_dyn.c b/src/obj/elf/link_dyn.c
@@ -42,7 +42,9 @@
#include "link/link.h"
#include "link/link_arch.h"
#include "link/link_internal.h"
+#include "obj/bytebuf.h"
#include "obj/elf/elf.h"
+#include "obj/elf/link_dyn.h"
#include "obj/format.h"
/* ---- small allocators (mirror layout_iplt's helpers) ---- */
@@ -82,48 +84,7 @@ static u32 dyn_alloc_sections(LinkImage* img, u32 nsec) {
return base;
}
-/* ---- byte-builder for .dynstr / .gnu.hash ---- */
-
-typedef struct ByteBuf {
- Heap* heap;
- u8* data;
- u32 len;
- u32 cap;
-} ByteBuf;
-
-static void bb_init(ByteBuf* b, Heap* h) {
- b->heap = h;
- b->data = NULL;
- b->len = 0;
- b->cap = 0;
-}
-static void bb_reserve(ByteBuf* b, u32 need) {
- if (need <= b->cap) return;
- (void)VEC_GROW(b->heap, b->data, b->cap, need);
-}
-static u32 bb_append(ByteBuf* b, const void* src, u32 n) {
- u32 off = b->len;
- bb_reserve(b, b->len + n);
- if (n) memcpy(b->data + b->len, src, n);
- b->len += n;
- return off;
-}
-static u32 bb_append_str(ByteBuf* b, const char* s, u32 n) {
- /* Linear dedup over what we've appended so far. Strtabs are small. */
- if (n == 0) return 0;
- if (b->len > n) {
- u32 i;
- for (i = 0; i + n < b->len; ++i) {
- if (b->data[i + n] == 0 && memcmp(b->data + i, s, n) == 0) return i;
- }
- }
- u32 off = b->len;
- bb_reserve(b, b->len + n + 1u);
- memcpy(b->data + b->len, s, n);
- b->data[b->len + n] = 0;
- b->len += n + 1u;
- return off;
-}
+/* The .dynstr / .gnu.hash byte-builder is the shared ObjByteBuf (objbb_*). */
/* ---- GNU-hash computation (psABI v1 hash) ----
* Body layout:
@@ -302,7 +263,7 @@ static void collect_needed(Linker* l, LinkImage* img, LinkDynState* dyn) {
* for Scrt1.o's `environ` and `__progname` definitions. */
static void build_dynsym(LinkImage* img, LinkDynState* dyn,
- const ImportLists* il, ByteBuf* dynstr) {
+ const ImportLists* il, ObjByteBuf* dynstr) {
Heap* h = img->heap;
u32 nimports = il->nfuncs + il->ndatas;
u32 ndynsym = 1u + il->nexports + nimports; /* +1 for null slot */
@@ -318,7 +279,7 @@ static void build_dynsym(LinkImage* img, LinkDynState* dyn,
* the empty string. */
{
u8 z = 0;
- bb_append(dynstr, &z, 1);
+ objbb_append(dynstr, &z, 1);
}
/* Per-symbol: dedupe `sym_dynidx` lookup table. Sized to LinkSymId
@@ -353,7 +314,7 @@ static void build_dynsym(LinkImage* img, LinkDynState* dyn,
size_t namelen = nm_s.len;
u8 elf_type = elf_st_type(s->kind);
u8 elf_bind = elf_st_bind(s->bind);
- r->st_name = bb_append_str(dynstr, nm, (u32)namelen);
+ r->st_name = objbb_append_str(dynstr, nm, (u32)namelen);
r->st_info = ELF64_ST_INFO(elf_bind, elf_type);
r->st_other = STV_DEFAULT;
/* The emitter refreshes defined-symbol values after the final header
@@ -373,7 +334,7 @@ static void build_dynsym(LinkImage* img, LinkDynState* dyn,
Slice nm_s = pool_slice(img->c->global, s->name);
const char* nm = nm_s.s;
size_t namelen = nm_s.len;
- r->st_name = bb_append_str(dynstr, nm, (u32)namelen);
+ r->st_name = objbb_append_str(dynstr, nm, (u32)namelen);
r->st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
r->st_other = STV_DEFAULT;
r->st_shndx = SHN_UNDEF;
@@ -394,7 +355,7 @@ static void build_dynsym(LinkImage* img, LinkDynState* dyn,
elf_type = STT_TLS;
else if (s->kind == SK_NOTYPE)
elf_type = STT_NOTYPE;
- r->st_name = bb_append_str(dynstr, nm, (u32)namelen);
+ r->st_name = objbb_append_str(dynstr, nm, (u32)namelen);
r->st_info = ELF64_ST_INFO(STB_GLOBAL, elf_type);
r->st_other = STV_DEFAULT;
r->st_shndx = SHN_UNDEF;
@@ -498,7 +459,7 @@ static void ver_process_import(VerBuild* vb, LinkSymId lsid) {
}
static void build_versions(Linker* l, LinkImage* img, LinkDynState* dyn,
- const ImportLists* il, ByteBuf* dynstr) {
+ const ImportLists* il, ObjByteBuf* dynstr) {
Heap* h = img->heap;
VerBuild vb;
u32 i;
@@ -566,7 +527,7 @@ static void build_versions(Linker* l, LinkImage* img, LinkDynState* dyn,
p = vn;
for (si = 0; si < nson; ++si) {
Slice so_s = pool_slice(l->c->global, sonames[si]);
- u32 file_off = bb_append_str(dynstr, so_s.s, (u32)so_s.len);
+ u32 file_off = objbb_append_str(dynstr, so_s.s, (u32)so_s.len);
u8* vn_rec = p;
u32 cnt = 0;
u8* aux;
@@ -577,7 +538,7 @@ static void build_versions(Linker* l, LinkImage* img, LinkDynState* dyn,
u32 name_off;
if (vb.reqs[r].soname != sonames[si]) continue;
ver_s = pool_slice(l->c->global, vb.reqs[r].version);
- name_off = bb_append_str(dynstr, ver_s.s, (u32)ver_s.len);
+ name_off = objbb_append_str(dynstr, ver_s.s, (u32)ver_s.len);
wr_u32_le(p + 0, elf_sysv_hash(ver_s.s, (u32)ver_s.len)); /* vna_hash */
wr_u16_le(p + 4, 0); /* vna_flags */
wr_u16_le(p + 6, vb.reqs[r].index); /* vna_other */
@@ -624,7 +585,7 @@ static void build_versions(Linker* l, LinkImage* img, LinkDynState* dyn,
* loader's correctness check (false positives only cost a chain scan). */
static void build_gnu_hash(Heap* h, LinkImage* img, LinkDynState* dyn,
- const ByteBuf* dynstr) {
+ const ObjByteBuf* dynstr) {
u32 hashed = (dyn->ndynsym > dyn->first_global)
? (dyn->ndynsym - dyn->first_global)
: 0u;
@@ -735,7 +696,7 @@ void layout_dyn(Linker* l, LinkImage* img) {
LinkDynState* dyn;
LinkDynState dyn_probe;
ImportLists imports;
- ByteBuf dynstr;
+ ObjByteBuf dynstr;
u64 page;
const LinkArchDesc* arch;
const ObjElfArchOps* elf_arch;
@@ -799,7 +760,7 @@ void layout_dyn(Linker* l, LinkImage* img) {
* DT_NEEDED soname strings the .dynamic body references; intern
* them after the import names so build_dynsym's de-dup also covers
* any name that happens to collide with a soname. */
- bb_init(&dynstr, h);
+ objbb_init(&dynstr, h);
build_dynsym(img, dyn, &imports, &dynstr);
{
u32 ni;
@@ -807,7 +768,7 @@ void layout_dyn(Linker* l, LinkImage* img) {
Slice s_s = pool_slice(l->c->global, dyn->needed[ni]);
const char* s = s_s.s;
size_t slen = s_s.len;
- if (s && slen) (void)bb_append_str(&dynstr, s, (u32)slen);
+ if (s && slen) (void)objbb_append_str(&dynstr, s, (u32)slen);
}
}
/* Symbol versioning: assign per-import version requirements and append the
diff --git a/src/obj/elf/link_dyn.h b/src/obj/elf/link_dyn.h
@@ -0,0 +1,143 @@
+#ifndef KIT_OBJ_ELF_LINK_DYN_H
+#define KIT_OBJ_ELF_LINK_DYN_H
+
+/* ELF dynamic-link synthesis state.
+ *
+ * DynSymRec / DynRela are ELF64 wire-format records and LinkDynState is the
+ * ELF linker's working state for .dynsym / .dynstr / .gnu.hash / .rela.* /
+ * .plt / .got.plt / .dynamic. They live here, in an ELF-only header, so the
+ * format-neutral link_internal.h can carry an opaque `LinkDynState* dyn` on
+ * LinkImage without leaking ELF field names into the COFF/Mach-O linkers.
+ * Only src/obj/elf/link.c and src/obj/elf/link_dyn.c include this. */
+
+#include "core/core.h"
+#include "link/link.h"
+
+/* ---- Dynamic-link synthesis state (Phase 4) ----
+ *
+ * Owned by LinkImage when emit_pie is set. Holds the synthesized
+ * .interp / .dynsym / .dynstr / .gnu.hash / .rela.dyn / .rela.plt /
+ * .plt / .got.plt / .dynamic content plus the section ids the emit
+ * pass needs to fill PT_DYNAMIC and the .dynamic body.
+ *
+ * Phase 4 builds the dynsym/dynstr/gnu.hash content and the JUMP_SLOT
+ * .rela.plt records (one per imported function, against its synthetic
+ * .got.plt slot). The .plt body is allocated but not emitted (Phase 5).
+ * Phase 6 populates .rela.dyn with R_AARCH64_RELATIVE records for any
+ * internal absolute reloc seen during reloc-apply.
+ *
+ * Layout invariants this struct enforces:
+ * - dynsym entry 0 is the reserved STN_UNDEF slot (zero-filled).
+ * - dynsym entries 1..nimport_func+nimport_data are imports, in the
+ * order PLT-functions first, then GOT-data.
+ * - PLT slots and JUMP_SLOT entries match the import_func order 1:1.
+ * - .got.plt has 3 reserved leading u64 slots (per AArch64 psABI:
+ * slot 0 = &.dynamic, slot 1 = link_map cookie, slot 2 =
+ * _dl_runtime_resolve), then one slot per imported function.
+ */
+
+typedef struct DynSymRec {
+ u32 st_name; /* offset into .dynstr */
+ u8 st_info;
+ u8 st_other;
+ u16 st_shndx;
+ u64 st_value;
+ u64 st_size;
+} DynSymRec;
+
+typedef struct DynRela {
+ u64 r_offset; /* image-relative vaddr of the patch site */
+ u64 r_info; /* ELF64_R_INFO(dynsym_index, elf_reloc_type) */
+ i64 r_addend;
+} DynRela;
+
+typedef struct LinkDynState {
+ /* PT_INTERP / .interp. interp_path is interned in compiler->global. */
+ Sym interp_path;
+ LinkSectionId sec_interp;
+
+ /* .dynsym */
+ LinkSectionId sec_dynsym;
+ DynSymRec* dynsym;
+ u32 ndynsym; /* incl. slot-0 STN_UNDEF */
+ u32 first_global; /* sh_info value: index of first non-local entry */
+
+ /* .dynstr */
+ LinkSectionId sec_dynstr;
+ u8* dynstr;
+ u32 dynstr_len;
+
+ /* .gnu.hash */
+ LinkSectionId sec_gnu_hash;
+ u8* gnu_hash;
+ u32 gnu_hash_len;
+
+ /* GNU symbol versioning. Emitted only when at least one imported symbol
+ * binds to a versioned DSO export (nverneed > 0); otherwise all three are
+ * zero and no version sections / DT_VER* entries are produced (musl/static
+ * links are unchanged). .gnu.version is one u16 per .dynsym entry;
+ * .gnu.version_r holds Verneed/Vernaux requirements keyed by DT_NEEDED
+ * soname. Both carry only .dynstr offsets + version indices (no vaddrs), so
+ * the bytes are final at layout time and copied verbatim during emit. */
+ LinkSectionId sec_gnu_version;
+ u8* versym; /* ndynsym * 2 bytes */
+ u32 versym_len;
+ LinkSectionId sec_gnu_version_r;
+ u8* verneed; /* nverneed Verneed records + their Vernaux */
+ u32 verneed_len;
+ u32 nverneed; /* DT_VERNEEDNUM */
+
+ /* .rela.dyn — R_AARCH64_GLOB_DAT (imports against .got slots) and
+ * R_AARCH64_RELATIVE (PIE internal abs64 fixups, populated during
+ * Phase 6 emit). Pre-sized at layout time; the RELATIVE tail is
+ * filled in during emit. */
+ LinkSectionId sec_rela_dyn;
+ DynRela* rela_dyn;
+ u32 nrela_dyn; /* number of records currently populated */
+ u32 cap_rela_dyn; /* allocation capacity (records, not bytes) */
+
+ /* .rela.plt — R_AARCH64_JUMP_SLOT, one per imported function. */
+ LinkSectionId sec_rela_plt;
+ DynRela* rela_plt;
+ u32 nrela_plt;
+
+ /* .plt — 32-byte PLT0 stub + 16 bytes per imported function. Body
+ * is allocated (zero-initialized) but not emitted in Phase 4. */
+ LinkSectionId sec_plt;
+ u32 nplt; /* number of imported functions */
+ u64 plt_vaddr; /* image-relative .plt base */
+ u64 plt_size;
+
+ /* .got.plt — 24 reserved bytes + 8 per PLT slot. */
+ LinkSectionId sec_got_plt;
+ u64 got_plt_vaddr;
+ u64 got_plt_size;
+
+ /* .dynamic — PT_DYNAMIC body. Built at layout time; its size is
+ * fixed once we know the DT_NEEDED count. */
+ LinkSectionId sec_dynamic;
+ u64 dynamic_vaddr;
+ u64 dynamic_size;
+ u32 ndyn_entries;
+
+ /* DT_NEEDED list (interned soname Syms, in input order). */
+ Sym* needed;
+ u32 nneeded;
+
+ /* Per-import dynsym index, indexed by LinkSymId. 0 means "not
+ * imported / not in dynsym". Used by GLOB_DAT / JUMP_SLOT emit. */
+ u32* sym_dynidx; /* size = sym_dynidx_size */
+ u32 sym_dynidx_size;
+
+ /* Per-import PLT entry vaddr, indexed by LinkSymId (Phase 5). Set
+ * for every imported function: vaddr of its 16-byte PLT stub inside
+ * `.plt`. 0 means "no PLT stub" (symbol is data-only or not
+ * imported). apply_all_relocs reads this when redirecting a
+ * CALL26/JUMP26 against an imported function — S becomes the PLT
+ * entry vaddr instead of the symbol's (zero) vaddr. The vaddrs
+ * stored here track the post-shift values (shift_image_addresses
+ * bumps them along with .plt's segment vaddr). */
+ u64* sym_plt_vaddr; /* size = sym_dynidx_size */
+} LinkDynState;
+
+#endif
diff --git a/src/obj/macho/link.c b/src/obj/macho/link.c
@@ -51,6 +51,7 @@
#include "link/link_arch.h"
#include "link/link_internal.h"
#include "link/link_reloc_desc.h"
+#include "obj/bytebuf.h"
#include "obj/format.h"
#include "obj/macho/macho.h"
@@ -84,70 +85,9 @@
#define LC_DATA_IN_CODE_C 0x29u
#define LC_CODE_SIGNATURE_C 0x1du
-/* ---- byte buffer ---- */
-
-typedef struct MByte {
- Heap* heap;
- u8* data;
- u32 len;
- u32 cap;
-} MByte;
-
-static void mbuf_init(MByte* b, Heap* h) {
- b->heap = h;
- b->data = NULL;
- b->len = 0;
- b->cap = 0;
-}
-static void mbuf_fini(MByte* b) {
- if (b->data) b->heap->free(b->heap, b->data, b->cap);
- b->data = NULL;
- b->cap = b->len = 0;
-}
-static void mbuf_reserve(MByte* b, u32 need) {
- if (need <= b->cap) return;
- (void)VEC_GROW(b->heap, b->data, b->cap, need);
-}
-static u32 mbuf_align(MByte* b, u32 a) {
- u32 n = (u32)ALIGN_UP((u64)b->len, (u64)a);
- if (n > b->len) {
- mbuf_reserve(b, n);
- memset(b->data + b->len, 0, n - b->len);
- b->len = n;
- }
- return b->len;
-}
-static u32 mbuf_append(MByte* b, const void* src, u32 n) {
- u32 off = b->len;
- mbuf_reserve(b, b->len + n);
- if (n) memcpy(b->data + b->len, src, n);
- b->len += n;
- return off;
-}
-static u32 mbuf_u32(MByte* b, u32 v) {
- u8 t[4];
- wr_u32_le(t, v);
- return mbuf_append(b, t, 4);
-}
-static u32 mbuf_u16(MByte* b, u16 v) {
- u8 t[2];
- wr_u16_le(t, v);
- return mbuf_append(b, t, 2);
-}
-static u32 mbuf_u64(MByte* b, u64 v) {
- u8 t[8];
- wr_u64_le(t, v);
- return mbuf_append(b, t, 8);
-}
-static u32 mbuf_u8(MByte* b, u8 v) { return mbuf_append(b, &v, 1); }
-static u32 mbuf_str(MByte* b, const char* s, u32 n) {
- u32 off = b->len;
- mbuf_reserve(b, b->len + n + 1u);
- if (n) memcpy(b->data + b->len, s, n);
- b->data[b->len + n] = 0;
- b->len += n + 1u;
- return off;
-}
+/* ---- byte buffer ----
+ * Linkedit tables / load commands / the output file are staged in the shared
+ * ObjByteBuf (objbb_*). */
/* ---- imports + dylibs ---- */
@@ -348,14 +288,14 @@ typedef struct MCtx {
u64 headers_size; /* header + loadcmds */
/* LINKEDIT contents */
- MByte chained_fixups;
- MByte exports_trie;
- MByte symtab; /* binary nlist_64 array */
- MByte strtab;
- MByte indirect; /* u32 array */
- MByte fn_starts;
- MByte data_in_code;
- MByte codesig;
+ ObjByteBuf chained_fixups;
+ ObjByteBuf exports_trie;
+ ObjByteBuf symtab; /* binary nlist_64 array */
+ ObjByteBuf strtab;
+ ObjByteBuf indirect; /* u32 array */
+ ObjByteBuf fn_starts;
+ ObjByteBuf data_in_code;
+ ObjByteBuf codesig;
u32 chained_fixups_off;
u32 exports_trie_off;
@@ -1681,8 +1621,8 @@ static void emit_pointer(u8* slot, int is_bind, u32 ord_or_target_lo,
static void build_chained_fixups(MCtx* x, FixList* fl) {
Heap* h = x->h;
- MByte* out = &x->chained_fixups;
- mbuf_init(out, h);
+ ObjByteBuf* out = &x->chained_fixups;
+ objbb_init(out, h);
/* Header (32 B):
* uint32 fixups_version (=0)
@@ -1693,16 +1633,16 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
* uint32 imports_format (=1)
* uint32 symbols_format (=0)
*/
- u32 hdr_pos = mbuf_u32(out, 0); /* fixups_version */
+ u32 hdr_pos = objbb_u32(out, 0); /* fixups_version */
(void)hdr_pos;
- u32 starts_offset_pos = mbuf_u32(out, 0);
- u32 imports_offset_pos = mbuf_u32(out, 0);
- u32 symbols_offset_pos = mbuf_u32(out, 0);
- mbuf_u32(out, x->nimports_real);
- mbuf_u32(out, DYLD_CHAINED_IMPORT);
- mbuf_u32(out, 0); /* symbols uncompressed */
+ u32 starts_offset_pos = objbb_u32(out, 0);
+ u32 imports_offset_pos = objbb_u32(out, 0);
+ u32 symbols_offset_pos = objbb_u32(out, 0);
+ objbb_u32(out, x->nimports_real);
+ objbb_u32(out, DYLD_CHAINED_IMPORT);
+ objbb_u32(out, 0); /* symbols uncompressed */
/* dyld expects 8-byte alignment of the starts table. */
- mbuf_align(out, 4);
+ objbb_align(out, 4);
/* dyld_chained_starts_in_image:
* uint32 seg_count
@@ -1713,10 +1653,10 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
*/
u32 starts_off = out->len;
wr_u32_le(out->data + starts_offset_pos, starts_off);
- mbuf_u32(out, x->nsegs);
+ objbb_u32(out, x->nsegs);
/* Reserve seg_info_offset[]. */
u32 seg_info_offsets_pos = out->len;
- for (u32 i = 0; i < x->nsegs; ++i) mbuf_u32(out, 0);
+ for (u32 i = 0; i < x->nsegs; ++i) objbb_u32(out, 0);
/* Sort fixsites by vaddr globally. */
sort_sites(fl->a, fl->n);
@@ -1733,7 +1673,7 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
}
if (!count) continue;
/* Page-align this struct to 4. */
- mbuf_align(out, 4);
+ objbb_align(out, 4);
u32 sis_off = out->len;
/* Patch seg_info_offset[si] to (sis_off - starts_off). */
wr_u32_le(out->data + seg_info_offsets_pos + si * 4u, sis_off - starts_off);
@@ -1753,14 +1693,14 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
* uint16 page_count
* uint16 page_start[page_count] (0xFFFF = no fixups in page)
*/
- u32 sis_size_pos = mbuf_u32(out, 0); /* fill below */
- mbuf_u16(out, (u16)MZ_PAGE);
- mbuf_u16(out, (u16)DYLD_CHAINED_PTR_64);
- mbuf_u64(out, (u64)x->segs[si].fileoff); /* segment file offset */
- mbuf_u32(out, 0);
- mbuf_u16(out, (u16)page_count);
+ u32 sis_size_pos = objbb_u32(out, 0); /* fill below */
+ objbb_u16(out, (u16)MZ_PAGE);
+ objbb_u16(out, (u16)DYLD_CHAINED_PTR_64);
+ objbb_u64(out, (u64)x->segs[si].fileoff); /* segment file offset */
+ objbb_u32(out, 0);
+ objbb_u16(out, (u16)page_count);
u32 page_starts_pos = out->len;
- for (u32 p = 0; p < page_count; ++p) mbuf_u16(out, 0xFFFFu);
+ for (u32 p = 0; p < page_count; ++p) objbb_u16(out, 0xFFFFu);
/* size includes the page_start array */
u32 sis_size = out->len - sis_size_pos + 4u;
/* Hmm, the `size` field is the size of *this* struct. We measure
@@ -1847,17 +1787,17 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
/* Imports table: one dyld_chained_import (4B) per real import.
* Layout: lib_ordinal:8, weak:1, name_offset:23. Internal-GOT
* entries are not bound by dyld so they're omitted here. */
- mbuf_align(out, 4);
+ objbb_align(out, 4);
u32 imports_off = out->len;
wr_u32_le(out->data + imports_offset_pos, imports_off);
/* We need to first build the symbol pool to know name offsets. */
u32 symbols_off = imports_off + x->nimports_real * 4u;
/* Reserve imports area. */
- for (u32 i = 0; i < x->nimports_real; ++i) mbuf_u32(out, 0);
+ for (u32 i = 0; i < x->nimports_real; ++i) objbb_u32(out, 0);
/* Emit symbols (each NUL-terminated). Set name_offset on each import. */
wr_u32_le(out->data + symbols_offset_pos, out->len);
/* Leading NUL for offset 0. */
- mbuf_u8(out, 0);
+ objbb_u8(out, 0);
for (u32 i = 0; i < x->nimports_real; ++i) {
MachImp* mi = &x->imports[i];
Slice nm_s = pool_slice(x->c->global, mi->name);
@@ -1871,7 +1811,7 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
(unsigned)x->ndylibs);
}
u32 off = out->len - symbols_off;
- mbuf_str(out, nm, (u32)nl);
+ objbb_str(out, nm, (u32)nl);
/* Patch the import slot. */
u32 packed = ((u32)mi->dylib_ord & 0xffu) |
((u32)(mi->weak ? 1u : 0u) << 8) | ((off & 0x7fffffu) << 9);
@@ -1886,12 +1826,12 @@ static void build_chained_fixups(MCtx* x, FixList* fl) {
* entry symbol's VA-relative offset. This is enough for dyld; binaries
* with a real exports trie include more data but we don't need it. */
-static void uleb128(MByte* out, u64 v) {
+static void uleb128(ObjByteBuf* out, u64 v) {
do {
u8 byte = v & 0x7fu;
v >>= 7;
if (v) byte |= 0x80u;
- mbuf_u8(out, byte);
+ objbb_u8(out, byte);
} while (v);
}
@@ -1915,23 +1855,23 @@ static void build_exports_trie(MCtx* x) {
* Easiest: single root node with children_count=1, child label = "_main",
* child offset points to a leaf node.
*/
- MByte* out = &x->exports_trie;
- mbuf_init(out, x->h);
+ ObjByteBuf* out = &x->exports_trie;
+ objbb_init(out, x->h);
LinkImage* img = x->img;
LinkSymbol* esym = sym_at(img, img->entry_sym);
if (!esym || !esym->defined) {
/* No entry — emit a single empty terminal trie. */
- mbuf_u8(out, 0); /* terminal_size 0 */
- mbuf_u8(out, 0); /* children 0 */
+ objbb_u8(out, 0); /* terminal_size 0 */
+ objbb_u8(out, 0); /* children 0 */
return;
}
Slice nm_s = pool_slice(x->c->global, esym->name);
const char* nm = nm_s.s;
size_t nl = nm_s.len;
if (!nm || nl == 0) {
- mbuf_u8(out, 0);
- mbuf_u8(out, 0);
+ objbb_u8(out, 0);
+ objbb_u8(out, 0);
return;
}
/* leaf node: terminal_size = sizeof(uleb(flags)+uleb(offset))
@@ -1957,21 +1897,21 @@ static void build_exports_trie(MCtx* x) {
leaf_pos = next;
}
- mbuf_u8(out, 0); /* root terminal size */
- mbuf_u8(out, 1); /* children_count */
- mbuf_str(out, nm, (u32)nl);
+ objbb_u8(out, 0); /* root terminal size */
+ objbb_u8(out, 1); /* children_count */
+ objbb_str(out, nm, (u32)nl);
uleb128(out, leaf_pos);
/* leaf node */
if (out->len != leaf_pos)
compiler_panic(x->c, SRCLOC_NONE,
"macho: exports trie leaf offset mismatch");
/* terminal_size byte then payload */
- mbuf_u8(out, (u8)leaf_payload_len);
+ objbb_u8(out, (u8)leaf_payload_len);
uleb128(out, flags);
uleb128(out, entry_off);
- mbuf_u8(out, 0); /* children_count */
+ objbb_u8(out, 0); /* children_count */
/* Pad trie to 8 bytes. */
- mbuf_align(out, 8);
+ objbb_align(out, 8);
}
/* ---- symtab + strtab + indirect symtab ---- */
@@ -1987,12 +1927,12 @@ typedef struct NlistRec {
static void build_symtab(MCtx* x) {
Heap* h = x->h;
LinkImage* img = x->img;
- mbuf_init(&x->symtab, h);
- mbuf_init(&x->strtab, h);
- mbuf_init(&x->indirect, h);
+ objbb_init(&x->symtab, h);
+ objbb_init(&x->strtab, h);
+ objbb_init(&x->indirect, h);
/* strtab leading NUL */
- mbuf_u8(&x->strtab, 0);
+ objbb_u8(&x->strtab, 0);
/* Approach:
* - Add one local nlist per defined LinkSymbol (locals + non-imported
@@ -2049,7 +1989,7 @@ static void build_symtab(MCtx* x) {
const char* nm = nm_s.s;
size_t nl = nm_s.len;
u32 strx = x->strtab.len;
- if (nm && nl) mbuf_str(&x->strtab, nm, (u32)nl);
+ if (nm && nl) objbb_str(&x->strtab, nm, (u32)nl);
u8 t[16];
u8 nt = N_SECT | N_EXT;
@@ -2061,7 +2001,7 @@ static void build_symtab(MCtx* x) {
t[5] = n_sect;
wr_u16_le(t + 6, s->bind == SB_WEAK ? N_WEAK_DEF : 0);
wr_u64_le(t + 8, s->vaddr);
- mbuf_append(&x->symtab, t, 16);
+ objbb_append(&x->symtab, t, 16);
++n_extdef;
}
@@ -2074,7 +2014,7 @@ static void build_symtab(MCtx* x) {
const char* nm = nm_s.s;
size_t nl = nm_s.len;
u32 strx = x->strtab.len;
- if (nm && nl) mbuf_str(&x->strtab, nm, (u32)nl);
+ if (nm && nl) objbb_str(&x->strtab, nm, (u32)nl);
u8 t[16];
wr_u32_le(t + 0, strx);
@@ -2086,7 +2026,7 @@ static void build_symtab(MCtx* x) {
if (mi->weak) desc |= N_WEAK_REF;
wr_u16_le(t + 6, desc);
wr_u64_le(t + 8, 0);
- mbuf_append(&x->symtab, t, 16);
+ objbb_append(&x->symtab, t, 16);
++n_undef;
}
@@ -2105,7 +2045,7 @@ static void build_symtab(MCtx* x) {
MachImp* mi = &x->imports[k];
if (!mi->stub_idx) continue;
u32 sym_idx = imp_first_symtab_idx + k;
- mbuf_u32(&x->indirect, sym_idx);
+ objbb_u32(&x->indirect, sym_idx);
++indirect_start;
}
}
@@ -2118,7 +2058,7 @@ static void build_symtab(MCtx* x) {
MachImp* mi = &x->imports[k];
u32 sym_idx = mi->internal ? 0x80000000u /* INDIRECT_SYMBOL_LOCAL */
: (imp_first_symtab_idx + k);
- mbuf_u32(&x->indirect, sym_idx);
+ objbb_u32(&x->indirect, sym_idx);
++indirect_start;
}
}
@@ -2140,10 +2080,10 @@ static void layout_linkedit(MCtx* x) {
/* LC_FUNCTION_STARTS is a ULEB128 stream terminated by a zero byte. Keep a
* real empty table here so tools that rewrite LINKEDIT preserve the
* canonical blob order between exports and the symbol table. */
- mbuf_init(&x->fn_starts, x->h);
- mbuf_u8(&x->fn_starts, 0);
- mbuf_init(&x->data_in_code, x->h);
- mbuf_init(&x->codesig, x->h);
+ objbb_init(&x->fn_starts, x->h);
+ objbb_u8(&x->fn_starts, 0);
+ objbb_init(&x->data_in_code, x->h);
+ objbb_init(&x->codesig, x->h);
u64 cur = x->linkedit_fileoff;
/* chained fixups */
@@ -2242,9 +2182,9 @@ static void build_codesig_skeleton(MCtx* x, u32 code_limit, const char* ident) {
/* SuperBlob: 12 hdr + 8 slot + cd. */
u32 sb_size = 12u + 8u + cd_size;
- MByte* out = &x->codesig;
- mbuf_init(out, x->h);
- mbuf_reserve(out, sb_size);
+ ObjByteBuf* out = &x->codesig;
+ objbb_init(out, x->h);
+ objbb_reserve(out, sb_size);
memset(out->data, 0, sb_size);
out->len = sb_size;
@@ -2311,27 +2251,27 @@ static void compute_codesig(MCtx* x, const u8* full_file, u32 file_len_excl_cs,
/* ---- final emission ---- */
-static void emit_load_command_segment(MByte* lc, MCtx* x, u32 segidx) {
+static void emit_load_command_segment(ObjByteBuf* lc, MCtx* x, u32 segidx) {
MSeg* sg = &x->segs[segidx];
u32 seg_cmd_size = MACHO_SEGCMD64_SIZE + sg->nouts * MACHO_SECT64_SIZE;
u32 base = lc->len;
- mbuf_u32(lc, LC_SEGMENT_64);
- mbuf_u32(lc, seg_cmd_size);
+ objbb_u32(lc, LC_SEGMENT_64);
+ objbb_u32(lc, seg_cmd_size);
/* segname: 16 bytes zero-padded */
u8 nm[16];
memset(nm, 0, 16);
size_t nlen = slice_from_cstr(sg->name).len;
if (nlen > 16) nlen = 16;
memcpy(nm, sg->name, nlen);
- mbuf_append(lc, nm, 16);
- mbuf_u64(lc, sg->vmaddr);
- mbuf_u64(lc, sg->vmsize);
- mbuf_u64(lc, sg->fileoff);
- mbuf_u64(lc, sg->filesize);
- mbuf_u32(lc, sg->maxprot);
- mbuf_u32(lc, sg->initprot);
- mbuf_u32(lc, sg->nouts);
- mbuf_u32(lc, 0); /* flags */
+ objbb_append(lc, nm, 16);
+ objbb_u64(lc, sg->vmaddr);
+ objbb_u64(lc, sg->vmsize);
+ objbb_u64(lc, sg->fileoff);
+ objbb_u64(lc, sg->filesize);
+ objbb_u32(lc, sg->maxprot);
+ objbb_u32(lc, sg->initprot);
+ objbb_u32(lc, sg->nouts);
+ objbb_u32(lc, 0); /* flags */
for (u32 j = 0; j < sg->nouts; ++j) {
OutSec* o = &x->outs[sg->first_out + j];
@@ -2344,22 +2284,22 @@ static void emit_load_command_segment(MByte* lc, MCtx* x, u32 segidx) {
size_t gl = slice_from_cstr(sg->name).len; /* segname must match */
if (gl > 16) gl = 16;
memcpy(gname, sg->name, gl);
- mbuf_append(lc, sname, 16);
- mbuf_append(lc, gname, 16);
- mbuf_u64(lc, o->vaddr);
- mbuf_u64(lc, o->size);
- mbuf_u32(lc, (u32)o->file_offset);
+ objbb_append(lc, sname, 16);
+ objbb_append(lc, gname, 16);
+ objbb_u64(lc, o->vaddr);
+ objbb_u64(lc, o->size);
+ objbb_u32(lc, (u32)o->file_offset);
/* align is power of 2; encode as log2. */
u32 a = o->align ? o->align : 1u;
u32 al = 0;
while ((1u << al) < a) ++al;
- mbuf_u32(lc, al);
- mbuf_u32(lc, 0); /* reloff */
- mbuf_u32(lc, 0); /* nreloc */
- mbuf_u32(lc, o->flags);
- mbuf_u32(lc, o->reserved1);
- mbuf_u32(lc, o->reserved2);
- mbuf_u32(lc, 0); /* reserved3 */
+ objbb_u32(lc, al);
+ objbb_u32(lc, 0); /* reloff */
+ objbb_u32(lc, 0); /* nreloc */
+ objbb_u32(lc, o->flags);
+ objbb_u32(lc, o->reserved1);
+ objbb_u32(lc, o->reserved2);
+ objbb_u32(lc, 0); /* reserved3 */
}
(void)base;
}
@@ -2427,8 +2367,8 @@ void link_emit_macho(LinkImage* img, Writer* w) {
x.segs[MSEG_LINKEDIT].vmsize = ALIGN_UP(le_size, MZ_PAGE);
/* Build load commands buffer. */
- MByte lc;
- mbuf_init(&lc, x.h);
+ ObjByteBuf lc;
+ objbb_init(&lc, x.h);
/* LC_SEGMENT_64 for each segment with sections (and PAGEZERO/LINKEDIT). */
emit_load_command_segment(&lc, &x, 0); /* PAGEZERO */
@@ -2441,24 +2381,24 @@ void link_emit_macho(LinkImage* img, Writer* w) {
emit_load_command_segment(&lc, &x, MSEG_LINKEDIT); /* LINKEDIT */
/* LC_DYLD_CHAINED_FIXUPS (linkedit_data_command: 16B) */
- mbuf_u32(&lc, LC_DYLD_CHAINED_FIXUPS);
- mbuf_u32(&lc, 16);
- mbuf_u32(&lc, x.chained_fixups_off);
- mbuf_u32(&lc, x.chained_fixups.len);
+ objbb_u32(&lc, LC_DYLD_CHAINED_FIXUPS);
+ objbb_u32(&lc, 16);
+ objbb_u32(&lc, x.chained_fixups_off);
+ objbb_u32(&lc, x.chained_fixups.len);
/* LC_DYLD_EXPORTS_TRIE */
- mbuf_u32(&lc, LC_DYLD_EXPORTS_TRIE);
- mbuf_u32(&lc, 16);
- mbuf_u32(&lc, x.exports_trie_off);
- mbuf_u32(&lc, x.exports_trie.len);
+ objbb_u32(&lc, LC_DYLD_EXPORTS_TRIE);
+ objbb_u32(&lc, 16);
+ objbb_u32(&lc, x.exports_trie_off);
+ objbb_u32(&lc, x.exports_trie.len);
/* LC_SYMTAB */
- mbuf_u32(&lc, LC_SYMTAB);
- mbuf_u32(&lc, MACHO_SYMTAB_CMD_SIZE);
- mbuf_u32(&lc, x.symtab_off);
- mbuf_u32(&lc, x.nsyms);
- mbuf_u32(&lc, x.strtab_off);
- mbuf_u32(&lc, x.strtab.len);
+ objbb_u32(&lc, LC_SYMTAB);
+ objbb_u32(&lc, MACHO_SYMTAB_CMD_SIZE);
+ objbb_u32(&lc, x.symtab_off);
+ objbb_u32(&lc, x.nsyms);
+ objbb_u32(&lc, x.strtab_off);
+ objbb_u32(&lc, x.strtab.len);
/* LC_DYSYMTAB */
/* nlocal=0, nextdef=#defined-globals, nundef=#imports. We tracked
@@ -2467,39 +2407,39 @@ void link_emit_macho(LinkImage* img, Writer* w) {
u32 nlocal = 0;
u32 nundef = x.nimports_real;
u32 nextdef = (x.nsyms > nundef) ? x.nsyms - nundef - nlocal : 0;
- mbuf_u32(&lc, LC_DYSYMTAB);
- mbuf_u32(&lc, MACHO_DYSYMTAB_CMD_SIZE);
- mbuf_u32(&lc, 0); /* ilocalsym */
- mbuf_u32(&lc, nlocal);
- mbuf_u32(&lc, nlocal);
- mbuf_u32(&lc, nextdef);
- mbuf_u32(&lc, nlocal + nextdef);
- mbuf_u32(&lc, nundef);
- mbuf_u32(&lc, 0);
- mbuf_u32(&lc, 0); /* tocoff, ntoc */
- mbuf_u32(&lc, 0);
- mbuf_u32(&lc, 0); /* modtaboff, nmodtab */
- mbuf_u32(&lc, 0);
- mbuf_u32(&lc, 0); /* extrefsymoff, nextrefsyms */
- mbuf_u32(&lc, x.indirect_off);
- mbuf_u32(&lc, x.indirect.len / 4u);
- mbuf_u32(&lc, 0);
- mbuf_u32(&lc, 0); /* extreloff, nextrel */
- mbuf_u32(&lc, 0);
- mbuf_u32(&lc, 0); /* locreloff, nlocrel */
+ objbb_u32(&lc, LC_DYSYMTAB);
+ objbb_u32(&lc, MACHO_DYSYMTAB_CMD_SIZE);
+ objbb_u32(&lc, 0); /* ilocalsym */
+ objbb_u32(&lc, nlocal);
+ objbb_u32(&lc, nlocal);
+ objbb_u32(&lc, nextdef);
+ objbb_u32(&lc, nlocal + nextdef);
+ objbb_u32(&lc, nundef);
+ objbb_u32(&lc, 0);
+ objbb_u32(&lc, 0); /* tocoff, ntoc */
+ objbb_u32(&lc, 0);
+ objbb_u32(&lc, 0); /* modtaboff, nmodtab */
+ objbb_u32(&lc, 0);
+ objbb_u32(&lc, 0); /* extrefsymoff, nextrefsyms */
+ objbb_u32(&lc, x.indirect_off);
+ objbb_u32(&lc, x.indirect.len / 4u);
+ objbb_u32(&lc, 0);
+ objbb_u32(&lc, 0); /* extreloff, nextrel */
+ objbb_u32(&lc, 0);
+ objbb_u32(&lc, 0); /* locreloff, nlocrel */
/* LC_LOAD_DYLINKER */
{
const char* dyld = "/usr/lib/dyld";
u32 dyld_len = (u32)slice_from_cstr(dyld).len;
u32 cmd_size = (u32)ALIGN_UP((u64)(12u + dyld_len + 1u), 8u);
- mbuf_u32(&lc, LC_LOAD_DYLINKER);
- mbuf_u32(&lc, cmd_size);
- mbuf_u32(&lc, 12u); /* name offset within cmd */
- u32 wrote = mbuf_str(&lc, dyld, dyld_len);
+ objbb_u32(&lc, LC_LOAD_DYLINKER);
+ objbb_u32(&lc, cmd_size);
+ objbb_u32(&lc, 12u); /* name offset within cmd */
+ u32 wrote = objbb_str(&lc, dyld, dyld_len);
(void)wrote;
/* Pad to cmd_size. */
- while (lc.len < (u32)((u64)mbuf_align(&lc, 1) + 0)) {
+ while (lc.len < (u32)((u64)objbb_align(&lc, 1) + 0)) {
/* no-op */
break;
}
@@ -2508,29 +2448,29 @@ void link_emit_macho(LinkImage* img, Writer* w) {
/* Walk back: lc grew by 12 + (strlen+1). Pad to cmd_size. */
u32 cmd_start_back = lc.len - (12u + dyld_len + 1u);
u32 pad_needed = cmd_size - (lc.len - cmd_start_back);
- while (pad_needed-- > 0) mbuf_u8(&lc, 0);
+ while (pad_needed-- > 0) objbb_u8(&lc, 0);
(void)want;
}
/* LC_UUID */
- mbuf_u32(&lc, LC_UUID);
- mbuf_u32(&lc, 24);
- mbuf_append(&lc, x.uuid, 16);
+ objbb_u32(&lc, LC_UUID);
+ objbb_u32(&lc, 24);
+ objbb_append(&lc, x.uuid, 16);
/* LC_BUILD_VERSION */
- mbuf_u32(&lc, LC_BUILD_VERSION);
- mbuf_u32(&lc, 24);
- mbuf_u32(&lc, 1); /* PLATFORM_MACOS */
- mbuf_u32(&lc, (12u << 16) | 0); /* minos 12.0.0 */
- mbuf_u32(&lc, (12u << 16) | 0); /* sdk 12.0.0 */
- mbuf_u32(&lc, 0); /* ntools */
+ objbb_u32(&lc, LC_BUILD_VERSION);
+ objbb_u32(&lc, 24);
+ objbb_u32(&lc, 1); /* PLATFORM_MACOS */
+ objbb_u32(&lc, (12u << 16) | 0); /* minos 12.0.0 */
+ objbb_u32(&lc, (12u << 16) | 0); /* sdk 12.0.0 */
+ objbb_u32(&lc, 0); /* ntools */
/* LC_MAIN — entryoff is offset within __TEXT segment from its file
* start (0). */
- mbuf_u32(&lc, LC_MAIN);
- mbuf_u32(&lc, 24);
- mbuf_u64(&lc, (u64)x.entry_offset); /* entryoff = vaddr - __TEXT.vmaddr */
- mbuf_u64(&lc, 0); /* stacksize */
+ objbb_u32(&lc, LC_MAIN);
+ objbb_u32(&lc, 24);
+ objbb_u64(&lc, (u64)x.entry_offset); /* entryoff = vaddr - __TEXT.vmaddr */
+ objbb_u64(&lc, 0); /* stacksize */
/* LC_LOAD_DYLIB per dylib. */
for (u32 i = 0; i < x.ndylibs; ++i) {
@@ -2539,32 +2479,32 @@ void link_emit_macho(LinkImage* img, Writer* w) {
size_t nl = nm_s.len;
u32 cmd_size = (u32)ALIGN_UP((u64)(24u + (u32)nl + 1u), 8u);
u32 cmd_start = lc.len;
- mbuf_u32(&lc, LC_LOAD_DYLIB);
- mbuf_u32(&lc, cmd_size);
- mbuf_u32(&lc, 24u); /* name offset */
- mbuf_u32(&lc, 0); /* timestamp */
- mbuf_u32(&lc, (1u << 16)); /* current_version 1.0 */
- mbuf_u32(&lc, (1u << 16)); /* compat_version 1.0 */
- mbuf_str(&lc, nm ? nm : "", (u32)nl);
- while (lc.len - cmd_start < cmd_size) mbuf_u8(&lc, 0);
+ objbb_u32(&lc, LC_LOAD_DYLIB);
+ objbb_u32(&lc, cmd_size);
+ objbb_u32(&lc, 24u); /* name offset */
+ objbb_u32(&lc, 0); /* timestamp */
+ objbb_u32(&lc, (1u << 16)); /* current_version 1.0 */
+ objbb_u32(&lc, (1u << 16)); /* compat_version 1.0 */
+ objbb_str(&lc, nm ? nm : "", (u32)nl);
+ while (lc.len - cmd_start < cmd_size) objbb_u8(&lc, 0);
}
/* LC_FUNCTION_STARTS / LC_DATA_IN_CODE */
- mbuf_u32(&lc, LC_FUNCTION_STARTS_C);
- mbuf_u32(&lc, 16);
- mbuf_u32(&lc, x.fn_starts_off);
- mbuf_u32(&lc, x.fn_starts.len);
+ objbb_u32(&lc, LC_FUNCTION_STARTS_C);
+ objbb_u32(&lc, 16);
+ objbb_u32(&lc, x.fn_starts_off);
+ objbb_u32(&lc, x.fn_starts.len);
- mbuf_u32(&lc, LC_DATA_IN_CODE_C);
- mbuf_u32(&lc, 16);
- mbuf_u32(&lc, x.data_in_code_off);
- mbuf_u32(&lc, 0);
+ objbb_u32(&lc, LC_DATA_IN_CODE_C);
+ objbb_u32(&lc, 16);
+ objbb_u32(&lc, x.data_in_code_off);
+ objbb_u32(&lc, 0);
/* LC_CODE_SIGNATURE */
- mbuf_u32(&lc, LC_CODE_SIGNATURE_C);
- mbuf_u32(&lc, 16);
- mbuf_u32(&lc, x.codesig_off);
- mbuf_u32(&lc, x.codesig_size);
+ objbb_u32(&lc, LC_CODE_SIGNATURE_C);
+ objbb_u32(&lc, 16);
+ objbb_u32(&lc, x.codesig_off);
+ objbb_u32(&lc, x.codesig_size);
/* Sanity: lc.len + MACHO_HDR64_SIZE must equal headers_size we
* predicted in plan_layout. If not, we mis-sized — panic. */
@@ -2578,8 +2518,8 @@ void link_emit_macho(LinkImage* img, Writer* w) {
/* ---- now stream the file ---- */
/* The Writer in kit allows seek; we'll write a flat buffer first
* (so we can hash it for codesig) and flush at the end. */
- MByte file;
- mbuf_init(&file, x.h);
+ ObjByteBuf file;
+ objbb_init(&file, x.h);
/* mach_header_64 */
u32 ncmds = 0;
@@ -2596,12 +2536,12 @@ void link_emit_macho(LinkImage* img, Writer* w) {
/* (chained, exports_trie, symtab, dysymtab, dyld, uuid, build_version,
* main, fn_starts, data_in_code, codesig) = 11 */
- mbuf_u32(&file, MH_MAGIC_64);
- mbuf_u32(&file, x.macho->cputype);
- mbuf_u32(&file, x.macho->cpusubtype);
- mbuf_u32(&file, MH_EXECUTE);
- mbuf_u32(&file, ncmds);
- mbuf_u32(&file, lc.len);
+ objbb_u32(&file, MH_MAGIC_64);
+ objbb_u32(&file, x.macho->cputype);
+ objbb_u32(&file, x.macho->cpusubtype);
+ objbb_u32(&file, MH_EXECUTE);
+ objbb_u32(&file, ncmds);
+ objbb_u32(&file, lc.len);
{
u32 mh_flags = MH_DYLDLINK | MH_TWOLEVEL | MH_NOUNDEFS | MH_PIE;
/* dyld scans __thread_vars and allocates a pthread_key for each
@@ -2609,10 +2549,10 @@ void link_emit_macho(LinkImage* img, Writer* w) {
* thunk pointer is silently patched to _tlv_bootstrap_error. Apple's
* ld sets it whenever the image contains S_THREAD_LOCAL_* sections. */
if (x.ntlv) mh_flags |= MH_HAS_TLV_DESCRIPTORS;
- mbuf_u32(&file, mh_flags);
+ objbb_u32(&file, mh_flags);
}
- mbuf_u32(&file, 0); /* reserved */
- mbuf_append(&file, lc.data, lc.len);
+ objbb_u32(&file, 0); /* reserved */
+ objbb_append(&file, lc.data, lc.len);
/* Pad to first section's file offset. */
/* __TEXT first section begins at headers_size; we wrote header+lc =
@@ -2624,47 +2564,47 @@ void link_emit_macho(LinkImage* img, Writer* w) {
MSec* m = &x.secs[i];
if (m->is_zerofill || m->size == 0) continue;
/* Pad up to m->file_offset. */
- while (file.len < m->file_offset) mbuf_u8(&file, 0);
+ while (file.len < m->file_offset) objbb_u8(&file, 0);
if (m->synth_data) {
- mbuf_append(&file, m->synth_data, m->synth_size);
+ objbb_append(&file, m->synth_data, m->synth_size);
} else {
LinkSection* ls = &img->sections[m->link_sec_id - 1u];
u32 segid = ls->segment_id;
u8* base =
(segid != LINK_SEG_NONE) ? img->segment_bytes[segid - 1u] : NULL;
if (base && ls->size) {
- mbuf_append(&file, base + ls->input_offset, (u32)ls->size);
+ objbb_append(&file, base + ls->input_offset, (u32)ls->size);
} else if (ls->size) {
- for (u64 k = 0; k < ls->size; ++k) mbuf_u8(&file, 0);
+ for (u64 k = 0; k < ls->size; ++k) objbb_u8(&file, 0);
}
}
}
/* Pad to LINKEDIT start. */
- while (file.len < x.linkedit_fileoff) mbuf_u8(&file, 0);
+ while (file.len < x.linkedit_fileoff) objbb_u8(&file, 0);
/* LINKEDIT contents in declared order. */
- while (file.len < x.chained_fixups_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.chained_fixups.data, x.chained_fixups.len);
- while (file.len < x.exports_trie_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.exports_trie.data, x.exports_trie.len);
- while (file.len < x.fn_starts_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.fn_starts.data, x.fn_starts.len);
- while (file.len < x.data_in_code_off) mbuf_u8(&file, 0);
+ while (file.len < x.chained_fixups_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.chained_fixups.data, x.chained_fixups.len);
+ while (file.len < x.exports_trie_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.exports_trie.data, x.exports_trie.len);
+ while (file.len < x.fn_starts_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.fn_starts.data, x.fn_starts.len);
+ while (file.len < x.data_in_code_off) objbb_u8(&file, 0);
/* empty */
- while (file.len < x.symtab_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.symtab.data, x.symtab.len);
- while (file.len < x.indirect_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.indirect.data, x.indirect.len);
- while (file.len < x.strtab_off) mbuf_u8(&file, 0);
- mbuf_append(&file, x.strtab.data, x.strtab.len);
- while (file.len < x.codesig_off) mbuf_u8(&file, 0);
+ while (file.len < x.symtab_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.symtab.data, x.symtab.len);
+ while (file.len < x.indirect_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.indirect.data, x.indirect.len);
+ while (file.len < x.strtab_off) objbb_u8(&file, 0);
+ objbb_append(&file, x.strtab.data, x.strtab.len);
+ while (file.len < x.codesig_off) objbb_u8(&file, 0);
/* Compute codesig hashes over file bytes [0, codesig_off). */
/* The codesig blob currently has zero hashes; hash now. */
compute_codesig(&x, file.data, x.codesig_off, "a.out");
/* Append codesig. */
- mbuf_append(&file, x.codesig.data, x.codesig.len);
+ objbb_append(&file, x.codesig.data, x.codesig.len);
/* Stream out. */
kit_writer_seek(w, 0);
@@ -2672,16 +2612,16 @@ void link_emit_macho(LinkImage* img, Writer* w) {
/* Cleanup. */
fix_fini(&fl);
- mbuf_fini(&lc);
- mbuf_fini(&file);
- mbuf_fini(&x.chained_fixups);
- mbuf_fini(&x.exports_trie);
- mbuf_fini(&x.symtab);
- mbuf_fini(&x.strtab);
- mbuf_fini(&x.indirect);
- mbuf_fini(&x.fn_starts);
- mbuf_fini(&x.data_in_code);
- mbuf_fini(&x.codesig);
+ objbb_fini(&lc);
+ objbb_fini(&file);
+ objbb_fini(&x.chained_fixups);
+ objbb_fini(&x.exports_trie);
+ objbb_fini(&x.symtab);
+ objbb_fini(&x.strtab);
+ objbb_fini(&x.indirect);
+ objbb_fini(&x.fn_starts);
+ objbb_fini(&x.data_in_code);
+ objbb_fini(&x.codesig);
if (x.imports) x.h->free(x.h, x.imports, 0); /* VEC_GROW: cap unknown */
if (x.dylibs) x.h->free(x.h, x.dylibs, 0);
if (x.sym_to_imp)
diff --git a/src/obj/obj_secnames.c b/src/obj/obj_secnames.c
@@ -280,7 +280,7 @@ Sym obj_format_c_mangle(Compiler* c, const char* name) {
if (!c || !name) return 0;
prefix = obj_format_c_label_prefix(c);
plen = slice_from_cstr(prefix).len;
- if (plen == 0) return pool_intern_slice(c->global, slice_from_cstr(name));
+ if (plen == 0) return pool_intern_cstr(c->global, name);
n = slice_from_cstr(name).len;
h = (Heap*)c->ctx->heap;
buf = (char*)h->alloc(h, n + plen + 1u, 1);
diff --git a/src/opt/ir.h b/src/opt/ir.h
@@ -564,7 +564,6 @@ typedef enum OptAllocKind {
OPT_ALLOC_NONE,
OPT_ALLOC_HARD,
OPT_ALLOC_SPILL,
- OPT_ALLOC_SPLIT,
} OptAllocKind;
typedef enum OptLocKind {
@@ -580,21 +579,6 @@ typedef struct OptLoc {
FrameSlot spill_slot;
} OptLoc;
-typedef struct OptAllocSegment {
- u32 start;
- u32 end;
- u32 block;
- u8 loc_kind;
- u8 cls;
- Reg hard_reg;
- FrameSlot spill_slot;
- FrameSlot spill_home;
- u8 reload_at_start;
- u8 store_at_end;
- u8 pad[2];
- u32 next;
-} OptAllocSegment;
-
typedef struct OptPRegInfo {
u32 first_pos;
u32 last_pos;
@@ -731,10 +715,6 @@ typedef struct Func {
MFunc* mir; /* physical post-allocation IR; HIR stays virtual */
u32* opt_coalesce_parent;
u32* opt_coalesce_size;
- OptAllocSegment* opt_alloc_segments;
- u32 opt_nalloc_segments;
- u32 opt_alloc_segments_cap;
- u32* opt_first_segment_by_preg;
OptUse* opt_uses;
u32 opt_nuses, opt_uses_cap;
diff --git a/src/opt/opt.c b/src/opt/opt.c
@@ -56,6 +56,28 @@ typedef struct OptImpl {
HASHMAP_DEFINE(OptFuncIndex, ObjSymId, u32, hash_u32);
+/* Section-indexed view of the relocatable-data symbols, built once so the
+ * data-reloc rooting pass can find the symbol that storage-contains a reloc in
+ * O(syms-in-section) rather than scanning every symbol per reloc. `entries` is
+ * packed by section (stable iteration order within a section preserved so the
+ * "first containing symbol" choice matches the old symiter walk); `by_section`
+ * maps a section id to its [start, start+count) slice of `entries`. */
+typedef struct OptDataSymEntry {
+ ObjSymId id;
+ u64 begin; /* containment range start (s->value) */
+ u64 end; /* containment range end (begin + max(size, 1)) */
+ int exported;
+} OptDataSymEntry;
+typedef struct OptSecSlice {
+ u32 start;
+ u32 count;
+} OptSecSlice;
+HASHMAP_DEFINE(OptSecSymIndex, ObjSecId, OptSecSlice, hash_u32);
+typedef struct OptDataSymTable {
+ OptDataSymEntry* entries;
+ OptSecSymIndex by_section;
+} OptDataSymTable;
+
/* A symbol whose definition can be replaced at link time must not have its body
* inlined — the inlined copy would defeat the override. Weak definitions are
* interposable in every output kind, so they are never safe to inline. (The
@@ -119,8 +141,8 @@ static void opt_dbg_dump(OptImpl* o, Func* f, const char* tag) {
kit_writer_mem(o->c->ctx->heap, &w);
opt_ir_dump(f, w);
bytes = kit_writer_mem_bytes(w, &len);
- compiler_panic(o->c, f->desc.loc, "DUMP %s:\n%.*s", tag, (int)len,
- (const char*)bytes);
+ diag_emit(o->c->ctx->diag, KIT_DIAG_NOTE, f->desc.loc, "DUMP %s:\n%.*s", tag,
+ (int)len, (const char*)bytes);
}
/* CFG-prep prefix shared by the streaming and whole-program pipelines: lower's
@@ -236,7 +258,7 @@ static void opt_o1_native_finish(OptImpl* o, Func* f, int cfg_dirty) {
metrics_scope_begin(o->c, "opt.regalloc");
memset(®alloc_live, 0, sizeof regalloc_live);
- opt_regalloc_locations(f, 0, ®alloc_live);
+ opt_regalloc_locations(f, ®alloc_live);
metrics_scope_end(o->c, "opt.regalloc");
metrics_scope_begin(o->c, "opt.regalloc.verify");
opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE);
@@ -371,8 +393,8 @@ static void opt_dbg_dump_cg(OptImpl* o, const CgIrFunc* f) {
kit_writer_mem(o->c->ctx->heap, &w);
cg_ir_func_dump(f, w);
bytes = kit_writer_mem_bytes(w, &len);
- compiler_panic(o->c, f->desc.loc, "CGIR:\n%.*s", (int)len,
- (const char*)bytes);
+ diag_emit(o->c->ctx->diag, KIT_DIAG_NOTE, f->desc.loc, "CGIR:\n%.*s",
+ (int)len, (const char*)bytes);
}
static void opt_on_func(void* user, CgIrFunc* cg_func) {
@@ -631,37 +653,98 @@ static void opt_mark_data_reloc_graph(OptImpl* o, OptFuncIndex* index,
}
}
-static ObjSymId opt_data_reloc_exported_root_sym(OptImpl* o, const Reloc* r) {
+/* Build the section-indexed table of relocatable-data symbols in two passes
+ * (count per section, then fill), keeping per-section iteration order stable so
+ * downstream "first containing symbol" lookups match the historical symiter
+ * walk. `nsym` is the symbol-iterator upper bound already computed by the
+ * caller. */
+static void opt_data_sym_table_build(OptImpl* o, OptDataSymTable* t, u32 nsym) {
ObjSymIter* it;
ObjSymEntry ent;
+ OptSecSymIndex_init_cap(&t->by_section, o->c->ctx->heap, 0);
+ t->entries = nsym ? arena_array(o->c->tu, OptDataSymEntry, nsym) : NULL;
+ /* Pass 1: count relocatable-data symbols per section. */
+ it = obj_symiter_new(o->target->obj);
+ while (it && obj_symiter_next(it, &ent)) {
+ const ObjSym* s = ent.sym;
+ OptSecSlice* slot;
+ if (!opt_sym_is_relocatable_data(s)) continue;
+ slot = OptSecSymIndex_get(&t->by_section, s->section_id);
+ if (slot) {
+ slot->count++;
+ } else {
+ OptSecSlice sl = {0, 1};
+ (void)OptSecSymIndex_set(&t->by_section, s->section_id, sl);
+ }
+ }
+ if (it) obj_symiter_free(it);
+ /* Assign each section a contiguous [start, start+count) slice and reset the
+ * counts so pass 2 can use them as fill cursors. */
+ {
+ u32 next = 0;
+ for (u32 i = 0; i < t->by_section.cap; ++i) {
+ if (!t->by_section.slots[i].k) continue;
+ t->by_section.slots[i].v.start = next;
+ next += t->by_section.slots[i].v.count;
+ t->by_section.slots[i].v.count = 0;
+ }
+ }
+ /* Pass 2: place symbols in their section slice, preserving iteration order. */
+ it = obj_symiter_new(o->target->obj);
+ while (it && obj_symiter_next(it, &ent)) {
+ const ObjSym* s = ent.sym;
+ OptSecSlice* slot;
+ OptDataSymEntry* e;
+ if (!opt_sym_is_relocatable_data(s)) continue;
+ slot = OptSecSymIndex_get(&t->by_section, s->section_id);
+ if (!slot) continue; /* unreachable: pass 1 inserted every such section */
+ e = &t->entries[slot->start + slot->count++];
+ e->id = ent.id;
+ e->begin = s->value;
+ e->end = s->value + (s->size ? s->size : 1u);
+ e->exported = s->bind != SB_LOCAL || (s->flags & KIT_CG_SYM_USED) ? 1 : 0;
+ }
+ if (it) obj_symiter_free(it);
+}
+
+static void opt_data_sym_table_fini(OptDataSymTable* t) {
+ OptSecSymIndex_fini(&t->by_section);
+}
+
+/* Find the data symbol whose storage contains reloc `r`, restricted to roots:
+ * if the reloc's section is RETAIN any containing symbol roots it, otherwise
+ * only an exported symbol does. Returns the first match in the section's stable
+ * order (matching the old per-symbol scan). */
+static ObjSymId opt_data_reloc_exported_root_sym(OptImpl* o,
+ const OptDataSymTable* t,
+ const Reloc* r) {
const Section* sec;
+ const OptSecSlice* slice;
+ int retained;
if (!r || r->removed || r->section_id == OBJ_SEC_NONE) return OBJ_SYM_NONE;
sec = obj_section_get(o->target->obj, r->section_id);
if (!sec || sec->removed || sec->kind == SEC_TEXT) return OBJ_SYM_NONE;
- it = obj_symiter_new(o->target->obj);
- if (!it) return OBJ_SYM_NONE;
- while (obj_symiter_next(it, &ent)) {
- const ObjSym* s = ent.sym;
- if (!opt_reloc_inside_sym(r, s)) continue;
- if (sec->flags & SF_RETAIN) {
- obj_symiter_free(it);
- return ent.id;
- }
- if (s->bind == SB_LOCAL && !(s->flags & KIT_CG_SYM_USED)) continue;
- obj_symiter_free(it);
- return ent.id;
+ slice = OptSecSymIndex_get(&t->by_section, r->section_id);
+ if (!slice) return OBJ_SYM_NONE;
+ retained = (sec->flags & SF_RETAIN) ? 1 : 0;
+ for (u32 i = 0; i < slice->count; ++i) {
+ const OptDataSymEntry* e = &t->entries[slice->start + i];
+ if ((u64)r->offset < e->begin || (u64)r->offset >= e->end) continue;
+ if (retained) return e->id;
+ if (!e->exported) continue;
+ return e->id;
}
- obj_symiter_free(it);
return OBJ_SYM_NONE;
}
-static void opt_root_exported_data_relocs(OptImpl* o, ObjSymSet* data_seen,
+static void opt_root_exported_data_relocs(OptImpl* o, const OptDataSymTable* t,
+ ObjSymSet* data_seen,
ObjSymId* data_queue,
u32* data_qtail) {
u32 nrel = obj_reloc_total(o->target->obj);
for (u32 i = 0; i < nrel; ++i) {
const Reloc* r = obj_reloc_at(o->target->obj, i);
- ObjSymId sym = opt_data_reloc_exported_root_sym(o, r);
+ ObjSymId sym = opt_data_reloc_exported_root_sym(o, t, r);
if (sym != OBJ_SYM_NONE)
opt_enqueue_data_sym(o, data_seen, data_queue, data_qtail, sym);
}
@@ -716,6 +799,7 @@ static void opt_whole_module_finalize(OptImpl* o, const CgIrModule* module,
OptFuncIndex index;
ObjSymSet preserved;
ObjSymSet data_seen;
+ OptDataSymTable data_syms;
u8* reachable;
u8* queued;
u32* queue;
@@ -739,6 +823,7 @@ static void opt_whole_module_finalize(OptImpl* o, const CgIrModule* module,
if (it) obj_symiter_free(it);
}
data_queue = arena_array(o->c->tu, ObjSymId, nsym);
+ opt_data_sym_table_build(o, &data_syms, nsym);
opt_resolve_duplicate_funcs(o, module, &index);
opt_build_preserved_set(o, &preserved);
opt_internalize_non_preserved(o, module, &preserved);
@@ -748,7 +833,8 @@ static void opt_whole_module_finalize(OptImpl* o, const CgIrModule* module,
opt_mark_func(reachable, queued, queue, &qtail, i);
}
opt_root_aliases(o, module, &index, reachable, queued, queue, &qtail);
- opt_root_exported_data_relocs(o, &data_seen, data_queue, &data_qtail);
+ opt_root_exported_data_relocs(o, &data_syms, &data_seen, data_queue,
+ &data_qtail);
opt_mark_data_reloc_graph(o, &index, reachable, queued, queue, &qtail,
&data_seen, data_queue, &data_qhead, &data_qtail);
while (qhead < qtail) {
@@ -760,6 +846,7 @@ static void opt_whole_module_finalize(OptImpl* o, const CgIrModule* module,
opt_mark_data_reloc_graph(o, &index, reachable, queued, queue, &qtail,
&data_seen, data_queue, &data_qhead, &data_qtail);
}
+ opt_data_sym_table_fini(&data_syms);
for (u32 i = 0; i < module->nfuncs; ++i) {
CgIrFunc* cg_func = module->funcs[i];
if (cg_func && cg_func->removed) continue;
diff --git a/src/opt/opt.h b/src/opt/opt.h
@@ -145,9 +145,7 @@ void opt_ir_dump(Func*, Writer*);
void opt_ssa_dump(Func*, Writer*);
void opt_rewrite_dump(Func*, Writer*);
void opt_coalesce(Func*);
-void opt_regalloc_locations(Func*, int allow_live_range_split,
- OptLiveInfo* live_out);
-void opt_regalloc(Func*, int allow_live_range_split);
+void opt_regalloc_locations(Func*, OptLiveInfo* live_out);
void opt_lower_to_mir(Func*, const OptLiveInfo*);
void opt_mir_combine(Func*);
void opt_mir_dce(Func*);
diff --git a/src/opt/opt_internal.h b/src/opt/opt_internal.h
@@ -16,13 +16,6 @@ typedef struct OptHardBlockLive {
OptHardRegSet live_def;
} OptHardBlockLive;
-typedef struct OptPassCtx {
- Compiler* c;
- Func* f;
- Arena* arena;
- const char* stage;
-} OptPassCtx;
-
typedef struct FuncSet FuncSet;
struct FuncSet {
Compiler* c;
@@ -55,7 +48,9 @@ typedef struct OptAnalysis {
} OptAnalysis;
typedef enum OptAnalysisFlag {
- OPT_ANALYSIS_CFG = 1u << 0,
+ /* OPT_ANALYSIS_CFG was tracked but never queried (opt_build_cfg always
+ * rebuilt unconditionally), so the bit and its maintenance were removed.
+ * Values stay stable to keep any persisted masks unambiguous. */
OPT_ANALYSIS_DEF_USE = 1u << 1,
OPT_ANALYSIS_DOM = 1u << 2,
OPT_ANALYSIS_LOOP = 1u << 3,
diff --git a/src/opt/pass_analysis.c b/src/opt/pass_analysis.c
@@ -634,8 +634,7 @@ static void verify_allocations(Func* f, const char* stage) {
u8 cls = opt_preg_loc_cls(f, r);
if (cls >= OPT_REG_CLASSES)
opt_fail(f, stage, "bad preg alloc class", r, cls);
- if (pi && pi->alloc_kind != alloc_kind &&
- !(pi->alloc_kind == OPT_ALLOC_SPLIT && alloc_kind == OPT_ALLOC_SPLIT))
+ if (pi && pi->alloc_kind != alloc_kind)
opt_fail(f, stage, "alloc kind mirror mismatch", r, pi->alloc_kind);
switch ((OptAllocKind)alloc_kind) {
case OPT_ALLOC_NONE:
@@ -665,38 +664,11 @@ static void verify_allocations(Func* f, const char* stage) {
opt_fail(f, stage, "spill alloc location mismatch", r,
opt_preg_spill_slot(f, r));
break;
- case OPT_ALLOC_SPLIT:
- if (!f->opt_first_segment_by_preg)
- opt_fail(f, stage, "split allocation missing segments", r, 0);
- if (loc && loc->kind != OPT_LOC_NONE)
- opt_fail(f, stage, "split alloc location mismatch", r, loc->kind);
- break;
default:
opt_fail(f, stage, "bad allocation kind", r, alloc_kind);
break;
}
}
- if (f->opt_first_segment_by_preg && f->opt_alloc_segments) {
- for (PReg r = 1; r < opt_reg_count(f); ++r) {
- for (u32 si = f->opt_first_segment_by_preg[r]; si != OPT_RANGE_NONE;
- si = f->opt_alloc_segments[si].next) {
- OptAllocSegment* s = &f->opt_alloc_segments[si];
- if (s->block >= f->nblocks)
- opt_fail(f, stage, "bad split block", r, si);
- if (s->start > s->end) opt_fail(f, stage, "bad split range", r, si);
- if (s->cls >= OPT_REG_CLASSES)
- opt_fail(f, stage, "bad split class", r, s->cls);
- if (s->loc_kind == OPT_LOC_HARD) {
- if (s->hard_reg == (Reg)REG_NONE || s->hard_reg >= OPT_MAX_HARD_REGS)
- opt_fail(f, stage, "bad split hard reg", r, s->hard_reg);
- } else if (s->loc_kind == OPT_LOC_STACK) {
- verify_frame_slot(f, stage, s->spill_slot, "bad split spill slot");
- } else if (s->loc_kind != OPT_LOC_NONE) {
- opt_fail(f, stage, "bad split loc kind", r, s->loc_kind);
- }
- }
- }
- }
}
static void verify_rewritten(Func* f, const char* stage) {
diff --git a/src/opt/pass_cfg.c b/src/opt/pass_cfg.c
@@ -216,8 +216,8 @@ u32 opt_split_edge(Func* f, u32 pred, u32 succ) {
} else {
ir_note_emit(f, edge);
}
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
return edge;
}
@@ -305,5 +305,4 @@ void opt_build_cfg(Func* f) {
f->blocks[t].preds[f->blocks[t].npreds++] = b;
}
}
- opt_analysis_mark_valid(f, OPT_ANALYSIS_CFG);
}
diff --git a/src/opt/pass_copy.c b/src/opt/pass_copy.c
@@ -26,21 +26,6 @@ static int copy_values(const Inst* in, Val* dst, Val* src) {
return 1;
}
-static u32 def_count(Func* f, Val v) {
- u32 n = 0;
- if (v == VAL_NONE) return 0;
- for (u32 b = 0; b < f->nblocks; ++b) {
- Block* bl = &f->blocks[b];
- for (u32 i = 0; i < bl->ninsts; ++i) {
- Inst* in = &bl->insts[i];
- if (in->def == v) ++n;
- for (u32 d = 0; d < in->ndefs; ++d)
- if (in->defs[d] == v) ++n;
- }
- }
- return n;
-}
-
static void replace_one_use(Func* f, const OptUse* use, Val src) {
Inst* in = &f->blocks[use->block].insts[use->inst];
switch ((OptUseKind)use->kind) {
@@ -206,8 +191,54 @@ static int simplify_convert_chain(Func* f, Inst* outer, const ConvertStep* o,
return 0;
}
-static int cleanup_one_copy(Func* f) {
+/* Precompute the number of definitions of every value in a single sweep so the
+ * batch copy pass can test the def_count==1 removability condition in O(1)
+ * instead of rescanning the whole function per copy. */
+static void compute_def_counts(Func* f, u32* ndef) {
+ memset(ndef, 0, sizeof(*ndef) * f->nvals);
+ for (u32 b = 0; b < f->nblocks; ++b) {
+ Block* bl = &f->blocks[b];
+ for (u32 i = 0; i < bl->ninsts; ++i) {
+ Inst* in = &bl->insts[i];
+ if (in->def != VAL_NONE && in->def < f->nvals) ++ndef[in->def];
+ for (u32 d = 0; d < in->ndefs; ++d)
+ if (in->defs[d] != VAL_NONE && in->defs[d] < f->nvals)
+ ++ndef[in->defs[d]];
+ }
+ }
+}
+
+/* Follow the removed-copy chain dst -> src to its final representative: a value
+ * that is not itself the dst of a removed copy (or a self-copy). `repl[v]` is
+ * VAL_NONE when v is not a removed-copy dst. Bounded by nvals to stay safe if a
+ * pathological IR forms a copy cycle. */
+static Val resolve_copy(Func* f, const Val* repl, Val v) {
+ for (u32 hops = 0; hops < f->nvals; ++hops) {
+ Val next;
+ if (v == VAL_NONE || v >= f->nvals) break;
+ next = repl[v];
+ if (next == VAL_NONE || next == v) break;
+ v = next;
+ }
+ return v;
+}
+
+/* Collect every removable IR_COPY in one def-use pass, resolve copy chains to
+ * their final source, then redirect all uses and drop the copies together —
+ * O(N) rather than the O(N^2) "rebuild + remove one" loop. Removing a copy
+ * never changes another value's def_count (a removable non-self copy is the
+ * sole def of its dst), so a single pass reaches the fixpoint. */
+static int cleanup_copies_pass(Func* f) {
+ u32* ndef;
+ Val* repl;
+ int removed_any = 0;
opt_rebuild_def_use(f);
+ if (!f->nvals) return 0;
+ ndef = arena_array(f->arena, u32, f->nvals);
+ repl = arena_array(f->arena, Val, f->nvals);
+ for (u32 v = 0; v < f->nvals; ++v) repl[v] = VAL_NONE;
+ compute_def_counts(f, ndef);
+ /* First sweep: tag each removable copy's dst with its immediate src. */
for (u32 b = 0; b < f->nblocks; ++b) {
Block* bl = &f->blocks[b];
for (u32 i = 0; i < bl->ninsts; ++i) {
@@ -215,18 +246,35 @@ static int cleanup_one_copy(Func* f) {
Val dst = VAL_NONE;
Val src = VAL_NONE;
if (!copy_values(in, &dst, &src)) continue;
+ if (dst == VAL_NONE || dst >= f->nvals) continue;
if (dst != src && !same_val_shape(f, dst, src)) continue;
- if (dst != src && def_count(f, dst) != 1) continue;
- for (u32 u = f->opt_first_use_by_val[dst]; u != OPT_USE_NONE;
- u = f->opt_uses[u].next_for_val)
- replace_one_use(f, &f->opt_uses[u], src);
+ if (dst != src && ndef[dst] != 1) continue;
+ repl[dst] = src;
+ removed_any = 1;
+ }
+ }
+ if (!removed_any) return 0;
+ /* Second sweep: redirect uses to the resolved source and erase the copies. */
+ for (u32 b = 0; b < f->nblocks; ++b) {
+ Block* bl = &f->blocks[b];
+ for (u32 i = 0; i < bl->ninsts; ++i) {
+ Inst* in = &bl->insts[i];
+ Val dst = VAL_NONE;
+ Val src = VAL_NONE;
+ Val final_src;
+ if (!copy_values(in, &dst, &src)) continue;
+ if (dst >= f->nvals || repl[dst] == VAL_NONE) continue;
+ final_src = resolve_copy(f, repl, dst);
+ if (final_src != dst)
+ for (u32 u = f->opt_first_use_by_val[dst]; u != OPT_USE_NONE;
+ u = f->opt_uses[u].next_for_val)
+ replace_one_use(f, &f->opt_uses[u], final_src);
remove_copy_inst(in);
- opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE);
- compact_copies(f);
- return 1;
}
}
- return 0;
+ opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE);
+ compact_copies(f);
+ return 1;
}
static int cleanup_one_extension(Func* f) {
@@ -255,7 +303,7 @@ static int cleanup_one_extension(Func* f) {
void opt_copy_cleanup(Func* f) {
if (!f || f->opt_rewritten) return;
- while (cleanup_one_copy(f)) {
+ while (cleanup_copies_pass(f)) {
}
opt_rebuild_def_use(f);
}
diff --git a/src/opt/pass_inline.c b/src/opt/pass_inline.c
@@ -585,8 +585,8 @@ static int inline_call_site(Func* caller, u32 block_idx, u32 inst_idx,
}
inline_rebuild_emit_order(caller, block_idx, &map, cont);
- opt_analysis_invalidate(caller, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ caller, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
return 1;
}
diff --git a/src/opt/pass_jump.c b/src/opt/pass_jump.c
@@ -589,8 +589,8 @@ static int full_collapse_same_target_branches(Func* f) {
void opt_jump_cleanup(Func* f, OptJumpCleanupStage stage) {
if (!f) return;
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
JumpCleanupCtx c = jump_cleanup_ctx(f);
if (stage == OPT_JUMP_CLEANUP_CFG) {
cleanup_invert_jump_fallthrough(&c);
@@ -607,8 +607,8 @@ void opt_jump_opt(Func* f) {
if (!f) return;
int changed = 0;
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
for (u32 iter = 0; iter < f->nblocks; ++iter) {
JumpCleanupCtx c = jump_cleanup_ctx(f);
@@ -625,8 +625,8 @@ void opt_jump_opt(Func* f) {
}
if (changed) {
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
}
opt_build_cfg(f);
}
diff --git a/src/opt/pass_lower.c b/src/opt/pass_lower.c
@@ -586,14 +586,6 @@ static FrameSlot spill_slot_for(Func* f, PReg v) {
static u32 hard_loc_bit(u8 cls, Reg r) { return ((u32)cls * 32u) + (u32)r; }
-typedef struct OptAllocCandidate {
- PReg v; /* coalesce-root PReg with live ranges */
- u32 spill_cost;
- u32 live_length;
- u8 tied;
- u8 pad[3];
-} OptAllocCandidate;
-
typedef struct OptAllocGroupInfo {
PReg root;
u32 spill_cost;
@@ -607,6 +599,13 @@ typedef struct OptAllocGroupInfo {
u8 pad[3];
} OptAllocGroupInfo;
+typedef struct OptAllocCandidate {
+ PReg v; /* coalesce-root PReg with live ranges */
+ /* Group info computed once during candidate collection and reused by the
+ * assignment loop, which would otherwise recompute it per candidate. */
+ OptAllocGroupInfo gi;
+} OptAllocCandidate;
+
typedef struct OptAllocator {
OptLoc* locs; /* per-PReg result (cls, hard_reg, spill_slot) */
@@ -714,9 +713,13 @@ static u32 hard_reg_alloc_score(Func* f, const OptAllocator* a,
static int alloc_candidate_higher(const OptAllocCandidate* a,
const OptAllocCandidate* b) {
- if (a->tied != b->tied) return a->tied > b->tied;
- if (a->spill_cost != b->spill_cost) return a->spill_cost > b->spill_cost;
- if (a->live_length != b->live_length) return a->live_length < b->live_length;
+ int a_tied = a->gi.tied_hard_reg >= 0;
+ int b_tied = b->gi.tied_hard_reg >= 0;
+ if (a_tied != b_tied) return a_tied > b_tied;
+ if (a->gi.spill_cost != b->gi.spill_cost)
+ return a->gi.spill_cost > b->gi.spill_cost;
+ if (a->gi.live_length != b->gi.live_length)
+ return a->gi.live_length < b->gi.live_length;
return a->v < b->v;
}
@@ -1037,10 +1040,7 @@ static int alloc_group_conflicts_bit(const OptAllocator* a, u32 bit) {
}
static void opt_assign_ranges(Func* f, const OptLiveRangeSet* ranges,
- OptAllocator* a, int allow_live_range_split) {
- (void)allow_live_range_split; /* live-range splitting deferred per
- doc/plan/OPTIMIZER.md; the parameter is
- passed through for ABI compatibility. */
+ OptAllocator* a) {
memset(a, 0, sizeof *a);
a->point_count = ranges->point_count ? ranges->point_count : 1u;
a->hard_loc_bits = OPT_REG_CLASSES * 32u;
@@ -1069,21 +1069,15 @@ static void opt_assign_ranges(Func* f, const OptLiveRangeSet* ranges,
if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
PReg root = alloc_coalesce_root(f, v);
if (root != v) continue;
- OptAllocGroupInfo gi;
- alloc_group_info(f, ranges, root, &gi);
cands[n].v = v;
- cands[n].spill_cost = gi.spill_cost;
- cands[n].live_length = gi.live_length;
- cands[n].tied = gi.tied_hard_reg >= 0;
- memset(cands[n].pad, 0, sizeof cands[n].pad);
+ alloc_group_info(f, ranges, root, &cands[n].gi);
++n;
}
alloc_sort_candidates(cands, n);
for (u32 i = 0; i < n; ++i) {
PReg v = cands[i].v;
- OptAllocGroupInfo gi;
- alloc_group_info(f, ranges, v, &gi);
+ OptAllocGroupInfo gi = cands[i].gi;
OptPRegInfo* vi = &f->preg_info[v];
u8 cls = gi.cls;
alloc_compute_group_conflicts(f, a, ranges, v);
@@ -1257,26 +1251,6 @@ typedef struct RewriteList {
u32 cap;
} RewriteList;
-typedef enum EdgeMatKind {
- EDGE_MAT_STORE,
- EDGE_MAT_RELOAD,
-} EdgeMatKind;
-
-typedef struct EdgeMat {
- u32 pred;
- u32 succ;
- PReg v;
- Reg hard_reg;
- u8 kind;
- u8 pad[3];
-} EdgeMat;
-
-typedef struct EdgeMatPlan {
- EdgeMat* mats;
- u32 n;
- u32 cap;
-} EdgeMatPlan;
-
typedef struct RewriteOut {
Inst* data;
u32 cap;
@@ -1371,16 +1345,6 @@ static Operand hard_operand(Func* f, PReg v) {
return o;
}
-static Operand hard_operand_reg(Func* f, PReg v, Reg hard_reg) {
- Operand o;
- memset(&o, 0, sizeof o);
- o.kind = OPK_REG;
- o.cls = opt_preg_loc_cls(f, v);
- o.type = opt_reg_type(f, v);
- o.v.reg = hard_reg;
- return o;
-}
-
static void append_store_preg(Func* f, RewriteList* out, PReg v) {
Inst* st = list_push(f, out, IR_STORE);
st->opnds = arena_array(f->arena, Operand, 2);
@@ -1399,26 +1363,6 @@ static void append_load_preg(Func* f, RewriteList* out, PReg v) {
ld->extra.mem = spill_mem(f, v);
}
-static void append_store_preg_hard(Func* f, RewriteList* out, PReg v,
- Reg hard_reg) {
- Inst* st = list_push(f, out, IR_STORE);
- st->opnds = arena_array(f->arena, Operand, 2);
- st->opnds[0] = spill_addr(f, v);
- st->opnds[1] = hard_operand_reg(f, v, hard_reg);
- st->nopnds = 2;
- st->extra.mem = spill_mem(f, v);
-}
-
-static void append_load_preg_hard(Func* f, RewriteList* out, PReg v,
- Reg hard_reg) {
- Inst* ld = list_push(f, out, IR_LOAD);
- ld->opnds = arena_array(f->arena, Operand, 2);
- ld->opnds[0] = hard_operand_reg(f, v, hard_reg);
- ld->opnds[1] = spill_addr(f, v);
- ld->nopnds = 2;
- ld->extra.mem = spill_mem(f, v);
-}
-
static Reg scratch_for(Func* f, u8 cls, u32* next) {
u32 n = f->opt_scratch_reg_count[cls];
if (!n) return REG_NONE;
@@ -1431,19 +1375,8 @@ typedef struct RewriteCtx {
RewriteList* before;
RewriteList* after;
u32 next_scratch[OPT_REG_CLASSES];
- u32 raw_point;
} RewriteCtx;
-static const OptAllocSegment* split_segment_at(Func* f, PReg v, u32 raw_point) {
- if (!f->opt_first_segment_by_preg || v >= opt_reg_count(f)) return NULL;
- for (u32 si = f->opt_first_segment_by_preg[v]; si != OPT_RANGE_NONE;
- si = f->opt_alloc_segments[si].next) {
- const OptAllocSegment* s = &f->opt_alloc_segments[si];
- if (s->start <= raw_point && raw_point < s->end) return s;
- }
- return NULL;
-}
-
static void rewrite_one_operand(Func* f, Inst* owner, Operand* op, int is_def,
void* arg) {
RewriteCtx* c = (RewriteCtx*)arg;
@@ -1455,15 +1388,7 @@ static void rewrite_one_operand(Func* f, Inst* owner, Operand* op, int is_def,
op->v.reg = opt_preg_hard_reg(f, v);
return;
}
- if (alloc_kind == OPT_ALLOC_SPLIT) {
- const OptAllocSegment* seg = split_segment_at(f, v, c->raw_point);
- if (seg && seg->loc_kind == OPT_LOC_HARD) {
- op->v.reg = seg->hard_reg;
- return;
- }
- } else if (alloc_kind != OPT_ALLOC_SPILL) {
- return;
- }
+ if (alloc_kind != OPT_ALLOC_SPILL) return;
u8 cls = opt_preg_loc_cls(f, v);
Reg scratch = scratch_for(f, cls, &c->next_scratch[cls]);
if (scratch == (Reg)REG_NONE) {
@@ -1497,7 +1422,7 @@ static void rewrite_call_arg_operand(Func* f, Operand* op) {
u8 alloc_kind = opt_preg_alloc_kind(f, v);
if (alloc_kind == OPT_ALLOC_HARD) {
op->v.reg = opt_preg_hard_reg(f, v);
- } else if (alloc_kind == OPT_ALLOC_SPILL || alloc_kind == OPT_ALLOC_SPLIT) {
+ } else if (alloc_kind == OPT_ALLOC_SPILL) {
*op = spill_addr(f, v);
}
}
@@ -1506,7 +1431,6 @@ static void rewrite_store_value_operand(Func* f, Inst* owner, Operand* op,
RewriteCtx* ctx) {
PReg v;
u8 alloc_kind;
- const OptAllocSegment* seg;
if (!op || op->kind != OPK_REG) return;
v = (PReg)op->v.reg;
if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
@@ -1515,15 +1439,6 @@ static void rewrite_store_value_operand(Func* f, Inst* owner, Operand* op,
op->v.reg = opt_preg_hard_reg(f, v);
return;
}
- if (alloc_kind == OPT_ALLOC_SPLIT) {
- seg = split_segment_at(f, v, ctx->raw_point);
- if (seg && seg->loc_kind == OPT_LOC_HARD) {
- op->v.reg = seg->hard_reg;
- return;
- }
- *op = spill_addr(f, v);
- return;
- }
if (alloc_kind == OPT_ALLOC_SPILL) {
*op = spill_addr(f, v);
return;
@@ -1614,186 +1529,6 @@ static PReg* rewrite_collect_call_save_pregs(Func* f, u32* count_out) {
return pregs;
}
-static void append_split_reloads_at(Func* f, RewriteList* out, u32 block,
- u32 raw_point) {
- if (!f->opt_first_segment_by_preg) return;
- for (PReg v = 1; v < opt_reg_count(f); ++v) {
- if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_SPLIT) continue;
- for (u32 si = f->opt_first_segment_by_preg[v]; si != OPT_RANGE_NONE;
- si = f->opt_alloc_segments[si].next) {
- const OptAllocSegment* s = &f->opt_alloc_segments[si];
- if (s->block == block && s->start == raw_point && s->reload_at_start &&
- s->loc_kind == OPT_LOC_HARD) {
- append_load_preg_hard(f, out, v, s->hard_reg);
- }
- }
- }
-}
-
-static void append_split_stores_at(Func* f, RewriteList* out, u32 block,
- u32 raw_point) {
- if (!f->opt_first_segment_by_preg) return;
- for (PReg v = 1; v < opt_reg_count(f); ++v) {
- if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_SPLIT) continue;
- for (u32 si = f->opt_first_segment_by_preg[v]; si != OPT_RANGE_NONE;
- si = f->opt_alloc_segments[si].next) {
- const OptAllocSegment* s = &f->opt_alloc_segments[si];
- if (s->block == block && s->end == raw_point && s->store_at_end &&
- s->loc_kind == OPT_LOC_HARD) {
- append_store_preg_hard(f, out, v, s->hard_reg);
- }
- }
- }
-}
-
-static void edge_mat_push(Func* f, EdgeMatPlan* plan, u32 pred, u32 succ,
- PReg v, Reg hard_reg, EdgeMatKind kind) {
- if (!plan || pred >= f->nblocks || succ >= f->nblocks) return;
- if (hard_reg == (Reg)REG_NONE) return;
- if (plan->n == plan->cap) {
- u32 ncap = plan->cap ? plan->cap * 2u : 16u;
- EdgeMat* nm = arena_array(f->arena, EdgeMat, ncap);
- if (plan->mats) memcpy(nm, plan->mats, sizeof(plan->mats[0]) * plan->n);
- plan->mats = nm;
- plan->cap = ncap;
- }
- EdgeMat* m = &plan->mats[plan->n++];
- memset(m, 0, sizeof *m);
- m->pred = pred;
- m->succ = succ;
- m->v = v;
- m->hard_reg = hard_reg;
- m->kind = (u8)kind;
-}
-
-static void prepare_split_edge_materialization(Func* f, EdgeMatPlan* plan) {
- if (!f || !plan || !f->opt_first_segment_by_preg) return;
- u32* block_base = arena_array(f->arena, u32, f->nblocks ? f->nblocks : 1u);
- u32 raw = 0;
- for (u32 b = 0; b < f->nblocks; ++b) {
- block_base[b] = raw;
- raw += f->blocks[b].ninsts ? f->blocks[b].ninsts : 1u;
- }
-
- for (PReg v = 1; v < opt_reg_count(f); ++v) {
- if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_SPLIT) continue;
- for (u32 si = f->opt_first_segment_by_preg[v]; si != OPT_RANGE_NONE;
- si = f->opt_alloc_segments[si].next) {
- OptAllocSegment* s = &f->opt_alloc_segments[si];
- if (s->loc_kind != OPT_LOC_HARD || s->block >= f->nblocks) continue;
- Block* bl = &f->blocks[s->block];
- u32 block_start = block_base[s->block];
- u32 block_end = block_start + (bl->ninsts ? bl->ninsts : 1u);
- if (s->reload_at_start && s->start == block_start && bl->npreds) {
- for (u32 p = 0; p < bl->npreds; ++p)
- edge_mat_push(f, plan, bl->preds[p], s->block, v, s->hard_reg,
- EDGE_MAT_RELOAD);
- s->reload_at_start = 0;
- }
- if (s->store_at_end && s->end == block_end && bl->nsucc) {
- for (u32 e = 0; e < bl->nsucc; ++e)
- edge_mat_push(f, plan, s->block, bl->succ[e], v, s->hard_reg,
- EDGE_MAT_STORE);
- s->store_at_end = 0;
- }
- }
- }
-}
-
-static int lower_is_terminator(const Inst* in) {
- if (!in) return 0;
- switch ((IROp)in->op) {
- case IR_BR:
- case IR_CONDBR:
- case IR_CMP_BRANCH:
- case IR_SWITCH:
- case IR_INDIRECT_BRANCH:
- case IR_RET:
- case IR_UNREACHABLE:
- case IR_BREAK_TO:
- case IR_CONTINUE_TO:
- return 1;
- case IR_INTRINSIC: {
- IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux;
- return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP);
- }
- default:
- return 0;
- }
-}
-
-static void block_insert_list(Func* f, u32 block, const RewriteList* list) {
- if (!f || block >= f->nblocks || !list || !list->n) return;
- Block* bl = &f->blocks[block];
- u32 term = bl->ninsts && lower_is_terminator(&bl->insts[bl->ninsts - 1u]);
- u32 insert_at = bl->ninsts - term;
- Inst* insts = arena_zarray(f->arena, Inst, bl->ninsts + list->n);
- if (insert_at) memcpy(insts, bl->insts, sizeof(Inst) * insert_at);
- memcpy(insts + insert_at, list->data, sizeof(Inst) * list->n);
- if (bl->ninsts > insert_at) {
- memcpy(insts + insert_at + list->n, bl->insts + insert_at,
- sizeof(Inst) * (bl->ninsts - insert_at));
- }
- bl->insts = insts;
- bl->ninsts += list->n;
- bl->cap = bl->ninsts;
-}
-
-typedef struct EdgePlace {
- u32 pred;
- u32 succ;
- u32 block;
-} EdgePlace;
-
-static u32 edge_place_for(Func* f, EdgePlace** places, u32* nplaces, u32* cap,
- u32 pred, u32 succ) {
- for (u32 i = 0; i < *nplaces; ++i)
- if ((*places)[i].pred == pred && (*places)[i].succ == succ)
- return (*places)[i].block;
- if (*nplaces == *cap) {
- u32 ncap = *cap ? *cap * 2u : 16u;
- EdgePlace* np = arena_array(f->arena, EdgePlace, ncap);
- if (*places) memcpy(np, *places, sizeof((*places)[0]) * *nplaces);
- *places = np;
- *cap = ncap;
- }
- u32 block = opt_split_edge(f, pred, succ);
- EdgePlace* p = &(*places)[(*nplaces)++];
- p->pred = pred;
- p->succ = succ;
- p->block = block;
- return block;
-}
-
-static void apply_split_edge_materialization(Func* f, const EdgeMatPlan* plan) {
- if (!f || !plan || !plan->n) return;
- EdgePlace* places = NULL;
- u32 nplaces = 0;
- u32 place_cap = 0;
- for (u32 i = 0; i < plan->n; ++i) {
- const EdgeMat* m = &plan->mats[i];
- if (m->pred < f->nblocks && m->succ < f->nblocks)
- (void)edge_place_for(f, &places, &nplaces, &place_cap, m->pred, m->succ);
- }
- for (u32 pass = 0; pass < 2; ++pass) {
- EdgeMatKind kind = pass == 0 ? EDGE_MAT_STORE : EDGE_MAT_RELOAD;
- for (u32 i = 0; i < plan->n; ++i) {
- const EdgeMat* m = &plan->mats[i];
- if ((EdgeMatKind)m->kind != kind) continue;
- u32 block =
- edge_place_for(f, &places, &nplaces, &place_cap, m->pred, m->succ);
- RewriteList list;
- memset(&list, 0, sizeof list);
- if (kind == EDGE_MAT_STORE)
- append_store_preg_hard(f, &list, m->v, m->hard_reg);
- else
- append_load_preg_hard(f, &list, m->v, m->hard_reg);
- block_insert_list(f, block, &list);
- }
- }
- opt_build_cfg(f);
-}
-
static void rewrite_func(Func* f, const OptLiveInfo* live_info) {
u32 words = live_info ? live_info->words : f->opt_live_words;
if (!words) words = bit_words(opt_reg_count(f));
@@ -1807,12 +1542,6 @@ static void rewrite_func(Func* f, const OptLiveInfo* live_info) {
InstRefs refs;
memset(&refs, 0, sizeof refs);
u32 live_active_words = 0;
- u32* block_base = arena_array(f->arena, u32, f->nblocks ? f->nblocks : 1u);
- u32 raw = 0;
- for (u32 b = 0; b < f->nblocks; ++b) {
- block_base[b] = raw;
- raw += f->blocks[b].ninsts ? f->blocks[b].ninsts : 1u;
- }
for (u32 b = 0; b < f->nblocks; ++b) {
Block* bl = &f->blocks[b];
RewriteOut out;
@@ -1843,7 +1572,6 @@ static void rewrite_func(Func* f, const OptLiveInfo* live_info) {
memset(&ctx, 0, sizeof ctx);
ctx.before = &before;
ctx.after = &after;
- ctx.raw_point = block_base[b] + i;
if ((IROp)in.op == IR_CALL) {
IRCallAux* aux = (IRCallAux*)in.extra.aux;
if (aux) {
@@ -1877,9 +1605,6 @@ static void rewrite_func(Func* f, const OptLiveInfo* live_info) {
append_live_call_saves(f, &call_restores, &in, live, live_active_words,
&refs, call_save_pregs, ncall_save_pregs, 1);
}
- append_split_reloads_at(f, &before, b, block_base[b] + i);
- append_split_stores_at(f, &after, b, block_base[b] + i + 1u);
-
out_prepend_list_reverse(f, &out, &after);
out_prepend_list_reverse(f, &out, &call_restores);
out_prepend_inst(f, &out, &in);
@@ -2116,8 +1841,7 @@ static void opt_verify_alloc(Func* f, const OptLiveInfo* live) {
}
}
-static void opt_regalloc_place(Func* f, int allow_live_range_split,
- OptLiveInfo* live_out) {
+static void opt_regalloc_place(Func* f, OptLiveInfo* live_out) {
metrics_scope_begin(f->c, "opt.live_ranges.regalloc");
OptLiveInfo live;
opt_live_blocks(f, &live);
@@ -2134,10 +1858,9 @@ static void opt_regalloc_place(Func* f, int allow_live_range_split,
for (PReg v = 1; v < opt_reg_count(f); ++v)
f->preg_info[v].forbidden_hard_regs |=
f->preg_info[v].clobbered_hard_regs;
- /* MIR coalesces only at -O2 (mir-gen.c:9431); match that here. At O1 the
- * point-bitmap allocator emits copies through the natural conflict-free
- * path. IRF_NO_COALESCE protects SSA edge copies inserted at O2. */
- if (allow_live_range_split) opt_coalesce_ranges(f, &ranges);
+ /* This O1 point-bitmap allocator does not coalesce or split live ranges; it
+ * emits copies through the natural conflict-free path. MIR coalesces at -O2
+ * (mir-gen.c). */
metrics_count(f->c, "opt.live_words", f->opt_live_words);
metrics_count(f->c, "opt.ranges", ranges.nranges);
metrics_count(f->c, "opt.range_points", ranges.point_count);
@@ -2158,22 +1881,11 @@ static void opt_regalloc_place(Func* f, int allow_live_range_split,
metrics_scope_end(f->c, "opt.live_ranges.regalloc");
OptAllocator alloc;
- opt_assign_ranges(f, &ranges, &alloc, allow_live_range_split);
- if (!allow_live_range_split) opt_verify_alloc(f, &live);
+ opt_assign_ranges(f, &ranges, &alloc);
+ opt_verify_alloc(f, &live);
if (live_out) *live_out = live;
}
-void opt_regalloc_locations(Func* f, int allow_live_range_split,
- OptLiveInfo* live_out) {
- opt_regalloc_place(f, allow_live_range_split, live_out);
-}
-
-void opt_regalloc(Func* f, int allow_live_range_split) {
- OptLiveInfo live;
- opt_regalloc_place(f, allow_live_range_split, &live);
- EdgeMatPlan edge_mats;
- memset(&edge_mats, 0, sizeof edge_mats);
- if (allow_live_range_split) prepare_split_edge_materialization(f, &edge_mats);
- rewrite_func(f, &live);
- apply_split_edge_materialization(f, &edge_mats);
+void opt_regalloc_locations(Func* f, OptLiveInfo* live_out) {
+ opt_regalloc_place(f, live_out);
}
diff --git a/src/opt/pass_o2.c b/src/opt/pass_o2.c
@@ -512,8 +512,8 @@ void opt_block_cloning(Func* f) {
}
if (changed) {
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
opt_build_cfg(f);
}
opt_rebuild_def_use(f);
@@ -1776,8 +1776,8 @@ void opt_gvn(Func* f) {
}
if (ctx.cfg_changed) {
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
opt_build_cfg(f);
gvn_realign_phi_preds(f);
} else if (ctx.changed) {
diff --git a/src/opt/pass_ssa.c b/src/opt/pass_ssa.c
@@ -918,8 +918,8 @@ static void realign_phi_preds(Func* f) {
void opt_make_conventional_ssa(Func* f) {
if (!f) return;
- opt_analysis_invalidate(f, OPT_ANALYSIS_CFG | OPT_ANALYSIS_DEF_USE |
- OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
+ opt_analysis_invalidate(
+ f, OPT_ANALYSIS_DEF_USE | OPT_ANALYSIS_DOM | OPT_ANALYSIS_LOOP);
for (u32 b = 0; b < f->nblocks; ++b) {
Block* bl = &f->blocks[b];
if (!bl->ninsts || (IROp)bl->insts[0].op != IR_PHI) continue;
diff --git a/test/driver/run.sh b/test/driver/run.sh
@@ -1077,7 +1077,7 @@ if "$KIT" cc -O1 --emit=ir -c "$work/ir.c" -o "$work/ir.out" \
grep -q "^func sym#" "$work/ir.out" &&
grep -q "binop" "$work/ir.out" &&
grep -q "iadd" "$work/ir.out" &&
- grep -q "ret values=\[" "$work/ir.out"; then
+ grep -q "ret value=" "$work/ir.out"; then
ok "cc-emit-ir"
else
not_ok "cc-emit-ir" "$work/ir-emit.err"