Skip to content

BTF relocations#3966

Open
vadorovsky wants to merge 7 commits into
rust-lang:masterfrom
vadorovsky:btf-relocations
Open

BTF relocations#3966
vadorovsky wants to merge 7 commits into
rust-lang:masterfrom
vadorovsky:btf-relocations

Conversation

@vadorovsky

@vadorovsky vadorovsky commented May 31, 2026

Copy link
Copy Markdown

View all comments

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

@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 thread text/0000-btf-relocations.md Outdated
Comment thread text/0000-btf-relocations.md Outdated
Comment thread text/0000-btf-relocations.md Outdated
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...?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, that's right. Although instead of saying "is actually external", I would say that the struct definition is a reference point for the runtime checks.

To be fair, I proposed a field projection (like in clang) in my pre-RFC, but given the complexity, I'm not pursuing it for now.

Comment thread text/0000-btf-relocations.md Outdated
Comment thread text/0000-btf-relocations.md Outdated
Comment thread text/0000-btf-relocations.md Outdated
Comment thread text/0000-btf-relocations.md Outdated
@workingjubilee workingjubilee added the I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. label Jun 30, 2026
@workingjubilee

workingjubilee commented Jun 30, 2026

Copy link
Copy Markdown
Member

Syntax questions and my nits aside, this will want a sponsor from T-lang for proceeding as a nightly experiment (which will later come back to re-informing this RFC). I have nominated it so that if you do not find a sponsor in your own time they eventually see it appear on their agenda and one of them can either take up the gauntlet or they can decline it. And this comment as explanation for why I nominated it, of course. This may be a very eventual eventually, so I do suggest finding a sponsor in your own time.

@traviscross traviscross added the P-lang-drag-1 Lang team prioritization drag level 1. label Jul 1, 2026
Comment thread text/0000-btf-relocations.md Outdated
@traviscross traviscross added the I-lang-radar Items that are on lang's radar and will need eventual work or consideration. label Jul 1, 2026
@traviscross

traviscross commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

We talked about this in the lang call today. We were interested to hear what the people working on @rust-lang/rust-for-linux think about this, as well as what those working on reflection (@oli-obk, @scottmcm, rust-lang/rust-project-goals#406) and projection (@BennoLossin, @dingxiangfei2009, @tmandry, rust-lang/rust-project-goals#390) think.

If this generally makes sense to people, I'll be happy to champion a lang experiment here.

cc @lqd @Nadrieril

@traviscross traviscross added P-lang-drag-2 Lang team prioritization drag level 2. and removed P-lang-drag-1 Lang team prioritization drag level 1. labels Jul 1, 2026
@Darksonn

Darksonn commented Jul 1, 2026

Copy link
Copy Markdown
Member

Also needs input from sized hierarchy side. cc @davidtwco

#t-lang > BTF relocations proposal @ 💬

Comment on lines +383 to +392
This overlaps with the accepted [Field Projections project goal][field-projections],
which is exploring virtual places as a general mechanism for custom field
projection. This RFC deliberately does not depend on that work: it provides the
low-level BTF field metadata queries needed for CO-RE today, while leaving a
future field-projection-based ergonomic surface open.

Providing the field-info queries proposed in this RFC does not rule out
exploring this alternative in the future. On the contrary, Clang provides both
explicit field-info builtins and field projection. It makes sense to treat these
as separate RFCs.

@Darksonn Darksonn Jul 1, 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.

I think this RFC could be written to make it more clear that we expect field access and offset_of! to work in the future. For example, the offset_of! section is phrased as if we could never support offset_of! with btf relocations.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point, I'm going to rephrase this (while still making it clear it's out of scope of this RFC).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done 97478ed

Comment thread text/0000-btf-relocations.md
Comment thread text/0000-btf-relocations.md Outdated

`offset_of!` is intentionally a constant layout query. It does not preserve the
field identity needed to emit a BTF relocation. Reusing it would either silently
produce non-relocatable code or require changing the meaning of an existing

@oli-obk oli-obk Jul 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How is the new macro with the same API change anything? Why isn't that also silently producing wrong things when e.g. a field doesn't exist at all in the relocation table?

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

How is the new macro with the same API change anything?

When I submitted the pre-RFC, where I initially proposed emission of BTF relocations in regular field projections and offset_of! , the main feedback was that it goes against the principle that Sized types should have layout known at compile time, which is not the case with BTF-relocatable types.

Then the discussion went towards the hierarchy of Sized types RFC #3729 which would eliminate this problem. But then we also agreed treating BTF-relocatable types as !Sized and allowing access to them only with dedicated macros/intrinsics could be a good way to keep the feature rolling without hard dependency on #3729.

Why isn't that also silently producing wrong things when e.g. a field doesn't exist at all in the relocation table?

Not sure if I got this question right - are you asking about what field_byte_offset! and field_byte_size! macros produce when a field does not exist? That's a great question and actually made me realize that they should probably return an Option:

core::btf::field_byte_offset!(Carrier, field.path) -> Option<usize>
core::btf::field_byte_size!(Carrier, field.path) -> Option<usize>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jup, that makes sense now, thx

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done 6116056

@vadorovsky

Copy link
Copy Markdown
Author

We talked about this in the lang call today. We were interested to hear what the people working on @rust-lang/rust-for-linux think about this, as well as what those working on reflection (@oli-obk, @scottmcm, rust-lang/rust-project-goals#406) and projection (@BennoLossin, @dingxiangfei2009, @tmandry, rust-lang/rust-project-goals#390) think.

If this generally makes sense to people, I'll be happy to champion a lang experiment here.

cc @lqd @Nadrieril

Thank you, @traviscross, for the discussion and for offering to champion this. I appreciate the lang team's interest. I'm going to modify the RFC according to the comments later today.

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
It's better to emit a compile error.
@rustbot

rustbot commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Comment thread text/0000-btf-relocations.md Outdated
Move it under `#relocatable-field-access` section. Make it clear that
we don't rule the `offset_of!` support out in the future.
Make the other macros sound by returning `None` when a field does not
exist. That makes `field_exists!` redundant, so remove it.

@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.

Cool. I resolved my concerns where they seemed no longer useful to think about or seemed to be adequately encompassed into the RFC. Noticed some typos, or at least incomplete substitutions.

View changes since this review

}

const PID_OFFSET: usize = core::mem::offset_of!(task_struct, pid);
// error: cannot use `offset_of!` with a `#[repr(Btf)]` type

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.

Suggested change
// error: cannot use `offset_of!` with a `#[repr(Btf)]` type
// error: cannot use `offset_of!` with a `#[btf_relocatable]` type


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.

Suggested change
// error: cannot access fields of a `#[repr(Btf)]` type directly
// error: cannot access fields of a `#[btf_relocatable]` type directly

Comment on lines +232 to +237
Direct field projection from a `#[btf_relocatable]` ADT is rejected. This
includes projections reached through autoderef:

```rust
task.pid
```

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 actually feel slightly unclear why this code example is here because it doesn't seem to illustrate the paragraph before it.

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

Labels

I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. P-lang-drag-2 Lang team prioritization drag level 2. 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.

8 participants