Skip to content

fix(keystore): build macOS helper for target arch, not build host#370

Open
hibari-tech wants to merge 1 commit into
solana-foundation:mainfrom
hibari-tech:fix/keychain-helper-target-arch
Open

fix(keystore): build macOS helper for target arch, not build host#370
hibari-tech wants to merge 1 commit into
solana-foundation:mainfrom
hibari-tech:fix/keychain-helper-target-arch

Conversation

@hibari-tech

@hibari-tech hibari-tech commented May 25, 2026

Copy link
Copy Markdown

Summary

Fixes #369. The Swift keychain helper was compiled with no -target, so swiftc defaulted to the build host's architecture. On Apple Silicon CI runners this produced an arm64 helper even when cross-compiling the crate for x86_64-apple-darwin — leaving the released x86_64 pay binary with an arm64 helper embedded via include_bytes!.

At first run that helper is extracted to ~/.cache/pay/pay.sh and posix_spawned, which returns EBADARCH (86) ("Bad CPU type in executable") on Intel Macs, breaking pay setup.

This passes -target {CARGO_CFG_TARGET_ARCH}-apple-macos11 to swiftc so the helper always matches the crate's target arch (option 1 from #369; happy to switch to a universal helper if preferred).

Test plan (verified locally on Intel macOS 15.7.7)

  • just lint (Prettier + ESLint + cargo clippy --workspace --all-targets -- -D warnings) — clean
  • cargo fmt --check — clean
  • cargo build --release --bin pay — succeeds
  • target/release/build/pay-keystore-*/out/pay-helper is Mach-O 64-bit executable x86_64 (was arm64 before the fix)
  • pay-keystore unit tests: 47/47 pass
  • rm ~/.cache/pay/pay.sh && pay setup --backend keychain --force → "Account secured in Apple Keychain" (was Keystore error: pay.sh: Bad CPU type in executable (os error 86))

Note on just ci / just test

cargo test --workspace is flaky on main independently of this PR: pay-core::server::proxy::tests::forward_request_injects_hmac_auth and at least one neighbouring test both set_var/remove_var the same _TEST_ALIBABA_ACCESS_KEY_SECRET env var, and std::env is process-global, so they race under cargo test's default thread pool and intermittently fail with HMAC secret env var not set. The failing test passes deterministically in isolation:

cargo test -p pay-core --features server --lib \
  server::proxy::tests::forward_request_injects_hmac_auth

The flake is in a different crate from this change (pay-core vs pay-keystore) and is not introduced by this PR. Happy to file a follow-up if a maintainer confirms the diagnosis.

🤖 Generated with Claude Code

The Swift helper was compiled with no `-target`, so swiftc defaulted to the
build host's architecture. On Apple Silicon CI hosts this produced an arm64
helper even when cross-compiling the crate for `x86_64-apple-darwin`, leaving
the released x86_64 pay binary with an arm64 helper embedded.

At first run the helper is extracted to `~/.cache/pay/pay.sh` and
`posix_spawn`ed, which returns `EBADARCH (86)` ("Bad CPU type in executable")
on Intel Macs, making `pay setup` unusable.

Pass `-target {CARGO_CFG_TARGET_ARCH}-apple-macos11` so the helper always
matches the crate's target arch.

Verified locally on Intel macOS 15.7.7: helper is now `Mach-O 64-bit
executable x86_64` and `pay setup --backend keychain --force` completes
("Account secured in Apple Keychain").

Fixes solana-foundation#369

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented May 25, 2026

Copy link
Copy Markdown

@hibari-tech is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented May 25, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a cross-compilation bug where the Swift keychain helper was built for the build host's architecture instead of the crate's target architecture, causing an EBADARCH failure at runtime on Intel Macs when the helper was compiled on an Apple Silicon CI runner.

  • Passes -target {CARGO_CFG_TARGET_ARCH}-apple-macos11 to swiftc so the helper binary always matches the crate's target. The fix works correctly for x86_64 targets but uses the Rust/LLVM name aarch64 where Apple/Swift expect arm64 in the target triple, which would cause swiftc to reject the target string and fall back to an empty sentinel for any arm64 target.
  • The macos11 minimum deployment target is hardcoded in the new triple, which is an intentional design choice consistent with the rest of the fix.

Confidence Score: 3/5

Safe to merge for the x86_64 fix, but introduces a regression for arm64 targets due to the aarch64/arm64 naming mismatch in Apple target triples.

The fix correctly solves the cross-compilation bug for x86_64, but the constructed Swift target triple uses aarch64 (the Rust/LLVM name) instead of arm64 (the Apple/Swift name). For any arm64 target — including a native Apple Silicon build — swiftc will receive an invalid -target aarch64-apple-macos11 flag, likely error out, and fall back to the empty sentinel, leaving the helper to be compiled at runtime instead of being pre-embedded.

rust/crates/keystore/build.rs — the arch name mapping needs to translate aarch64 to arm64 before constructing the Swift target triple.

Important Files Changed

Filename Overview
rust/crates/keystore/build.rs Adds -target flag to swiftc to match the crate's target arch; correctly fixes x86_64 cross-compilation but uses aarch64 instead of arm64 in the Apple target triple, which breaks arm64 builds.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[build.rs starts on macOS host] --> B[Read CARGO_CFG_TARGET_ARCH]
    B --> C{arch value}
    C -->|x86_64| D["swift_target = x86_64-apple-macos11 ✅"]
    C -->|aarch64| E["swift_target = aarch64-apple-macos11 ⚠️\nApple expects arm64, not aarch64"]
    D --> F["swiftc -O -target x86_64-apple-macos11 -o pay-helper"]
    E --> G["swiftc -O -target aarch64-apple-macos11 -o pay-helper"]
    F --> H{swiftc success?}
    G --> I{swiftc success?}
    H -->|yes| J[codesign helper → embed in binary]
    H -->|no| K[write empty sentinel → compiled at runtime]
    I -->|yes| J
    I -->|no| K
Loading

Reviews (1): Last reviewed commit: "fix(keystore): build macOS helper for ta..." | Re-trigger Greptile

Comment on lines +40 to +42
let target_arch =
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "x86_64".to_string());
let swift_target = format!("{target_arch}-apple-macos11");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 The CARGO_CFG_TARGET_ARCH value for 64-bit ARM is aarch64, but Apple/LLVM target triples use arm64 — they are not the same string. Passing -target aarch64-apple-macos11 to swiftc is invalid; the correct triple is arm64-apple-macos11. On a native Apple Silicon build or when cross-compiling to arm64, swiftc will reject the target string and fall back to an empty sentinel, regressing the very scenario this PR intended to fix for arm64 users.

Suggested change
let target_arch =
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "x86_64".to_string());
let swift_target = format!("{target_arch}-apple-macos11");
let target_arch =
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "x86_64".to_string());
// Swift/Apple target triples use "arm64", not "aarch64" (Rust/LLVM name).
let swift_arch = match target_arch.as_str() {
"aarch64" => "arm64",
other => other,
};
let swift_target = format!("{swift_arch}-apple-macos11");

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.

pay setup fails on Intel macOS — embedded keychain helper is arm64-only in x86_64 release

1 participant