How to Fix: Cranelift: cranelift-object: unimplemented relocation Aarch64AdrPrelPgHi21 on aarch64-apple-darwin
Cranelift on Apple Silicon breaks on non-preemptible data because Mach-O relocation support is incomplete
When cranelift-object targets aarch64-apple-darwin, declaring data with Linkage values other than Preemptible can trigger generation of the relocation Aarch64AdrPrelPgHi21. That relocation is valid for AArch64 code generation patterns, but in this pipeline it reaches an object emission path that does not implement the required Mach-O handling, causing compilation or object creation to fail.
Understanding the Root Cause
This bug appears at the intersection of Cranelift IR lowering, AArch64 addressing modes, and Mach-O relocation encoding on Apple platforms.
On aarch64-apple-darwin, references to data symbols are often materialized using an ADRP + ADD/LDR sequence. The ADRP instruction computes the page-relative base address of a symbol, and the corresponding relocation frequently maps to Aarch64AdrPrelPgHi21. This is a normal code generation strategy for local or non-interposable data.
The problem is that cranelift-object does not fully implement emission for this relocation in the affected path. So when you call module.declare_anonymous_data or declare_data with non-Preemptible linkage, Cranelift may treat the symbol as directly addressable and emit relocation kinds that require local-page relocation support. During object generation, the backend reaches an unimplemented relocation case and aborts.
Why does Linkage::Preemptible behave differently? Because preemptible symbols must be treated as externally interposable. That changes how references are lowered and usually avoids the relocation form that hits the unimplemented code path. In practice, it routes symbol access through relocation patterns that the Mach-O object writer already supports.
In short, the bug is not that your data declaration is invalid. The bug is that non-preemptible data on Apple Silicon can force a relocation kind that cranelift-object does not yet encode for Mach-O.
Step-by-Step Solution
There are two practical fixes depending on whether you need a local symbol or just need the build to succeed immediately.
Option 1: Use Linkage::Preemptible as a workaround
If the symbol does not need strict local/non-preemptible semantics, the fastest workaround is to declare the data as Preemptible. This avoids the unsupported relocation path on affected versions.
use cranelift_module::{DataDescription, Linkage, Module};
let data_id = module.declare_data("my_data", Linkage::Preemptible, false, false)?;
let mut data = DataDescription::new();
data.define(vec![1, 2, 3, 4].into_boxed_slice());
module.define_data(data_id, &data)?;
For anonymous data, apply the same idea where possible by using declarations and access patterns that do not require a local-page relocation form.
Option 2: Patch the relocation handling in cranelift-object
If you are fixing the issue in the compiler or maintaining a fork, the real solution is to add support for the missing relocation mapping for Mach-O on AArch64 Darwin.
The implementation typically requires these steps:
- Find where FinalizedMachReloc or the equivalent relocation translation logic converts Cranelift relocation kinds into object-format-specific relocations.
- Add handling for Aarch64AdrPrelPgHi21.
- Map it to the correct Mach-O ARM64 page relocation expected by Apple tooling.
- Ensure the paired low relocation such as page offset or addend-based relocation is also emitted correctly when needed.
- Verify the symbol kind and addend rules for Mach-O are respected.
A conceptual patch looks like this:
match reloc_kind {
Reloc::Aarch64AdrPrelPgHi21 => {
// Translate to the Mach-O ARM64 page-based relocation.
// Exact enum names depend on the object crate / cranelift-object version.
macho_reloc = Some(translate_adrp_page_reloc(symbol, addend)?);
}
Reloc::Aarch64AddAbsLo12Nc => {
macho_reloc = Some(translate_pageoff12_reloc(symbol, addend)?);
}
_ => { /* existing handling */ }
}
Because enum names and relocation APIs vary by version, inspect the current object crate and the cranelift-object relocation sink used in your checkout.
Option 3: Force an access pattern that avoids the unsupported relocation
In some code generators, you can avoid direct local data addressing by changing how the symbol is imported or referenced. For example:
- Declare the data as externally visible or preemptible.
- Use indirection through a function call or table when acceptable.
- Avoid embedding direct references to local data in JIT/object code for this target until relocation support is fixed.
This is less ideal than a backend patch, but it is useful in production if you need a short-term release-safe mitigation.
Recommended validation steps
# Rebuild your project for Apple Silicon
cargo build --target aarch64-apple-darwin
# If you maintain Cranelift locally, run the relevant test suites
cargo test -p cranelift-object
cargo test -p cranelift-filetests
# Inspect relocations in the generated object file
otool -rv path/to/output.o
When the fix is correct, the object should be emitted successfully and the relocation dump should show valid ARM64 Mach-O relocations instead of stopping at an unimplemented case.
Common Edge Cases
1. The workaround compiles, but symbol semantics change
Using Linkage::Preemptible is a workaround, not always a semantic equivalent. If your symbol is meant to be private, local, or non-interposable, switching linkage may affect linking behavior, visibility, or optimization opportunities.
2. You fix Aarch64AdrPrelPgHi21 but another paired relocation fails
On AArch64, page-based relocations usually come in pairs. Supporting only the ADRP half may expose a second failure for the low 12-bit relocation such as an ADD or LDR companion relocation. Validate both halves of the address materialization sequence.
3. The issue only happens for object emission, not JIT
That is expected. cranelift-jit and cranelift-object use different relocation application paths. A bug in object-format relocation encoding may not appear in JIT execution.
4. The failure depends on optimization level or code shape
Small IR changes can influence whether Cranelift chooses a direct symbol reference, a constant pool access, or another materialization strategy. If the crash seems inconsistent, compare the generated machine code and relocation records rather than only the source code.
5. Cross-compiling from non-macOS hosts can hide or complicate the problem
If you are generating Mach-O objects on Linux or another host, ensure your test setup still validates the output with Apple-compatible tooling or a relocation-aware parser. Some failures only become obvious when inspected with platform-specific tools.
FAQ
Why does this happen only on aarch64-apple-darwin?
Because the issue depends on the combination of AArch64 relocation kinds and Mach-O relocation encoding. Other architectures or object formats like ELF may already support the corresponding relocation path.
Is Linkage::Preemptible a real fix?
No. It is a practical workaround that changes code generation enough to avoid the unimplemented relocation. The real fix is to implement the missing relocation handling in cranelift-object for Mach-O on Apple Silicon.
How do I know whether my patch is correct?
Build a minimal reproducer that declares data with non-Preemptible linkage, generate a Mach-O object, and inspect the relocations using Apple Mach-O tooling or LLVM object inspection tools. The object should emit cleanly, link successfully, and show correct page-based ARM64 relocations.
The key takeaway is simple: the crash is caused by an unsupported Mach-O relocation translation for local-style AArch64 data references. If you need an immediate unblock, use Linkage::Preemptible. If you need a durable fix, implement Aarch64AdrPrelPgHi21 relocation support in cranelift-object and validate the full relocation pair.