{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiet4ddlcwvac57knrehiaylwyxp75h4czt7ip4ksh62jpfw6n75ym",
    "uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mmctukbmtgy2"
  },
  "path": "/t/pre-rfc-btf-relocations/24161?page=2#post_25",
  "publishedAt": "2026-05-20T20:24:19.000Z",
  "site": "https://internals.rust-lang.org",
  "tags": [
    "tamird",
    "@kornel",
    "Aya",
    "@anakryiko",
    "@Jules-Bertholet",
    "GitHub - vadorovsky/rust at btf-relocations-v2 · GitHub"
  ],
  "textContent": "Sorry for the silence. I took some time to re-think the entire approach and at the end, together with tamird, we came up with way simpler solution that will not require any changes to the field projection, and aligns with what @kornel proposes.\n\nkornel:\n\n> Does it have to be a feature of structs and arrays?\n>\n> Could it instead be expressed as a macro or intrinsic function that gives an offset to use with `ptr::read`? (which could be sufficient to make getters and setters for something struct-like that isn't an actual struct)\n>\n> Or maybe it could be implemented as getters and setters on extern opaque types?\n>\n> It wouldn't be as neat as 1st class structs that work with literals and pattern matching, but might be sufficient to interact with kernel structs treating them as an API.\n\nYes, after experimenting with that idea, I'm convinced this might be a good direction.\n\nFor reference, clang has the following intrinsic that allows to request information about BTF-relocatable type:\n\n\n    uint32_t __builtin_bpf_preserve_field_info(void *member_ptr, uint64_t info_kind);\n\n\nwhere the `info_kind` can be:\n\n  * `FIELD_EXISTENCE` (`0`) - returns a `0` or `1` indicating whether a field exists.\n  * `FIELD_OFFSET` (`1`) - returns an offset of the field, and emits a BTF relocation, so the loader can patch the offset.\n  * `FIELD_SIZE` (`2`) - returns a size of the field.\n\n\n\nWe could expose the same functionality in Rust through three intrinsics (each of them having a return type appropriate in Rust's context):\n\n\n    pub fn relocatable_field_byte_offset<T: PointeeSized>(variant: usize, field: usize) -> usize\n    pub fn relocatable_field_byte_size<T: PointeeSized>(variant: usize, field: usize) -> usize\n    pub fn relocatable_field_exists<T: PointeeSized>(variant: usize, field: usize) -> bool\n\n\nAnd... that would be it on Rust compiler's side. No changes to MIR and the field projection. No direct exposure of `preserve_access` LLVM intrinsics (at least to the users).\n\nWhy?\n\nWith BTF-relocatable types, we are not sure whether a field exists during a runtime. Therefore, a field access does not provide any guarantees of soundess. That goes against Rust's guarantees of safety.\n\nWhat goes in line with Rust's safety guarantees, however, is a way to access a relocatable field only if we meet certain circumstances - that the field exists, and that the size of the field is as we expect.\n\nThe mentioned intrinsics would provide building blocks for safe abstraction for accessing relocatable fields. To be precise - such safe abstraction could be provided outside of Rust standard library, it could be done in Aya or a dedicated third-party library. The first step towards safety would be the following function with an error type:\n\n\n    #[derive(Debug, Eq, PartialEq)]\n    pub enum RelocatableFieldAccessError {\n        SizeMismatch { expected: usize, actual: usize },\n        ProbeRead(i32),\n    }\n\n    #[inline(always)]\n    pub fn relocatable_field_read<T>(ptr: *const T) -> Result<Option<T>, RelocatableFieldAccessError> {\n        if relocatable_field_exists(ptr) {\n            let expected = core::mem::size_of::<T>();\n            let actual = relocatable_field_byte_size(ptr);\n            if expected == actual {\n                let offset = relocatable_field_byte_offset(ptr);\n                // SAFETY: We trust LLVM to emit a correct BTF relocation for\n                // that offset, and we trust BPF loader libraries to patch it\n                // accordingly.\n                let field = unsafe {\n                    bpf_probe_read_kernel(&*ptr.add(offset))\n                        .map_err(|code| RelocatableFieldAccessError::ProbeRead(code))?\n                };\n                Ok(Some(field))\n            } else {\n                Err(RelocatableFieldAccessError::SizeMismatch { expected, actual })\n            }\n        } else {\n            Ok(None)\n        }\n    }\n\n\nAnd then a BTF-relocatable structure with getters that use the function:\n\n\n    #[repr(C)]\n    struct task_struct {\n        pid: i32,\n        tgid: i32,\n    }\n\n    impl task_struct {\n        pub fn pid(&self) -> Result<Option<i32>, RelocatableFieldAccessError> {\n            relocatable_field_read(::core::ptr::addr_of!(self.pid))\n        }\n\n        pub fn tgid(&self) -> Result<Option<i32>, RelocatableFieldAccessError> {\n            relocatable_field_read(::core::ptr::addr_of!(self.pid))\n        }\n    }\n\n\nGeneration of such getters could be hidden behind a proc macro.\n\nThat would also solve the problem of different kernel versions removing or renaming fields, without having to rely on version suffixes mechanism from clang that was described by @anakryiko and mentioned by @Jules-Bertholet.\n\nThe only point from @kornel's proposition which I'm unsure about is using extern opaque types. We would need some way of declaring its layout and letting Rust (and a backend, like LLVM) to generate the debug info for it. The weakness of the code example I provided above is that the `task_struct` type is a sized struct. An open question is how can it be marked as `!Sized`, perhaps that brings us back to the `#[repr(btf)]` idea.\n\nUpdated experimental branch: GitHub - vadorovsky/rust at btf-relocations-v2 · GitHub",
  "title": "[Pre-RFC] BTF relocations"
}