Skip to content

Commit 8e92969

Browse files
committed
implement Windows SRWLock shims
1 parent dca00ab commit 8e92969

File tree

4 files changed

+193
-6
lines changed

4 files changed

+193
-6
lines changed

src/shims/tls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
230230
fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> {
231231
let this = self.eval_context_mut();
232232
let active_thread = this.get_active_thread();
233-
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
233+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
234234
// Windows has a special magic linker section that is run on certain events.
235235
// Instead of searching for that section and supporting arbitrary hooks in there
236236
// (that would be basically https://github.com/rust-lang/miri/issues/450),

src/shims/windows/dlsym.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ use rustc_middle::mir;
22

33
use crate::*;
44
use helpers::check_arg_count;
5+
use shims::windows::sync::EvalContextExt as _;
56

67
#[derive(Debug, Copy, Clone)]
78
pub enum Dlsym {
89
AcquireSRWLockExclusive,
10+
ReleaseSRWLockExclusive,
11+
TryAcquireSRWLockExclusive,
912
AcquireSRWLockShared,
13+
ReleaseSRWLockShared,
14+
TryAcquireSRWLockShared,
1015
}
1116

1217
impl Dlsym {
@@ -15,7 +20,11 @@ impl Dlsym {
1520
pub fn from_str(name: &str) -> InterpResult<'static, Option<Dlsym>> {
1621
Ok(match name {
1722
"AcquireSRWLockExclusive" => Some(Dlsym::AcquireSRWLockExclusive),
23+
"ReleaseSRWLockExclusive" => Some(Dlsym::ReleaseSRWLockExclusive),
24+
"TryAcquireSRWLockExclusive" => Some(Dlsym::TryAcquireSRWLockExclusive),
1825
"AcquireSRWLockShared" => Some(Dlsym::AcquireSRWLockShared),
26+
"ReleaseSRWLockShared" => Some(Dlsym::ReleaseSRWLockShared),
27+
"TryAcquireSRWLockShared" => Some(Dlsym::TryAcquireSRWLockShared),
1928
"SetThreadStackGuarantee" => None,
2029
"GetSystemTimePreciseAsFileTime" => None,
2130
_ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
@@ -38,13 +47,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3847
match dlsym {
3948
Dlsym::AcquireSRWLockExclusive => {
4049
let &[ptr] = check_arg_count(args)?;
41-
let lock = this.deref_operand(ptr)?; // points to ptr-sized data
42-
throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented");
50+
this.AcquireSRWLockExclusive(ptr)?;
51+
}
52+
Dlsym::ReleaseSRWLockExclusive => {
53+
let &[ptr] = check_arg_count(args)?;
54+
this.ReleaseSRWLockExclusive(ptr)?;
55+
}
56+
Dlsym::TryAcquireSRWLockExclusive => {
57+
let &[ptr] = check_arg_count(args)?;
58+
let ret = this.TryAcquireSRWLockExclusive(ptr)?;
59+
this.write_scalar(Scalar::from_u8(ret), dest)?;
4360
}
4461
Dlsym::AcquireSRWLockShared => {
4562
let &[ptr] = check_arg_count(args)?;
46-
let lock = this.deref_operand(ptr)?; // points to ptr-sized data
47-
throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented");
63+
this.AcquireSRWLockShared(ptr)?;
64+
}
65+
Dlsym::ReleaseSRWLockShared => {
66+
let &[ptr] = check_arg_count(args)?;
67+
this.ReleaseSRWLockShared(ptr)?;
68+
}
69+
Dlsym::TryAcquireSRWLockShared => {
70+
let &[ptr] = check_arg_count(args)?;
71+
let ret = this.TryAcquireSRWLockShared(ptr)?;
72+
this.write_scalar(Scalar::from_u8(ret), dest)?;
4873
}
4974
}
5075

src/shims/windows/foreign_items.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
2121
// HANDLE = isize
2222
// DWORD = ULONG = u32
2323
// BOOL = i32
24+
// BOOLEAN = u8
2425
match link_name {
2526
// Environment related shims
2627
"GetEnvironmentVariableW" => {
@@ -301,7 +302,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
301302
#[allow(non_snake_case)]
302303
let &[_lpCriticalSection] = check_arg_count(args)?;
303304
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
304-
// There is only one thread, so this always succeeds and returns TRUE
305+
// There is only one thread, so this always succeeds and returns TRUE.
305306
this.write_scalar(Scalar::from_i32(1), dest)?;
306307
}
307308

src/shims/windows/sync.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use rustc_target::abi::Size;
2+
3+
use crate::*;
4+
5+
// Locks are pointer-sized pieces of data, initialized to 0.
6+
// We use them to count readers, with usize::MAX representing the write-locked state.
7+
8+
fn deref_lock<'mir, 'tcx: 'mir>(
9+
ecx: &mut MiriEvalContext<'mir, 'tcx>,
10+
lock_op: OpTy<'tcx, Tag>,
11+
) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> {
12+
// `lock` is a pointer to `void*`; cast it to a pointer to `usize`.
13+
let lock = ecx.deref_operand(lock_op)?;
14+
let usize = ecx.machine.layouts.usize;
15+
assert_eq!(lock.layout.size, usize.size);
16+
Ok(lock.offset(Size::ZERO, MemPlaceMeta::None, usize, ecx)?)
17+
}
18+
19+
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
20+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
21+
#[allow(non_snake_case)]
22+
fn AcquireSRWLockExclusive(
23+
&mut self,
24+
lock_op: OpTy<'tcx, Tag>,
25+
) -> InterpResult<'tcx> {
26+
let this = self.eval_context_mut();
27+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
28+
29+
let lock = deref_lock(this, lock_op)?;
30+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
31+
if lock_val == 0 {
32+
// Currently not locked. Lock it.
33+
let new_val = Scalar::from_machine_usize(this.machine_usize_max(), this);
34+
this.write_scalar(new_val, lock.into())?;
35+
} else {
36+
// Lock is already held. This is a deadlock.
37+
throw_machine_stop!(TerminationInfo::Deadlock);
38+
}
39+
40+
Ok(())
41+
}
42+
43+
#[allow(non_snake_case)]
44+
fn TryAcquireSRWLockExclusive(
45+
&mut self,
46+
lock_op: OpTy<'tcx, Tag>,
47+
) -> InterpResult<'tcx, u8> {
48+
let this = self.eval_context_mut();
49+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
50+
51+
let lock = deref_lock(this, lock_op)?;
52+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
53+
if lock_val == 0 {
54+
// Currently not locked. Lock it.
55+
let new_val = this.machine_usize_max();
56+
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
57+
Ok(1)
58+
} else {
59+
// Lock is already held.
60+
Ok(0)
61+
}
62+
}
63+
64+
#[allow(non_snake_case)]
65+
fn ReleaseSRWLockExclusive(
66+
&mut self,
67+
lock_op: OpTy<'tcx, Tag>,
68+
) -> InterpResult<'tcx> {
69+
let this = self.eval_context_mut();
70+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
71+
72+
let lock = deref_lock(this, lock_op)?;
73+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
74+
if lock_val == this.machine_usize_max() {
75+
// Currently locked. Unlock it.
76+
let new_val = 0;
77+
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
78+
} else {
79+
// Lock is not locked.
80+
throw_ub_format!("calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked");
81+
}
82+
83+
Ok(())
84+
}
85+
86+
#[allow(non_snake_case)]
87+
fn AcquireSRWLockShared(
88+
&mut self,
89+
lock_op: OpTy<'tcx, Tag>,
90+
) -> InterpResult<'tcx> {
91+
let this = self.eval_context_mut();
92+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
93+
94+
let lock = deref_lock(this, lock_op)?;
95+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
96+
if lock_val == this.machine_usize_max() {
97+
// Currently write locked. This is a deadlock.
98+
throw_machine_stop!(TerminationInfo::Deadlock);
99+
} else {
100+
// Bump up read counter (cannot overflow as we just checkd against usize::MAX);
101+
let new_val = lock_val+1;
102+
// Make sure this does not reach the "write locked" flag.
103+
if new_val == this.machine_usize_max() {
104+
throw_unsup_format!("SRWLock read-acquired too many times");
105+
}
106+
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
107+
}
108+
109+
Ok(())
110+
}
111+
112+
#[allow(non_snake_case)]
113+
fn TryAcquireSRWLockShared(
114+
&mut self,
115+
lock_op: OpTy<'tcx, Tag>,
116+
) -> InterpResult<'tcx, u8> {
117+
let this = self.eval_context_mut();
118+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
119+
120+
let lock = deref_lock(this, lock_op)?;
121+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
122+
if lock_val == this.machine_usize_max() {
123+
// Currently write locked.
124+
Ok(0)
125+
} else {
126+
// Bump up read counter (cannot overflow as we just checkd against usize::MAX);
127+
let new_val = lock_val+1;
128+
// Make sure this does not reach the "write locked" flag.
129+
if new_val == this.machine_usize_max() {
130+
throw_unsup_format!("SRWLock read-acquired too many times");
131+
}
132+
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
133+
Ok(1)
134+
}
135+
}
136+
137+
#[allow(non_snake_case)]
138+
fn ReleaseSRWLockShared(
139+
&mut self,
140+
lock_op: OpTy<'tcx, Tag>,
141+
) -> InterpResult<'tcx> {
142+
let this = self.eval_context_mut();
143+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
144+
145+
let lock = deref_lock(this, lock_op)?;
146+
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
147+
if lock_val == this.machine_usize_max() {
148+
// Currently write locked. This is a UB.
149+
throw_ub_format!("calling ReleaseSRWLockShared on write-locked SRWLock");
150+
} else if lock_val == 0 {
151+
// Currently not locked at all.
152+
throw_ub_format!("calling ReleaseSRWLockShared on unlocked SRWLock");
153+
} else {
154+
// Decrement read counter (cannot overflow as we just checkd against 0);
155+
let new_val = lock_val-1;
156+
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
157+
}
158+
159+
Ok(())
160+
}
161+
}

0 commit comments

Comments
 (0)