2023-06-01 14:04:48

by Alice Ryhl

[permalink] [raw]
Subject: [PATCH v2 2/8] rust: add offset_of! macro

From: Wedson Almeida Filho <[email protected]>

This macro is used to compute the offset of a field in a struct.

This commit enables an unstable feature that is 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 feature 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<SubStruct>`, 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 <[email protected]>
Co-developed-by: Alice Ryhl <[email protected]>
Signed-off-by: Alice Ryhl <[email protected]>
Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
---
rust/kernel/lib.rs | 35 +++++++++++++++++++++++++++++++++++
scripts/Makefile.build | 2 +-
2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index eaded02ffb01..7ea777b731e6 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -14,6 +14,7 @@
#![no_std]
#![feature(allocator_api)]
#![feature(coerce_unsized)]
+#![feature(const_refs_to_cell)]
#![feature(dispatch_from_dyn)]
#![feature(new_uninit)]
#![feature(receiver_trait)]
@@ -98,3 +99,37 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
// instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
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 78175231c969..819510694769 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_refs_to_cell,new_uninit

rust_common_cmd = \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
--
2.41.0.rc0.172.g3f132b7071-goog



2023-06-01 17:56:04

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] rust: add offset_of! macro

On Thu, 1 Jun 2023 13:49:40 +0000
Alice Ryhl <[email protected]> wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> This macro is used to compute the offset of a field in a struct.
>
> This commit enables an unstable feature that is 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 feature 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<SubStruct>`, 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Reviewed-by: Gary Guo <[email protected]>

> ---
> rust/kernel/lib.rs | 35 +++++++++++++++++++++++++++++++++++
> scripts/Makefile.build | 2 +-
> 2 files changed, 36 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index eaded02ffb01..7ea777b731e6 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -14,6 +14,7 @@
> #![no_std]
> #![feature(allocator_api)]
> #![feature(coerce_unsized)]
> +#![feature(const_refs_to_cell)]
> #![feature(dispatch_from_dyn)]
> #![feature(new_uninit)]
> #![feature(receiver_trait)]
> @@ -98,3 +99,37 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
> // instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
> 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 78175231c969..819510694769 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_refs_to_cell,new_uninit
>
> rust_common_cmd = \
> RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \


2023-06-02 11:06:21

by Andreas Hindborg

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] rust: add offset_of! macro


Alice Ryhl <[email protected]> writes:

> From: Wedson Almeida Filho <[email protected]>
>
> This macro is used to compute the offset of a field in a struct.
>
> This commit enables an unstable feature that is 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 feature 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<SubStruct>`, 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>
> ---
> rust/kernel/lib.rs | 35 +++++++++++++++++++++++++++++++++++
> scripts/Makefile.build | 2 +-
> 2 files changed, 36 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index eaded02ffb01..7ea777b731e6 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -14,6 +14,7 @@
> #![no_std]
> #![feature(allocator_api)]
> #![feature(coerce_unsized)]
> +#![feature(const_refs_to_cell)]
> #![feature(dispatch_from_dyn)]
> #![feature(new_uninit)]
> #![feature(receiver_trait)]
> @@ -98,3 +99,37 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
> // instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
> 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) => {{

Could we add a descriptive comment?

// Prevent deref coersion to `$field` by requiring `$type`
// has a field named `$field`

BR Andreas

> + 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 78175231c969..819510694769 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_refs_to_cell,new_uninit
>
> rust_common_cmd = \
> RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \


2023-06-07 15:23:07

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] rust: add offset_of! macro

Andreas Hindborg (Samsung) <[email protected]> writes:
> Alice Ryhl <[email protected]> writes:
>> +#[macro_export]
>> +macro_rules! offset_of {
>> + ($type:path, $field:ident) => {{
>
> Could we add a descriptive comment?
>
> // Prevent deref coersion to `$field` by requiring `$type`
> // has a field named `$field`
>
> BR Andreas

Sure. It will be in the next version of the patchset.

Alice

2023-06-11 16:07:06

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] rust: add offset_of! macro

On 01.06.23 15:49, Alice Ryhl wrote:
> From: Wedson Almeida Filho <[email protected]>
>
> This macro is used to compute the offset of a field in a struct.
>
> This commit enables an unstable feature that is 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 feature 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<SubStruct>`, 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 <[email protected]>
> Co-developed-by: Alice Ryhl <[email protected]>
> Signed-off-by: Alice Ryhl <[email protected]>
> Reviewed-by: Martin Rodriguez Reboredo <[email protected]>

Reviewed-by: Benno Lossin <[email protected]>

--
Cheers,
Benno

> ---
> rust/kernel/lib.rs | 35 +++++++++++++++++++++++++++++++++++
> scripts/Makefile.build | 2 +-
> 2 files changed, 36 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index eaded02ffb01..7ea777b731e6 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -14,6 +14,7 @@
> #![no_std]
> #![feature(allocator_api)]
> #![feature(coerce_unsized)]
> +#![feature(const_refs_to_cell)]
> #![feature(dispatch_from_dyn)]
> #![feature(new_uninit)]
> #![feature(receiver_trait)]
> @@ -98,3 +99,37 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
> // instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
> 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 78175231c969..819510694769 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_refs_to_cell,new_uninit
>
> rust_common_cmd = \
> RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
> --
> 2.41.0.rc0.172.g3f132b7071-goog
>