Skip to content

Optimize macOS code signing with single-pass patch+sign for thin Mach-O binaries - #2588

Open
wolfv wants to merge 1 commit into
mainfrom
claude/arwen-codesign-rattler-frrg3u
Open

Optimize macOS code signing with single-pass patch+sign for thin Mach-O binaries#2588
wolfv wants to merge 1 commit into
mainfrom
claude/arwen-codesign-rattler-frrg3u

Conversation

@wolfv

@wolfv wolfv commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

This PR optimizes the code signing process for macOS binaries during package installation by implementing a single-pass streaming approach for thin Mach-O binaries.

Key improvements:

  1. Single-pass patch+sign+hash for thin Mach-O binaries: Instead of patching the binary, then re-signing it (requiring a full re-read and re-hash), thin Mach-O binaries now flow through a streaming signer in one pass. The patched bytes are signed and hashed on-the-fly, eliminating the extra I/O overhead.

  2. In-process code signing: Replaced reliance on spawning /usr/bin/codesign with the arwen_codesign crate for in-process ad-hoc signing. This is orders of magnitude faster and works cross-platform (e.g., signing macOS binaries from Linux).

  3. Smarter signing decisions: Added binary_content_will_change() to detect whether prefix replacement actually modifies the binary content. If the placeholder doesn't occur in the binary, the original linker signature is preserved, avoiding unnecessary re-signing.

  4. Fallback support: On macOS hosts, /usr/bin/codesign remains available as a fallback for fat binaries and any edge cases the in-process signer cannot handle.

Technical changes:

  • Refactored link_file() to compute whether re-signing is needed upfront
  • Added patch_and_sign_single_pass() for the optimized streaming path
  • Updated apple_codesign.rs to use arwen_codesign for in-process signing
  • Added comprehensive tests for thin and fat Mach-O binaries with and without placeholder replacement
  • Added test fixtures (test_exe_linker_signed, test_exe_fat) for macOS code signing tests

Fixes #<issue_number>

How Has This Been Tested?

  • Added three new unit tests in link.rs:
    • test_macho_binary_is_resigned_after_prefix_replacement: Verifies thin Mach-O binaries are re-signed after prefix replacement and the hash/size are correct
    • test_macho_binary_without_placeholder_is_not_resigned: Confirms that when the placeholder doesn't occur, the original linker signature is preserved
    • test_fat_macho_binary_is_resigned: Validates fat (universal) binaries are properly re-signed with all architecture slices
  • Tests run on any host platform (signing happens in-process)
  • Existing tests continue to pass

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added sufficient tests to cover my changes

https://claude.ai/code/session_01PWnq7uDMrjDi3q3b9P4qn3

Replace the `/usr/bin/codesign` subprocess with in-process ad-hoc
signing via the arwen-codesign crate when linking prefix-patched
binaries on macOS targets.

Highlights:

- Thin Mach-O binaries are now patched, re-signed and hashed in a
  single streaming pass: one read of the source, one write of the
  destination. Previously the flow was write -> spawn codesign (which
  reads and rewrites the whole file) -> re-read to recompute the hash.
  In-process signing runs at ~1.3 GB/s and saves ~20-40 ms of process
  spawn overhead per binary, which adds up over the thousands of
  dylibs in a typical environment.
- Fat (universal) binaries are signed per architecture slice in
  memory. `/usr/bin/codesign` is kept only as a fallback on macOS
  hosts for anything the in-process signer cannot handle.
- Signing now also works when installing macOS environments from
  non-macOS hosts (previously spawning /usr/bin/codesign simply
  failed).
- Whether a binary needs re-signing is now decided by scanning the
  source for the placeholder instead of comparing against the
  paths.json sha256; binaries in packages that do not record a sha256
  were previously modified but never re-signed, leaving them
  un-runnable on Apple Silicon.
- The identifier is derived from the file name, and entitlements are
  preserved, matching the previous
  `codesign --force --sign - --preserve-metadata=entitlements`
  invocation.

Tests link real Mach-O fixtures (linker-signed thin + fat) through
`link_file` and validate the resulting signatures with
`arwen_codesign::verify`; because signing is in-process these tests
run on any platform.

The arwen-codesign dependency currently points at a git branch and
should be switched to the crates.io release once >= 0.1.0 is
published.

User prompt: "I want to use arwen codesign in rattler instead of
calling codesign executable for adhoc signing. We might need to add a
'streaming' high performance adhoc signing ... can you take a look?
Also make sure arwen codesign is _PRODUCTION READY_ and crazy ass
fast!"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWnq7uDMrjDi3q3b9P4qn3
Comment thread Cargo.toml
apple-native-keyring-store = { version = "1.0.0", default-features = false }
archspec = "0.2.0"
# TODO: switch to the crates.io release once arwen-codesign >= 0.1.0 is published
arwen-codesign = { git = "https://github.com/wolfv/arwen", branch = "claude/arwen-codesign-rattler-frrg3u" }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is released Id love to pull this in!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants