From: Wedson Almeida Filho <[email protected]>
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<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]>
---
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 c718524056a6..cdf9fe999328 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(core_ffi_c)]
#![feature(dispatch_from_dyn)]
#![feature(explicit_generic_args_with_impl_trait)]
@@ -102,3 +104,36 @@ 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:ty, $field:ident) => {{
+ 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 9f94fc83f086..ec583d13dde2 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 := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
+rust_allowed_features := const_ptr_offset_from,const_refs_to_cell,core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
rust_common_cmd = \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
--
2.40.1.606.ga4b1b128d6-goog
On 5/17/23 17:31, 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 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<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]>
On Wed, 17 May 2023 20:31:14 +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 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<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]>
> ---
> 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 c718524056a6..cdf9fe999328 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(core_ffi_c)]
> #![feature(dispatch_from_dyn)]
> #![feature(explicit_generic_args_with_impl_trait)]
> @@ -102,3 +104,36 @@ 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:ty, $field:ident) => {{
> + 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
> + }
This has no protection against using `Deref`. The memoffset crate has a
let $type { $field: _, .. };
line to ensure that the field is a direct member of type and deref is
not happening.
> + }};
> +}
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index 9f94fc83f086..ec583d13dde2 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 := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
> +rust_allowed_features := const_ptr_offset_from,const_refs_to_cell,core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
Side note: once we bump our compiler to 1.71, we should switch to the
built-in `offset_of!` macro and get rid of these unstable features.
Best,
Gary
Gary Guo <[email protected]> writes:
> On Wed, 17 May 2023 20:31:14 +0000
> Alice Ryhl <[email protected]> wrote:
>> +#[macro_export]
>> +macro_rules! offset_of {
>> + ($type:ty, $field:ident) => {{
>> + 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
>> + }
>
> This has no protection against using `Deref`. The memoffset crate has a
>
> let $type { $field: _, .. };
>
> line to ensure that the field is a direct member of type and deref is
> not happening.
Added. I had to change `$type:ty` to `$type:path` to get that to
compile, since otherwise you can't use the token in a pattern. However,
I think it's fine - this is temporary anyway until the standard library
gets the macro.
>> + }};
>> +}
>> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
>> index 9f94fc83f086..ec583d13dde2 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 := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
>> +rust_allowed_features := const_ptr_offset_from,const_refs_to_cell,core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
>
> Side note: once we bump our compiler to 1.71, we should switch to the
> built-in `offset_of!` macro and get rid of these unstable features.
Agreed. I mentioned that in the commit message.
Alice
Gary Guo <[email protected]> writes:
> On Wed, 17 May 2023 20:31:14 +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 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<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]>
>> ---
>> 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 c718524056a6..cdf9fe999328 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(core_ffi_c)]
>> #![feature(dispatch_from_dyn)]
>> #![feature(explicit_generic_args_with_impl_trait)]
>> @@ -102,3 +104,36 @@ 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:ty, $field:ident) => {{
>> + 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
>> + }
>
> This has no protection against using `Deref`. The memoffset crate has a
>
> let $type { $field: _, .. };
>
> line to ensure that the field is a direct member of type and deref is
> not happening.
Good catch ????
>
>> + }};
>> +}
>> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
>> index 9f94fc83f086..ec583d13dde2 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 := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
>> +rust_allowed_features := const_ptr_offset_from,const_refs_to_cell,core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro
>
> Side note: once we bump our compiler to 1.71, we should switch to the
> built-in `offset_of!` macro and get rid of these unstable features.
>
> Best,
> Gary