Allow working with Rust versions prior to 1.77. The code was taken from Rust's Discourse platform and is used with permission of the author.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- qemu/Cargo.toml | 3 + qemu/build.rs | 5 ++ qemu/src/hw/core/device_impl.rs | 4 +- qemu/src/lib.rs | 4 ++ qemu/src/qom/object_impl.rs | 13 +++-- qemu/src/util/mod.rs | 1 + qemu/src/util/offset_of.rs | 99 +++++++++++++++++++++++++++++++++ qemu/tests/main.rs | 11 +++- 9 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 qemu/build.rs create mode 100644 qemu/src/util/offset_of.rs diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index a07a449..93808a5 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -12,3 +12,6 @@ cstr = { version = "=0.2.10" } [dev-dependencies] matches = ">=0" + +[build-dependencies] +version_check = { version = "~0.9" } diff --git a/qemu/build.rs b/qemu/build.rs new file mode 100644 index 0000000..34f7b49 --- /dev/null +++ b/qemu/build.rs @@ -0,0 +1,5 @@ +fn main() { + if let Some(true) = version_check::is_min_version("1.77.0") { + println!("cargo:rustc-cfg=has_offset_of"); + } +} diff --git a/qemu/src/hw/core/device_impl.rs b/qemu/src/hw/core/device_impl.rs index 80b0e5e..b1d2f04 100644 --- a/qemu/src/hw/core/device_impl.rs +++ b/qemu/src/hw/core/device_impl.rs @@ -111,7 +111,7 @@ macro_rules! qdev_prop { $kind, $name, (<$crate::conf_type!($type) as ConstDefault>::DEFAULT).$field, - <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + std::mem::offset_of!($crate::conf_type!($type), $field) + <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + $crate::offset_of!($crate::conf_type!($type), $field) ) }; } @@ -126,7 +126,7 @@ macro_rules! qdev_define_type { @extends $super $(,$supers)*, $crate::Object); unsafe impl $crate::DeviceTypeImpl for $struct { - const CONF_OFFSET: usize = std::mem::offset_of!($struct, conf); + const CONF_OFFSET: usize = $crate::offset_of!($struct, conf); fn properties() -> *const $crate::Property { static mut PROPERTIES: &'static [$crate::Property] = &[$($props),+]; diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index 3f0491c..2d43a25 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -31,3 +31,7 @@ pub use util::foreign::IntoNative; pub use util::foreign::OwnedPointer; pub use util::zeroed::Zeroed; pub type Result<T> = std::result::Result<T, Error>; + +// with_offsets is exported directly from util::offset_of +#[cfg(has_offset_of)] +pub use std::mem::offset_of; diff --git a/qemu/src/qom/object_impl.rs b/qemu/src/qom/object_impl.rs index 61546b6..b1768b9 100644 --- a/qemu/src/qom/object_impl.rs +++ b/qemu/src/qom/object_impl.rs @@ -95,11 +95,14 @@ unsafe fn rust_type_register<T: TypeImpl + ObjectImpl>() { #[macro_export] macro_rules! qom_define_type { ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; @extends $super:ty $(,$supers:ty)*) => { - struct $struct { - // self.base dropped by call to superclass instance_finalize - base: std::mem::ManuallyDrop<$super>, - conf: $conf_ty, - state: $state_ty, + $crate::with_offsets! { + #[repr(C)] + struct $struct { + // self.base dropped by call to superclass instance_finalize + base: std::mem::ManuallyDrop<$super>, + conf: $conf_ty, + state: $state_ty, + } } // Define IsA markers for the struct itself and all the superclasses diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs index 9c081b6..e4df7c9 100644 --- a/qemu/src/util/mod.rs +++ b/qemu/src/util/mod.rs @@ -1,3 +1,4 @@ pub mod error; pub mod foreign; +pub mod offset_of; pub mod zeroed; diff --git a/qemu/src/util/offset_of.rs b/qemu/src/util/offset_of.rs new file mode 100644 index 0000000..4ce5188 --- /dev/null +++ b/qemu/src/util/offset_of.rs @@ -0,0 +1,99 @@ +#[cfg(not(has_offset_of))] +#[macro_export] +macro_rules! offset_of { + ($Container:ty, $field:ident) => { + <$Container>::offset_to.$field + }; +} + +/// A wrapper for struct declarations, that allows using `offset_of!` in +/// versions of Rust prior to 1.77 +#[macro_export] +macro_rules! with_offsets { + // source: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df + // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla + ( + #[repr(C)] + $(#[$struct_meta:meta])* + $struct_vis:vis + struct $StructName:ident { + $( + $(#[$field_meta:meta])* + $field_vis:vis + $field_name:ident : $field_ty:ty + ),* + $(,)? + } + ) => ( + #[repr(C)] + $(#[$struct_meta])* + $struct_vis + struct $StructName { + $( + $(#[$field_meta])* + $field_vis + $field_name : $field_ty , + )* + } + + #[cfg(not(has_offset_of))] + #[allow(nonstandard_style)] + const _: () = { + pub + struct StructOffsets { + $( + $field_vis + $field_name: usize, + )* + } + struct Helper; + impl $StructName { + pub + const offset_to: StructOffsets = StructOffsets { + $( + $field_name: Helper::$field_name, + )* + }; + } + const END_OF_PREV_FIELD: usize = 0; + $crate::with_offsets! { + @names [ $($field_name)* ] + @tys [ $($field_ty ,)*] + } + }; + ); + + ( + @names [] + @tys [] + ) => (); + + ( + @names [$field_name:ident $($other_names:tt)*] + @tys [$field_ty:ty , $($other_tys:tt)*] + ) => ( + impl Helper { + const $field_name: usize = { + let align = + std::mem::align_of::<$field_ty>() + ; + let trail = + END_OF_PREV_FIELD % align + ; + 0 + END_OF_PREV_FIELD + + (align - trail) + * [1, 0][(trail == 0) as usize] + }; + } + const _: () = { + const END_OF_PREV_FIELD: usize = + Helper::$field_name + + std::mem::size_of::<$field_ty>() + ; + $crate::with_offsets! { + @names [$($other_names)*] + @tys [$($other_tys)*] + } + }; + ); +} diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs index 601e92b..854c626 100644 --- a/qemu/tests/main.rs +++ b/qemu/tests/main.rs @@ -14,11 +14,16 @@ use qemu::DeviceState; use qemu::Result; +use qemu::with_offsets; + use std::cell::RefCell; -#[derive(Default, ConstDefault)] -struct TestConf { - foo: bool, +with_offsets! { + #[repr(C)] + #[derive(Default, ConstDefault)] + struct TestConf { + foo: bool, + } } #[derive(Default)] -- 2.45.2