Merge pull request #2039 from rust-osdev/abi

replace ABI-related unit tests with const checks
This commit is contained in:
Philipp Schuster 2026-08-23 13:34:19 +00:00 committed by GitHub
commit ef8641b510
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 49 deletions

View file

@ -86,6 +86,14 @@ pub type VirtualAddress = u64;
#[repr(transparent)]
pub struct Boolean(pub u8);
// Ensure ABI guarantees for Boolean, as promised in [0].
//
// [0]: https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192
const _: () = {
assert!(align_of::<Boolean>() == 1);
assert!(size_of::<Boolean>() == 1);
};
impl Boolean {
/// [`Boolean`] representing `true`.
///
@ -169,12 +177,10 @@ mod tests {
use super::*;
#[test]
/// Test the properties promised in [0]. This also applies for the other
/// architectures.
/// Test the properties promised in [0] and convenient rusty conversions.
///
/// [0] https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192
fn test_boolean_abi() {
assert_eq!(size_of::<Boolean>(), 1);
fn test_boolean_conversions() {
assert_eq!(Boolean::from(true).0, 1);
assert_eq!(Boolean::from(false).0, 0);
assert_eq!(Boolean::TRUE.0, 1);

View file

@ -130,6 +130,20 @@ pub union IpAddress {
pub v6: Ipv6Address,
}
// Ensure ABI guarantees for IpAddress.
const _: () = {
#[repr(C, packed)]
struct PackedHelper<T>(T);
assert!(align_of::<IpAddress>() == 4);
assert!(size_of::<IpAddress>() == 16);
// The type must be usable in a packed struct, even when it is normally
// 4 byte aligned.
assert!(align_of::<PackedHelper<IpAddress>>() == 1);
assert!(size_of::<PackedHelper<IpAddress>>() == 16);
};
impl IpAddress {
/// Zeroed variant where all bytes are guaranteed to be initialized to zero.
pub const ZERO: Self = Self { addr: [0; 4] };
@ -355,20 +369,6 @@ mod tests {
assert_eq!(efi_mac_addr.into_ethernet_addr(), ethernet_octets);
}
// Ensure that our IpAddress type can be put into a packed struct,
// even when it is normally 4 byte aligned.
#[test]
fn test_efi_ip_address_abi() {
#[repr(C, packed)]
struct PackedHelper<T>(T);
assert_eq!(align_of::<IpAddress>(), 4);
assert_eq!(size_of::<IpAddress>(), 16);
assert_eq!(align_of::<PackedHelper<IpAddress>>(), 1);
assert_eq!(size_of::<PackedHelper<IpAddress>>(), 16);
}
/// Tests the From-impls from the documentation.
#[test]
fn test_promised_from_impls() {

View file

@ -64,6 +64,13 @@ pub struct DevicePathProtocol {
// followed by payload (dynamically sized)
}
// Ensure ABI guarantees for DevicePathProtocol. The struct is naturally
// packed; thus, we don't need to explicitly specify `packed`.
const _: () = {
assert!(size_of::<DevicePathProtocol>() == 4);
assert!(align_of::<DevicePathProtocol>() == 1);
};
impl DevicePathProtocol {
pub const GUID: Guid = guid!("09576e91-6d3f-11d2-8e39-00a0c969723b");
@ -300,16 +307,3 @@ pub struct DevicePathUtilitiesProtocol {
impl DevicePathUtilitiesProtocol {
pub const GUID: Guid = guid!("0379be4e-d706-437d-b037-edb82fb772a4");
}
#[cfg(test)]
mod tests {
use super::*;
/// Test that ensures the struct is packed. Thus, we don't need to
/// explicitly specify `packed`.
#[test]
fn abi() {
assert_eq!(size_of::<DevicePathProtocol>(), 4);
assert_eq!(align_of::<DevicePathProtocol>(), 1);
}
}

View file

@ -110,6 +110,17 @@ mod tests {
}
}
// Some basic sanity checks of the helper types so that we can catch
// problems early that miri would detect otherwise.
const _: () = {
assert!(size_of::<SomeData>() == 4);
assert!(align_of::<SomeData>() == 1);
// The size is 16 instead of 4, as in Rust the size is always a
// multiple of the alignment.
assert!(size_of::<SomeDataAlign16>() == 16);
assert!(align_of::<SomeDataAlign16>() == 16);
};
/// Function that behaves like the other UEFI functions. It takes a
/// mutable reference to a buffer memory that represents a [`SomeData`]
/// instance.
@ -139,20 +150,6 @@ mod tests {
Ok(data)
}
// Some basic sanity checks so that we can catch problems early that miri would detect
// otherwise.
#[test]
fn test_some_data_type_size_constraints() {
assert_eq!(size_of::<SomeData>(), 4);
assert_eq!(SomeData::alignment(), 1);
assert_eq!(
size_of::<SomeDataAlign16>(),
16,
"The size must be 16 instead of 4, as in Rust the runtime size is a multiple of the alignment."
);
assert_eq!(SomeDataAlign16::alignment(), 16);
}
// Tests `uefi_function_stub_read` which is the foundation for the `test_make_boxed_utility`
// test.
#[test]

View file

@ -509,6 +509,10 @@ pub struct DevicePath {
data: [u8],
}
// References to the DST are transmuted from/to slice references; ensure
// both have the same fat-pointer layout.
const _: () = assert!(size_of::<&DevicePath>() == size_of::<&[u8]>());
impl ProtocolPointer for DevicePath {
unsafe fn ptr_from_ffi(ptr: *const c_void) -> *const Self {
// SAFETY: The memory is valid.
@ -1119,9 +1123,6 @@ mod tests {
#[test]
fn test_to_owned() {
// Relevant assertion to verify the transmute is fine.
assert_eq!(size_of::<&DevicePath>(), size_of::<&[u8]>());
let raw_data = create_raw_device_path();
// SAFETY: The memory is valid.
let dp = unsafe { DevicePath::from_ffi_ptr(raw_data.as_ptr().cast()) };

View file

@ -27,6 +27,10 @@ pub struct PciIoAddress {
pub ext_reg: u32,
}
// The address is converted to/from a raw u64 value; ensure both have the
// same size.
const _: () = assert!(size_of::<PciIoAddress>() == size_of::<u64>());
impl PciIoAddress {
/// Create address pointing to the device identified by `bus`, `dev` and `fun` ids.
#[must_use]
@ -170,7 +174,6 @@ mod tests {
#[test]
#[expect(clippy::unusual_byte_groupings)]
fn test_pci_ioaddr_raw_conversion() {
assert_eq!(size_of::<u64>(), size_of::<PciIoAddress>());
let srcaddr = PciIoAddress {
reg: 0x11,
fun: 0x33,