Repository navigation
Conversation
Add arm64e-apple-visionos, modelled on arm64e-apple-ios: the same data layout, the same apple-a12 baseline and the same pointer-authentication features. Bootstrap's stage 0 compiler does not know the target yet, so it goes on the sanity check's list of targets missing from stage 0.
arm64e-apple-ios and arm64e-apple-tvos name +v8.3a,+paca,+pacg in their specs; arm64e-apple-darwin relied on its apple-m1 CPU to imply them. Name them here too, so that pointer authentication does not depend on the CPU and survives -Ctarget-cpu=generic.
…ple targets Give PointerAuthConfig an arm64e Apple profile, built from the target alone: the platform fixes the ABI, so -Zpointer-authentication does not change it. The profile matches what clang's Darwin driver turns on for arm64e: authenticated calls, returns and indirect gotos, auth traps, and C function pointers signed with key IA and no diversity. It leaves off what Darwin does not use: jump-table hardening, signed initializer arrays and the ELF GOT. With the profile on, upstream's code gives every generated function the four ptrauth attributes, the C main and __rust_try included, and signs extern "C" function pointers. Its module flags describe the ELF PAuth ABI and assert the pauthtest target, so they are now emitted for ELF objects only. The previous patch's ptrauth.abi-version flag is not carried over: no part of LLVM reads it, and Mach-O objects record ABI version 0 whatever the module says. Add Target::is_apple_arm64e for the targets this applies to.
Upstream signs only functions with the C or system ABI, and signs them wherever codegen takes their address, so direct calls to them carry a ptrauth bundle until LLVM's InstCombine folds the pair away; in an unoptimised build they stay authenticated indirect calls. On arm64e Apple targets the C and Swift code in the same process authenticates every indirect call, and Rust should not be the soft spot. Widening upstream's two ABI filters alone would turn every direct Rust call into a signed indirect one. Add a scope to PointerAuthConfig instead. ExternC is upstream's behaviour and stays the pauthtest default. AllFunctions, the arm64e Apple profile, signs a function's address, whatever its ABI, where it becomes a value: fn item and closure casts, the main pointer passed to lang_start, constants, statics, extern_weak statics and v-table entries. Direct callees, lang-item calls, drop glue called directly, thread-local shims and the personality take the new direct-callee schema, which is none under AllFunctions, so direct calls stay direct. A call gets a ptrauth bundle exactly when its callee is not a function symbol and not inline assembly; this covers calls through dyn, closures and the try_fn pointer in __rust_try, whatever their ABI. Key IA and discriminator 0 are the C function-pointer schema on Darwin, so pointers cross between Rust, C and Swift in both directions. Direct calls, asm sym operands and the personality slot stay raw.
Clang's Darwin driver does not turn on -fptrauth-init-fini, so C code on arm64e leaves the entries of __mod_init_func and __mod_term_func unsigned. Upstream recognises only the ELF .init_array and .fini_array names, so on Mach-O such an entry was signed as an ordinary function pointer; the old patch did the same, which is why Sequoia's #[ctor] entry was signed in the 1.x app. Recognise the Mach-O sections too, by name or by section type, and give them the profile's init/fini schema, which on arm64e Apple targets is none. The constructor that the ctor crate registers is now laid out exactly as clang lays out a C constructor.
…tication ABI -Ctarget-feature=-paca, -pacg or LLVM's -pauth would build arm64e code that cannot authenticate, while every function still carries the ptrauth attributes and every indirect call its bundle. Reject them on arm64e Apple targets, one error per feature, and keep them out of the backend feature list. -Zbranch-protection=pac-ret asks for a second return-address scheme on top of ptrauth-returns. Reject it there too; bti and gcs stay allowed. -Zpointer-authentication cannot change what the platform fixes. It was ignored with a warning, as on any target other than pauthtest; on arm64e Apple targets it is now an error, so that a build does not seem to have changed pointer authentication when it has not.
The validation workflow last ran its tests on 10 August. Scheduled runs then failed at checkout every week, on a branch name the 206d116 rename had removed. The fix of 21 October was pushed with a skip-CI marker, the workflow had no pull_request trigger, and no run has started since. Port the workflow onto the refresh and: - run it on pull requests, alongside pushes to rust-stable-with-arm64e, the weekly schedule and manual dispatch; - give every run the shape bootstrap expects in CI, a merge commit whose first parent is the newest upstream bors commit, so that it downloads that commit's prebuilt LLVM instead of building LLVM from source. The last green run found its base on the fork's retired main branch; - run upstream's pauthtest tests next to the arm64e tests, to show that the Linux path is unchanged, plus targets-elf, both run-make tests and tidy; - add a runtime smoke case for the arm64e scope: a dyn call and a closure through fn pointers, catch_unwind, a thread, and a constructor in __mod_init_func. The host-binary smoke cases stay non-blocking, since a runner may not run arm64e binaries, or may run them with pointer authentication off.
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79c51fa689
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
v8.3a implies paca and pacg, but disabling a feature only disables the features that imply it, in rustc and in LLVM alike. So -v8.3a leaves pauth on, and the flag check rightly lets it through. Add a revision of the call test with -Ctarget-feature=-v8.3a: it only assembles if the backend still has pauth, so a change to either side's implication rules would show here.
GitHub dropped both jobs of the first validation run after 15 minutes without a runner: "Due to capacity constraints, jobs targeting macOS arm64 runners may experience longer queue times." The macos-15 and macos-26 images are the ones short of capacity. xcode-27, the macOS 27 image in public preview, takes jobs at once; the main repository's CI runs there. Run the one job on xcode-27. The host triple is now fixed in the job, since that image may not carry a rustc of its own to ask.
The first validation run built the patch and passed every pauthtest
test and five of the arm64e codegen tests. Four arm64e tests failed on
their own patterns, not on the code they check:
- LLVM 23 prints a !guid attachment after a function's attribute
group, so `#0 {` no longer follows the attributes.
- The catch_unwind intrinsic is now generic over its data pointer and
returns bool, and rustc panics on the old declaration.
- `CHECK-NOT: ptrauth` matched the test crate's own name in rustc's IR
comments. Look for the bundle and a signed callee instead.
- LLVM merged two identical empty functions, so the terminator static
pointed at the initializer function. Give them different signatures.
In each case the IR showed the intended result: unsigned Mach-O
initializer entries, a signed ordinary static, unsigned direct callees.
x test stops at the first failing suite, so one failing codegen test hid the assembly, UI and run-make results. Pass --no-fail-fast.
Under CI, bootstrap refuses an x test without --stage ("x.py was run
under CI with an implicit `--stage 1`"). The test step already passes
--stage 1; the tidy step now does too.
A tidy failure skipped the stage1 std build and every runtime smoke step, the evidence that matters most, and each run takes about twenty minutes. Tidy now always runs; the std build runs whenever the codegen crates check, even if a test failed; the smoke steps run whenever the std build succeeded. Every failure still fails the job.
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.
This rebuilds the fork's arm64e pointer-authentication patch on Rust 1.100.0, on top of upstream's own pointer-authentication code. It is not a rebase: the old patch ran its own mechanism beside upstream's, and a plain rebase conflicts in ten files. Here the patch is a Darwin profile in upstream's
PointerAuthConfig, plus the fork's wider signing scope, the flag checks and the visionOS target.base/1.100is upstream'sstablebranch at0ef1bc3860, which reads 1.100.0. The 1.100.0 tag does not exist yet; the base moves to the tag when it does.rust-stable-with-arm64e, the stage1 toolchain workflow, and any toolchain build. Those are the next stage.Commit by commit
1.
feat(arm64e): add Apple visionOS target. The old commit, carried over. It is modelled onarm64e-apple-ios. One change: the docs name the maintainer without an@.2.
feat(arm64e): list the pauth features in the arm64e-apple-darwin spec. From the old "model default Apple ptrauth features" commit, only the line that mattered:arm64e-apple-darwinnow names+v8.3a,+paca,+pacglike the iOS and tvOS targets do. Pointer authentication no longer depends on the CPU. Thearm64e_featureshelper is gone; each spec just names its features.3.
feat(arm64e): turn on upstream's pointer authentication for arm64e Apple targets. Replaces the old "emit clang-compatible ptrauth metadata" commit.PointerAuthConfig::from_rawreturns a new arm64e Apple profile for these targets, built from the target alone.mainand__rust_try. The old patch added these in three places of its own.ptrauth.abi-versionmodule flag. Nothing in LLVM reads it, and Mach-O objects always record ABI version 0. Also dropped:LLVMRustAddModuleFlagMetadata, the attribute helpers, and thepaca/pacgsymbols.Target::is_apple_arm64e.At this commit the arm64e targets behave like upstream's
pauthtest: onlyextern "C"pointers are signed. That is a working state, and the next commit widens it.4.
feat(arm64e): sign every function pointer that becomes a value. Replaces the old "authenticate indirect function calls" and "authenticate function pointers used as data" commits.PointerAuthConfiggets ascope.ExternCis upstream's behaviour and stays thepauthtestdefault.AllFunctionsis the arm64e Apple profile.AllFunctions, a function's address is signed where it becomes a value, whatever its ABI: fn item and closure casts, themainpointer given tolang_start, constant operands, statics,extern_weakstatics and v-table entries.pointer_authentication_direct_callees()schema. It is upstream's schema underExternCand none underAllFunctions. So direct calls stay direct, even at-O0. Upstream signs direct callees too and relies on InstCombine to fold them back; widening its ABI filter alone would have made every direct Rust call an authenticated indirect call in debug builds.ptrauthbundle exactly when its callee is not a function symbol and not inline assembly. This coversdyncalls, closures and thetry_fncall inside__rust_try, whatever their ABI.scalar_to_backendpasses no schema, so a constantfnpointer used as a value would stay unsigned. The LLVM backend now passes the function schema there underAllFunctions.SimpleCxfield,get_fn_addr_for_data,arm64e_fn_ptr_for_data, theLLVMConstantPtrAuthbinding (upstream'sLLVMRustConstPtrAuthis used), and the bundle inSBuilder::call(its only caller, the allocator shim, makes direct calls).LLVMRustIsNonGVFunctionPointerTyin C++, which CFI and KCFI also use. The new check is a small Rust helper,llvm::is_indirect_callee, so other targets are untouched.5.
feat(arm64e): leave Mach-O initializer pointers unsigned, as clang does. New. This settles gap I-2 of the assessment. Clang on Darwin does not sign the entries of__mod_init_funcand__mod_term_func. Upstream recognises only ELF.init_array/.fini_array, so the old patch signed them; that is why Sequoia's#[ctor]entry was signed in the 1.x app. The Mach-O sections are now recognised, by name or by section type, and take the profile's init/fini schema, which is none. A runtime smoke case checks that such a constructor runs.6.
fix(arm64e): reject flags that would change the arm64e pointer-authentication ABI. Replaces the two old flag-check commits, rebased onto 1.100's restructuredtarget_features.rsand the renameddiagnostics.rsfiles.-Ctarget-feature=-paca,-pacgor-pauthis an error, one per feature, and is kept out of the backend feature list.-Zbranch-protectionwithpac-retis an error;btiandgcsstay allowed.-Zpointer-authenticationis an error on these targets. Upstream ignores it with a warning on every target butpauthtest; here it would silently do nothing.7.
ci(arm64e): run the fork's validation on pull requests again. See "The validation workflow" below.8.
test(arm64e): check that disabling v8.3a keeps pointer authentication. Added after review.v8.3aimpliespacaandpacg, but disabling a feature only disables the features that imply it, in rustc and in LLVM. So-Ctarget-feature=-v8.3akeepspauth, and the flag check rightly allows it. A new revision of the assembly call test uses that flag; it only assembles if the backend still haspauth.9.
ci(arm64e): run the validation on the xcode-27 image. The first run's jobs waited 15 minutes onmacos-15andmacos-26and were dropped without a runner, for lack of GitHub capacity. See "The validation workflow" below.10.
test(arm64e): fix four codegen tests for LLVM 23 and Rust 1.100. The first full run passed everypauthtestcodegen test and five arm64e codegen tests. Four arm64e tests failed on their own patterns: LLVM 23 prints a!guidattachment after a function's attributes; thecatch_unwindintrinsic changed its signature; a negative check matched the test crate's own name; and LLVM merged two identical test functions. In each case the IR showed the intended result.11.
ci(arm64e): run every test suite even when one fails. Adds--no-fail-fast, so one failing suite no longer hides the others.The old "close ptrauth IR emission gaps" commit needs nothing new: upstream now attributes the C
mainand__rust_tryitself, and inline assembly is excluded by the new indirect-callee check. Its commit message also described a "late stripping" of bundles that was never in the code.Where this differs from the assessment's plan
-Zpointer-authenticationis an error on arm64e Apple targets, rather than a subset being accepted.Tests
All arm64e tests have revisions for darwin, iOS, tvOS and visionOS unless noted.
Ported from the old patch, updated where the code changed:
codegen-llvm/arm64e-apple-ptrauth.rs: the four attributes,+pauth, BTI and GCS flags. Now also checks that no jump-table hardening, ELF PAuth flag orptrauth.abi-versionflag appears.codegen-llvm/arm64e-apple-ptrauth-calls.rsandassembly-llvm/arm64e-apple-ptrauth-calls.rs: indirect calls get the bundle andblraaz/braaz; direct calls do not.codegen-llvm/arm64e-apple-ptrauth-fnptr-data.rs: a function pointer passed as an argument is signed.codegen-llvm/arm64e-apple-ptrauth-invoke.rs:__rust_tryhas the attributes, and itsinvokethroughtry_fnhas the bundle.codegen-llvm/arm64e-apple-ptrauth-entry.rs: the Cmainhas the attributes. Now also checks that themainpointer given tolang_startis signed.codegen-llvm/arm64e-apple-ptrauth-inline-asm.rsand-callbr.rs: inline assembly andasm gotoget no bundle.run-make/arm64e-ptrauth-serialized-output: no direct call keeps a bundle in IR, bitcode or embedded bitcode. The old workflow never ran it.-Ctarget-featureand-Zbranch-protection, rewritten for the per-feature messages.New:
codegen-llvm/arm64e-apple-ptrauth-scope.rs, at-O0: Rust-ABI fn pointers, closures, constants, statics,extern_weakstatics and v-table entries are signed; calls through a Rust fn pointer and throughdyncarry the bundle; direct Rust and C calls carry none.codegen-llvm/arm64e-apple-ptrauth-init-term.rs:__mod_init_funcand__mod_term_funcentries stay unsigned, found by name or by type; the same pointer in an ordinary static is signed.run-make/arm64e-ptrauth-allocator-shim: every function of the allocator shim, which is its own LLVM module, has the four attributes.ui/pointer_authentication/arm64e_apple_fixed_pointer_authentication.rs:-Zpointer-authenticationis rejected.Upstream's
pauthtesttests run unchanged, to show the Linux path is untouched:codegen-llvm/pauth/,assembly-llvm/pauth-basic.rs,ui/pointer_authentication/,ui/statics/crt-static-pauthtest.rs,ui/target_modifiers/incompatible_pauth.rs, plusassembly-llvm/targets/targets-elf.rsandtargets-macho.rs. (pauth-attr-special-funcs.rsisonly-pauthtest, so it is skipped on a macOS host.)The validation workflow
It last ran its tests on 10 August. Weekly runs then failed at checkout on a branch name that a rename had removed. The fix of 21 October was pushed with a skip-CI marker, and the workflow had no
pull_requesttrigger, so nothing has run since. Now:rust-stable-with-arm64e, weekly, and by hand.x checkon the codegen crates, the tests above, tidy, and a stage1 std build forarm64e-apple-darwin.xcode-27: GitHub's macOS 27 image, in public preview. The standardmacos-15andmacos-26images are short of capacity, and GitHub dropped both jobs of the first run without a runner. So the matrix covers macOS 27 only, no longer macOS 15 and 26. Thexcode-27label may change when the image becomes generally available.dyncall and a closure through fn pointers,catch_unwind, a thread, a__mod_init_funcconstructor). They also report whether the binary's fn pointers carry a signature, which tells whether the runner runs arm64e with pointer authentication on.Upstream's own
CIworkflow also fires on this PR. It stops at once at its channel check, because it expects a stable-channel PR to target a branch namedstable. That failure says nothing about the patch.Gaps that remain
Unchanged from the assessment unless noted:
__ptrauthfields, blocks, Objective-C, C++ virtuals) crash if called from Rust.extern_weakstatics are now signed.transmutefrom an integer) fails when called.fncast to*const ()). I have not checked what clang does for the same cast in a C static initializer.