Environment
- hermetic_cc_toolchain: v4.1.0
- Zig version: 0.12.0 (LLVM 17)
- Target:
aarch64-linux-gnu
- Host:
x86_64-linux
- Bazel compilation modes affected:
fastbuild, dbg (not opt)
Symptom
Indirect or virtual C++ calls that return large structs (e.g. std::string) crash with
SIGSEGV in fastbuild and dbg modes on AArch64. The same code works correctly in
opt mode.
Root Cause
The Zig compiler (LLVM 17) selects x8 as a scratch register when dispatching indirect
or virtual calls (blr x8). On AArch64, x8 is also the struct-return (sret) register:
the caller writes the return buffer address into x8 before the call, and the callee writes
the result to that address.
When x8 holds a vtable function pointer at the call site, the callee receives
x8 = function address instead of x8 = return buffer address, and writes the returned
struct into the function's own .text — which is read-only — causing SIGSEGV.
At -O2 the register allocator avoids this conflict by choosing a different register
(e.g. x9) for the function pointer and preserving x8 for sret. opt mode already
implies -O2 via --compilation_mode=opt, which is why only fastbuild and dbg are
affected.
Workaround
Append -O2 to compiler_flags for all AArch64 targets in toolchain/zig_cc_toolchain.bzl:
if ctx.attr.target.startswith("aarch64"):
compiler_flags.append("-O2")
Applying this unconditionally (including in opt mode) is harmless since opt already
compiles at -O2.
▎ Note: ctx.attr.target_cpu is always "k8" for Linux regardless of architecture.
▎ Use ctx.attr.target (the full Zig target triple, e.g. "aarch64-linux-gnu.2.35") to
▎ detect AArch64.
Environment
aarch64-linux-gnux86_64-linuxfastbuild,dbg(notopt)Symptom
Indirect or virtual C++ calls that return large structs (e.g.
std::string) crash withSIGSEGVinfastbuildanddbgmodes on AArch64. The same code works correctly inoptmode.Root Cause
The Zig compiler (LLVM 17) selects
x8as a scratch register when dispatching indirector virtual calls (
blr x8). On AArch64,x8is also the struct-return (sret) register:the caller writes the return buffer address into
x8before the call, and the callee writesthe result to that address.
When
x8holds a vtable function pointer at the call site, the callee receivesx8 = function addressinstead ofx8 = return buffer address, and writes the returnedstruct into the function's own
.text— which is read-only — causingSIGSEGV.At
-O2the register allocator avoids this conflict by choosing a different register(e.g.
x9) for the function pointer and preservingx8forsret.optmode alreadyimplies
-O2via--compilation_mode=opt, which is why onlyfastbuildanddbgareaffected.
Workaround
Append
-O2tocompiler_flagsfor all AArch64 targets intoolchain/zig_cc_toolchain.bzl: