Skip to content

Commit 536f2bb

Browse files
committed
Implemented FLS Windows functions
1 parent 9d44da0 commit 536f2bb

File tree

4 files changed

+106
-5
lines changed

4 files changed

+106
-5
lines changed

src/shims/tls.rs

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

@@ -251,7 +251,7 @@ impl<'tcx> TlsDtorsState<'tcx> {
251251
// Determine which destructors to run.
252252
let dtors = this.lookup_windows_tls_dtors()?;
253253
// And move to the next state, that runs them.
254-
break 'new_state WindowsDtors(dtors);
254+
break 'new_state WindowsDtors(Default::default(), dtors);
255255
}
256256
_ => {
257257
// No TLS dtor support.
@@ -273,7 +273,13 @@ impl<'tcx> TlsDtorsState<'tcx> {
273273
Poll::Ready(()) => break 'new_state Done,
274274
}
275275
}
276-
WindowsDtors(dtors) => {
276+
WindowsDtors(state, dtors) => {
277+
// Fls destructors are scheduled before the tls callback, similar to pthread dtors.
278+
match this.schedule_next_windows_fls_dtor(state)? {
279+
Poll::Pending => return interp_ok(Poll::Pending), // just keep going
280+
Poll::Ready(()) => {}
281+
}
282+
277283
if let Some((dtor, span)) = dtors.pop() {
278284
this.schedule_windows_tls_dtor(dtor, span)?;
279285
return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter)
@@ -361,6 +367,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
361367
fn schedule_next_pthread_tls_dtor(
362368
&mut self,
363369
state: &mut RunningDtorState,
370+
) -> InterpResult<'tcx, Poll<()>> {
371+
self.schedule_next_tls_dtor_callback(state, ExternAbi::C { unwind: false })
372+
}
373+
374+
/// Schedule a TLS destructor. Returns `true` if found
375+
/// a destructor to schedule, and `false` otherwise.
376+
fn schedule_next_tls_dtor_callback(
377+
&mut self,
378+
state: &mut RunningDtorState,
379+
caller_abi: ExternAbi,
364380
) -> InterpResult<'tcx, Poll<()>> {
365381
let this = self.eval_context_mut();
366382
let active_thread = this.active_thread();
@@ -381,7 +397,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
381397

382398
this.call_thread_root_function(
383399
instance,
384-
ExternAbi::C { unwind: false },
400+
caller_abi,
385401
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
386402
None,
387403
span,
@@ -392,4 +408,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
392408

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

src/shims/windows/foreign_items.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
487487
this.write_int(1, dest)?;
488488
}
489489

490+
// Fiber-local storage - similar to TLS but supports destructors.
491+
"FlsAlloc" => {
492+
// Create key and return it.
493+
let [dtor] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
494+
let dtor = this.read_pointer(dtor)?;
495+
496+
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
497+
let dtor = if !this.ptr_is_null(dtor)? {
498+
Some((
499+
this.get_ptr_fn(dtor)?.as_instance()?,
500+
this.machine.current_user_relevant_span(),
501+
))
502+
} else {
503+
None
504+
};
505+
506+
let key = this.machine.tls.create_tls_key(dtor, dest.layout.size)?;
507+
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
508+
}
509+
"FlsGetValue" => {
510+
let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
511+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
512+
let active_thread = this.active_thread();
513+
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
514+
this.write_scalar(ptr, dest)?;
515+
}
516+
"FlsSetValue" => {
517+
let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
518+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
519+
let active_thread = this.active_thread();
520+
let new_data = this.read_scalar(new_ptr)?;
521+
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
522+
523+
// Return success (`1`).
524+
this.write_int(1, dest)?;
525+
}
526+
"FlsFree" => {
527+
let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
528+
let key = u128::from(this.read_scalar(key)?.to_u32()?);
529+
this.machine.tls.delete_tls_key(key)?;
530+
531+
// Return success (`1`).
532+
this.write_int(1, dest)?;
533+
}
534+
490535
// Access to command-line arguments
491536
"GetCommandLineW" => {
492537
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)