diff mbox series

[RESEND,v9,7/9] rust: add crate to expose bindings and interfaces

Message ID 20240828-rust-pl011-v9-7-35579191f17c@linaro.org (mailing list archive)
State New, archived
Headers show
Series Add Rust build support, ARM PL011 device impl | expand

Commit Message

Manos Pitsidianakis Aug. 28, 2024, 4:11 a.m. UTC
Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
provides some declaration macros for symbols visible to the rest of
QEMU.

Co-authored-by: Junjie Mao <junjie.mao@intel.com>
Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
 MAINTAINERS                       |   6 ++
 rust/meson.build                  |   1 +
 rust/qemu-api/.gitignore          |   2 +
 rust/qemu-api/Cargo.lock          |   7 ++
 rust/qemu-api/Cargo.toml          |  26 +++++++
 rust/qemu-api/README.md           |  17 +++++
 rust/qemu-api/build.rs            |  14 ++++
 rust/qemu-api/meson.build         |  21 ++++++
 rust/qemu-api/src/definitions.rs  | 109 +++++++++++++++++++++++++++
 rust/qemu-api/src/device_class.rs | 128 +++++++++++++++++++++++++++++++
 rust/qemu-api/src/lib.rs          | 154 ++++++++++++++++++++++++++++++++++++++
 rust/qemu-api/src/tests.rs        |  49 ++++++++++++
 rust/rustfmt.toml                 |   7 ++
 13 files changed, 541 insertions(+)

Comments

Alex Bennée Aug. 28, 2024, 1:08 p.m. UTC | #1
Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:

> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
> provides some declaration macros for symbols visible to the rest of
> QEMU.

As mentioned on IRC I'm hitting a compilation error that bisects to this
commit:

  [148/1010] Generating bindings for Rust rustmod-bindgen-rust_wrapper.h
  FAILED: bindings.rs
  /home/alex/.cargo/bin/bindgen ../../rust/wrapper.h --output /home/alex/lsrc/qemu.git/builds/rust/bindings.rs --disable-header-comment --raw-line '// @generated' --ctypes-prefix core::ffi --formatter rustfmt --generate-block --generate-cstr --impl-debug --merge-extern-blocks --no-doc-comments --use-core --with-derive-default --allowlist-file '/home/alex/lsrc/qemu.git/include/.*' --allowlist-file '/home/alex/lsrc/qemu.git/.*' --allowlist-file '/home/alex/lsrc/qemu.git/builds/rust/.*' -- -I/home/alex/lsrc/qemu.git/. -I/home/alex/lsrc/qemu.git/builds/rust/. -I/home/alex/lsrc/qemu.git/include -I/home/alex/lsrc/qemu.git/builds/rust/include -I/usr/include/capstone -I/usr/include/p11-kit-1 -I/usr/include/pixman-1 -I/usr/include/libpng16 -I/usr/include/spice-server -I/usr/include/spice-1 -I/usr/include/spice-1 -DSTRUCT_IOVEC_DEFINED -I/usr/include/libusb-1.0 -I/usr/include/SDL2 -D_REENTRANT -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/slirp -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -DNCURSES_WIDECHAR=1 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/vte-2.91 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gtk-3.0 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/spice-server -I/usr/include/spice-1 -I/usr/include/cacard -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/nss -I/usr/include/nspr -I/usr/include/PCSC -pthread -D_REENTRANT -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/p11-kit-1 -I/usr/include/fuse3 -I/usr/include/x86_64-linux-gnu -D_FILE_OFFSET_BITS=64 -D__USE_FILE_OFFSET64 -D__USE_LARGEFILE64 -DUSE_POSIX_ACLS=1 -I/usr/include/uuid -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -std=gnu11 -MD -MQ ../../rust/wrapper.h -MF wrapper.h.d
  /usr/include/liburing.h:296:3: error: use of undeclared identifier 'memory_order_release'
  /usr/include/liburing.h:1080:11: error: use of undeclared identifier 'memory_order_acquire'
  /usr/include/liburing.h:1116:9: error: use of undeclared identifier 'memory_order_acquire'
  /usr/include/liburing.h:1125:9: error: use of undeclared identifier 'memory_order_relaxed'
  /usr/include/liburing.h:1161:2: error: use of undeclared identifier 'memory_order_relaxed'
  /usr/include/liburing.h:1197:19: error: use of undeclared identifier 'memory_order_acquire'
  /usr/include/liburing.h:1267:10: error: use of undeclared identifier 'memory_order_relaxed'
  /usr/include/liburing.h:1269:10: error: use of undeclared identifier 'memory_order_acquire'
  /usr/include/liburing.h:1320:2: error: use of undeclared identifier 'memory_order_release'
  panicked at /home/alex/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-cli-0.69.4/main.rs:52:36:
  Unable to generate bindings: ClangDiagnostic("/usr/include/liburing.h:296:3: error: use of undeclared identifier 'memory_order_release'\n/usr/include/liburing.h:1080:11: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1116:9: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1125:9: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1161:2: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1197:19: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1267:10: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1269:10: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1320:2: error: use of undeclared identifier 'memory_order_release'\n")

And I wasn't able to force the newer clang with:

  env CLANG_PATH=/usr/bin/clang-15 ../../configure '--disable-docs' --target-list=aarch64-softmmu --enable-rust

So if clang is required for bindgen we at least need:

  - to probe for clang (separately from --cc)
  - allow the user to override the default
  - pass the clang we want to use somehow to bindgen

Briefly looking at the bindgen code I see:

  fn main() {
      let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

      let mut dst =
          File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
      dst.write_all(env::var("TARGET").unwrap().as_bytes())
          .unwrap();

      // On behalf of clang_sys, rebuild ourselves if important configuration
      // variables change, to ensure that bindings get rebuilt if the
      // underlying libclang changes.
      println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
      println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
      println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");
      println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
      println!(
          "cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_{}",
          std::env::var("TARGET").unwrap()
      );
      println!(
          "cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_{}",
          std::env::var("TARGET").unwrap().replace('-', "_")
      );
  }

So I wonder if this depends on what libclang versions can be found when
cargo builds it? Although if it does it must do so dynamically:

  $ ldd /home/alex/.cargo/bin/bindgen
          linux-vdso.so.1 (0x00007f7cb43d8000)
          libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f7cb438c000)
          libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7cb42ad000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7cb3a1f000)
          /lib64/ld-linux-x86-64.so.2 (0x00007f7cb43da000)
Junjie Mao Aug. 30, 2024, 1:19 a.m. UTC | #2
On 8/28/2024 9:08 PM, Alex Bennée wrote:
> Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:
> 
>> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
>> provides some declaration macros for symbols visible to the rest of
>> QEMU.
> 
> As mentioned on IRC I'm hitting a compilation error that bisects to this
> commit:
> 
>    [148/1010] Generating bindings for Rust rustmod-bindgen-rust_wrapper.h
>    FAILED: bindings.rs
>    /home/alex/.cargo/bin/bindgen ../../rust/wrapper.h --output /home/alex/lsrc/qemu.git/builds/rust/bindings.rs --disable-header-comment --raw-line '// @generated' --ctypes-prefix core::ffi --formatter rustfmt --generate-block --generate-cstr --impl-debug --merge-extern-blocks --no-doc-comments --use-core --with-derive-default --allowlist-file '/home/alex/lsrc/qemu.git/include/.*' --allowlist-file '/home/alex/lsrc/qemu.git/.*' --allowlist-file '/home/alex/lsrc/qemu.git/builds/rust/.*' -- -I/home/alex/lsrc/qemu.git/. -I/home/alex/lsrc/qemu.git/builds/rust/. -I/home/alex/lsrc/qemu.git/include -I/home/alex/lsrc/qemu.git/builds/rust/include -I/usr/include/capstone -I/usr/include/p11-kit-1 -I/usr/include/pixman-1 -I/usr/include/libpng16 -I/usr/include/spice-server -I/usr/include/spice-1 -I/usr/include/spice-1 -DSTRUCT_IOVEC_DEFINED -I/usr/include/libusb-1.0 -I/usr/include/SDL2 -D_REENTRANT -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/slirp -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -DNCURSES_WIDECHAR=1 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/vte-2.91 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/gtk-3.0 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -pthread -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/spice-server -I/usr/include/spice-1 -I/usr/include/cacard -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/nss -I/usr/include/nspr -I/usr/include/PCSC -pthread -D_REENTRANT -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/p11-kit-1 -I/usr/include/fuse3 -I/usr/include/x86_64-linux-gnu -D_FILE_OFFSET_BITS=64 -D__USE_FILE_OFFSET64 -D__USE_LARGEFILE64 -DUSE_POSIX_ACLS=1 -I/usr/include/uuid -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -std=gnu11 -MD -MQ ../../rust/wrapper.h -MF wrapper.h.d
>    /usr/include/liburing.h:296:3: error: use of undeclared identifier 'memory_order_release'
>    /usr/include/liburing.h:1080:11: error: use of undeclared identifier 'memory_order_acquire'
>    /usr/include/liburing.h:1116:9: error: use of undeclared identifier 'memory_order_acquire'
>    /usr/include/liburing.h:1125:9: error: use of undeclared identifier 'memory_order_relaxed'
>    /usr/include/liburing.h:1161:2: error: use of undeclared identifier 'memory_order_relaxed'
>    /usr/include/liburing.h:1197:19: error: use of undeclared identifier 'memory_order_acquire'
>    /usr/include/liburing.h:1267:10: error: use of undeclared identifier 'memory_order_relaxed'
>    /usr/include/liburing.h:1269:10: error: use of undeclared identifier 'memory_order_acquire'
>    /usr/include/liburing.h:1320:2: error: use of undeclared identifier 'memory_order_release'
>    panicked at /home/alex/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-cli-0.69.4/main.rs:52:36:
>    Unable to generate bindings: ClangDiagnostic("/usr/include/liburing.h:296:3: error: use of undeclared identifier 'memory_order_release'\n/usr/include/liburing.h:1080:11: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1116:9: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1125:9: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1161:2: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1197:19: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1267:10: error: use of undeclared identifier 'memory_order_relaxed'\n/usr/include/liburing.h:1269:10: error: use of undeclared identifier 'memory_order_acquire'\n/usr/include/liburing.h:1320:2: error: use of undeclared identifier 'memory_order_release'\n")

Those missing identifiers should have been defined in stdatomic.h which is part 
of C11. You can check if that header exists under the default header search 
paths (which is listed by clang -E -Wp,-v -) and whether that file declares 
those enum constants. If so, you can further check whether there is any 
stdatomic.h elsewhere that shadows the standard one.

> 
> And I wasn't able to force the newer clang with:
> 
>    env CLANG_PATH=/usr/bin/clang-15 ../../configure '--disable-docs' --target-list=aarch64-softmmu --enable-rust
> 
> So if clang is required for bindgen we at least need:
> 
>    - to probe for clang (separately from --cc)
>    - allow the user to override the default
>    - pass the clang we want to use somehow to bindgen
> 
> Briefly looking at the bindgen code I see:
> 
>    fn main() {
>        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
> 
>        let mut dst =
>            File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
>        dst.write_all(env::var("TARGET").unwrap().as_bytes())
>            .unwrap();
> 
>        // On behalf of clang_sys, rebuild ourselves if important configuration
>        // variables change, to ensure that bindings get rebuilt if the
>        // underlying libclang changes.
>        println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
>        println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
>        println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");
>        println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
>        println!(
>            "cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_{}",
>            std::env::var("TARGET").unwrap()
>        );
>        println!(
>            "cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_{}",
>            std::env::var("TARGET").unwrap().replace('-', "_")
>        );
>    }

This is build.rs which is a build script invoked by cargo. The entry of the 
bindgen executable is in the bindgen-cli crate.

---
Best Regards
Junjie Mao

> 
> So I wonder if this depends on what libclang versions can be found when
> cargo builds it? Although if it does it must do so dynamically:
> 
>    $ ldd /home/alex/.cargo/bin/bindgen
>            linux-vdso.so.1 (0x00007f7cb43d8000)
>            libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f7cb438c000)
>            libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7cb42ad000)
>            libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7cb3a1f000)
>            /lib64/ld-linux-x86-64.so.2 (0x00007f7cb43da000)
>
Manos Pitsidianakis Aug. 30, 2024, 6:43 a.m. UTC | #3

Junjie Mao Aug. 30, 2024, 8:50 a.m. UTC | #4
On 8/30/2024 2:43 PM, Manos Pitsidianakis wrote:
> 
Alex Bennée Aug. 30, 2024, 11:03 a.m. UTC | #5
Junjie Mao <junjie.mao@intel.com> writes:

> On 8/28/2024 9:08 PM, Alex Bennée wrote:
>> Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:
>> 
>>> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
>>> provides some declaration macros for symbols visible to the rest of
>>> QEMU.
>> As mentioned on IRC I'm hitting a compilation error that bisects to
>> this
>> commit:
>>    [148/1010] Generating bindings for Rust
>> rustmod-bindgen-rust_wrapper.h
>>    FAILED: bindings.rs
>>    /home/alex/.cargo/bin/bindgen ../../rust/wrapper.h --output
>> /home/alex/lsrc/qemu.git/builds/rust/bindings.rs
>> --disable-header-comment --raw-line '// @generated' --ctypes-prefix
>> core::ffi --formatter rustfmt --generate-block --generate-cstr
>> --impl-debug --merge-extern-blocks --no-doc-comments --use-core
>> --with-derive-default --allowlist-file
>> '/home/alex/lsrc/qemu.git/include/.*' --allowlist-file
>> '/home/alex/lsrc/qemu.git/.*' --allowlist-file
>> '/home/alex/lsrc/qemu.git/builds/rust/.*' --
>> -I/home/alex/lsrc/qemu.git/.
>> -I/home/alex/lsrc/qemu.git/builds/rust/.
>> -I/home/alex/lsrc/qemu.git/include
>> -I/home/alex/lsrc/qemu.git/builds/rust/include
>> -I/usr/include/capstone -I/usr/include/p11-kit-1
>> -I/usr/include/pixman-1 -I/usr/include/libpng16
>> -I/usr/include/spice-server -I/usr/include/spice-1
>> -I/usr/include/spice-1 -DSTRUCT_IOVEC_DEFINED
>> -I/usr/include/libusb-1.0 -I/usr/include/SDL2 -D_REENTRANT
>> -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -pthread -I/usr/include/libmount -I/usr/include/blkid
>> -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -pthread
>> -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/slirp
>> -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -DNCURSES_WIDECHAR=1 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
>> -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0
>> -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -I/usr/include/harfbuzz -I/usr/include/freetype2
>> -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid
>> -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1
>> -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu
>> -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0
>> -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0
>> -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include
>> -pthread -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0
>> -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -I/usr/include/harfbuzz -I/usr/include/freetype2
>> -I/usr/include/libpng16 -I/usr/include/libmount -I/usr/include/blkid
>> -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1
>> -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu
>> -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0
>> -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0
>> -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include
>> -pthread -I/usr/include/vte-2.91 -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount
>> -I/usr/include/blkid -I/usr/include/pango-1.0
>> -I/usr/include/harfbuzz -I/usr/include/freetype2
>> -I/usr/include/libpng16 -I/usr/include/fribidi -I/usr/include/cairo
>> -I/usr/include/pixman-1 -I/usr/include/gtk-3.0
>> -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu
>> -I/usr/include/gio-unix-2.0 -I/usr/include/atk-1.0
>> -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0
>> -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include
>> -pthread -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -I/usr/include/spice-server -I/usr/include/spice-1
>> -I/usr/include/cacard -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/nss
>> -I/usr/include/nspr -I/usr/include/PCSC -pthread -D_REENTRANT
>> -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT
>> -I/usr/include/p11-kit-1 -I/usr/include/fuse3
>> -I/usr/include/x86_64-linux-gnu -D_FILE_OFFSET_BITS=64
>> -D__USE_FILE_OFFSET64 -D__USE_LARGEFILE64 -DUSE_POSIX_ACLS=1
>> -I/usr/include/uuid -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
>> -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1
>> -I/usr/include/p11-kit-1 -I/usr/include/p11-kit-1 -D_GNU_SOURCE
>> -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -I/usr/include/glib-2.0
>> -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -std=gnu11 -MD -MQ
>> ../../rust/wrapper.h -MF wrapper.h.d
>>    /usr/include/liburing.h:296:3: error: use of undeclared identifier 'memory_order_release'
>>    /usr/include/liburing.h:1080:11: error: use of undeclared identifier 'memory_order_acquire'
>>    /usr/include/liburing.h:1116:9: error: use of undeclared identifier 'memory_order_acquire'
>>    /usr/include/liburing.h:1125:9: error: use of undeclared identifier 'memory_order_relaxed'
>>    /usr/include/liburing.h:1161:2: error: use of undeclared identifier 'memory_order_relaxed'
>>    /usr/include/liburing.h:1197:19: error: use of undeclared identifier 'memory_order_acquire'
>>    /usr/include/liburing.h:1267:10: error: use of undeclared identifier 'memory_order_relaxed'
>>    /usr/include/liburing.h:1269:10: error: use of undeclared identifier 'memory_order_acquire'
>>    /usr/include/liburing.h:1320:2: error: use of undeclared identifier 'memory_order_release'
>>    panicked at /home/alex/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-cli-0.69.4/main.rs:52:36:
>>    Unable to generate bindings:
>> ClangDiagnostic("/usr/include/liburing.h:296:3: error: use of
>> undeclared identifier
>> 'memory_order_release'\n/usr/include/liburing.h:1080:11: error: use
>> of undeclared identifier
>> 'memory_order_acquire'\n/usr/include/liburing.h:1116:9: error: use
>> of undeclared identifier
>> 'memory_order_acquire'\n/usr/include/liburing.h:1125:9: error: use
>> of undeclared identifier
>> 'memory_order_relaxed'\n/usr/include/liburing.h:1161:2: error: use
>> of undeclared identifier
>> 'memory_order_relaxed'\n/usr/include/liburing.h:1197:19: error: use
>> of undeclared identifier
>> 'memory_order_acquire'\n/usr/include/liburing.h:1267:10: error: use
>> of undeclared identifier
>> 'memory_order_relaxed'\n/usr/include/liburing.h:1269:10: error: use
>> of undeclared identifier
>> 'memory_order_acquire'\n/usr/include/liburing.h:1320:2: error: use
>> of undeclared identifier 'memory_order_release'\n")
>
> Those missing identifiers should have been defined in stdatomic.h
> which is part of C11. You can check if that header exists under the
> default header search paths (which is listed by clang -E -Wp,-v -) and
> whether that file declares those enum constants. If so, you can
> further check whether there is any stdatomic.h elsewhere that shadows
> the standard one.

It is there:

  /usr/lib/llvm-14/lib/clang/14.0.6/include/stdatomic.h

in the search path:

  clang -E -Wp,-v -
  clang -cc1 version 14.0.6 based upon LLVM 14.0.6 default target x86_64-pc-linux-gnu
  ignoring nonexistent directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
  ignoring nonexistent directory "/include"
  #include "..." search starts here:
  #include <...> search starts here:
   /usr/lib/llvm-14/lib/clang/14.0.6/include
   /usr/local/include
   /usr/include/x86_64-linux-gnu
   /usr/include
  End of search list.

but not in the list above. Have we told clang not to include system
search paths in the bindgen invocation?
Manos Pitsidianakis Aug. 31, 2024, 8:25 a.m. UTC | #6
On Fri, 30 Aug 2024 14:03, Alex Bennée <alex.bennee@linaro.org> wrote:
[.snip.]
>
>It is there:
>
>  /usr/lib/llvm-14/lib/clang/14.0.6/include/stdatomic.h
>
>in the search path:
>
>  clang -E -Wp,-v -
>  clang -cc1 version 14.0.6 based upon LLVM 14.0.6 default target x86_64-pc-linux-gnu
>  ignoring nonexistent directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
>  ignoring nonexistent directory "/include"
>  #include "..." search starts here:
>  #include <...> search starts here:
>   /usr/lib/llvm-14/lib/clang/14.0.6/include
>   /usr/local/include
>   /usr/include/x86_64-linux-gnu
>   /usr/include
>  End of search list.
>
>but not in the list above. Have we told clang not to include system
>search paths in the bindgen invocation?

stdatomic.h was first included in c11, and compilers already shipped the 
header on their own (since they use builtin operations). The compiler 
header paths are considered special system header paths by compilers, 
and what's happening here is a bug in the manual searching logic in 
libclang like explained in the other replies. The bindgen invocation 
looks at system search paths otherwise, or it'd fail to find headers 
used in QEMU.
Junjie Mao Sept. 2, 2024, 5:58 a.m. UTC | #7
On 8/31/2024 4:25 PM, Manos Pitsidianakis wrote:
> On Fri, 30 Aug 2024 14:03, Alex Bennée <alex.bennee@linaro.org> wrote:
> [.snip.]
>>
>> It is there:
>>
>>  /usr/lib/llvm-14/lib/clang/14.0.6/include/stdatomic.h
>>
>> in the search path:
>>
>>  clang -E -Wp,-v -
>>  clang -cc1 version 14.0.6 based upon LLVM 14.0.6 default target 
>> x86_64-pc-linux-gnu
>>  ignoring nonexistent directory 
>> "/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
>>  ignoring nonexistent directory "/include"
>>  #include "..." search starts here:
>>  #include <...> search starts here:
>>   /usr/lib/llvm-14/lib/clang/14.0.6/include
>>   /usr/local/include
>>   /usr/include/x86_64-linux-gnu
>>   /usr/include
>>  End of search list.
>>
>> but not in the list above. Have we told clang not to include system
>> search paths in the bindgen invocation?
> 
> stdatomic.h was first included in c11, and compilers already shipped the header 
> on their own (since they use builtin operations). The compiler header paths are 
> considered special system header paths by compilers, and what's happening here 
> is a bug in the manual searching logic in libclang like explained in the other 

Do you mean the manual searching logic in the *clang-sys* crate?

There is also a related issue [1] on GitHub.

[1] https://github.com/rust-lang/rust-bindgen/issues/2682

> replies. The bindgen invocation looks at system search paths otherwise, or it'd 
> fail to find headers used in QEMU.

Another approach to work around that issue is to define BOTH CLANG_PATH and 
LIBCLANG_PATH env vars, e.g., CLANG_PATH=/usr/bin/clang-13 
LIBCLANG_PATH=/lib/x86_64-linux-gnu/libclang-13.so. That works in my environment.

The following is a draft to do it in meson.build. It does NOT work yet as 
bindgen() does not allow customizing environment variables. I'm checking how to 
make it in meson.

diff --git a/meson.build b/meson.build
index 68d5b53684..1f6fc21f8d 100644
--- a/meson.build
+++ b/meson.build
@@ -3879,6 +3879,30 @@ if have_rust and have_system
      capture : true,
      check: true).stdout().strip().split()

+  host_clang = find_program(
+    'clang', version: '>=5.0.0',
+    required: true, native: true)
+  host_libclang = run_command(
+    host_clang,
+    '-print-file-name=libclang.so',
+    capture: true,
+    check: true).stdout().strip()
+  # Clang prints an absolute path when the library is found
+  if not host_libclang.startswith('/')
+    host_clang_major = host_clang.version().split('.')[0]
+    host_libclang = run_command(
+      host_clang,
+      '-print-file-name=libclang-' + host_clang_major + '.so',
+      capture: true,
+      check: true).stdout().strip()
+  endif
+  if not host_libclang.startswith('/')
+    error('Cannot find libclang.so for ' + host_clang.full_path())
+  endif
+  message('Found libclang: ' + host_libclang)
+
+  # TODO: set CLANG_PATH to host_clang.full_path() and LIBCLANG_PATH to
+  # host_libclang when invoking bindgen
    bindings_rs = import('rust').bindgen(
      input: 'rust/wrapper.h',
      dependencies: common_ss.all_dependencies(),
Paolo Bonzini Sept. 4, 2024, 11:01 a.m. UTC | #8
On Wed, Aug 28, 2024 at 6:12 AM Manos Pitsidianakis
<manos.pitsidianakis@linaro.org> wrote:
>
> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
> provides some declaration macros for symbols visible to the rest of
> QEMU.

The only comment I have is that I would drop the allocator support
completely. It adds complications and, at least initially, we can
plan/hope that all cross-language allocations go through a constructor
function like error_setg() or object_new().

In the case of QOM, if we wanted to allocate objects in a Box rather
than malloc-ed memory, we could also put in the object a free function
that calls

   mem::drop(Box::from_raw(p))

Fixing rust.bindgen() to use the right CLANG_PATH/LIBCLANG_PATH should
probably be done in meson itself. The (not too hard) alternative is to
just use a custom_target.

Paolo
Junjie Mao Sept. 5, 2024, 3:05 a.m. UTC | #9
On 9/4/2024 7:01 PM, Paolo Bonzini wrote:
> On Wed, Aug 28, 2024 at 6:12 AM Manos Pitsidianakis
> <manos.pitsidianakis@linaro.org> wrote:
>>
>> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
>> provides some declaration macros for symbols visible to the rest of
>> QEMU.
> 
> The only comment I have is that I would drop the allocator support
> completely. It adds complications and, at least initially, we can
> plan/hope that all cross-language allocations go through a constructor
> function like error_setg() or object_new().
> 
> In the case of QOM, if we wanted to allocate objects in a Box rather
> than malloc-ed memory, we could also put in the object a free function
> that calls
> 
>     mem::drop(Box::from_raw(p))
> 
> Fixing rust.bindgen() to use the right CLANG_PATH/LIBCLANG_PATH should
> probably be done in meson itself. The (not too hard) alternative is to
> just use a custom_target.

To unblock this series at the moment a custom_target() is the only way. Fixes to 
either meson or clang-sys crate will take some time before they are available in 
a public release.

The ideal fix to me is that clang-sys always find clang and libclang of the same 
version. I can take a look at that crate and think about how that can be done.

Meanwhile, I opened a PR [1] to discuss this with the meson community. The PR 
itself adds the env argument to rust.bindgen(), but as you suggested meson can 
do more, e.g., when given a native clang executable, meson can search for the 
corresponding libclang and sets CLANG_PATH/LIBCLANG_PATH accordingly. I'll keep 
collecting their feedback and refining the changes in that PR.

[1] https://github.com/mesonbuild/meson/pull/13631

> 
> Paolo
>
Manos Pitsidianakis Sept. 5, 2024, 7:50 a.m. UTC | #10
On Wed, 04 Sep 2024 14:01, Paolo Bonzini <pbonzini@redhat.com> wrote:
>On Wed, Aug 28, 2024 at 6:12 AM Manos Pitsidianakis
><manos.pitsidianakis@linaro.org> wrote:
>>
>> Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and
>> provides some declaration macros for symbols visible to the rest of
>> QEMU.
>
>The only comment I have is that I would drop the allocator support
>completely. It adds complications and, at least initially, we can
>plan/hope that all cross-language allocations go through a constructor
>function like error_setg() or object_new().
>
>In the case of QOM, if we wanted to allocate objects in a Box rather
>than malloc-ed memory, we could also put in the object a free function
>that calls
>
>   mem::drop(Box::from_raw(p))
>
>Fixing rust.bindgen() to use the right CLANG_PATH/LIBCLANG_PATH should
>probably be done in meson itself. The (not too hard) alternative is to
>just use a custom_target.

Can we get the include directories of common_ss.all_dependencies() for a 
custom_target? Meson does this:

  for de in kwargs['dependencies']:
      for i in de.get_include_dirs():
          clang_args.extend([f'-I{x}' for x in i.to_string_list(
              state.environment.get_source_dir(), state.environment.get_build_dir())])
      clang_args.extend(de.get_all_compile_args())
      for s in de.get_sources():
          if isinstance(s, File):
              depend_files.append(s)
          elif isinstance(s, CustomTarget):
              depends.append(s)

But I think the functionality to extract this info from dep objects is 
not exposed to meson.build files.
Paolo Bonzini Sept. 6, 2024, 7:56 a.m. UTC | #11
On Mon, Sep 2, 2024 at 7:59 AM Junjie Mao <junjie.mao@intel.com> wrote:
> +      '-print-file-name=libclang-' + host_clang_major + '.so',

Note that libclang-MAJOR.so is a Debian-ism. On Fedora for example I
have libclang.so.MAJOR.MINOR instead.

Overall, this is a pain and I'd rather leave it to Meson developers to
fix it. Until then, you'll have to make sure your clang/libclang
versions match, or pass CLANG_PATH/LIBCLANG_PATH.

Paolo
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 642c07a9ff2ed2422ac50d1419751f92f81690bd..d35e9f6b20bb23c7580de488ebaabc6c031343d2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3348,6 +3348,12 @@  F: hw/core/register.c
 F: include/hw/register.h
 F: include/hw/registerfields.h
 
+Rust
+M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+S: Maintained
+F: rust/qemu-api
+F: rust/rustfmt.toml
+
 SLIRP
 M: Samuel Thibault <samuel.thibault@ens-lyon.org>
 S: Maintained
diff --git a/rust/meson.build b/rust/meson.build
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4a58d106b1dd1d7bee39dd129f57ddb5a95bd9b7 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -0,0 +1 @@ 
+subdir('qemu-api')
diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b9e7e004c867bd1b5d44e69634d78fdafbfc1306
--- /dev/null
+++ b/rust/qemu-api/.gitignore
@@ -0,0 +1,2 @@ 
+# Ignore generated bindings file overrides.
+src/bindings.rs
diff --git a/rust/qemu-api/Cargo.lock b/rust/qemu-api/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..e9c51a243a85499cfa3ce2f9da225184ddf05d24
--- /dev/null
+++ b/rust/qemu-api/Cargo.lock
@@ -0,0 +1,7 @@ 
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "qemu_api"
+version = "0.1.0"
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..efc369e5ae926fd5c63e38a68e621ff3ad3b6e3b
--- /dev/null
+++ b/rust/qemu-api/Cargo.toml
@@ -0,0 +1,26 @@ 
+[package]
+name = "qemu_api"
+version = "0.1.0"
+edition = "2021"
+authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
+license = "GPL-2.0-or-later"
+readme = "README.md"
+homepage = "https://www.qemu.org"
+description = "Rust bindings for QEMU"
+repository = "https://gitlab.com/qemu-project/qemu/"
+resolver = "2"
+publish = false
+keywords = []
+categories = []
+
+[dependencies]
+
+[features]
+default = ["allocator"]
+allocator = []
+
+# Do not include in any global workspace
+[workspace]
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)'] }
diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7588fa29ef37e29d424fc7c333ea1c9c6139181b
--- /dev/null
+++ b/rust/qemu-api/README.md
@@ -0,0 +1,17 @@ 
+# QEMU bindings and API wrappers
+
+This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs.
+
+The C bindings can be generated with `bindgen`, using this build target:
+
+```console
+$ ninja bindings.rs
+```
+
+## Generate Rust documentation
+
+To generate docs for this crate, including private items:
+
+```sh
+cargo doc --no-deps --document-private-items
+```
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..419b154c2d267a4b067b66f17b882bed0e65493a
--- /dev/null
+++ b/rust/qemu-api/build.rs
@@ -0,0 +1,14 @@ 
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::path::Path;
+
+fn main() {
+    if !Path::new("src/bindings.rs").exists() {
+        panic!(
+            "No generated C bindings found! Either build them manually with bindgen or with meson \
+             (`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
+        );
+    }
+}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..cf2b465e9f7cc7fb10af0d3aac469b58211cd64e
--- /dev/null
+++ b/rust/qemu-api/meson.build
@@ -0,0 +1,21 @@ 
+_qemu_api_rs = static_library(
+  'qemu_api',
+  structured_sources(
+    [
+      'src/lib.rs',
+      'src/definitions.rs',
+      'src/device_class.rs',
+    ],
+    {'.' : bindings_rs},
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: rustc_config_args + [
+    '--cfg', 'MESON',
+    '--cfg', 'feature="allocator"',
+  ],
+)
+
+qemu_api = declare_dependency(
+  link_with: _qemu_api_rs,
+)
diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4abd0253bd1ff4f3f2e11528329709ef54a43c24
--- /dev/null
+++ b/rust/qemu-api/src/definitions.rs
@@ -0,0 +1,109 @@ 
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Definitions required by QEMU when registering a device.
+
+/// Trait a type must implement to be registered with QEMU.
+pub trait ObjectImpl {
+    type Class;
+    const TYPE_INFO: crate::bindings::TypeInfo;
+    const TYPE_NAME: &'static ::core::ffi::CStr;
+    const PARENT_TYPE_NAME: Option<&'static ::core::ffi::CStr>;
+    const INSTANCE_INIT: ::core::option::Option<
+        unsafe extern "C" fn(obj: *mut crate::bindings::Object),
+    >;
+    const INSTANCE_POST_INIT: ::core::option::Option<
+        unsafe extern "C" fn(obj: *mut crate::bindings::Object),
+    >;
+    const INSTANCE_FINALIZE: ::core::option::Option<
+        unsafe extern "C" fn(obj: *mut crate::bindings::Object),
+    >;
+    const ABSTRACT: bool;
+}
+
+pub trait Class {
+    const CLASS_INIT: ::core::option::Option<
+        unsafe extern "C" fn(
+            klass: *mut crate::bindings::ObjectClass,
+            data: *mut core::ffi::c_void,
+        ),
+    >;
+    const CLASS_BASE_INIT: ::core::option::Option<
+        unsafe extern "C" fn(
+            klass: *mut crate::bindings::ObjectClass,
+            data: *mut core::ffi::c_void,
+        ),
+    >;
+}
+
+#[macro_export]
+macro_rules! module_init {
+    ($func:expr, $type:expr) => {
+        #[used]
+        #[cfg_attr(target_os = "linux", link_section = ".ctors")]
+        #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
+        #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
+        pub static LOAD_MODULE: extern "C" fn() = {
+            assert!($type < $crate::bindings::module_init_type_MODULE_INIT_MAX);
+
+            extern "C" fn __load() {
+                unsafe {
+                    $crate::bindings::register_module_init(Some($func), $type);
+                }
+            }
+
+            __load
+        };
+    };
+    (qom: $func:ident => $body:block) => {
+        // NOTE: To have custom identifiers for the ctor func we need to either supply
+        // them directly as a macro argument or create them with a proc macro.
+        #[used]
+        #[cfg_attr(target_os = "linux", link_section = ".ctors")]
+        #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
+        #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
+        pub static LOAD_MODULE: extern "C" fn() = {
+            extern "C" fn __load() {
+                #[no_mangle]
+                unsafe extern "C" fn $func() {
+                    $body
+                }
+
+                unsafe {
+                    $crate::bindings::register_module_init(
+                        Some($func),
+                        $crate::bindings::module_init_type_MODULE_INIT_QOM,
+                    );
+                }
+            }
+
+            __load
+        };
+    };
+}
+
+#[macro_export]
+macro_rules! type_info {
+    ($t:ty) => {
+        $crate::bindings::TypeInfo {
+            name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(),
+            parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME {
+                pname.as_ptr()
+            } else {
+                ::core::ptr::null_mut()
+            },
+            instance_size: ::core::mem::size_of::<$t>(),
+            instance_align: ::core::mem::align_of::<$t>(),
+            instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
+            instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
+            instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
+            abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
+            class_size:  ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(),
+            class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT,
+            class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
+            class_data: ::core::ptr::null_mut(),
+            interfaces: ::core::ptr::null_mut(),
+        };
+    }
+}
diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs
new file mode 100644
index 0000000000000000000000000000000000000000..69ee912c333c3dce8bc127a286acaadd57ca20b3
--- /dev/null
+++ b/rust/qemu-api/src/device_class.rs
@@ -0,0 +1,128 @@ 
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::sync::OnceLock;
+
+use crate::bindings::Property;
+
+#[macro_export]
+macro_rules! device_class_init {
+    ($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, reset_fn => $reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
+        #[no_mangle]
+        pub unsafe extern "C" fn $func(
+            klass: *mut $crate::bindings::ObjectClass,
+            _: *mut ::core::ffi::c_void,
+        ) {
+            let mut dc =
+                ::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
+            dc.as_mut().realize = $realize_fn;
+            dc.as_mut().reset = $reset_fn;
+            dc.as_mut().vmsd = &$vmsd;
+            $crate::bindings::device_class_set_props(dc.as_mut(), $props.as_mut_ptr());
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! define_property {
+    ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
+        $crate::bindings::Property {
+            name: {
+                #[used]
+                static _TEMP: &::core::ffi::CStr = $name;
+                _TEMP.as_ptr()
+            },
+            info: $prop,
+            offset: ::core::mem::offset_of!($state, $field)
+                .try_into()
+                .expect("Could not fit offset value to type"),
+            bitnr: 0,
+            bitmask: 0,
+            set_default: true,
+            defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval.into() },
+            arrayoffset: 0,
+            arrayinfo: ::core::ptr::null(),
+            arrayfieldsize: 0,
+            link_type: ::core::ptr::null(),
+        }
+    };
+    ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => {
+        $crate::bindings::Property {
+            name: {
+                #[used]
+                static _TEMP: &::core::ffi::CStr = $name;
+                _TEMP.as_ptr()
+            },
+            info: $prop,
+            offset: ::core::mem::offset_of!($state, $field)
+                .try_into()
+                .expect("Could not fit offset value to type"),
+            bitnr: 0,
+            bitmask: 0,
+            set_default: false,
+            defval: $crate::bindings::Property__bindgen_ty_1 { i: 0 },
+            arrayoffset: 0,
+            arrayinfo: ::core::ptr::null(),
+            arrayfieldsize: 0,
+            link_type: ::core::ptr::null(),
+        }
+    };
+}
+
+#[repr(C)]
+pub struct Properties<const N: usize>(pub OnceLock<[Property; N]>, pub fn() -> [Property; N]);
+
+impl<const N: usize> Properties<N> {
+    pub fn as_mut_ptr(&mut self) -> *mut Property {
+        _ = self.0.get_or_init(self.1);
+        self.0.get_mut().unwrap().as_mut_ptr()
+    }
+}
+
+#[macro_export]
+macro_rules! declare_properties {
+    ($ident:ident, $($prop:expr),*$(,)*) => {
+
+        const fn _calc_prop_len() -> usize {
+            let mut len = 1;
+            $({
+                _ = stringify!($prop);
+                len += 1;
+            })*
+            len
+        }
+        const PROP_LEN: usize = _calc_prop_len();
+
+        fn _make_properties() -> [$crate::bindings::Property; PROP_LEN] {
+            [
+                $($prop),*,
+                    unsafe { ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() },
+            ]
+        }
+
+        #[no_mangle]
+        pub static mut $ident: $crate::device_class::Properties<PROP_LEN> = $crate::device_class::Properties(::std::sync::OnceLock::new(), _make_properties);
+    };
+}
+
+#[macro_export]
+macro_rules! vm_state_description {
+    ($(#[$outer:meta])*
+     $name:ident,
+     $(name: $vname:expr,)*
+     $(unmigratable: $um_val:expr,)*
+    ) => {
+        #[used]
+        $(#[$outer])*
+        pub static $name: $crate::bindings::VMStateDescription = $crate::bindings::VMStateDescription {
+            $(name: {
+                #[used]
+                static VMSTATE_NAME: &::core::ffi::CStr = $vname;
+                $vname.as_ptr()
+            },)*
+            unmigratable: true,
+            ..unsafe { ::core::mem::MaybeUninit::<$crate::bindings::VMStateDescription>::zeroed().assume_init() }
+        };
+    }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..26637024907dc23d5ca81a8ce21cd9c307c022ea
--- /dev/null
+++ b/rust/qemu-api/src/lib.rs
@@ -0,0 +1,154 @@ 
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
+
+#[allow(
+    dead_code,
+    improper_ctypes_definitions,
+    improper_ctypes,
+    non_camel_case_types,
+    non_snake_case,
+    non_upper_case_globals,
+    clippy::missing_const_for_fn,
+    clippy::too_many_arguments,
+    clippy::approx_constant,
+    clippy::use_self,
+    clippy::useless_transmute,
+    clippy::missing_safety_doc,
+)]
+#[rustfmt::skip]
+pub mod bindings;
+
+unsafe impl Send for bindings::Property {}
+unsafe impl Sync for bindings::Property {}
+unsafe impl Sync for bindings::TypeInfo {}
+unsafe impl Sync for bindings::VMStateDescription {}
+
+pub mod definitions;
+pub mod device_class;
+
+#[cfg(test)]
+mod tests;
+
+use std::alloc::{GlobalAlloc, Layout};
+
+#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
+extern "C" {
+    fn g_aligned_alloc0(
+        n_blocks: bindings::gsize,
+        n_block_bytes: bindings::gsize,
+        alignment: bindings::gsize,
+    ) -> bindings::gpointer;
+    fn g_aligned_free(mem: bindings::gpointer);
+}
+
+#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
+extern "C" {
+    fn qemu_memalign(alignment: usize, size: usize) -> *mut ::core::ffi::c_void;
+    fn qemu_vfree(ptr: *mut ::core::ffi::c_void);
+}
+
+extern "C" {
+    fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer;
+    fn g_free(mem: bindings::gpointer);
+}
+
+/// An allocator that uses the same allocator as QEMU in C.
+///
+/// It is enabled by default with the `allocator` feature.
+///
+/// To set it up manually as a global allocator in your crate:
+///
+/// ```ignore
+/// use qemu_api::QemuAllocator;
+///
+/// #[global_allocator]
+/// static GLOBAL: QemuAllocator = QemuAllocator::new();
+/// ```
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+pub struct QemuAllocator {
+    _unused: [u8; 0],
+}
+
+#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)]
+pub static GLOBAL: QemuAllocator = QemuAllocator::new();
+
+impl QemuAllocator {
+    // From the glibc documentation, on GNU systems, malloc guarantees 16-byte
+    // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See
+    // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html.
+    // This alignment guarantee also applies to Windows and Android. On Darwin
+    // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems.
+    #[cfg(all(
+        target_pointer_width = "32",
+        not(any(target_os = "macos", target_os = "openbsd"))
+    ))]
+    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(8);
+    #[cfg(all(
+        target_pointer_width = "64",
+        not(any(target_os = "macos", target_os = "openbsd"))
+    ))]
+    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
+    #[cfg(all(
+        any(target_pointer_width = "32", target_pointer_width = "64"),
+        any(target_os = "macos", target_os = "openbsd")
+    ))]
+    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
+    #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
+    pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = None;
+
+    pub const fn new() -> Self {
+        Self { _unused: [] }
+    }
+}
+
+impl Default for QemuAllocator {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+// Sanity check.
+const _: [(); 8] = [(); ::core::mem::size_of::<*mut ::core::ffi::c_void>()];
+
+unsafe impl GlobalAlloc for QemuAllocator {
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
+        {
+            g_malloc0(layout.size().try_into().unwrap()).cast::<u8>()
+        } else {
+            #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
+            {
+                g_aligned_alloc0(
+                    layout.size().try_into().unwrap(),
+                    1,
+                    (8 * layout.align()).try_into().unwrap(),
+                )
+                .cast::<u8>()
+            }
+            #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
+            {
+                qemu_memalign(8 * layout.align(), layout.size()).cast::<u8>()
+            }
+        }
+    }
+
+    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+        if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
+        {
+            g_free(ptr.cast::<_>())
+        } else {
+            #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
+            {
+                g_aligned_free(ptr.cast::<_>())
+            }
+            #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
+            {
+                qemu_vfree(ptr.cast::<_>())
+            }
+        }
+    }
+}
diff --git a/rust/qemu-api/src/tests.rs b/rust/qemu-api/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..df54edbd4e27e7d2aafc243355d1826d52497c21
--- /dev/null
+++ b/rust/qemu-api/src/tests.rs
@@ -0,0 +1,49 @@ 
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use crate::{
+    bindings::*, declare_properties, define_property, device_class_init, vm_state_description,
+};
+
+#[test]
+fn test_device_decl_macros() {
+    // Test that macros can compile.
+    vm_state_description! {
+        VMSTATE,
+        name: c"name",
+        unmigratable: true,
+    }
+
+    #[repr(C)]
+    pub struct DummyState {
+        pub char_backend: CharBackend,
+        pub migrate_clock: bool,
+    }
+
+    declare_properties! {
+        DUMMY_PROPERTIES,
+            define_property!(
+                c"chardev",
+                DummyState,
+                char_backend,
+                unsafe { &qdev_prop_chr },
+                CharBackend
+            ),
+            define_property!(
+                c"migrate-clk",
+                DummyState,
+                migrate_clock,
+                unsafe { &qdev_prop_bool },
+                bool
+            ),
+    }
+
+    device_class_init! {
+        dummy_class_init,
+        props => DUMMY_PROPERTIES,
+        realize_fn => None,
+        reset_fn => None,
+        vmsd => VMSTATE,
+    }
+}
diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml
new file mode 100644
index 0000000000000000000000000000000000000000..ebecb99fe09e010d66c06b324f43d77b8ee00ea9
--- /dev/null
+++ b/rust/rustfmt.toml
@@ -0,0 +1,7 @@ 
+edition = "2021"
+format_generated_files = false
+format_code_in_doc_comments = true
+format_strings = true
+imports_granularity = "Crate"
+group_imports = "StdExternalCrate"
+wrap_comments = true