implement Windows SRWLock shims
This commit is contained in:
parent
dca00ab85e
commit
8e92969948
4 changed files with 193 additions and 6 deletions
|
|
@ -230,7 +230,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let active_thread = this.get_active_thread();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
// Windows has a special magic linker section that is run on certain events.
|
||||
// Instead of searching for that section and supporting arbitrary hooks in there
|
||||
// (that would be basically https://github.com/rust-lang/miri/issues/450),
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ use rustc_middle::mir;
|
|||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
use shims::windows::sync::EvalContextExt as _;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Dlsym {
|
||||
AcquireSRWLockExclusive,
|
||||
ReleaseSRWLockExclusive,
|
||||
TryAcquireSRWLockExclusive,
|
||||
AcquireSRWLockShared,
|
||||
ReleaseSRWLockShared,
|
||||
TryAcquireSRWLockShared,
|
||||
}
|
||||
|
||||
impl Dlsym {
|
||||
|
|
@ -15,7 +20,11 @@ impl Dlsym {
|
|||
pub fn from_str(name: &str) -> InterpResult<'static, Option<Dlsym>> {
|
||||
Ok(match name {
|
||||
"AcquireSRWLockExclusive" => Some(Dlsym::AcquireSRWLockExclusive),
|
||||
"ReleaseSRWLockExclusive" => Some(Dlsym::ReleaseSRWLockExclusive),
|
||||
"TryAcquireSRWLockExclusive" => Some(Dlsym::TryAcquireSRWLockExclusive),
|
||||
"AcquireSRWLockShared" => Some(Dlsym::AcquireSRWLockShared),
|
||||
"ReleaseSRWLockShared" => Some(Dlsym::ReleaseSRWLockShared),
|
||||
"TryAcquireSRWLockShared" => Some(Dlsym::TryAcquireSRWLockShared),
|
||||
"SetThreadStackGuarantee" => None,
|
||||
"GetSystemTimePreciseAsFileTime" => None,
|
||||
_ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
|
||||
|
|
@ -38,13 +47,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match dlsym {
|
||||
Dlsym::AcquireSRWLockExclusive => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let lock = this.deref_operand(ptr)?; // points to ptr-sized data
|
||||
throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented");
|
||||
this.AcquireSRWLockExclusive(ptr)?;
|
||||
}
|
||||
Dlsym::ReleaseSRWLockExclusive => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
this.ReleaseSRWLockExclusive(ptr)?;
|
||||
}
|
||||
Dlsym::TryAcquireSRWLockExclusive => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let ret = this.TryAcquireSRWLockExclusive(ptr)?;
|
||||
this.write_scalar(Scalar::from_u8(ret), dest)?;
|
||||
}
|
||||
Dlsym::AcquireSRWLockShared => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let lock = this.deref_operand(ptr)?; // points to ptr-sized data
|
||||
throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented");
|
||||
this.AcquireSRWLockShared(ptr)?;
|
||||
}
|
||||
Dlsym::ReleaseSRWLockShared => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
this.ReleaseSRWLockShared(ptr)?;
|
||||
}
|
||||
Dlsym::TryAcquireSRWLockShared => {
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let ret = this.TryAcquireSRWLockShared(ptr)?;
|
||||
this.write_scalar(Scalar::from_u8(ret), dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// HANDLE = isize
|
||||
// DWORD = ULONG = u32
|
||||
// BOOL = i32
|
||||
// BOOLEAN = u8
|
||||
match link_name {
|
||||
// Environment related shims
|
||||
"GetEnvironmentVariableW" => {
|
||||
|
|
@ -301,7 +302,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
#[allow(non_snake_case)]
|
||||
let &[_lpCriticalSection] = check_arg_count(args)?;
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
|
||||
// There is only one thread, so this always succeeds and returns TRUE
|
||||
// There is only one thread, so this always succeeds and returns TRUE.
|
||||
this.write_scalar(Scalar::from_i32(1), dest)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
use rustc_target::abi::Size;
|
||||
|
||||
use crate::*;
|
||||
|
||||
// Locks are pointer-sized pieces of data, initialized to 0.
|
||||
// We use them to count readers, with usize::MAX representing the write-locked state.
|
||||
|
||||
fn deref_lock<'mir, 'tcx: 'mir>(
|
||||
ecx: &mut MiriEvalContext<'mir, 'tcx>,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> {
|
||||
// `lock` is a pointer to `void*`; cast it to a pointer to `usize`.
|
||||
let lock = ecx.deref_operand(lock_op)?;
|
||||
let usize = ecx.machine.layouts.usize;
|
||||
assert_eq!(lock.layout.size, usize.size);
|
||||
Ok(lock.offset(Size::ZERO, MemPlaceMeta::None, usize, ecx)?)
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
#[allow(non_snake_case)]
|
||||
fn AcquireSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == 0 {
|
||||
// Currently not locked. Lock it.
|
||||
let new_val = Scalar::from_machine_usize(this.machine_usize_max(), this);
|
||||
this.write_scalar(new_val, lock.into())?;
|
||||
} else {
|
||||
// Lock is already held. This is a deadlock.
|
||||
throw_machine_stop!(TerminationInfo::Deadlock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn TryAcquireSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, u8> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == 0 {
|
||||
// Currently not locked. Lock it.
|
||||
let new_val = this.machine_usize_max();
|
||||
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
|
||||
Ok(1)
|
||||
} else {
|
||||
// Lock is already held.
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn ReleaseSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == this.machine_usize_max() {
|
||||
// Currently locked. Unlock it.
|
||||
let new_val = 0;
|
||||
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
|
||||
} else {
|
||||
// Lock is not locked.
|
||||
throw_ub_format!("calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn AcquireSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == this.machine_usize_max() {
|
||||
// Currently write locked. This is a deadlock.
|
||||
throw_machine_stop!(TerminationInfo::Deadlock);
|
||||
} else {
|
||||
// Bump up read counter (cannot overflow as we just checkd against usize::MAX);
|
||||
let new_val = lock_val+1;
|
||||
// Make sure this does not reach the "write locked" flag.
|
||||
if new_val == this.machine_usize_max() {
|
||||
throw_unsup_format!("SRWLock read-acquired too many times");
|
||||
}
|
||||
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn TryAcquireSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, u8> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == this.machine_usize_max() {
|
||||
// Currently write locked.
|
||||
Ok(0)
|
||||
} else {
|
||||
// Bump up read counter (cannot overflow as we just checkd against usize::MAX);
|
||||
let new_val = lock_val+1;
|
||||
// Make sure this does not reach the "write locked" flag.
|
||||
if new_val == this.machine_usize_max() {
|
||||
throw_unsup_format!("SRWLock read-acquired too many times");
|
||||
}
|
||||
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn ReleaseSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
|
||||
let lock = deref_lock(this, lock_op)?;
|
||||
let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
|
||||
if lock_val == this.machine_usize_max() {
|
||||
// Currently write locked. This is a UB.
|
||||
throw_ub_format!("calling ReleaseSRWLockShared on write-locked SRWLock");
|
||||
} else if lock_val == 0 {
|
||||
// Currently not locked at all.
|
||||
throw_ub_format!("calling ReleaseSRWLockShared on unlocked SRWLock");
|
||||
} else {
|
||||
// Decrement read counter (cannot overflow as we just checkd against 0);
|
||||
let new_val = lock_val-1;
|
||||
this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue