kit

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

commit edf85f383ffd2d87fdfa84d839ccd4fe23505403
parent 0035bf0c8efe3a8b38fb19e4ee9118b750c29ed5
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  9 May 2026 04:15:59 -0700

driver: update ar, cc, ld subcommands and Makefile

Diffstat:
MMakefile | 2++
Mdriver/ar.c | 149++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mdriver/cc.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdriver/ld.c | 13+++++++++++++
4 files changed, 212 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile @@ -51,3 +51,5 @@ build/driver/%.o: driver/%.c clean: rm -rf build + +include test/test.mk diff --git a/driver/ar.c b/driver/ar.c @@ -15,12 +15,19 @@ * cfree ar x archive.a [members...] extract members to cwd * cfree ar p archive.a [members...] print members to stdout * + * Modifiers (combine with above): + * s emit a System V `/` symbol-index member at the head of the + * archive. Only valid combined with r/c (i.e. rs, cs, rcs). + * Each member is opened with cfree_obj_open and its globally- + * defined symbols (CFREE_SB_GLOBAL with section != NONE) are + * indexed; non-object members contribute no symbols. + * * Reproducibility: SOURCE_DATE_EPOCH (when set to a positive integer) is * written to ar_date for every member on write. Long member names are * routed through a `//` extended-name table. * - * Not yet implemented: d (delete), q (quick append), s (symbol index), - * v (verbose). Encountering any of those mode letters yields a usage + * Not yet implemented: d (delete), q (quick append), v (verbose), and + * standalone `s` without r/c. Encountering any of those yields a usage * error with exit code 2. */ #define AR_TOOL "ar" @@ -186,7 +193,7 @@ static int ar_do_print(DriverEnv* env, const char* archive_path, */ static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers, char** member_paths, - int has_r, int has_c) + int has_r, int has_c, int has_s) { uint32_t nnew = nmembers > 0 ? (uint32_t)nmembers : 0u; CfreeBytesInput* members = NULL; @@ -200,6 +207,13 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, int have_old = 0; int rc = 0; uint32_t i; + /* Per-member symbol storage (only used when has_s). msyms is the array + * passed to cfree_ar_write; sym_allocs holds the single per-member + * blob backing each msyms[i].names + name bytes (one allocation per + * member, sized in sym_alloc_szs for the matching driver_free). */ + CfreeArMemberSymbols* msyms = NULL; + void** sym_allocs = NULL; + size_t* sym_alloc_szs = NULL; opts.epoch = ar_epoch_from_env(); opts.long_names = 1; @@ -307,6 +321,116 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, } } + if (has_s && nm > 0) { + msyms = (CfreeArMemberSymbols*)driver_alloc_zeroed( + env, (size_t)nm * sizeof(*msyms)); + sym_allocs = (void**)driver_alloc_zeroed( + env, (size_t)nm * sizeof(*sym_allocs)); + sym_alloc_szs = (size_t*)driver_alloc_zeroed( + env, (size_t)nm * sizeof(*sym_alloc_szs)); + if (!msyms || !sym_allocs || !sym_alloc_szs) { + driver_errf(AR_TOOL, "out of memory"); + rc = 1; + goto done; + } + for (i = 0; i < nm; ++i) { + CfreeBytesInput in; + CfreeObjFile* of; + CfreeObjSymIter* it; + CfreeObjSymInfo si; + uint32_t count = 0; + size_t name_bytes = 0; + size_t alloc_sz; + char* blob; + const char** name_arr; + char* name_storage; + size_t cursor = 0; + + in.name = members[i].name; + in.data = members[i].data; + in.len = members[i].len; + in.lang = 0; + + of = cfree_obj_open(&cenv, &in); + if (!of) continue; /* not an object file → no symbols */ + + /* Pass A: count globally-defined symbols and total name bytes. */ + it = cfree_obj_symiter_new(of); + if (!it) { + cfree_obj_close(of); + driver_errf(AR_TOOL, "out of memory"); + rc = 1; + goto done; + } + while (cfree_obj_symiter_next(it, &si)) { + if (si.bind != CFREE_SB_GLOBAL) continue; + if (si.section == CFREE_SECTION_NONE) continue; + if (!si.name || !si.name[0]) continue; + count += 1; + { + const char* p = si.name; + while (*p++) ++name_bytes; + name_bytes += 1; /* NUL */ + } + } + cfree_obj_symiter_free(it); + + if (count == 0) { + cfree_obj_close(of); + continue; + } + + /* Single allocation: [name_arr][name_storage]. Names are copied + * out of the obj file before close so they outlive cfree_obj_close. */ + alloc_sz = (size_t)count * sizeof(const char*) + name_bytes; + blob = (char*)driver_alloc_zeroed(env, alloc_sz); + if (!blob) { + cfree_obj_close(of); + driver_errf(AR_TOOL, "out of memory"); + rc = 1; + goto done; + } + name_arr = (const char**)blob; + name_storage = blob + (size_t)count * sizeof(const char*); + + /* Pass B: copy names. */ + it = cfree_obj_symiter_new(of); + if (!it) { + driver_free(env, blob, alloc_sz); + cfree_obj_close(of); + driver_errf(AR_TOOL, "out of memory"); + rc = 1; + goto done; + } + { + uint32_t k = 0; + while (cfree_obj_symiter_next(it, &si) && k < count) { + const char* p; + char* dst; + if (si.bind != CFREE_SB_GLOBAL) continue; + if (si.section == CFREE_SECTION_NONE) continue; + if (!si.name || !si.name[0]) continue; + dst = name_storage + cursor; + name_arr[k] = dst; + for (p = si.name; *p; ++p) *dst++ = *p; + *dst++ = '\0'; + cursor = (size_t)(dst - name_storage); + k++; + } + count = k; + } + cfree_obj_symiter_free(it); + cfree_obj_close(of); + + sym_allocs[i] = blob; + sym_alloc_szs[i] = alloc_sz; + msyms[i].names = name_arr; + msyms[i].count = count; + } + opts.symbol_index = 1; + opts.member_symbols = msyms; + } + out = cenv.file_io->open_writer(cenv.file_io->user, archive_path); if (!out) { driver_errf(AR_TOOL, "failed to open: %s", archive_path); @@ -318,6 +442,14 @@ static int ar_do_write(DriverEnv* env, const char* archive_path, } done: + if (sym_allocs) { + for (i = 0; i < nm; ++i) { + if (sym_allocs[i]) driver_free(env, sym_allocs[i], sym_alloc_szs[i]); + } + driver_free(env, sym_allocs, (size_t)nm * sizeof(*sym_allocs)); + driver_free(env, sym_alloc_szs, (size_t)nm * sizeof(*sym_alloc_szs)); + } + if (msyms) driver_free(env, msyms, (size_t)nm * sizeof(*msyms)); if (new_fds) { for (i = 0; i < nnew; ++i) { if (new_fds[i].data) @@ -341,6 +473,7 @@ int driver_ar(int argc, char** argv) int do_print = 0; int has_r = 0; int has_c = 0; + int has_s = 0; int i; int rc; @@ -356,10 +489,11 @@ int driver_ar(int argc, char** argv) switch (mode[i]) { case 'r': do_write = 1; has_r = 1; break; case 'c': do_write = 1; has_c = 1; break; + case 's': has_s = 1; break; case 't': do_list = 1; break; case 'x': do_extract = 1; break; case 'p': do_print = 1; break; - case 'd': case 'q': case 's': case 'v': + case 'd': case 'q': case 'v': driver_errf(AR_TOOL, "operation not implemented: %c", mode[i]); return 2; default: @@ -367,6 +501,10 @@ int driver_ar(int argc, char** argv) return 2; } } + if (has_s && !do_write) { + driver_errf(AR_TOOL, "s requires r or c"); + return 2; + } { int kinds = !!do_write + !!do_list + !!do_extract + !!do_print; @@ -395,7 +533,8 @@ int driver_ar(int argc, char** argv) } else if (do_print) { rc = ar_do_print(&env, archive_path, argc, argv, 3); } else { - rc = ar_do_write(&env, archive_path, argc - 3, argv + 3, has_r, has_c); + rc = ar_do_write(&env, archive_path, argc - 3, argv + 3, + has_r, has_c, has_s); } driver_env_fini(&env); diff --git a/driver/cc.c b/driver/cc.c @@ -8,7 +8,7 @@ * without -c compiles all C sources, links any .o/.a inputs alongside, and * emits an executable. The flag surface is a GCC subset: * - * -c -E -o -O0/1/2 -g + * -c -E -o -O0/1/2 -g --dump-tokens * -I -isystem -D -U * -M -MM -MD -MMD -MF -MT -MQ -MP * -target TRIPLE @@ -49,6 +49,7 @@ typedef struct CcOptions { int compile_only; /* -c */ int preprocess_only; /* -E */ + int dump_tokens; /* --dump-tokens */ int opt_level; /* -O0/-O1/-O2 */ int debug_info; /* -g */ int warnings_are_errors; /* -Werror */ @@ -122,7 +123,7 @@ typedef struct CcOptions { static void cc_usage(void) { driver_errf(CC_TOOL, "%s", - "usage: cfree cc [-c|-E] [-o out] [-O0|-O1|-O2] [-g]\n" + "usage: cfree cc [-c|-E|--dump-tokens] [-o out] [-O0|-O1|-O2] [-g]\n" " [-I dir]... [-isystem dir]...\n" " [-D name[=body]]... [-U name]...\n" " [-M|-MM|-MD|-MMD] [-MF file] [-MT target]... [-MP]\n" @@ -463,6 +464,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) if (driver_streq(a, "-c")) { o->compile_only = 1; continue; } if (driver_streq(a, "-E")) { o->preprocess_only = 1; continue; } + if (driver_streq(a, "--dump-tokens")) { o->dump_tokens = 1; continue; } if (driver_streq(a, "-g")) { o->debug_info = 1; continue; } if (driver_streq(a, "-O0")){ o->opt_level = 0; continue; } if (driver_streq(a, "-O1")){ o->opt_level = 1; continue; } @@ -615,8 +617,12 @@ static int cc_parse(int argc, char** argv, CcOptions* o) driver_errf(CC_TOOL, "-c and -E are mutually exclusive"); return 1; } - if (o->shared && (o->compile_only || o->preprocess_only)) { - driver_errf(CC_TOOL, "-shared is incompatible with -c/-E"); + if (o->dump_tokens && (o->compile_only || o->preprocess_only)) { + driver_errf(CC_TOOL, "--dump-tokens is incompatible with -c/-E"); + return 1; + } + if (o->shared && (o->compile_only || o->preprocess_only || o->dump_tokens)) { + driver_errf(CC_TOOL, "-shared is incompatible with -c/-E/--dump-tokens"); return 1; } if (!o->shared && (o->soname || o->nrpaths)) { @@ -636,11 +642,17 @@ static int cc_parse(int argc, char** argv, CcOptions* o) return 1; } } + if (o->dump_tokens) { + if (total_sources != 1 || total_link != 0) { + driver_errf(CC_TOOL, "--dump-tokens requires exactly one C source and no .o/.a inputs"); + return 1; + } + } { int dep_only = (o->dep_mode == CC_DEP_M || o->dep_mode == CC_DEP_MM); int dep_with_compile = (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD); - if (o->dep_mode != CC_DEP_NONE && o->preprocess_only) { - driver_errf(CC_TOOL, "-M* is incompatible with -E"); + if (o->dep_mode != CC_DEP_NONE && (o->preprocess_only || o->dump_tokens)) { + driver_errf(CC_TOOL, "-M* is incompatible with -E/--dump-tokens"); return 1; } if (dep_only && total_sources != 1) { @@ -720,6 +732,39 @@ out: return rc; } +static int cc_dump_tokens(DriverEnv* env, const CcOptions* o) +{ + CfreeEnv cenv = driver_env_to_cfree(env); + CfreeCompiler* compiler = NULL; + CfreeWriter* writer = NULL; + CfreeFileData fd = {0}; + CfreeBytesInput input = {0}; + int rc = 1; + int loaded = 0; + + if (cc_load_single_source(env, &cenv, o, &input, &fd, &loaded) != 0) goto out; + + writer = cenv.file_io->open_writer(cenv.file_io->user, o->output_path); + if (!writer) { + driver_errf(CC_TOOL, "failed to open output: %s", o->output_path); + goto out; + } + + compiler = cfree_compiler_new(o->target, &cenv); + if (!compiler) { + driver_errf(CC_TOOL, "failed to initialize compiler"); + goto out; + } + + rc = cfree_dump_tokens(compiler, &input, writer); + +out: + if (compiler) cfree_compiler_free(compiler); + if (writer) cfree_writer_close(writer); + if (loaded) cenv.file_io->release(cenv.file_io->user, &fd); + return rc; +} + /* ---- header-dependency output (-M family) ---- */ typedef struct CcDepList { @@ -1267,6 +1312,8 @@ int driver_cc(int argc, char** argv) if (co.preprocess_only) { rc = cc_preprocess(&env, &co, &pp); + } else if (co.dump_tokens) { + rc = cc_dump_tokens(&env, &co); } else if (co.dep_mode == CC_DEP_M || co.dep_mode == CC_DEP_MM) { rc = cc_run_deps_only(&env, &co, &pp); } else if (co.compile_only) { diff --git a/driver/ld.c b/driver/ld.c @@ -516,6 +516,19 @@ static int ld_run_link(LdOptions* o) } } + /* Auto-detect target from the first object input, falling back to + * the host target. PIC overrides parsed from flags are preserved. */ + if (o->nobject_files > 0) { + CfreeTarget detected; + if (cfree_detect_target(obj_lf[0].data.data, + obj_lf[0].data.size, + &detected) == 0) { + uint8_t pic = o->target.pic; + o->target = detected; + if (pic != CFREE_PIC_NONE) o->target.pic = pic; + } + } + compiler = cfree_compiler_new(o->target, &cenv); if (!compiler) { driver_errf(LD_TOOL, "failed to initialize compiler");