Skip to content

BTF relocations#3966

Open
vadorovsky wants to merge 1 commit into
rust-lang:masterfrom
vadorovsky:btf-relocations
Open

BTF relocations#3966
vadorovsky wants to merge 1 commit into
rust-lang:masterfrom
vadorovsky:btf-relocations

Conversation

@vadorovsky

@vadorovsky vadorovsky commented May 31, 2026

Copy link
Copy Markdown

The RFC proposes the btf_relocatable_types feature gate, that provides a #[repr(Btf)] representation and low-level field-info intrinsics that emit BTF (BPF TypeFormat) CO-RE (Compile Once, Run Everywhere) relocations.

It also documents the relationship to offset_of!, ordinary field projection and LLVM BPF lowering.

This feature was originally proposed as pre-RFC.

Rendered

vadorovsky added a commit to vadorovsky/rust that referenced this pull request May 31, 2026
BTF, the BPF Type Format, encodes type information for both the running
Linux kernel and compiled eBPF programs. An eBPF object can carry
relocation records that describe field and aggregate accesses in terms
of BTF types instead of fixed offsets; at load time, the loader compares
the program's BTF with the kernel's BTF and rewrites those accesses to
the correct offsets for the target kernel. This mechanism is often
referred to as "CO-RE relocations" or "BTF relocations".

`offset_of` always folds to a plain layout constant and does not
preserve enough information for BTF CO-RE relocation emission. As a
result, it is not suitable for relocatable field queries on BPF targets.

Add three explicit intrinsics for BTF field metadata queries:

* `btf_field_byte_offset`
* `btf_field_byte_size`
* `btf_field_exists`

The user-facing BTF relocatable type support remains behind the
`btf_relocations` feature gate, so use of this experimental CO-RE
surface requires an explicit nightly opt-in.

These intrinsics provide a frontend surface for CO-RE relocations.
Unlike `offset_of`, they remain visible to backend codegen and can lower
to relocatable field-info queries instead of immediate layout constants.

For LLVM, lower these intrinsics through `@llvm.bpf.preserve.field.info`
with the corresponding query kind. The necessary
`@llvm.preserve.{struct,array,union}.access.index` chain is constructed
internally during lowering, but it is not exposed as part of the
user-facing API.

On targets or backends without BTF relocation support, fall back to the
ordinary layout-computed result: the field offset for
`btf_field_byte_offset`, the field size for `btf_field_byte_size`, and
`true` for `btf_field_exists`.

The language-level design for this feature is proposed in
rust-lang/rfcs#3966.
vadorovsky added a commit to vadorovsky/rust that referenced this pull request May 31, 2026
BTF, the BPF Type Format, encodes type information for both the running
Linux kernel and compiled eBPF programs. An eBPF object can carry
relocation records that describe field and aggregate accesses in terms
of BTF types instead of fixed offsets; at load time, the loader compares
the program's BTF with the kernel's BTF and rewrites those accesses to
the correct offsets for the target kernel. This mechanism is often
referred to as "CO-RE relocations" or "BTF relocations".

`offset_of` always folds to a plain layout constant and does not
preserve enough information for BTF CO-RE relocation emission. As a
result, it is not suitable for relocatable field queries on BPF targets.

Add three intrinsics for BTF field metadata queries:

* `btf_field_byte_offset`
* `btf_field_byte_size`
* `btf_field_exists`

Their availability is hidden behind the `btf_relocations` feature gate.

Unlike `offset_of`, they remain visible to backend codegen and can lower
to relocatable field-info queries instead of immediate layout constants.

For LLVM, lower these intrinsics through `@llvm.bpf.preserve.field.info`
with the corresponding query kind. The necessary
`@llvm.preserve.{struct,array,union}.access.index` chain is constructed
internally during lowering, but it is not exposed as part of the
user-facing API.

On targets or backends without BTF relocation support, fall back to:

* The field offset for `btf_field_byte_offset`.
* The field size for `btf_field_byte_size`.
* `true` for `btf_field_exists`.

The language-level design for this feature is proposed in
rust-lang/rfcs#3966.
@Noratrieb Noratrieb added T-lang Relevant to the language team, which will review and decide on the RFC. T-opsem Relevant to the operational semantics team, which will review and decide on the RFC. labels May 31, 2026
@vadorovsky vadorovsky force-pushed the btf-relocations branch 3 times, most recently from 0a822d7 to 2ec6577 Compare June 1, 2026 11:38
The RFC proposes the `btf_relocations` feature gate, which provides a
`#[repr(Btf)]` representation and `core::btf` field-info macros that
emit BTF (BPF Type Format)[0] CO-RE (Compile Once, Run Everywhere)[1]
relocations.

It also documents the relationship to `offset_of!`, ordinary field
projection, and LLVM BPF lowering.

This feature was originally proposed as a pre-RFC[2].

[0] https://docs.kernel.org/bpf/btf.html
[1] https://nakryiko.com/posts/bpf-portability-and-co-re/
[2] https://internals.rust-lang.org/t/pre-rfc-btf-relocations/24161/25

@workingjubilee workingjubilee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mostly minutiae that would benefit from being clarified.

View changes since this review

Comment on lines +68 to +83
`#[repr(Btf)]` is intentionally not just a layout hint. It marks a type as one
whose fields should not be accessed through ordinary Rust field projection for
accesses that are meant to be relocatable. For such types, direct field access
is rejected:

```rust
#![feature(btf_relocations)]

#[repr(Btf)]
pub struct task_struct {
pub pid: i32,
}

fn pid(task: &task_struct) -> i32 {
task.pid
// error: cannot access fields of a `#[repr(Btf)]` type directly

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would rather we do not use another repr that has the same "feature" as repr(packed), for which we seem to still be encountering new issues: rust-lang/rust#157011

I would prefer that we find some new way to express it syntactically that makes it harder for us to make more such mistakes in the future. Essentially, because these are sort of an Alien Being relative to normal structs, maybe they should not use the syntax of normal structs at all?

But I also wish to acknowledge that I do not have any objection to the fundamental behavior proposed, so this should not block any initial experimentation. It is more of a yet-to-be-resolved-concern type of deal. Unanswered question? "Can anyone think of something better?"


These restrictions avoid silently producing non-relocatable code for operations
that appear to query a relocatable type. Code that genuinely wants a normal
non-relocatable Rust type should not use `#[repr(Btf)]`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It isn't clear to me that this achieves its intended goal? Or I don't understand the goal?

The current proposal includes a "compile-time fallback" to normal layout behavior, several lines back.

On targets or backends without BTF
relocation support, they fall back to the current compilation unit's ordinary
layout information.

This is a fallback path that is sometimes permitted based on some variable that can differ based on the circumstances of the compilation. So if we do not trust the compiler to correctly handle the matter, it can always choose a "wrong" path and silently produce directly non-relocatable accesses despite using all these special macros. According to my understanding, we have gone to some length to supposedly prevent silent failure via introducing syntactic noise, and then allowed for silent failure?

Thus we must trust the compiler anyways, right? So what are we trying to guard against?

Comment on lines +259 to +263
A `#[repr(Btf)]` type uses C-compatible field layout. In compiler terms,
`repr(Btf)` implies the layout constraints of `repr(C)` and also marks the type
as BTF-relocatable. This gives the backend stable field ordering and offsets for
the compile-time fallback while preserving a distinct marker for type checking
and codegen.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this intended to use actually compatible with C layout or is it intended to simply use a linear layout? See RFC #3845 for a description of that problem and a proposed solution, which is orthogonal to this RFC except insofar as it implies the "what do you really mean?" question.

AFAIK the platforms that this relocatable-fields proposal is relevant for are ones where the notional repr(C_actual) and repr(ordered_fields) are identical... ish... but that sort of statement is prone to breaking down in the face of new discoveries, so we might as well get ahead of that.

Comment on lines +133 to +142
#[inline]
pub fn pid(&self) -> Option<&i32> {
self.has_pid().then(|| {
let offset = core::btf::field_byte_offset!(task_struct, pid);
let ptr = self as *const task_struct as *const u8;

// SAFETY: the BTF relocation says that `se.vruntime` exists in the
// target layout, and the returned offset is relative to `task_struct`.
Some(unsafe { &*(ptr.add(offset) as *const i32) })
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So a typo in a field name can result in simply turning this into a None and never taking the "active" code path, right? Since the relocation is functionally a set of runtime checks, and we would have to assume we take the field name for granted, and the entire reason to use these relocations is the struct definition per se is actually external to the program...?

Comment on lines +55 to +56
A type that should participate in BTF relocation is written with
`#[repr(Btf)]`:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something to consider with this as repr: Is this meant to be exclusive with other repr hints?

  • align?
  • packed?

A `#[repr(Btf)]` type uses C-compatible field layout. In compiler terms,
`repr(Btf)` implies the layout constraints of `repr(C)` and also marks the type
as BTF-relocatable. This gives the backend stable field ordering and offsets for
the compile-time fallback while preserving a distinct marker for type checking

@workingjubilee workingjubilee Jun 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What do we mean here, or what do we achieve, by having a "compile-time fallback"?

There's an implicit alternative here: No fallback. What are we giving up if we choose that instead?

Comment on lines +378 to +381
relocations. This is attractive ergonomically, but it requires a more intrusive
change to MIR and the design of a proper abstract machine with operational
semantics. It also has some similarities to the [`Sized` hierarchy
RFC][sized-hierarchy], which has not yet been accepted.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This statement seems to be wearing its shirt backwards, so to speak. We do have an abstract machine, but rather we would need to fit these magic accesses into that semantics, which seems like a lot.

Suggested change
relocations. This is attractive ergonomically, but it requires a more intrusive
change to MIR and the design of a proper abstract machine with operational
semantics. It also has some similarities to the [`Sized` hierarchy
RFC][sized-hierarchy], which has not yet been accepted.
relocations. This is attractive ergonomically, but it requires a
more intrusive change to MIR and the design of a proper
operational semantics for these relocatable accesses.
It also has some similarities to the [`Sized` hierarchy
RFC][sized-hierarchy], which has not yet been accepted.

Comment on lines +337 to +345
If the target, backend, or codegen mode cannot emit BTF field relocations, the
field-info queries fall back to ordinary layout-computed values:

* `field_byte_offset!` returns the complete field-path offset from the
current compilation layout.
* `field_byte_size!` returns the field size from the current compilation
layout.
* `field_exists!` returns `true` for a field path present in the current
compilation layout.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This would just become a name resolution question, right? It doesn't have anything to actually do with layout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-lang Relevant to the language team, which will review and decide on the RFC. T-opsem Relevant to the operational semantics team, which will review and decide on the RFC.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants