Including Rust in an Xcode project with Pointer Authentication (arm64e)

3 January 2026

When you add the "Enhanced Security" Capability to an iOS app in Xcode, one of the options is "Authenticate Pointers" (ENABLE_POINTER_AUTHENTICATION). This is checked by default. Therefore somebody who is adding this Capability in order to use Apple's exciting new Memory Integrity Enforcement feature will also have pointer authentication enabled, unless they go out of their way to disable it.

I won't go into detail here about what this authentication actually does; see Apple's docs. What's of interest to us today is that this changes the ABI. It's treated as an entirely separate architecture called arm64e. Now, arm64e is not something that developers of Apple software are usually too concerned about. When we ship software for Apple Silicon devices it tends to be for arm64, also known as "aarch64" in the naming scheme that Rust uses. Apple, however, compiles their first-party binaries in arm64e so they are getting the benefit of pointer authentication. Compare these examples:

% file /usr/bin/gzip
/usr/bin/gzip: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/usr/bin/gzip (for architecture x86_64):    Mach-O 64-bit executable x86_64
/usr/bin/gzip (for architecture arm64e):    Mach-O 64-bit executable arm64e

% file ~/.cargo/bin/cargo
/Users/tk/.cargo/bin/cargo: Mach-O 64-bit executable arm64

It's pretty neat if we can use arm64e in our own software and gain those additional protections. The trouble is that now all our libraries and static library artefacts involved in building that app need to be compiled for arm64e. If you are a Normal Apple Developer who builds everything from source files directly in Xcode, including your C/C++/ObjC targets, then this isn't a big deal. You merely have to change the target architectures to include "arm64e". Xcode knows what that is and takes care of the rest.

Where things get more challenging is if you are relying on static libraries acquired from third parties or if part of your app is written in a language that doesn't have complete arm64e support, like Rust. If you're the author of one of these libraries you're probably going to have to fix this or otherwise explain to your downstream users that they can't use the "Authenticate Pointers" feature.

The tracking issue for Rust is rust-lang/rust#73628. Among other things, a large part of the problem is that Apple hasn't finished upstreaming all of the changes to the arm64e architecture. It appears that there is a breaking change in the arm64e support where previously the "capabilities" mask in the Mach-O headers was 0x00 and now it's 0x80. If you try to compile an "old" arm64e static lib then this results in linker errors like this:

ld: ignoring file '…': found architecture 'arm64e.old', required architecture 'arm64e'

So what can you do about it? Well, for most people the sensible thing to do is wait. Apple will presumably get around to upstreaming all of the arm64e changes to LLVM and then the Rust folks will update their compiler and this will be a target you can add and use via rustup. Everything will be simple.

If you are impatient, however, there is another way:

  1. Prepare a custom build of the Rust toolchain for target arm64e-apple-ios, using the LLVM fork from the Swift project (which is ultimately what Xcode uses/expects)
  2. Patch issues with Rust using a different LLVM backend than usual
  3. Patch the arm64e support to make sure we set the "new" arm64e capabilities instead of the old value which comes out as "arm64e.old"
  4. Build static libs with that toolchain, which can now be used freely in an Xcode project with "Authenticate Pointers" enabled.

I have recently done this and you can see the resulting build scripts and patches at this repo: thombles/rust-arm64e. (Note: LLMs assisted in the debugging and creation of these patches.) There is also a binary release attached to that repo which will install it as a local toolchain on your Mac. All of this is super janky and I don't suggest using it for anything other than experimentation. In particular, it seems to crash if you do a standard debug build so I suggest using RUSTFLAGS="-C debuginfo=1". Despite all these shortcomings, I tried this for a simple Rust staticlib and I was able to successfully build and run it as part of an arm64e iOS app on a real device. Hooray.

So why did I do all this? Mostly by mistake, to be honest. I'm rather excited about MIE, and since the Enhanced Security capability added pointer authentication I was under the misapprehension that MIE required it as a baseline to do its work. This is not actually true; they're two separate features and MIE works just fine without it. By the time I figured this out I had already hacked together this dodgy toolchain. So here we are.


Serious Computer Business Blog by Thomas Karpiniec
Posts RSS, Atom