[fbl][rust] Use bindgen

Rather than hard-coding the size of alignment of various structures, use
bindgen to generate that information from the C++ structures.

Bug: 512840182
Change-Id: I78da718cfadae00f2163710788344a8de5b20f58
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1656781
Commit-Queue: Adam Barth <abarth@google.com>
Reviewed-by: Adrian Danis <adanis@google.com>
Fuchsia-Auto-Submit: Adam Barth <abarth@google.com>
Reviewed-by: Erick Tryzelaar <etryzelaar@google.com>
diff --git a/zircon/system/ulib/fbl/rust/BUILD.gn b/zircon/system/ulib/fbl/rust/BUILD.gn
index 0250fa3..23c83a0 100644
--- a/zircon/system/ulib/fbl/rust/BUILD.gn
+++ b/zircon/system/ulib/fbl/rust/BUILD.gn
@@ -5,12 +5,15 @@
 # https://opensource.org/licenses/MIT
 
 import("//build/components.gni")
+import("//build/rust/rustc_bindgen.gni")
 import("//build/rust/rustc_library.gni")
 
 rustc_library("fbl") {
   edition = "2024"
+  validations = [ ":fbl_bindgen_golden" ]
   sources = [
     "src/array.rs",
+    "src/bindings.rs",
     "src/canary.rs",
     "src/conditional_select_nospec.rs",
     "src/confine_array_index.rs",
@@ -80,3 +83,41 @@
   testonly = true
   deps = [ ":fbl-rust-test-pkg" ]
 }
+
+rustc_bindgen_golden("fbl_bindgen_golden") {
+  headers = [ "bindgen.h" ]
+  checked_in_source = "src/bindings.rs"
+
+  type_allowlist = [ "fbl_bindgen::.*" ]
+
+  include_dirs = [
+    "//prebuilt/third_party/clang/${host_os}-${host_cpu}/include/x86_64-unknown-linux-gnu/c++/v1",
+    "//prebuilt/third_party/clang/${host_os}-${host_cpu}/include/c++/v1",
+    "//sdk/lib/stdcompat/include",
+    "//sdk/lib/zircon-assert",
+    "//zircon/system/public",
+    "//zircon/system/ulib/fbl/include",
+  ]
+
+  additional_clang_flags = [
+    "-x",
+    "c++",
+    "-std=c++20",
+    "-stdlib=libc++",
+    "--sysroot=" + rebase_path("//prebuilt/third_party/sysroot/linux", "//"),
+  ]
+
+  additional_bindgen_flags = [
+    "--rust-edition",
+    "2021",
+  ]
+
+  notice_year = 2026
+  use_core = true
+
+  expect_lints = [
+    "dead_code",
+    "non_camel_case_types",
+    "non_upper_case_globals",
+  ]
+}
diff --git a/zircon/system/ulib/fbl/rust/bindgen.h b/zircon/system/ulib/fbl/rust/bindgen.h
new file mode 100644
index 0000000..7933586
--- /dev/null
+++ b/zircon/system/ulib/fbl/rust/bindgen.h
@@ -0,0 +1,44 @@
+// Copyright 2026 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ZIRCON_SYSTEM_ULIB_FBL_RUST_BINDGEN_H_
+#define ZIRCON_SYSTEM_ULIB_FBL_RUST_BINDGEN_H_
+
+#include <stddef.h>
+
+#include <fbl/canary.h>
+#include <fbl/intrusive_double_list.h>
+#include <fbl/intrusive_single_list.h>
+#include <fbl/intrusive_wavl_tree.h>
+#include <fbl/ref_counted.h>
+
+namespace fbl_bindgen {
+
+struct DummyObject;
+
+using Canary32 = fbl::Canary<0x12345678>;
+
+struct CanaryContainer {
+  fbl::Canary<0x12345678> canary;
+};
+
+struct SinglyNodeWrapper {
+  fbl::SinglyLinkedListNodeState<DummyObject*> state;
+};
+
+struct DoublyNodeWrapper {
+  fbl::DoublyLinkedListNodeState<DummyObject*> state;
+};
+
+struct WAVLNodeWrapper {
+  fbl::WAVLTreeNodeState<DummyObject*> state;
+};
+
+struct RefCountedObject : public fbl::RefCounted<RefCountedObject> {
+  int value;
+};
+
+}  // namespace fbl_bindgen
+
+#endif  // ZIRCON_SYSTEM_ULIB_FBL_RUST_BINDGEN_H_
diff --git a/zircon/system/ulib/fbl/rust/src/bindings.rs b/zircon/system/ulib/fbl/rust/src/bindings.rs
new file mode 100644
index 0000000..c81b552
--- /dev/null
+++ b/zircon/system/ulib/fbl/rust/src/bindings.rs
@@ -0,0 +1,78 @@
+// Copyright 2026 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is automatically generated by //zircon/system/ulib/fbl/rust:fbl_bindgen_golden.
+
+#![expect(dead_code)]
+#![expect(non_camel_case_types)]
+#![expect(non_upper_case_globals)]
+
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fbl_internal_ContainerPtrTraits {
+    pub _address: u8,
+}
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_internal_CommonNodeStateBase {
+    pub _address: u8,
+}
+pub const fbl_NodeOptions_None: fbl_NodeOptions = 0;
+pub const fbl_NodeOptions_AllowCopy: fbl_NodeOptions = 1;
+pub const fbl_NodeOptions_AllowCopyFromContainer: fbl_NodeOptions = 2;
+pub const fbl_NodeOptions_AllowMove: fbl_NodeOptions = 4;
+pub const fbl_NodeOptions_AllowMoveFromContainer: fbl_NodeOptions = 8;
+pub const fbl_NodeOptions_AllowCopyMove: fbl_NodeOptions = 5;
+pub const fbl_NodeOptions_AllowCopyMoveFromContainer: fbl_NodeOptions = 10;
+pub const fbl_NodeOptions_AllowMultiContainerUptr: fbl_NodeOptions = 16;
+pub const fbl_NodeOptions_AllowRemoveFromContainer: fbl_NodeOptions = 32;
+pub const fbl_NodeOptions_AllowClearUnsafe: fbl_NodeOptions = 64;
+pub const fbl_NodeOptions_ReservedBits: fbl_NodeOptions = 17293822569102704640;
+pub type fbl_NodeOptions = u64;
+pub type fbl_DoublyLinkedListNodeState_Base = fbl_internal_CommonNodeStateBase;
+pub type fbl_DoublyLinkedListNodeState_PtrType<PtrType_> = PtrType_;
+pub type fbl_DoublyLinkedListNodeState_PtrTraits = fbl_internal_ContainerPtrTraits;
+pub type fbl_SinglyLinkedListNodeState_Base = fbl_internal_CommonNodeStateBase;
+pub type fbl_SinglyLinkedListNodeState_PtrType<PtrType_> = PtrType_;
+pub type fbl_SinglyLinkedListNodeState_PtrTraits = fbl_internal_ContainerPtrTraits;
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_bindgen_DummyObject {
+    _unused: [u8; 0],
+}
+pub type fbl_bindgen_Canary32 = u32;
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_bindgen_CanaryContainer {
+    pub canary: u32,
+}
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_bindgen_SinglyNodeWrapper {
+    pub state: __BindgenOpaqueArray8<[u8; 8usize]>,
+}
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_bindgen_DoublyNodeWrapper {
+    pub state: __BindgenOpaqueArray8<[u8; 16usize]>,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fbl_bindgen_WAVLNodeWrapper {
+    pub state: __BindgenOpaqueArray8<[u8; 32usize]>,
+}
+#[repr(C)]
+#[derive(Debug)]
+pub struct fbl_bindgen_RefCountedObject {
+    pub _base: u32,
+    pub value: ::core::ffi::c_int,
+}
diff --git a/zircon/system/ulib/fbl/rust/src/canary.rs b/zircon/system/ulib/fbl/rust/src/canary.rs
index 61e8621..ae547b1 100644
--- a/zircon/system/ulib/fbl/rust/src/canary.rs
+++ b/zircon/system/ulib/fbl/rust/src/canary.rs
@@ -45,9 +45,6 @@
     magic: u32,
 }
 
-zr::static_assert!(core::mem::size_of::<Canary<{ magic(b"test") }>>() == 4);
-zr::static_assert!(core::mem::align_of::<Canary<{ magic(b"test") }>>() == 4);
-
 impl<const MAGIC: u32> Canary<MAGIC> {
     /// Create a new Canary with the specified magic value.
     pub const fn new() -> Self {
diff --git a/zircon/system/ulib/fbl/rust/src/doubly_linked_list.rs b/zircon/system/ulib/fbl/rust/src/doubly_linked_list.rs
index 070ec58..99ae530 100644
--- a/zircon/system/ulib/fbl/rust/src/doubly_linked_list.rs
+++ b/zircon/system/ulib/fbl/rust/src/doubly_linked_list.rs
@@ -74,14 +74,6 @@
     }
 }
 
-// Static asserts for layout verification.
-::zr::static_assert!(
-    core::mem::size_of::<DoublyLinkedListNode<()>>() == 2 * core::mem::size_of::<*mut ()>()
-);
-::zr::static_assert!(
-    core::mem::align_of::<DoublyLinkedListNode<()>>() == core::mem::align_of::<*mut ()>()
-);
-
 impl<T> Drop for DoublyLinkedListNode<T> {
     fn drop(&mut self) {
         debug_assert!(!self.in_container(), "Object destroyed while still in container");
diff --git a/zircon/system/ulib/fbl/rust/src/intrusive_container_test_support.rs b/zircon/system/ulib/fbl/rust/src/intrusive_container_test_support.rs
index bd7cd5c..dfa5609 100644
--- a/zircon/system/ulib/fbl/rust/src/intrusive_container_test_support.rs
+++ b/zircon/system/ulib/fbl/rust/src/intrusive_container_test_support.rs
@@ -311,3 +311,57 @@
 ::zr::static_assert!(core::mem::offset_of!(SharedRefObject, allocated_in_rust) == 64);
 ::zr::static_assert!(core::mem::offset_of!(SharedRefObject, destruction_flag) == 72);
 ::zr::static_assert!(core::mem::size_of::<SharedRefObject>() == 80);
+
+// Bindgen layout asserts for production library types
+type BindgenCanaryContainer = crate::bindings::fbl_bindgen_CanaryContainer;
+#[allow(dead_code)]
+struct RustCanaryContainer {
+    canary: crate::Canary<0x12345678>,
+}
+::zr::static_assert!(
+    core::mem::size_of::<RustCanaryContainer>() == core::mem::size_of::<BindgenCanaryContainer>()
+);
+::zr::static_assert!(
+    core::mem::align_of::<RustCanaryContainer>() == core::mem::align_of::<BindgenCanaryContainer>()
+);
+
+type BindgenRefCountedObject = crate::bindings::fbl_bindgen_RefCountedObject;
+#[allow(dead_code)]
+struct RustRefCountedObject {
+    _base: crate::RefCounted,
+    value: i32,
+}
+::zr::static_assert!(
+    core::mem::size_of::<RustRefCountedObject>() == core::mem::size_of::<BindgenRefCountedObject>()
+);
+::zr::static_assert!(
+    core::mem::align_of::<RustRefCountedObject>()
+        == core::mem::align_of::<BindgenRefCountedObject>()
+);
+
+::zr::static_assert!(
+    core::mem::size_of::<crate::SinglyLinkedListNode<()>>()
+        == core::mem::size_of::<crate::bindings::fbl_bindgen_SinglyNodeWrapper>()
+);
+::zr::static_assert!(
+    core::mem::align_of::<crate::SinglyLinkedListNode<()>>()
+        == core::mem::align_of::<crate::bindings::fbl_bindgen_SinglyNodeWrapper>()
+);
+
+::zr::static_assert!(
+    core::mem::size_of::<crate::DoublyLinkedListNode<()>>()
+        == core::mem::size_of::<crate::bindings::fbl_bindgen_DoublyNodeWrapper>()
+);
+::zr::static_assert!(
+    core::mem::align_of::<crate::DoublyLinkedListNode<()>>()
+        == core::mem::align_of::<crate::bindings::fbl_bindgen_DoublyNodeWrapper>()
+);
+
+::zr::static_assert!(
+    core::mem::size_of::<crate::WavlTreeNode<()>>()
+        == core::mem::size_of::<crate::bindings::fbl_bindgen_WAVLNodeWrapper>()
+);
+::zr::static_assert!(
+    core::mem::align_of::<crate::WavlTreeNode<()>>()
+        == core::mem::align_of::<crate::bindings::fbl_bindgen_WAVLNodeWrapper>()
+);
diff --git a/zircon/system/ulib/fbl/rust/src/lib.rs b/zircon/system/ulib/fbl/rust/src/lib.rs
index c342fd3..351ee88 100644
--- a/zircon/system/ulib/fbl/rust/src/lib.rs
+++ b/zircon/system/ulib/fbl/rust/src/lib.rs
@@ -8,6 +8,9 @@
 
 extern crate self as fbl;
 
+#[cfg(test)]
+mod bindings;
+
 mod array;
 mod canary;
 mod conditional_select_nospec;
diff --git a/zircon/system/ulib/fbl/rust/src/singly_linked_list.rs b/zircon/system/ulib/fbl/rust/src/singly_linked_list.rs
index f4559e9..4e50c90 100644
--- a/zircon/system/ulib/fbl/rust/src/singly_linked_list.rs
+++ b/zircon/system/ulib/fbl/rust/src/singly_linked_list.rs
@@ -765,13 +765,6 @@
     }
 
     zr::static_assert!(
-        core::mem::size_of::<SinglyLinkedListNode<()>>() == core::mem::size_of::<*mut ()>()
-    );
-    zr::static_assert!(
-        core::mem::align_of::<SinglyLinkedListNode<()>>() == core::mem::align_of::<*mut ()>()
-    );
-
-    zr::static_assert!(
         core::mem::size_of::<SinglyLinkedList<*mut DummyTarget>>()
             == core::mem::size_of::<*mut DummyTarget>()
     );
diff --git a/zircon/system/ulib/fbl/rust/src/wavl_tree.rs b/zircon/system/ulib/fbl/rust/src/wavl_tree.rs
index cdf0565..8da637f 100644
--- a/zircon/system/ulib/fbl/rust/src/wavl_tree.rs
+++ b/zircon/system/ulib/fbl/rust/src/wavl_tree.rs
@@ -338,12 +338,6 @@
     }
 }
 
-// Static asserts for layout verification.
-::zr::static_assert!(core::mem::size_of::<WavlTreeNode<()>>() == 32);
-::zr::static_assert!(core::mem::align_of::<WavlTreeNode<()>>() == 8);
-::zr::static_assert!(core::mem::size_of::<WavlTreeNode<(), i32>>() == 32);
-::zr::static_assert!(core::mem::align_of::<WavlTreeNode<(), i32>>() == 8);
-
 impl<T, R: WavlTreeRank> Drop for WavlTreeNode<T, R> {
     fn drop(&mut self) {
         debug_assert!(!self.in_container(), "Object destroyed while still in container");