Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/shims/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Expand All @@ -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,
Expand All @@ -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 })
}
}
45 changes: 45 additions & 0 deletions src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
31 changes: 30 additions & 1 deletion tests/pass/tls/windows-tls.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,47 @@
//@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<unsafe extern "system" fn(lpflsdata: *mut c_void)>) -> u32;
fn FlsSetValue(key: u32, val: *mut c_void) -> bool;
fn FlsGetValue(key: u32) -> *mut c_void;
fn FlsFree(key: u32) -> bool;
}

fn main() {
let key = unsafe { TlsAlloc() };
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();
}
2 changes: 2 additions & 0 deletions tests/pass/tls/windows-tls.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
exiting thread
dtor2