fix: stop linking released macOS binaries to /nix/store libiconv - #2742
Merged
MarcusSorealheis merged 1 commit intoSep 5, 2026
Merged
Conversation
The aarch64-apple-darwin release binary recorded an absolute /nix/store/...-libiconv-113/lib/libiconv.2.dylib install name, so dyld refused to launch it on any machine but the one that built it. Nix's darwin libiconv is Apple's own libiconv, and it exports the same symbols as the copy macOS keeps in the dyld shared cache. Repointing the install name at /usr/lib/libiconv.2.dylib is therefore ABI-safe and leaves the binary with no Nix store references at all. install_name_tool invalidates the ad-hoc signature arm64 macOS requires, so the rewrite runs in preFixup and autoSignDarwinBinariesHook re-signs it during fixup. The build now fails outright if any /nix/store reference survives, and the release workflow additionally unpacks the finished tarball and runs the binary, so a regression cannot reach a published release unnoticed. Fixes TraceMachina#2727 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UmmEKcy5V7MJvqQz2Gu1dD
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
corcillo
approved these changes
Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Fixes #2727.
The
aarch64-apple-darwinrelease binary recorded an absolute/nix/store/...-libiconv-113/lib/libiconv.2.dylibinstall name, so dyld refusedto launch it on any machine but the one that built it — including machines that
do have Nix, since that particular store path isn't there.
Nix's darwin
libiconvis Apple's own libiconv, not GNU libiconv. I dumped bothsymbol tables and they match: the nixpkgs build (115.100.1) and the copy macOS
keeps in the dyld shared cache each export
_iconv_open/_iconv/_iconv_close/_iconvctl/_iconvlist/_libiconv_set_relocation_prefix,and neither exports the GNU-style
_libiconv_*entry points. Repointing theinstall name at
/usr/lib/libiconv.2.dylibis therefore an ABI-safe swap ratherthan just a path rewrite that happens to load.
So
flake.nixnow rewrites the install name inpreFixupfor darwin targets andthen fails the build if any
/nix/storereference survives in$out/bin.install_name_toolinvalidates the ad-hoc signature arm64 macOS requires, whichis why the rewrite runs in
preFixup—autoSignDarwinBinariesHookre-signsduring fixup, after it.
I also considered dropping
p.libiconvfrombuildInputsand letting the linkerpick up an SDK stub, which would have recorded
/usr/lib/libiconv.2.dylibnaturally.
apple-sdk_14ships nolibiconv.tbd, so that isn't available.release.yamlgrows a smoke test that unpacks the finished tarball and runs it,because the only reason this reached users is that nothing ever executed the
artifact we published. It's skipped for
aarch64-unknown-linux-musl, which iscross-built on an x86_64 runner and can't be executed there.
How was this verified?
Built
.#nativelink-aarch64-darwinon an M-series Mac running macOS 26.6 andchecked the actual release binary, not a proxy:
otool -Lon the builtnativelinknow reads/usr/lib/libiconv.2.dylib,matching the good state described in the issue. All three binaries in
$out/bin(nativelink,redis_store_tester,cas_speed_check) come outwith zero
/nix/storereferences.codesign --verify --verbosereports "valid on disk" and "satisfies itsDesignated Requirement", so the re-signing after
install_name_toolworks.install -m 0755into a stagingdir,
tar -czf, extract into a fresh directory outside the store — and ranthe extracted binary. It prints
Usage: nativelinkinstead of dying in dyld,which is the failure mode from the issue.
Before landing on this shape I confirmed the mechanics in a standalone
derivation: that
preFixupreally does run beforeautoSignDarwinBinariesHook,that
otoolandinstall_name_toolare onPATHin the darwin stdenv whilecodesignis not (hence the hook rather than a direct call), and that a Cprogram relinked this way resolves
iconv_openagainst the system library atruntime.
I could not exercise the
release.yamlchange itself, since that workflow onlyruns on a published release. It's shell I read carefully rather than shell I
watched pass, and the
grep 'Usage: nativelink'assertion is matched againstthe real binary output quoted above.
Risk
Low, and contained to macOS builds — the added attributes are behind
p.stdenv.targetPlatform.isDarwin, so both Linux musl targets are byte-for-byteunaffected.
The rewrite is deliberately narrow: it only touches
libiconv*/libcharset*under
/nix/store, and anything else from the store trips the guard and failsthe build. That's the intended failure direction — a future dependency that
drags in another store dylib breaks the build loudly instead of shipping another
binary that won't start. Whoever hits it has to decide between relocating it or
linking statically, which is the right call to make deliberately.
The one real assumption is that
/usr/lib/libiconv.2.dylibstays present in thedyld shared cache and ABI-compatible. It's been there since the shared cache
existed, and the release smoke test would catch it if that ever changed, at
release time rather than in a user's hands.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UmmEKcy5V7MJvqQz2Gu1dD
This change is