Skip to content

Commit 2b118f5

Browse files
committed
Implemented FLS Windows functions
1 parent 751fca0 commit 2b118f5

File tree

4 files changed

+103
-5
lines changed

4 files changed

+103
-5
lines changed

src/shims/tls.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ enum TlsDtorsStatePriv<'tcx> {
221221
PthreadDtors(RunningDtorState),
222222
/// For Windows Dtors, we store the list of functions that we still have to call.
223223
/// These are functions from the magic `.CRT$XLB` linker section.
224-
WindowsDtors(Vec<ImmTy<'tcx>>),
224+
WindowsDtors(RunningDtorState, Vec<ImmTy<'tcx>>),
225225
Done,
226226
}
227227

@@ -249,7 +249,7 @@ impl<'tcx> TlsDtorsState<'tcx> {
249249
// Determine which destructors to run.
250250
let dtors = this.lookup_windows_tls_dtors()?;
251251
// And move to the next state, that runs them.
252-
break 'new_state WindowsDtors(dtors);
252+
break 'new_state WindowsDtors(Default::default(), dtors);
253253
}
254254
_ => {
255255
// No TLS dtor support.
@@ -271,7 +271,13 @@ impl<'tcx> TlsDtorsState<'tcx> {
271271
Poll::Ready(()) => break 'new_state Done,
272272
}
273273
}
274-
WindowsDtors(dtors) => {
274+
WindowsDtors(state, dtors) => {
275+
// Fls destructors are scheduled before the tls callback, similar to pthread dtors.
276+
match this.schedule_next_windows_fls_dtor(state)? {
277+
Poll::Pending => return interp_ok(Poll::Pending), // just keep going
278+
Poll::Ready(()) => {}
279+
}
280+
275281
if let Some(dtor) = dtors.pop() {
276282
this.schedule_windows_tls_dtor(dtor)?;
277283
return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter)
@@ -359,6 +365,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
359365
fn schedule_next_pthread_tls_dtor(
360366
&mut self,
361367
state: &mut RunningDtorState,
368+
) -> InterpResult<'tcx, Poll<()>> {
369+
self.schedule_next_tls_dtor_callback(state, ExternAbi::C { unwind: false })
370+
}
371+
372+
/// Schedule a TLS destructor. Returns `true` if found
373+
/// a destructor to schedule, and `false` otherwise.
374+
fn schedule_next_tls_dtor_callback(
375+
&mut self,
376+
state: &mut RunningDtorState,
377+
caller_abi: ExternAbi,
362378
) -> InterpResult<'tcx, Poll<()>> {
363379
let this = self.eval_context_mut();
364380
let active_thread = this.active_thread();
@@ -379,7 +395,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379395

380396
this.call_function(
381397
instance,
382-
ExternAbi::C { unwind: false },
398+
caller_abi,
383399
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
384400
None,
385401
ReturnContinuation::Stop { cleanup: true },
@@ -390,4 +406,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
390406

391407
interp_ok(Poll::Ready(()))
392408
}
409+
410+
/// Schedule a Windows FLS destructor. Returns `true` if found
411+
/// a destructor to schedule, and `false` otherwise.
412+
fn schedule_next_windows_fls_dtor(
413+
&mut self,
414+
state: &mut RunningDtorState,
415+
) -> InterpResult<'tcx, Poll<()>> {
416+
self.schedule_next_tls_dtor_callback(state, ExternAbi::System { unwind: false })
417+
}
393418
}

src/shims/windows/foreign_items.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,48 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
476476
this.write_int(1, dest)?;
477477
}
478478

479+
// Fiber-local storage - similar to TLS but supports destructors.
480+
"FlsAlloc" => {
481+
// Create key and return it.
482+
let [dtor] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
483+
let dtor = this.read_pointer(dtor)?;
484+
485+
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
486+
let dtor = if !this.ptr_is_null(dtor)? {
487+
Some(this.get_ptr_fn(dtor)?.as_instance()?)
488+
} else {
489+
None
490+
};
491+
492+
let key = this.machine.tls.create_tls_key(dtor, dest.layout.size)?;
493+
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
494+
}
495+
"FlsGetValue" => {
496+
let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
497+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
498+
let active_thread = this.active_thread();
499+
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
500+
this.write_scalar(ptr, dest)?;
501+
}
502+
"FlsSetValue" => {
503+
let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
504+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
505+
let active_thread = this.active_thread();
506+
let new_data = this.read_scalar(new_ptr)?;
507+
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
508+
509+
// Return success (`1`).
510+
this.write_int(1, dest)?;
511+
}
512+
"FlsFree" => {
513+
let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
514+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
515+
this.machine.tls.delete_tls_key(key)?;
516+
517+
// Return success (`1`).
518+
this.write_int(1, dest)?;
519+
}
520+
479521
// Access to command-line arguments
480522
"GetCommandLineW" => {
481523
let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;

tests/pass/tls/windows-tls.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
11
//@only-target: windows # this directly tests windows-only functions
22

33
use std::ffi::c_void;
4-
use std::ptr;
4+
use std::{ptr, thread};
55

66
extern "system" {
77
fn TlsAlloc() -> u32;
88
fn TlsSetValue(key: u32, val: *mut c_void) -> bool;
99
fn TlsGetValue(key: u32) -> *mut c_void;
1010
fn TlsFree(key: u32) -> bool;
11+
12+
fn FlsAlloc(lpcallback: Option<unsafe extern "system" fn(lpflsdata: *mut c_void)>) -> u32;
13+
fn FlsSetValue(key: u32, val: *mut c_void) -> bool;
14+
fn FlsGetValue(key: u32) -> *mut c_void;
15+
fn FlsFree(key: u32) -> bool;
1116
}
1217

1318
fn main() {
1419
let key = unsafe { TlsAlloc() };
1520
assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) });
1621
assert_eq!(unsafe { TlsGetValue(key).addr() }, 1);
1722
assert!(unsafe { TlsFree(key) });
23+
24+
extern "system" fn dtor1(val: *mut c_void) {
25+
assert_eq!(val.addr(), 1);
26+
println!("dtor1");
27+
}
28+
29+
extern "system" fn dtor2(val: *mut c_void) {
30+
assert_eq!(val.addr(), 1);
31+
println!("dtor2");
32+
}
33+
34+
thread::spawn(|| {
35+
let fls_key_1 = unsafe { FlsAlloc(Some(dtor1)) };
36+
assert!(unsafe { FlsSetValue(fls_key_1, ptr::without_provenance_mut(1)) });
37+
assert_eq!(unsafe { FlsGetValue(fls_key_1).addr() }, 1);
38+
assert!(unsafe { FlsFree(fls_key_1) });
39+
40+
let fls_key_2 = unsafe { FlsAlloc(Some(dtor2)) };
41+
assert!(unsafe { FlsSetValue(fls_key_2, ptr::without_provenance_mut(1)) });
42+
assert_eq!(unsafe { FlsGetValue(fls_key_2).addr() }, 1);
43+
println!("exiting thread");
44+
})
45+
.join()
46+
.unwrap();
1847
}

tests/pass/tls/windows-tls.stdout

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
exiting thread
2+
dtor2

0 commit comments

Comments
 (0)