Skip to content

Commit 4323a40

Browse files
committed
fix(macos): static libc++ via -Wl,-load_hidden (split-brain libc++ SIGSEGV)
CI forensics (PR #117, m1-m7 matrix + crash report): plain by-path archive linking leaves default-visibility libc++ symbols that dyld then unifies with the system libc++ from the shared cache — a split-brain where ostream<<int crosses from the static copy into the system copy's locale machinery (collate/num_put) and SIGSEGVs. -Wl,-load_hidden forces the archive AND hides its symbols; m6 (mixed C/C++) and m7 (pure-C++ int output) both run clean where m1/m3 crashed. (-hidden-l remains unusable under lld: it picks the sibling dylib.)
1 parent 5a213de commit 4323a40

1 file changed

Lines changed: 14 additions & 8 deletions

File tree

src/build/flags.cppm

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -398,14 +398,20 @@ CompileFlags compute_flags(const BuildPlan& plan) {
398398
auto libcxxAbiA = libDir / "libc++abi.a";
399399
if (std::filesystem::exists(libcxxA)
400400
&& std::filesystem::exists(libcxxAbiA)) {
401-
// Link the archives BY PATH. (-Wl,-hidden-l looked like
402-
// the canonical choice, but lld resolves it like a plain
403-
// -l and picks the sibling dylib in the same directory —
404-
// the binary then carries @rpath/libc++.1.dylib with no
405-
// rpath and dies at load. Observed on macos CI; path
406-
// form verified end-to-end incl. macos-14.)
407-
f.ldStdlibDefault = " -nostdlib++ " + escape_path(libcxxA)
408-
+ " " + escape_path(libcxxAbiA);
401+
// Link the archives via -Wl,-load_hidden,<path>: forces
402+
// the ARCHIVE (never a sibling dylib) and gives its
403+
// symbols hidden visibility. Both properties matter:
404+
// - plain BY-PATH linking leaves default-visibility
405+
// symbols that dyld then unifies with the system
406+
// libc++ pulled in via the shared cache — a
407+
// split-brain libc++ where ostream<<int crosses into
408+
// the system copy's locale machinery and SIGSEGVs
409+
// (CI forensics m1/m3 vs m6/m7).
410+
// - -Wl,-hidden-l resolves like a plain -l under lld
411+
// and picks the sibling DYLIB (load failure).
412+
f.ldStdlibDefault = " -nostdlib++"
413+
" -Wl,-load_hidden," + escape_path(libcxxA)
414+
+ " -Wl,-load_hidden," + escape_path(libcxxAbiA);
409415
}
410416
}
411417
std::string version_min;

0 commit comments

Comments
 (0)