kit

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

commit 0fa2b1d65c38f0213f6f5a2df8d73ee2e3d4553c
parent feb396e0fd9c03228accaafe31af0b6f1c8d6a7b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 10 Jun 2026 10:14:55 -0700

fix(c): emit asm-label rename as verbatim symbol, not re-mangled

An asm label (`extern T f(...) __asm__("name")`) is the verbatim linker
symbol name and must bypass C name mangling. decl_emit_cg_sym routed the
label back through kit_cg_c_linkage_name, which prepends the Mach-O leading
underscore. The Darwin headers spell that underscore themselves (e.g.
realpath: __asm("_realpath$DARWIN_EXTSN")), so the result was doubled
(__realpath$DARWIN_EXTSN) and unresolvable against libSystem.

Surfaced only in the stage2/3 self-build: host clang compiles driver/env/
macos.c correctly, so only kit-compiling-macos.c hit it. realpath is the
sole asm-labeled symbol kit references on arm64 macOS, so it was latent
since the realpath use landed (19bc7463).

Use the asm label verbatim; only mangle the plain source name. No-op on
ELF (mangler was already identity there). Adds a host-independent driver
regression test that targets Mach-O and asserts the verbatim symbol.

Diffstat:
Mlang/c/decl/decl.c | 11+++++++++--
Mtest/driver/run.sh | 25+++++++++++++++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/lang/c/decl/decl.c b/lang/c/decl/decl.c @@ -93,8 +93,15 @@ static ObjSymId decl_emit_cg_sym(DeclTable* t, const Decl* slot) { decl.kind = (slot->type && slot->type->kind == TY_FUNC) ? KIT_CG_DECL_FUNC : KIT_CG_DECL_OBJECT; decl.display_name = slot->name; - decl.linkage_name = - kit_cg_c_linkage_name(t->c, slot->asm_name ? slot->asm_name : slot->name); + /* An asm label (`extern T f(...) __asm__("name")`) is the verbatim linker + * symbol name: it bypasses C name mangling, including the Mach-O leading + * underscore. The Darwin system headers spell the underscore themselves + * (e.g. realpath's `__asm("_realpath$DARWIN_EXTSN")`), so routing the label + * back through kit_cg_c_linkage_name would double it. Only mangle the plain + * source name. */ + decl.linkage_name = slot->asm_name + ? slot->asm_name + : kit_cg_c_linkage_name(t->c, slot->name); decl.type = type_cg_id_in_pool(t->c, t->pool, slot->type); decl.sym = decl_sym_attrs(slot); if (decl.kind == KIT_CG_DECL_FUNC) { diff --git a/test/driver/run.sh b/test/driver/run.sh @@ -264,6 +264,31 @@ SRC run_ok "cc-implicit-freestanding-headers" "$KIT" cc -target aarch64-linux \ -c "$work/implicit-header.c" -o "$work/implicit-header.o" +# ---- asm-label rename emits the verbatim symbol (no doubled underscore) ---- +# `extern f __asm__("_name")` must produce the linker symbol "_name" exactly. +# The Darwin headers spell the Mach-O leading underscore themselves (e.g. +# realpath's __asm("_realpath$DARWIN_EXTSN")), so the C frontend must use the +# asm label verbatim instead of re-running it through the underscore mangler. +# Regression: doubling the underscore (__realpath$DARWIN_EXTSN) broke the +# stage2 self-build link against libSystem. Always target Mach-O so the check +# is host-independent. +cat > "$work/asm-rename.c" <<'SRC' +extern int probe(void) __asm__("_kit_rename_target"); +int caller(void) { return probe(); } +SRC +if "$KIT" cc -target arm64-apple-macos -c "$work/asm-rename.c" \ + -o "$work/asm-rename.o" 2> "$work/asm-rename.err" && + "$KIT" nm "$work/asm-rename.o" 2> "$work/asm-rename-nm.err" \ + | grep -qE '[[:space:]]_kit_rename_target$' && + ! "$KIT" nm "$work/asm-rename.o" 2>/dev/null | grep -q '__kit_rename_target'; then + ok "cc-asm-label-rename-verbatim-symbol" +else + { cat "$work/asm-rename.err" 2>/dev/null + "$KIT" nm "$work/asm-rename.o" 2>/dev/null | sed 's/^/nm: /'; } \ + > "$work/asm-rename.diag" + not_ok "cc-asm-label-rename-verbatim-symbol" "$work/asm-rename.diag" +fi + # ---- runtime auto-build + link via nm (aarch64) ---- mkdir -p "$work/rt-support/rt" cp -R "$repo_root/rt/include" "$work/rt-support/rt/include"