From 536f2bb979e4143875d3b027eabf05976009588a Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Mon, 10 Nov 2025 12:39:22 -0800 Subject: [PATCH] Implemented FLS Windows functions --- src/shims/tls.rs | 33 +++++++++++++++++++--- src/shims/windows/foreign_items.rs | 45 ++++++++++++++++++++++++++++++ tests/pass/tls/windows-tls.rs | 31 +++++++++++++++++++- tests/pass/tls/windows-tls.stdout | 2 ++ 4 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 tests/pass/tls/windows-tls.stdout diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 3ecd9b1ead..cdf7de5701 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -223,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> { PthreadDtors(RunningDtorState), /// For Windows Dtors, we store the list of functions that we still have to call. /// These are functions from the magic `.CRT$XLB` linker section. - WindowsDtors(Vec<(ImmTy<'tcx>, Span)>), + WindowsDtors(RunningDtorState, Vec<(ImmTy<'tcx>, Span)>), Done, } @@ -251,7 +251,7 @@ impl<'tcx> TlsDtorsState<'tcx> { // Determine which destructors to run. let dtors = this.lookup_windows_tls_dtors()?; // And move to the next state, that runs them. - break 'new_state WindowsDtors(dtors); + break 'new_state WindowsDtors(Default::default(), dtors); } _ => { // No TLS dtor support. @@ -273,7 +273,13 @@ impl<'tcx> TlsDtorsState<'tcx> { Poll::Ready(()) => break 'new_state Done, } } - WindowsDtors(dtors) => { + WindowsDtors(state, dtors) => { + // Fls destructors are scheduled before the tls callback, similar to pthread dtors. + match this.schedule_next_windows_fls_dtor(state)? { + Poll::Pending => return interp_ok(Poll::Pending), // just keep going + Poll::Ready(()) => {} + } + if let Some((dtor, span)) = dtors.pop() { this.schedule_windows_tls_dtor(dtor, span)?; return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) @@ -361,6 +367,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn schedule_next_pthread_tls_dtor( &mut self, state: &mut RunningDtorState, + ) -> InterpResult<'tcx, Poll<()>> { + self.schedule_next_tls_dtor_callback(state, ExternAbi::C { unwind: false }) + } + + /// Schedule a TLS destructor. Returns `true` if found + /// a destructor to schedule, and `false` otherwise. + fn schedule_next_tls_dtor_callback( + &mut self, + state: &mut RunningDtorState, + caller_abi: ExternAbi, ) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); let active_thread = this.active_thread(); @@ -381,7 +397,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_thread_root_function( instance, - ExternAbi::C { unwind: false }, + caller_abi, &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], None, span, @@ -392,4 +408,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Poll::Ready(())) } + + /// Schedule a Windows FLS destructor. Returns `true` if found + /// a destructor to schedule, and `false` otherwise. + fn schedule_next_windows_fls_dtor( + &mut self, + state: &mut RunningDtorState, + ) -> InterpResult<'tcx, Poll<()>> { + self.schedule_next_tls_dtor_callback(state, ExternAbi::System { unwind: false }) + } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 3bc52dddfe..bcc305ce87 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -487,6 +487,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } + // Fiber-local storage - similar to TLS but supports destructors. + "FlsAlloc" => { + // Create key and return it. + let [dtor] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let dtor = this.read_pointer(dtor)?; + + // Extract the function type out of the signature (that seems easier than constructing it ourselves). + let dtor = if !this.ptr_is_null(dtor)? { + Some(( + this.get_ptr_fn(dtor)?.as_instance()?, + this.machine.current_user_relevant_span(), + )) + } else { + None + }; + + let key = this.machine.tls.create_tls_key(dtor, dest.layout.size)?; + this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; + } + "FlsGetValue" => { + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let key = u128::from(this.read_scalar(key)?.to_u32()?); + let active_thread = this.active_thread(); + let ptr = this.machine.tls.load_tls(key, active_thread, this)?; + this.write_scalar(ptr, dest)?; + } + "FlsSetValue" => { + let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let key = u128::from(this.read_scalar(key)?.to_u32()?); + let active_thread = this.active_thread(); + let new_data = this.read_scalar(new_ptr)?; + this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?; + + // Return success (`1`). + this.write_int(1, dest)?; + } + "FlsFree" => { + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let key = u128::from(this.read_scalar(key)?.to_u32()?); + this.machine.tls.delete_tls_key(key)?; + + // Return success (`1`). + this.write_int(1, dest)?; + } + // Access to command-line arguments "GetCommandLineW" => { let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; diff --git a/tests/pass/tls/windows-tls.rs b/tests/pass/tls/windows-tls.rs index 58131be190..1847de64a5 100644 --- a/tests/pass/tls/windows-tls.rs +++ b/tests/pass/tls/windows-tls.rs @@ -1,13 +1,18 @@ //@only-target: windows # this directly tests windows-only functions use std::ffi::c_void; -use std::ptr; +use std::{ptr, thread}; extern "system" { fn TlsAlloc() -> u32; fn TlsSetValue(key: u32, val: *mut c_void) -> bool; fn TlsGetValue(key: u32) -> *mut c_void; fn TlsFree(key: u32) -> bool; + + fn FlsAlloc(lpcallback: Option) -> u32; + fn FlsSetValue(key: u32, val: *mut c_void) -> bool; + fn FlsGetValue(key: u32) -> *mut c_void; + fn FlsFree(key: u32) -> bool; } fn main() { @@ -15,4 +20,28 @@ fn main() { assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) }); assert_eq!(unsafe { TlsGetValue(key).addr() }, 1); assert!(unsafe { TlsFree(key) }); + + extern "system" fn dtor1(val: *mut c_void) { + assert_eq!(val.addr(), 1); + println!("dtor1"); + } + + extern "system" fn dtor2(val: *mut c_void) { + assert_eq!(val.addr(), 1); + println!("dtor2"); + } + + thread::spawn(|| { + let fls_key_1 = unsafe { FlsAlloc(Some(dtor1)) }; + assert!(unsafe { FlsSetValue(fls_key_1, ptr::without_provenance_mut(1)) }); + assert_eq!(unsafe { FlsGetValue(fls_key_1).addr() }, 1); + assert!(unsafe { FlsFree(fls_key_1) }); + + let fls_key_2 = unsafe { FlsAlloc(Some(dtor2)) }; + assert!(unsafe { FlsSetValue(fls_key_2, ptr::without_provenance_mut(1)) }); + assert_eq!(unsafe { FlsGetValue(fls_key_2).addr() }, 1); + println!("exiting thread"); + }) + .join() + .unwrap(); } diff --git a/tests/pass/tls/windows-tls.stdout b/tests/pass/tls/windows-tls.stdout new file mode 100644 index 0000000000..f3fedbb8eb --- /dev/null +++ b/tests/pass/tls/windows-tls.stdout @@ -0,0 +1,2 @@ +exiting thread +dtor2