diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs
index e11ea881282ce4207067d75772d0f139da78e4af..5c9502580b786dfc4e632dbab98284e3382f4679 100644
--- a/src/header/pthread/rwlock.rs
+++ b/src/header/pthread/rwlock.rs
@@ -114,7 +114,7 @@ pub unsafe extern "C" fn pthread_rwlock_destroy(rwlock: *mut pthread_rwlock_t) -
     0
 }
 
-pub(crate) type RlctRwlock = crate::sync::rwlock::Rwlock;
+pub(crate) type RlctRwlock = crate::sync::rwlock::InnerRwLock;
 
 #[derive(Clone, Copy, Default)]
 pub(crate) struct RlctRwlockAttr {
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 86852d3fe751fc49bc5d14698ef2631e26b100ab..8a4132c3c35e86b833f217fcccb7afd625e458c1 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -297,7 +297,7 @@ pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t {
 /// state.
 #[no_mangle]
 pub extern "C" fn drand48() -> c_double {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let mut xsubi = rand48::xsubi_lock();
     *xsubi = params.step(*xsubi);
     xsubi.get_f64()
@@ -330,7 +330,7 @@ pub extern "C" fn ecvt(
 /// state.
 #[no_mangle]
 pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let xsubi_mut: &mut [c_ushort; 3] = slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap();
     let new_xsubi_value = params.step(xsubi_mut.into());
     *xsubi_mut = new_xsubi_value.into();
@@ -521,7 +521,7 @@ pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_
 /// state.
 #[no_mangle]
 pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let xsubi_mut: &mut [c_ushort; 3] = slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap();
     let new_xsubi_value = params.step(xsubi_mut.into());
     *xsubi_mut = new_xsubi_value.into();
@@ -586,7 +586,7 @@ pub extern "C" fn labs(i: c_long) -> c_long {
 #[no_mangle]
 pub unsafe extern "C" fn lcong48(param: *mut c_ushort) {
     let mut xsubi = rand48::xsubi_lock();
-    let mut params = rand48::params_lock();
+    let mut params = rand48::params_mut();
 
     let param_slice = slice::from_raw_parts(param, 7);
 
@@ -643,7 +643,7 @@ pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t {
 /// state.
 #[no_mangle]
 pub extern "C" fn lrand48() -> c_long {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let mut xsubi = rand48::xsubi_lock();
     *xsubi = params.step(*xsubi);
     xsubi.get_u31()
@@ -843,7 +843,7 @@ pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char {
 /// state.
 #[no_mangle]
 pub extern "C" fn mrand48() -> c_long {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let mut xsubi = rand48::xsubi_lock();
     *xsubi = params.step(*xsubi);
     xsubi.get_i32()
@@ -860,7 +860,7 @@ pub extern "C" fn mrand48() -> c_long {
 /// state.
 #[no_mangle]
 pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long {
-    let params = rand48::params_lock();
+    let params = rand48::params();
     let xsubi_mut: &mut [c_ushort; 3] = slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap();
     let new_xsubi_value = params.step(xsubi_mut.into());
     *xsubi_mut = new_xsubi_value.into();
@@ -1196,7 +1196,7 @@ pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
 pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort {
     static mut BUFFER: [c_ushort; 3] = [0; 3];
 
-    let mut params = rand48::params_lock();
+    let mut params = rand48::params_mut();
     let mut xsubi = rand48::xsubi_lock();
 
     let seed16v_ref: &[c_ushort; 3] = slice::from_raw_parts(seed16v, 3).try_into().unwrap();
@@ -1296,7 +1296,7 @@ pub unsafe extern "C" fn srand(seed: c_uint) {
 /// state.
 #[no_mangle]
 pub extern "C" fn srand48(seedval: c_long) {
-    let mut params = rand48::params_lock();
+    let mut params = rand48::params_mut();
     let mut xsubi = rand48::xsubi_lock();
 
     params.reset();
diff --git a/src/header/stdlib/rand48.rs b/src/header/stdlib/rand48.rs
index 6d78d6c0cc2a0a4598a6a67340cd9cec879d4447..5b272bb52c1c018968fb740c4b82af72daafcb92 100644
--- a/src/header/stdlib/rand48.rs
+++ b/src/header/stdlib/rand48.rs
@@ -2,7 +2,10 @@
 
 use crate::{
     platform::types::*,
-    sync::{Mutex, MutexGuard},
+    sync::{
+        rwlock::{self, RwLock},
+        Mutex, MutexGuard,
+    },
 };
 
 /// A 48-bit integer, used for the 48-bit arithmetic in these functions.
@@ -119,13 +122,19 @@ impl Params {
     }
 }
 
-// TODO: consider using rwlock instead of mutex for more fine-grained access
-/// Immediately get the global Params lock, or panic if unsuccessful.
-pub fn params_lock<'a>() -> MutexGuard<'a, Params> {
-    static PARAMS: Mutex<Params> = Mutex::<Params>::new(Params::new());
+static PARAMS: RwLock<Params> = RwLock::<Params>::new(Params::new());
 
+/// Immediately get the global [`Params`] lock for reading, or panic if unsuccessful.
+pub fn params<'a>() -> rwlock::ReadGuard<'a, Params> {
     PARAMS
-        .try_lock()
+        .try_read()
+        .expect("unable to acquire LCG parameter lock")
+}
+
+/// Immediately get the global [`Params`] lock for writing, or panic if unsuccessful.
+pub fn params_mut<'a>() -> rwlock::WriteGuard<'a, Params> {
+    PARAMS
+        .try_write()
         .expect("unable to acquire LCG parameter lock")
 }
 
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
index 1ac0b59e9219f3626b78f549bfa88a45960e5289..f704751b3b1df97606fab1a23fc26cab8f3b5c4a 100644
--- a/src/ld_so/linker.rs
+++ b/src/ld_so/linker.rs
@@ -29,7 +29,7 @@ use crate::{
         types::{c_char, c_int, c_uint, c_void},
         Pal, Sys,
     },
-    sync::Mutex,
+    sync::rwlock::RwLock,
 };
 
 use super::{
@@ -49,7 +49,7 @@ use super::{
 // do better errors than just goblin::Error::Malformed
 
 // TODO: rwlock?
-static GLOBAL_SCOPE: Mutex<Scope> = Mutex::new(Scope::global());
+static GLOBAL_SCOPE: RwLock<Scope> = RwLock::new(Scope::global());
 
 /// Same as [`crate::fs::File`], but does not touch [`crate::platform::ERRNO`] as the dynamic
 /// linker does not have thread-local storage.
@@ -371,7 +371,7 @@ impl Linker {
                             eprintln!("[ld.so]: moving {} into the global scope", obj.name);
                         }
 
-                        let mut global_scope = GLOBAL_SCOPE.lock();
+                        let mut global_scope = GLOBAL_SCOPE.write();
                         obj.scope.move_into(&mut global_scope);
                     }
 
@@ -419,8 +419,8 @@ impl Linker {
         if let Some(handle) = handle.as_ref() {
             &handle.as_ref().scope
         } else {
-            guard = GLOBAL_SCOPE.lock();
-            &*guard
+            guard = GLOBAL_SCOPE.read();
+            &guard
         }
         .get_sym(name)
         .map(|(symbol, _, obj)| {
@@ -454,7 +454,7 @@ impl Linker {
         // objects map.
         if Arc::strong_count(&obj) == 2 {
             // Remove from the global scope.
-            match *GLOBAL_SCOPE.lock() {
+            match *GLOBAL_SCOPE.write() {
                 Scope::Global { ref mut objs } => {
                     objs.retain(|o| !Weak::ptr_eq(o, &Arc::downgrade(&obj)));
                 }
@@ -713,10 +713,10 @@ impl Linker {
         if let Some(dependent) = dependent {
             match scope {
                 ObjectScope::Local => dependent.scope.add(&obj),
-                ObjectScope::Global => GLOBAL_SCOPE.lock().add(&obj),
+                ObjectScope::Global => GLOBAL_SCOPE.write().add(&obj),
             }
         } else if let ObjectScope::Global = scope {
-            GLOBAL_SCOPE.lock().add(&obj);
+            GLOBAL_SCOPE.write().add(&obj);
         }
 
         objects_data.push(data);
@@ -817,7 +817,7 @@ impl Linker {
                             )))?;
 
                     let resolved = GLOBAL_SCOPE
-                        .lock()
+                        .read()
                         .get_sym(name)
                         .or_else(|| obj.scope.get_sym(name))
                         .map(|(sym, _, _)| sym.as_ptr())
@@ -869,7 +869,7 @@ impl Linker {
                     )))?;
 
                 let symbol = GLOBAL_SCOPE
-                    .lock()
+                    .read()
                     .get_sym(name)
                     .or_else(|| obj.scope.get_sym(name))
                     .map(|(sym, _, obj)| (sym, obj.tls_offset));
@@ -1067,7 +1067,7 @@ extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) ->
         )
     };
 
-    let resolved = resolve_sym(name.to_str().unwrap(), &[&GLOBAL_SCOPE.lock(), &obj.scope])
+    let resolved = resolve_sym(name.to_str().unwrap(), &[&GLOBAL_SCOPE.read(), &obj.scope])
         .expect(&format!("symbol '{}' not found", name.to_str().unwrap()))
         .as_ptr();
 
diff --git a/src/lib.rs b/src/lib.rs
index 64cc935b53f17c20542d32b829c2e0f1de167b1a..76fd2b781855cac32aff10d587b3e0f73486d71b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -32,6 +32,7 @@
 #![feature(sync_unsafe_cell)]
 #![feature(thread_local)]
 #![feature(vec_into_raw_parts)]
+#![feature(negative_impls)]
 #![allow(clippy::cast_lossless)]
 #![allow(clippy::cast_ptr_alignment)]
 #![allow(clippy::derive_hash_xor_eq)]
diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs
index 017696ce6b15869d626c9cef28aa2400a5f3ffd6..3c78230fa7241debcbc8bab536a8798fc87c9917 100644
--- a/src/platform/redox/clone.rs
+++ b/src/platform/redox/clone.rs
@@ -7,29 +7,6 @@ use syscall::{
     SetSighandlerData, SIGCONT,
 };
 
-use crate::sync::rwlock::Rwlock;
-
 use redox_rt::{proc::FdGuard, signal::sighandler_function};
 
-pub use redox_rt::proc::*;
-
-static CLONE_LOCK: Rwlock = Rwlock::new(crate::pthread::Pshared::Private);
-
-struct Guard;
-impl Drop for Guard {
-    fn drop(&mut self) {
-        CLONE_LOCK.unlock()
-    }
-}
-
-pub fn rdlock() -> impl Drop {
-    CLONE_LOCK.acquire_read_lock(None);
-
-    Guard
-}
-pub fn wrlock() -> impl Drop {
-    CLONE_LOCK.acquire_write_lock(None);
-
-    Guard
-}
-pub use redox_rt::thread::*;
+pub use redox_rt::{proc::*, thread::*};
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 2c6c2e9b3f965f88919934898afadfdd9bb29683..628dfb734e0a32160bb5da817ef660c58eff80f9 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -34,6 +34,7 @@ use crate::{
         unistd::{F_OK, R_OK, W_OK, X_OK},
     },
     io::{self, prelude::*, BufReader},
+    sync::rwlock::RwLock,
 };
 
 pub use redox_rt::proc::FdGuard;
@@ -75,6 +76,8 @@ macro_rules! path_from_c_str {
 
 use self::{exec::Executable, path::canonicalize};
 
+static CLONE_LOCK: RwLock<()> = RwLock::new(());
+
 /// Redox syscall implementation of the platform abstraction layer.
 pub struct Sys;
 
@@ -260,7 +263,7 @@ impl Pal for Sys {
 
     unsafe fn fork() -> Result<pid_t> {
         // TODO: Find way to avoid lock.
-        let _guard = clone::wrlock();
+        let _guard = CLONE_LOCK.write();
 
         Ok(clone::fork_impl()? as pid_t)
     }
@@ -726,7 +729,7 @@ impl Pal for Sys {
     }
 
     unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid> {
-        let _guard = clone::rdlock();
+        let _guard = CLONE_LOCK.read();
         let res = clone::rlct_clone_impl(stack);
 
         res.map(|mut fd| crate::pthread::OsTid {
diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs
index b787a8e85747f9e531d574e422bbde90b927368b..58c279f26e4d55b1e13283113fd6040a6eeb34a1 100644
--- a/src/sync/rwlock.rs
+++ b/src/sync/rwlock.rs
@@ -1,8 +1,13 @@
-use core::sync::atomic::{AtomicU32, Ordering};
+use core::{
+    cell::UnsafeCell,
+    fmt, ops,
+    ptr::NonNull,
+    sync::atomic::{AtomicU32, Ordering},
+};
 
 use crate::{header::time::timespec, pthread::Pshared};
 
-pub struct Rwlock {
+pub struct InnerRwLock {
     state: AtomicU32,
 }
 // PTHREAD_RWLOCK_INITIALIZER is defined as "all zeroes".
@@ -15,7 +20,7 @@ const EXCLUSIVE: u32 = COUNT_MASK;
 // supporting timeouts.
 // TODO: Add futex ops that use bitmasks.
 
-impl Rwlock {
+impl InnerRwLock {
     pub const fn new(_pshared: Pshared) -> Self {
         Self {
             state: AtomicU32::new(0),
@@ -143,3 +148,135 @@ impl Rwlock {
         }
     }
 }
+
+pub struct RwLock<T: ?Sized> {
+    inner: InnerRwLock,
+    data: UnsafeCell<T>,
+}
+
+unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
+unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
+
+impl<T> RwLock<T> {
+    pub const fn new(val: T) -> Self {
+        Self {
+            inner: InnerRwLock::new(Pshared::Private),
+            data: UnsafeCell::new(val),
+        }
+    }
+}
+
+impl<T: ?Sized> RwLock<T> {
+    pub fn read(&self) -> ReadGuard<'_, T> {
+        self.inner.acquire_read_lock(None);
+        unsafe { ReadGuard::new(self) }
+    }
+
+    pub fn write(&self) -> WriteGuard<'_, T> {
+        self.inner.acquire_write_lock(None);
+        unsafe { WriteGuard::new(self) }
+    }
+
+    pub fn try_read(&self) -> Option<ReadGuard<'_, T>> {
+        if self.inner.try_acquire_read_lock().is_ok() {
+            Some(unsafe { ReadGuard::new(self) })
+        } else {
+            None
+        }
+    }
+
+    pub fn try_write(&self) -> Option<WriteGuard<'_, T>> {
+        if self.inner.try_acquire_write_lock().is_ok() {
+            Some(unsafe { WriteGuard::new(self) })
+        } else {
+            None
+        }
+    }
+}
+
+pub struct ReadGuard<'a, T: ?Sized + 'a> {
+    lock: &'a RwLock<T>,
+}
+
+impl<T: ?Sized> !Send for ReadGuard<'_, T> {}
+unsafe impl<T: ?Sized + Sync> Sync for ReadGuard<'_, T> {}
+
+impl<'a, T: ?Sized> ReadGuard<'a, T> {
+    unsafe fn new(lock: &'a RwLock<T>) -> Self {
+        Self { lock }
+    }
+}
+
+impl<'a, T: ?Sized> ops::Deref for ReadGuard<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: We have shared reference to the data.
+        unsafe { &*self.lock.data.get() }
+    }
+}
+
+impl<'a, T: ?Sized> Drop for ReadGuard<'a, T> {
+    fn drop(&mut self) {
+        self.lock.inner.unlock();
+    }
+}
+
+impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for ReadGuard<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&**self, f)
+    }
+}
+
+impl<'a, T: ?Sized + fmt::Display> fmt::Display for ReadGuard<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(&**self, f)
+    }
+}
+
+pub struct WriteGuard<'a, T: ?Sized + 'a> {
+    lock: &'a RwLock<T>,
+}
+
+impl<T: ?Sized> !Send for WriteGuard<'_, T> {}
+unsafe impl<T: ?Sized + Sync> Sync for WriteGuard<'_, T> {}
+
+impl<'a, T: ?Sized> WriteGuard<'a, T> {
+    unsafe fn new(lock: &'a RwLock<T>) -> Self {
+        Self { lock }
+    }
+}
+
+impl<'a, T: ?Sized> ops::Deref for WriteGuard<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: We have exclusive reference to the data.
+        unsafe { &*self.lock.data.get() }
+    }
+}
+
+impl<'a, T: ?Sized> ops::DerefMut for WriteGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        // SAFETY: We have exclusive reference to the data.
+        unsafe { &mut *self.lock.data.get() }
+    }
+}
+
+impl<'a, T: ?Sized> Drop for WriteGuard<'a, T> {
+    fn drop(&mut self) {
+        self.lock.inner.unlock();
+    }
+}
+
+impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for WriteGuard<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&**self, f)
+    }
+}
+
+impl<'a, T: ?Sized + fmt::Display> fmt::Display for WriteGuard<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(&**self, f)
+    }
+}
diff --git a/tests/expected/bins_dynamic/ptrace.stdout b/tests/expected/bins_dynamic/ptrace.stdout
index c2e762dd87797f57166ae0277711391a9e072d64..824e89accd2a60dfb40609fd63b82c6ccda56547 100644
--- a/tests/expected/bins_dynamic/ptrace.stdout
+++ b/tests/expected/bins_dynamic/ptrace.stdout
@@ -1,30 +1,5 @@
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
-Post-syscall
-Wait...
 Child exited with status 0
diff --git a/tests/expected/bins_static/ptrace.stdout b/tests/expected/bins_static/ptrace.stdout
index c2e762dd87797f57166ae0277711391a9e072d64..824e89accd2a60dfb40609fd63b82c6ccda56547 100644
--- a/tests/expected/bins_static/ptrace.stdout
+++ b/tests/expected/bins_static/ptrace.stdout
@@ -1,30 +1,5 @@
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
 Set regs
-Post-syscall
-Wait...
------ Pre-syscall -----
-Wait...
-Get regs
-Post-syscall
-Wait...
 Child exited with status 0
diff --git a/tests/ptrace.c b/tests/ptrace.c
index ef01252b452e3bcfe55d377824c92c9bb3655c85..40123f47b16040951b5c78e1eb3c56e0b946588a 100644
--- a/tests/ptrace.c
+++ b/tests/ptrace.c
@@ -48,17 +48,18 @@ int main() {
 
         int status;
         while (true) {
-            puts("----- Pre-syscall -----");
+            // puts("----- Pre-syscall -----");
             result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
             ERROR_IF(ptrace, result, == -1);
             UNEXP_IF(ptrace, result, != 0);
-            puts("Wait...");
+            // puts("Wait...");
             result = waitpid(pid, &status, 0);
             ERROR_IF(waitpid, result, == -1);
-            if (WIFEXITED(status)) { break; }
+            if (WIFEXITED(status))
+                break;
 
             struct user_regs_struct regs;
-            puts("Get regs");
+            // puts("Get regs");
             result = ptrace(PTRACE_GETREGS, pid, NULL, &regs);
             ERROR_IF(ptrace, result, == -1);
 
@@ -69,14 +70,15 @@ int main() {
                 ERROR_IF(ptrace, result, == -1);
             }
 
-            puts("Post-syscall");
+            // puts("Post-syscall");
             result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
             ERROR_IF(ptrace, result, == -1);
             UNEXP_IF(ptrace, result, != 0);
-            puts("Wait...");
+            // puts("Wait...");
             result = waitpid(pid, &status, 0);
             ERROR_IF(waitpid, result, == -1);
-            if (WIFEXITED(status)) { break; }
+            if (WIFEXITED(status))
+                break;
         }
         printf("Child exited with status %d\n", WEXITSTATUS(status));
     }