From b0ab7983e06e9a454fa394e521ad98500c9fe50a Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Tue, 25 Apr 2023 13:33:10 +0200 Subject: [PATCH] rust: add offset_of! macro This macro is used to compute the offset of a field in a struct. This commit enables two unstable features that are necessary for using the macro in a constant. However, this is not a problem as the macro will become available from the Rust standard library soon [1]. The unstable features can be disabled again once that happens. The macro in this patch does not support sub-fields. That is, you cannot write `offset_of!(MyStruct, field.sub_field)` to get the offset of `sub_field` with `field`'s type being a struct with a field called `sub_field`. This is because `field` might be a `Box`, which means that you would be trying to compute the offset to something in an entirely different allocation. There's no easy way to fix the current macro to support subfields, but the version being added to the standard library should support it, so the limitation is temporary and not a big deal. Link: https://github.com/rust-lang/rust/issues/106655 [1] Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo --- rust/kernel/lib.rs | 36 ++++++++++++++++++++++++++++++++++++ scripts/Makefile.build | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index eaded02ffb0181..910023856d3a80 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -14,6 +14,8 @@ #![no_std] #![feature(allocator_api)] #![feature(coerce_unsized)] +#![feature(const_ptr_offset_from)] +#![feature(const_refs_to_cell)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] #![feature(receiver_trait)] @@ -98,3 +100,37 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! { // instead of `!`. See . loop {} } + +/// Calculates the offset of a field from the beginning of the struct it belongs to. +/// +/// # Examples +/// +/// ``` +/// #[repr(C)] +/// struct Test { +/// a: u64, +/// b: u32, +/// } +/// +/// assert_eq!(kernel::offset_of!(Test, b), 8); +/// ``` +#[macro_export] +macro_rules! offset_of { + ($type:path, $field:ident) => {{ + let $type { $field: _, .. }; + let tmp = ::core::mem::MaybeUninit::<$type>::uninit(); + let outer = tmp.as_ptr(); + // To avoid warnings when nesting `unsafe` blocks. + #[allow(unused_unsafe)] + // SAFETY: The pointer is valid and aligned, just not initialised; `addr_of` ensures that + // we don't actually read from `outer` (which would be UB) nor create an intermediate + // reference. + let inner = unsafe { ::core::ptr::addr_of!((*outer).$field) } as *const u8; + // To avoid warnings when nesting `unsafe` blocks. + #[allow(unused_unsafe)] + // SAFETY: The two pointers are within the same allocation block. + unsafe { + inner.offset_from(outer as *const u8) as usize + } + }}; +} diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 78175231c9699c..fe2a45691e3887 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- -rust_allowed_features := new_uninit +rust_allowed_features := const_ptr_offset_from,const_refs_to_cell,new_uninit rust_common_cmd = \ RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \