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:
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,
- <o_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, <o_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