kit

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

commit ce01a09feccca1f0f7a2c1c0af1e5f37ada34534
parent e65c7fb58d0ce0c8ab01662418758b92937379f9
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 15:25:53 -0700

cc: honor -Wl link side-reports (cref / memory-usage / map / symbols)

cc accepts -Wl,/-Xlinker report flags and records them in driver_link_flags
(map_path/symbols_path/cref_path/print_memory_usage), but the link ran inside
kit_build_link_with_lto, which creates/emits/frees the KitLinkSession
internally — cc never saw the session, so the reports were silently dropped
(exit 0, no file/output). ld and build-exe write them from the live session;
cc had regressed loud-error to silent-drop for --cref/--print-memory-usage and
had a pre-existing silent drop for -Map/--symbols.

Honor them (the cc/ld/build-exe-parity choice, not a loud re-error): add
kit_build_link_with_lto_report, a variant of kit_build_link_with_lto that
invokes a caller callback with the still-live session after a successful emit
(session ownership stays inside build.c; the handle is not leaked out).
kit_build_link_with_lto now delegates to it with a NULL callback.
cc supplies cc_link_write_reports, which writes map/symbols/cref via cc's
KitFileIO and memory-usage to stdout, only when any report was requested.

Test (test/buildcmds/run.sh): kit cc -Wl,--cref=FILE writes the table,
-Wl,--print-memory-usage prints the region summary, and -Wl,-Map=/--symbols=
write their side files. Red-confirmed: with the old call the cc invocations
still exit 0 but produce no output (8 file/content asserts fail).

Diffstat:
Mdriver/cmd/cc.c | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Minclude/kit/build.h | 13+++++++++++++
Msrc/api/build.c | 24++++++++++++++++++------
Mtest/buildcmds/run.sh | 24++++++++++++++++++++++++
4 files changed, 140 insertions(+), 8 deletions(-)

diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -2104,6 +2104,76 @@ out: return rc; } +/* Side-report writer for the link session: -Wl,-Map / --symbols / --cref FILE + * and --print-memory-usage. cc routes these through driver_link_flags but the + * link runs inside kit_build_link_with_lto_report, which hands back the live + * session here so the reports actually get written (rather than being silently + * dropped). Kept parallel to ld and build-exe's report writers. */ +typedef struct CcLinkReports { + DriverEnv* env; + const KitFileIO* io; + const CcOptions* o; +} CcLinkReports; + +static KitStatus cc_write_link_report(const CcLinkReports* r, + KitLinkSession* link, const char* path, + int symbols) { + KitWriter* w = NULL; + KitStatus st; + if (!path) return KIT_OK; + if (r->io->open_writer(r->io->user, path, &w) != KIT_OK) { + driver_errf(CC_TOOL, "failed to open %s output: %.*s", + symbols ? "symbols" : "map", + KIT_SLICE_ARG(kit_slice_cstr(path))); + return KIT_IO; + } + st = symbols + ? kit_link_session_write_symbols(link, r->o->link.symbols_format, w) + : kit_link_session_write_map(link, w); + if (st == KIT_OK) st = kit_writer_status(w); + kit_writer_close(w); + if (st != KIT_OK) + driver_errf(CC_TOOL, "failed to write %s: %.*s", symbols ? "symbols" : "map", + KIT_SLICE_ARG(kit_slice_cstr(path))); + return st; +} + +static KitStatus cc_link_write_reports(void* user, KitLinkSession* link) { + const CcLinkReports* r = (const CcLinkReports*)user; + KitStatus st; + st = cc_write_link_report(r, link, r->o->link.map_path, 0); + if (st == KIT_OK) + st = cc_write_link_report(r, link, r->o->link.symbols_path, 1); + if (st == KIT_OK && r->o->link.cref_path) { + KitWriter* w = NULL; + if (r->io->open_writer(r->io->user, r->o->link.cref_path, &w) != KIT_OK) { + driver_errf(CC_TOOL, "failed to open cref output: %.*s", + KIT_SLICE_ARG(kit_slice_cstr(r->o->link.cref_path))); + st = KIT_IO; + } else { + st = kit_link_session_write_cref(link, w); + if (st == KIT_OK) st = kit_writer_status(w); + kit_writer_close(w); + if (st != KIT_OK) + driver_errf(CC_TOOL, "failed to write cref: %.*s", + KIT_SLICE_ARG(kit_slice_cstr(r->o->link.cref_path))); + } + } + if (st == KIT_OK && r->o->link.print_memory_usage) { + KitWriter* w = driver_stdout_writer(r->env); + if (!w) { + driver_errf(CC_TOOL, "failed to print memory usage"); + st = KIT_IO; + } else { + st = kit_link_session_write_memory_usage(link, w); + if (st == KIT_OK) st = kit_writer_status(w); + kit_writer_close(w); + if (st != KIT_OK) driver_errf(CC_TOOL, "failed to print memory usage"); + } + } + return st; +} + /* exe/shared path: compile every source via a single KitCompiler, load * .o/.a/script inputs, and link. The link session borrows the per-source * KitObjBuilders; this function keeps ownership and frees them after the @@ -2335,8 +2405,21 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o, li.ndsos = o->inputs.ndsos; li.order = order; li.norder = norder; - st = kit_build_link_with_lto(compiler, &lopts, &li, &pending_lto, - &lto_batch, out_w); + { + /* Honor the link side-reports (-Wl,-Map / --symbols / --cref / + * --print-memory-usage) that flowed in via driver_link_flags by + * writing them from the live session — matching ld and build-exe. */ + CcLinkReports reports; + int want_reports = o->link.map_path || o->link.symbols_path || + o->link.cref_path || o->link.print_memory_usage; + reports.env = env; + reports.io = io; + reports.o = o; + st = kit_build_link_with_lto_report( + compiler, &lopts, &li, &pending_lto, &lto_batch, out_w, + want_reports ? cc_link_write_reports : NULL, + want_reports ? (void*)&reports : NULL); + } } rc = st == KIT_OK ? 0 : 1; } diff --git a/include/kit/build.h b/include/kit/build.h @@ -147,4 +147,17 @@ KIT_API KitStatus kit_build_link_with_lto(KitCompiler* compiler, const KitBuildBatchOptions* batch, KitWriter* out); +/* As kit_build_link_with_lto, but after a successful emit — and while the + * session is still live — invoke `on_linked(user, link)` so the caller can + * write side reports (map/symbols/cref/memory-usage) that need the resolved + * session. The session is still always freed before return; `on_linked` must + * not retain `link`. A non-KIT_OK return from `on_linked` fails the build (and + * aborts any pending LTO). `on_linked` may be NULL, in which case this behaves + * exactly like kit_build_link_with_lto. */ +KIT_API KitStatus kit_build_link_with_lto_report( + KitCompiler* compiler, const KitLinkSessionOptions* lopts, + const KitLinkInputs* in, KitBuildPendingLto* pending_lto, + const KitBuildBatchOptions* batch, KitWriter* out, + KitStatus (*on_linked)(void* user, KitLinkSession* link), void* user); + #endif diff --git a/src/api/build.c b/src/api/build.c @@ -302,12 +302,11 @@ static KitStatus build_link_add_inputs(KitLinkSession* link, return st; } -KitStatus kit_build_link_with_lto(KitCompiler* compiler, - const KitLinkSessionOptions* lopts, - const KitLinkInputs* in, - KitBuildPendingLto* pending_lto, - const KitBuildBatchOptions* batch, - KitWriter* out) { +KitStatus kit_build_link_with_lto_report( + KitCompiler* compiler, const KitLinkSessionOptions* lopts, + const KitLinkInputs* in, KitBuildPendingLto* pending_lto, + const KitBuildBatchOptions* batch, KitWriter* out, + KitStatus (*on_linked)(void* user, KitLinkSession* link), void* user) { KitLinkSession* link = NULL; BuildPreservedVec preserved; KitStatus st; @@ -331,6 +330,9 @@ KitStatus kit_build_link_with_lto(KitCompiler* compiler, } } if (st == KIT_OK) st = kit_link_session_emit(link, out); + /* Side reports (cref/memory-usage/map/symbols) need the resolved session, so + * write them here while `link` is still live. */ + if (st == KIT_OK && on_linked) st = on_linked(user, link); kit_link_session_free(link); if (preserved.syms) preserved.heap->free(preserved.heap, preserved.syms, @@ -340,6 +342,16 @@ KitStatus kit_build_link_with_lto(KitCompiler* compiler, return st; } +KitStatus kit_build_link_with_lto(KitCompiler* compiler, + const KitLinkSessionOptions* lopts, + const KitLinkInputs* in, + KitBuildPendingLto* pending_lto, + const KitBuildBatchOptions* batch, + KitWriter* out) { + return kit_build_link_with_lto_report(compiler, lopts, in, pending_lto, batch, + out, NULL, NULL); +} + KitStatus kit_build_link(KitCompiler* compiler, const KitLinkSessionOptions* lopts, const KitLinkInputs* in, KitWriter* out) { diff --git a/test/buildcmds/run.sh b/test/buildcmds/run.sh @@ -883,6 +883,30 @@ else skip_test fy-cref-imported-symbol "no libc.so fixture" skip_test fy-cref-imported-def-dso "no libc.so fixture" fi +# ==== BEGIN kernel-FZ Bug 11: cc honors -Wl link side-reports =============== +# cc routes -Wl,/-Xlinker report flags through driver_link_flags, but its link +# ran inside a build helper that created/emitted/freed the session internally, +# so --cref / --print-memory-usage / -Map / --symbols were silently dropped +# (exit 0, no output). cc must now write them from the live session, matching +# ld and build-exe. Reuses the xmain.c/xhelp.c cref fixtures above. +run_ok cc-wl-cref "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ + -e _start -Wl,--cref=cc_cref.out xmain.c xhelp.c -o cc_cref.elf +assert_file_exists cc-wl-cref-file cc_cref.out +contains cc-wl-cref-header cc_cref.out "cross reference table" +contains cc-wl-cref-symbol cc_cref.out "xref_helper" +# --print-memory-usage emits the per-region summary to stdout (captured in .out). +run_ok cc-wl-memuse "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ + -e _start -Wl,--print-memory-usage xmain.c xhelp.c -o cc_memuse.elf +contains cc-wl-memuse-header "$work/cc-wl-memuse.out" "Memory region" +# -Map / --symbols side files (the pre-existing silent drop) also get written. +run_ok cc-wl-map "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ + -e _start -Wl,-Map=cc_map.out -Wl,--symbols=cc_sym.out \ + xmain.c xhelp.c -o cc_map.elf +assert_file_exists cc-wl-map-file cc_map.out +contains cc-wl-map-segments cc_map.out "segments" +assert_file_exists cc-wl-symbols-file cc_sym.out +contains cc-wl-symbols-start cc_sym.out "_start" +# ==== END kernel-FZ Bug 11 ================================================== kit_summary build-driver kit_exit