about summary refs log tree commit diff
path: root/library/std
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-31 07:57:09 +0000
committerbors <bors@rust-lang.org>2022-08-31 07:57:09 +0000
commit12e4fd0755d7d976d4ee0f2004dc938290752ff7 (patch)
tree2a88229f6a91bb1741d22d923002d1b253346689 /library/std
parent7f442f8ba174fd4233a14ef4d7b577aa907db594 (diff)
parent49ed325759ca51034fb965b161a06edf9a33e73c (diff)
downloadrust-12e4fd0755d7d976d4ee0f2004dc938290752ff7.tar.gz
rust-12e4fd0755d7d976d4ee0f2004dc938290752ff7.zip
Auto merge of #101225 - matthiaskrgr:rollup-9s1chas, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #100970 (Allow deriving multipart suggestions)
 - #100984 (Reinstate preloading of some dll imports)
 - #101011 (Use getentropy when possible on all Apple platforms)
 - #101025 (Add tier-3 support for powerpc64 and riscv64 openbsd)
 - #101049 (Remove span fatal from ast lowering)
 - #101100 (Make call suggestions more general and more accurate)
 - #101171 (Fix UB from misalignment and provenance widening in `std::sys::windows`)
 - #101185 (Tweak `WellFormedLoc`s a bit)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std')
-rw-r--r--library/std/src/sys/unix/rand.rs94
-rw-r--r--library/std/src/sys/windows/c.rs9
-rw-r--r--library/std/src/sys/windows/compat.rs82
-rw-r--r--library/std/src/sys/windows/fs.rs69
-rw-r--r--library/std/src/sys/windows/mod.rs8
5 files changed, 163 insertions, 99 deletions
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index a6fe07873d7..40885417308 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -137,11 +137,9 @@ mod imp {
     }
 }
 
-#[cfg(target_os = "macos")]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
 mod imp {
-    use crate::fs::File;
-    use crate::io::Read;
-    use crate::sys::os::errno;
+    use crate::io;
     use crate::sys::weak::weak;
     use libc::{c_int, c_void, size_t};
 
@@ -155,7 +153,7 @@ mod imp {
                 for s in v.chunks_mut(256) {
                     let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
                     if ret == -1 {
-                        panic!("unexpected getentropy error: {}", errno());
+                        panic!("unexpected getentropy error: {}", io::Error::last_os_error());
                     }
                 }
                 true
@@ -163,14 +161,64 @@ mod imp {
             .unwrap_or(false)
     }
 
+    #[cfg(target_os = "macos")]
+    fn fallback_fill_bytes(v: &mut [u8]) {
+        use crate::fs::File;
+        use crate::io::Read;
+
+        let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
+        file.read_exact(v).expect("failed to read /dev/urandom")
+    }
+
+    // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
+    // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
+    // from `/dev/random` and which runs on its own thread accessed via GCD.
+    //
+    // This is very heavyweight compared to the alternatives, but they may not be usable:
+    // - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
+    // - `/dev/urandom` is not accessible inside the iOS app sandbox.
+    //
+    // Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
+    // better options are present.
+    #[cfg(target_os = "ios")]
+    fn fallback_fill_bytes(v: &mut [u8]) {
+        use crate::ptr;
+
+        enum SecRandom {}
+
+        #[allow(non_upper_case_globals)]
+        const kSecRandomDefault: *const SecRandom = ptr::null();
+
+        extern "C" {
+            fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
+        }
+
+        let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
+        if ret == -1 {
+            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+        }
+    }
+
+    // All supported versions of watchOS (>= 5) have support for `getentropy`.
+    #[cfg(target_os = "watchos")]
+    #[cold]
+    fn fallback_fill_bytes(_: &mut [u8]) {
+        unreachable!()
+    }
+
     pub fn fill_bytes(v: &mut [u8]) {
         if getentropy_fill_bytes(v) {
             return;
         }
 
-        // for older macos which doesn't support getentropy
-        let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
-        file.read_exact(v).expect("failed to read /dev/urandom")
+        // Older macOS versions (< 10.12) don't support `getentropy`. Fallback to
+        // reading from `/dev/urandom` on these systems.
+        //
+        // Older iOS versions (< 10) don't support it either. Fallback to
+        // `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable
+        // because the minimum supported version is 5 while `getentropy` became accessible
+        // in 3.
+        fallback_fill_bytes(v)
     }
 }
 
@@ -189,36 +237,6 @@ mod imp {
     }
 }
 
-// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
-// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
-// from `/dev/random` and which runs on its own thread accessed via GCD.
-// This seems needlessly heavyweight for the purposes of generating two u64s
-// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
-// only used on iOS where direct access to `/dev/urandom` is blocked by the
-// sandbox.
-#[cfg(any(target_os = "ios", target_os = "watchos"))]
-mod imp {
-    use crate::io;
-    use crate::ptr;
-    use libc::{c_int, size_t};
-
-    enum SecRandom {}
-
-    #[allow(non_upper_case_globals)]
-    const kSecRandomDefault: *const SecRandom = ptr::null();
-
-    extern "C" {
-        fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
-    }
-
-    pub fn fill_bytes(v: &mut [u8]) {
-        let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
-        if ret == -1 {
-            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
-        }
-    }
-}
-
 #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
 mod imp {
     use crate::ptr;
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index ef3f6a9ba17..c99c8efe436 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
 pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
 pub const MSG_PEEK: c_int = 0x2;
 
-pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
-
 #[repr(C)]
 #[derive(Copy, Clone)]
 pub struct linger {
@@ -503,6 +501,8 @@ pub struct FILE_END_OF_FILE_INFO {
     pub EndOfFile: LARGE_INTEGER,
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `rest` field!
 #[repr(C)]
 pub struct REPARSE_DATA_BUFFER {
     pub ReparseTag: c_uint,
@@ -511,6 +511,8 @@ pub struct REPARSE_DATA_BUFFER {
     pub rest: (),
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
 #[repr(C)]
 pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
     pub SubstituteNameOffset: c_ushort,
@@ -521,6 +523,8 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
     pub PathBuffer: WCHAR,
 }
 
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
 #[repr(C)]
 pub struct MOUNT_POINT_REPARSE_BUFFER {
     pub SubstituteNameOffset: c_ushort,
@@ -1032,7 +1036,6 @@ extern "system" {
     pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
     pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
     pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
-    pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
 
     pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
     pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
index 9c8ddc3aa1d..7dff81ecb8d 100644
--- a/library/std/src/sys/windows/compat.rs
+++ b/library/std/src/sys/windows/compat.rs
@@ -21,9 +21,52 @@
 
 use crate::ffi::{c_void, CStr};
 use crate::ptr::NonNull;
-use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::atomic::Ordering;
 use crate::sys::c;
 
+// This uses a static initializer to preload some imported functions.
+// The CRT (C runtime) executes static initializers before `main`
+// is called (for binaries) and before `DllMain` is called (for DLLs).
+//
+// It works by contributing a global symbol to the `.CRT$XCT` section.
+// The linker builds a table of all static initializer functions.
+// The CRT startup code then iterates that table, calling each
+// initializer function.
+//
+// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
+// If you're reading this and would like a guarantee here, please
+// file an issue for discussion; currently we don't guarantee any functionality
+// before main.
+// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
+#[used]
+#[link_section = ".CRT$XCT"]
+static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
+
+/// Preload some imported functions.
+///
+/// Note that any functions included here will be unconditionally loaded in
+/// the final binary, regardless of whether or not they're actually used.
+///
+/// Therefore, this should be limited to `compat_fn_optional` functions which
+/// must be preloaded or any functions where lazier loading demonstrates a
+/// negative performance impact in practical situations.
+///
+/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
+unsafe extern "C" fn init() {
+    // In an exe this code is executed before main() so is single threaded.
+    // In a DLL the system's loader lock will be held thereby synchronizing
+    // access. So the same best practices apply here as they do to running in DllMain:
+    // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
+    //
+    // DO NOT do anything interesting or complicated in this function! DO NOT call
+    // any Rust functions or CRT functions if those functions touch any global state,
+    // because this function runs during global initialization. For example, DO NOT
+    // do any dynamic allocation, don't call LoadLibrary, etc.
+
+    // Attempt to preload the synch functions.
+    load_synch_functions();
+}
+
 /// Helper macro for creating CStrs from literals and symbol names.
 macro_rules! ansi_str {
     (sym $ident:ident) => {{
@@ -75,20 +118,6 @@ impl Module {
         NonNull::new(module).map(Self)
     }
 
-    /// Load the library (if not already loaded)
-    ///
-    /// # Safety
-    ///
-    /// The module must not be unloaded.
-    pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
-        let module = c::LoadLibraryExA(
-            name.as_ptr(),
-            crate::ptr::null_mut(),
-            c::LOAD_LIBRARY_SEARCH_SYSTEM32,
-        );
-        NonNull::new(module).map(Self)
-    }
-
     // Try to get the address of a function.
     pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
         // SAFETY:
@@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
 
                 #[inline(always)]
                 pub fn option() -> Option<F> {
-                    let f = PTR.load(Ordering::Acquire);
-                    if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
-                }
-
-                #[cold]
-                fn try_load() -> Option<F> {
-                    $load_functions;
-                    NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
+                    // Miri does not understand the way we do preloading
+                    // therefore load the function here instead.
+                    #[cfg(miri)] $load_functions;
+                    NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
                 }
             }
         )+
@@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
 
         // Try loading the library and all the required functions.
         // If any step fails, then they all fail.
-        let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
+        let library = unsafe { Module::new(MODULE_NAME) }?;
         let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
         let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
 
-        c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
-        c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
+        c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
+        c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
         Some(())
     }
 
-    // Try to load the module but skip loading if a previous attempt failed.
-    static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
-    let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
-    LOAD_MODULE.store(module_loaded, Ordering::Release)
+    try_load();
 }
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 9653b1abfc3..0aa7c50ded1 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -11,7 +11,7 @@ use crate::slice;
 use crate::sync::Arc;
 use crate::sys::handle::Handle;
 use crate::sys::time::SystemTime;
-use crate::sys::{c, cvt};
+use crate::sys::{c, cvt, Align8};
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::thread;
 
@@ -326,9 +326,9 @@ impl File {
             cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
             let mut reparse_tag = 0;
             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
-                let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
-                    reparse_tag = buf.ReparseTag;
+                    reparse_tag = (*buf).ReparseTag;
                 }
             }
             Ok(FileAttr {
@@ -389,9 +389,9 @@ impl File {
             attr.file_size = info.AllocationSize as u64;
             attr.number_of_links = Some(info.NumberOfLinks);
             if attr.file_type().is_reparse_point() {
-                let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
-                    attr.reparse_tag = buf.ReparseTag;
+                    attr.reparse_tag = (*buf).ReparseTag;
                 }
             }
             Ok(attr)
@@ -458,38 +458,46 @@ impl File {
         Ok(Self { handle: self.handle.try_clone()? })
     }
 
-    fn reparse_point<'a>(
+    // NB: returned pointer is derived from `space`, and has provenance to
+    // match. A raw pointer is returned rather than a reference in order to
+    // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
+    fn reparse_point(
         &self,
-        space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
-    ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
+        space: &mut Align8<[u8]>,
+    ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
         unsafe {
             let mut bytes = 0;
             cvt({
+                // Grab this in advance to avoid it invalidating the pointer
+                // we get from `space.0.as_mut_ptr()`.
+                let len = space.0.len();
                 c::DeviceIoControl(
                     self.handle.as_raw_handle(),
                     c::FSCTL_GET_REPARSE_POINT,
                     ptr::null_mut(),
                     0,
-                    space.as_mut_ptr() as *mut _,
-                    space.len() as c::DWORD,
+                    space.0.as_mut_ptr().cast(),
+                    len as c::DWORD,
                     &mut bytes,
                     ptr::null_mut(),
                 )
             })?;
-            Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+            const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
+            Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
         }
     }
 
     fn readlink(&self) -> io::Result<PathBuf> {
-        let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+        let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let (_bytes, buf) = self.reparse_point(&mut space)?;
         unsafe {
-            let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
+            let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
                 c::IO_REPARSE_TAG_SYMLINK => {
                     let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
-                        &buf.rest as *const _ as *const _;
+                        ptr::addr_of!((*buf).rest).cast();
+                    assert!(info.is_aligned());
                     (
-                        &(*info).PathBuffer as *const _ as *const u16,
+                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
@@ -497,9 +505,10 @@ impl File {
                 }
                 c::IO_REPARSE_TAG_MOUNT_POINT => {
                     let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
-                        &buf.rest as *const _ as *const _;
+                        ptr::addr_of!((*buf).rest).cast();
+                    assert!(info.is_aligned());
                     (
-                        &(*info).PathBuffer as *const _ as *const u16,
+                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         false,
@@ -649,18 +658,18 @@ impl File {
 
 /// A buffer for holding directory entries.
 struct DirBuff {
-    buffer: Vec<u8>,
+    buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
 }
 impl DirBuff {
+    const BUFFER_SIZE: usize = 1024;
     fn new() -> Self {
-        const BUFFER_SIZE: usize = 1024;
-        Self { buffer: vec![0_u8; BUFFER_SIZE] }
+        Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
     }
     fn capacity(&self) -> usize {
-        self.buffer.len()
+        self.buffer.0.len()
     }
     fn as_mut_ptr(&mut self) -> *mut u8 {
-        self.buffer.as_mut_ptr().cast()
+        self.buffer.0.as_mut_ptr().cast()
     }
     /// Returns a `DirBuffIter`.
     fn iter(&self) -> DirBuffIter<'_> {
@@ -669,7 +678,7 @@ impl DirBuff {
 }
 impl AsRef<[u8]> for DirBuff {
     fn as_ref(&self) -> &[u8] {
-        &self.buffer
+        &self.buffer.0
     }
 }
 
@@ -697,9 +706,12 @@ impl<'a> Iterator for DirBuffIter<'a> {
         // used to get the file name slice.
         let (name, is_directory, next_entry) = unsafe {
             let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
+            // Guaranteed to be aligned in documentation for
+            // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+            assert!(info.is_aligned());
             let next_entry = (*info).NextEntryOffset as usize;
             let name = crate::slice::from_raw_parts(
-                (*info).FileName.as_ptr().cast::<u16>(),
+                ptr::addr_of!((*info).FileName).cast::<u16>(),
                 (*info).FileNameLength as usize / size_of::<u16>(),
             );
             let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -1337,9 +1349,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     let h = f.as_inner().as_raw_handle();
 
     unsafe {
-        let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-        let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
-        let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
+        let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let data_ptr = data.0.as_mut_ptr();
+        let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
+        let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
         let mut i = 0;
         // FIXME: this conversion is very hacky
         let v = br"\??\";
@@ -1359,7 +1372,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
         cvt(c::DeviceIoControl(
             h as *mut _,
             c::FSCTL_SET_REPARSE_POINT,
-            data.as_ptr() as *mut _,
+            data_ptr.cast(),
             (*db).ReparseDataLength + 8,
             ptr::null_mut(),
             0,
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index a9846a48488..340cae4066b 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -329,3 +329,11 @@ pub fn abort_internal() -> ! {
     }
     crate::intrinsics::abort();
 }
+
+/// Align the inner value to 8 bytes.
+///
+/// This is enough for almost all of the buffers we're likely to work with in
+/// the Windows APIs we use.
+#[repr(C, align(8))]
+#[derive(Copy, Clone)]
+pub(crate) struct Align8<T: ?Sized>(pub T);