commit c289ef65e1f1eaa78ef7a2370c97a9d7653a6eb6
parent 0cca95ea4a0d690b13f196cfadd82f6a34de859b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 12 Jun 2026 09:11:52 -0700
feat(dbg): complete the x86_64-macOS debugger host adapter
The POSIX dbg signal path marshals register state through per-(arch,OS)
ucontext TUs, but darwin only had the aarch64 one — an x86_64-macOS host
build errored in env.mk ("no ucontext marshalling"), so the debugger
(and `kit run`'s crash guard) had no x86_64 Darwin backing.
- Add driver/env/uctx_x86_64_macos.c: marshal Darwin's
__darwin_x86_thread_state64 (mc->__ss) <-> KitUnwindFrame using System
V x86-64 DWARF numbering, mirroring the aarch64-macOS sibling.
- Wire the darwin/x86_64 branch in mk/env.mk.
- Fix a latent kit_cross.sh bug: the clang/macOS cc wrapper omitted
-arch, so a `macos-x64` cross-build silently produced the native arm64
slice while env.mk selected HOST_ARCH=x86_64 sources. Pass -arch
(mapping kit's `aarch64` tag to Apple's `arm64`).
Verified via the clang lane: the x86_64 kit builds, links, and runs
under Rosetta; `kit dbg` stops at a breakpoint with correct register
state (rdi=20, rsi=22 for add(20,22)) and resumes to exit 42. Native
test-dbg unaffected (29/0).
Diffstat:
3 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/driver/env/uctx_x86_64_macos.c b/driver/env/uctx_x86_64_macos.c
@@ -0,0 +1,64 @@
+/* ucontext_t <-> KitUnwindFrame marshalling for x86_64 on macOS.
+ * The frame array uses DWARF numbering, which is the System V x86-64 order
+ * (rax=0 rdx=1 rcx=2 rbx=3 rsi=4 rdi=5 rbp=6 rsp=7 r8..r15=8..15 rip=16).
+ * mcontext_t is a pointer to _STRUCT_MCONTEXT; the GPRs live in Darwin's
+ * __darwin_x86_thread_state64 (__ss), named __rax, __rbx, … __rip. */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "env_posix.h"
+
+static mcontext_t dbg_mcontext(const ucontext_t* uc) {
+ mcontext_t mc;
+ memcpy(&mc, (const char*)uc + offsetof(ucontext_t, uc_mcontext), sizeof(mc));
+ return mc;
+}
+
+void dbg_ucontext_to_frame(const ucontext_t* uc, KitUnwindFrame* f) {
+ mcontext_t mc = dbg_mcontext(uc);
+ const struct __darwin_x86_thread_state64* ss = &mc->__ss;
+ memset(f, 0, sizeof(*f));
+ f->regs[0] = (uint64_t)ss->__rax;
+ f->regs[1] = (uint64_t)ss->__rdx;
+ f->regs[2] = (uint64_t)ss->__rcx;
+ f->regs[3] = (uint64_t)ss->__rbx;
+ f->regs[4] = (uint64_t)ss->__rsi;
+ f->regs[5] = (uint64_t)ss->__rdi;
+ f->regs[6] = (uint64_t)ss->__rbp;
+ f->regs[7] = (uint64_t)ss->__rsp;
+ f->regs[8] = (uint64_t)ss->__r8;
+ f->regs[9] = (uint64_t)ss->__r9;
+ f->regs[10] = (uint64_t)ss->__r10;
+ f->regs[11] = (uint64_t)ss->__r11;
+ f->regs[12] = (uint64_t)ss->__r12;
+ f->regs[13] = (uint64_t)ss->__r13;
+ f->regs[14] = (uint64_t)ss->__r14;
+ f->regs[15] = (uint64_t)ss->__r15;
+ f->regs[16] = (uint64_t)ss->__rip;
+ f->pc = (uint64_t)ss->__rip;
+ f->cfa = (uint64_t)ss->__rsp;
+}
+
+void dbg_frame_to_ucontext(const KitUnwindFrame* f, ucontext_t* uc) {
+ mcontext_t mc = dbg_mcontext(uc);
+ struct __darwin_x86_thread_state64* ss = &mc->__ss;
+ ss->__rax = f->regs[0];
+ ss->__rdx = f->regs[1];
+ ss->__rcx = f->regs[2];
+ ss->__rbx = f->regs[3];
+ ss->__rsi = f->regs[4];
+ ss->__rdi = f->regs[5];
+ ss->__rbp = f->regs[6];
+ ss->__rsp = f->regs[7];
+ ss->__r8 = f->regs[8];
+ ss->__r9 = f->regs[9];
+ ss->__r10 = f->regs[10];
+ ss->__r11 = f->regs[11];
+ ss->__r12 = f->regs[12];
+ ss->__r13 = f->regs[13];
+ ss->__r14 = f->regs[14];
+ ss->__r15 = f->regs[15];
+ ss->__rip = f->pc;
+}
diff --git a/mk/env.mk b/mk/env.mk
@@ -138,6 +138,8 @@ DRIVER_ENV_UCTX_SRC :=
ifeq ($(HOST_OS),darwin)
ifeq ($(HOST_ARCH),aarch64)
DRIVER_ENV_UCTX_SRC := driver/env/uctx_aarch64_macos.c
+else ifeq ($(HOST_ARCH),x86_64)
+DRIVER_ENV_UCTX_SRC := driver/env/uctx_x86_64_macos.c
endif
else ifeq ($(HOST_OS),linux)
ifeq ($(HOST_ARCH),aarch64)
diff --git a/scripts/kit_cross.sh b/scripts/kit_cross.sh
@@ -88,9 +88,17 @@ else
ct="$(clang_triple)"
if [ "$os" = macos ]; then
sdk="$(xcrun --show-sdk-path 2>/dev/null || true)"
+ # -arch selects the slice (x86_64 cross-builds on an arm64 host run under
+ # Rosetta); kit's `aarch64` tag is Apple's `arm64`. Without this the wrapper
+ # silently builds the native slice while env.mk selects HOST_ARCH sources.
+ case "$carch" in
+ aarch64) maarch=arm64 ;;
+ x86_64) maarch=x86_64 ;;
+ *) die "bad macos arch '$carch'" ;;
+ esac
cat > "$TCDIR/cc" <<EOF
#!/bin/sh
-exec clang ${sdk:+-isysroot "$sdk"} "\$@"
+exec clang -arch $maarch ${sdk:+-isysroot "$sdk"} "\$@"
EOF
else
command -v ld.lld >/dev/null 2>&1 || command -v lld >/dev/null 2>&1 || die "lld not found (needed for clang cross-link)"