Skip to content

fix: stop linking released macOS binaries to /nix/store libiconv - #2742

Merged
MarcusSorealheis merged 1 commit into
TraceMachina:mainfrom
MarcusSorealheis:fix-issue-2727-darwin-libiconv
Sep 5, 2026
Merged

fix: stop linking released macOS binaries to /nix/store libiconv#2742
MarcusSorealheis merged 1 commit into
TraceMachina:mainfrom
MarcusSorealheis:fix-issue-2727-darwin-libiconv

Conversation

@MarcusSorealheis

@MarcusSorealheis MarcusSorealheis commented Sep 5, 2026

Copy link
Copy Markdown
Member

What and why

Fixes #2727.

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 — including machines that
do have Nix, since that particular store path isn't there.

Nix's darwin libiconv is Apple's own libiconv, not GNU libiconv. I dumped both
symbol 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 the
install name at /usr/lib/libiconv.2.dylib is therefore an ABI-safe swap rather
than just a path rewrite that happens to load.

So flake.nix now rewrites the install name in preFixup for darwin targets and
then fails the build if any /nix/store reference survives in $out/bin.
install_name_tool invalidates the ad-hoc signature arm64 macOS requires, which
is why the rewrite runs in preFixupautoSignDarwinBinariesHook re-signs
during fixup, after it.

I also considered dropping p.libiconv from buildInputs and letting the linker
pick up an SDK stub, which would have recorded /usr/lib/libiconv.2.dylib
naturally. apple-sdk_14 ships no libiconv.tbd, so that isn't available.

release.yaml grows 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 is
cross-built on an x86_64 runner and can't be executed there.

How was this verified?

Built .#nativelink-aarch64-darwin on an M-series Mac running macOS 26.6 and
checked the actual release binary, not a proxy:

  • otool -L on the built nativelink now 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 out
    with zero /nix/store references.
  • codesign --verify --verbose reports "valid on disk" and "satisfies its
    Designated Requirement", so the re-signing after install_name_tool works.
  • Reproduced the release packaging exactly — install -m 0755 into a staging
    dir, tar -czf, extract into a fresh directory outside the store — and ran
    the extracted binary. It prints Usage: nativelink instead 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 preFixup really does run before autoSignDarwinBinariesHook,
that otool and install_name_tool are on PATH in the darwin stdenv while
codesign is not (hence the hook rather than a direct call), and that a C
program relinked this way resolves iconv_open against the system library at
runtime.

I could not exercise the release.yaml change itself, since that workflow only
runs on a published release. It's shell I read carefully rather than shell I
watched pass, and the grep 'Usage: nativelink' assertion is matched against
the 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-byte
unaffected.

The rewrite is deliberately narrow: it only touches libiconv*/libcharset*
under /nix/store, and anything else from the store trips the guard and fails
the 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.dylib stays present in the
dyld 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 Reviewable

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
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
nativelink Ready Ready Preview Sep 5, 2026 7:35pm UTC
nativelink-aidm Ready Ready Preview Sep 5, 2026 7:35pm UTC

Request Review

@MarcusSorealheis
MarcusSorealheis merged commit 63c9fd7 into TraceMachina:main Sep 5, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Released aarch64-apple-darwin binary does not start: links against an absolute /nix/store libiconv path

2 participants