RACE: Semantic-linker approach to stable cross-crate ABI without runtime overhead

This is a pre-RFC exploration. Presenting it to gather feedback before considering a formal RFC.


Problem

Rust currently has no stable ABI. This is a deliberate and reasonable decision β€” it allows the compiler to optimize freely. However, it creates a hard barrier in several real-world scenarios:

  • Bevy and other engines cannot distribute pre-compiled plugins as .so files
  • Rust for Linux is constrained to isolated drivers with no deeper integration
  • Any ecosystem requiring independently compiled binary modules hits this wall

The usual workaround is #[repr(C)], which is a lossy escape hatch β€” you lose Rust's type expressiveness and optimization potential.


Proposed concept: RACE (Rust Advanced Contract Enforcement)

Instead of freezing a fixed ABI at the language level, RACE proposes moving ABI resolution to link time via a semantic-aware linker.

1. .rcrate format Compiled crates emit an intermediate .rcrate file containing:

  • Polymorphic machine code
  • A Semantic Type & Effects Graph (STEG) β€” rich type metadata beyond what DWARF provides

2. Symbolic Layout Offsets Field offsets are not baked into instructions at compile time. Instead, the final linker resolves and patches them as computed constants β€” zero runtime cost, no temporary allocations.

3. vtable layout synthesis dyn Trait vtables are a core ABI concern β€” reordering methods breaks call sites. RACE proposes vtable remapping at link time: the linker matches methods by identity and type metadata, synthesizing an adapted vtable layout to satisfy the caller's expectations.

This moves ownership/lifetime/type contract validation entirely to compile+link time, producing a fully monolithic binary with no runtime abstraction layers β€” suitable for no_std and hard real-time environments.


What this would unlock

  • Distributable pre-compiled Bevy plugins
  • Deeper Rust integration in the Linux kernel
  • Stable plugin ecosystems without WASM overhead

Open questions

  • Is STEG expressible within or alongside existing MIR metadata?
  • Are there soundness barriers to vtable remapping that make this approach unsafe?
  • Has anything similar been explored β€” e.g. in Swift's ABI stability work?

since you can use field offsets in constant expressions which allow you to arbitrarily change types, your proposal essentially means delaying nearly all compilation work to link time. e.g.: Compiler Explorer

2 Likes

Good point. The concern is valid for cases where field offsets feed into constant expressions that influence control flow or type decisions. RACE would need to either restrict Symbolic Layout Offsets to non-const contexts, or define a subset of const expressions where layout independence is guaranteed. This is a real constraint worth scoping carefully - it doesn’t invalidate the approach but does narrow where SLO can safely apply

This sounds adjacent to the BPF work: [Pre-RFC] BTF relocations

Perhaps it could be done only for types explicitly marked as such?

2 Likes

The BTF parallel is apt - CO-RE (Compile Once, Run Everywhere) in BPF solves a similar relocation problem with type metadata, and it works well in practice. Explicit opt-in marking is a compelling constraint - it would directly address the const expression concern raised above, since only explicitly marked types would participate in layout deferral. This could be the right shape for a minimal first version

Sorry, but this is very high-level and sounds like "just implement the hard parts", without even touching on specifics.

The problem is well known. The benefits of solving it are well known. Solutions to this have already been explored in way more depth than this post provides.

Handwaving "Polymorphic machine code" is not advancing the solution. It's like "let's built it out of Unobtanium".

The whole difficulty is in how do you actually make it in a way that has useful performance, robust interface, and how do you either plug rustc or other compiler at install or load time, or integrate with existing linkers/loaders in a way that avoids need to have a compiler at load time. We have rlibs with MIR (requiring rustc at link time). There are solutions like Witness Tables in Swift (but with very high overhead unsuitable for many of Rust's design patterns). AFAIK there has been an experiment to implement automatic polymorphisation in rustc, and it didn't go anywhere, so even if the solution was "let's copy what Swift has proven to work", an implementation of something like it has already proven too difficult. Who's going to actually implement it?

Which linker? How do you make that work across platforms that Rust supports? How is that different from linking rlib?

4 Likes

You're right that this is high-level. The post was intentionally framed as a concept exploration rather than an implementation proposal - the goal was to get exactly this kind of feedback on where the hard parts actually are. The linker question and the polymorphisation history are gaps I wasn't aware of. Would you say the BTF/CO-RE direction is more tractable than the Swift Witness Tables approach for Rust's use cases?