diff --git a/Cargo.lock b/Cargo.lock
index 020c2726f33efef2d698dd1622586681e5bd42d8..0aa89491dcd44d20c43df9395a4d7ede714f2a43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "bitflags"
-version = "2.4.1"
+version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 
 [[package]]
 name = "cbitset"
@@ -198,6 +198,12 @@ dependencies = [
  "redox_syscall",
 ]
 
+[[package]]
+name = "redox-path"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f45c7275fe1467ea17542c01766ae9872fa98aa7fe06ba5ea6595f7a9f0699c6"
+
 [[package]]
 name = "redox_syscall"
 version = "0.4.1"
@@ -224,6 +230,7 @@ dependencies = [
  "rand_jitter",
  "rand_xorshift",
  "redox-exec",
+ "redox-path",
  "redox_syscall",
  "sc",
  "unicode-width",
diff --git a/Cargo.toml b/Cargo.toml
index bac37d96910f63bfecb4ba1052d6555729915edf..37c9fb26c28ebcfdbf41327670e2bb6088e9c111 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,13 @@ name = "relibc"
 crate-type = ["staticlib"]
 
 [workspace]
-members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "src/platform/redox/redox-exec"]
+members = [
+    "src/crt0",
+    "src/crti",
+    "src/crtn",
+    "src/ld_so",
+    "src/platform/redox/redox-exec",
+]
 exclude = ["tests", "dlmalloc-rs"]
 
 [build-dependencies]
@@ -47,6 +53,7 @@ sc = "0.2.3"
 [target.'cfg(target_os = "redox")'.dependencies]
 redox_syscall = "0.4"
 redox-exec = { path = "src/platform/redox/redox-exec" }
+redox-path = "0.1"
 
 [features]
 default = ["check_against_libc_crate"]
diff --git a/src/platform/redox/path.rs b/src/platform/redox/path.rs
index a06b8d175fb9c2ee05e5fe9d2d3d2deff70e6e21..0586f6825593da62c715c380c48a052d6d0b4405 100644
--- a/src/platform/redox/path.rs
+++ b/src/platform/redox/path.rs
@@ -1,78 +1,14 @@
-use syscall::{data::Stat, error::*, flag::*};
-
 use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
+use syscall::{data::Stat, error::*, flag::*};
 
 use super::{libcscheme, FdGuard};
 use crate::sync::Mutex;
 
+pub use redox_path::canonicalize_using_cwd;
+
 // TODO: Define in syscall
 const PATH_MAX: usize = 4096;
 
-/// Make a relative path absolute.
-///
-/// Given a cwd of "scheme:/path", this his function will turn "foo" into "scheme:/path/foo".
-/// "/foo" will turn into "scheme:/foo". "bar:/foo" will be used directly, as it is already
-/// absolute
-pub fn canonicalize_using_cwd<'a>(cwd_opt: Option<&str>, path: &'a str) -> Option<String> {
-    let mut canon = if path.find(':').is_none() {
-        let cwd = cwd_opt?;
-        let path_start = cwd.find(':')? + 1;
-
-        let mut canon = if !path.starts_with('/') {
-            let mut c = cwd.to_owned();
-            if !c.ends_with('/') {
-                c.push('/');
-            }
-            c
-        } else {
-            cwd[..path_start].to_owned()
-        };
-
-        canon.push_str(&path);
-        canon
-    } else {
-        path.to_owned()
-    };
-
-    // NOTE: assumes the scheme does not include anything like "../" or "./"
-    let mut result = {
-        let parts = canon
-            .split('/')
-            .rev()
-            .scan(0, |nskip, part| {
-                if part == "." {
-                    Some(None)
-                } else if part == ".." {
-                    *nskip += 1;
-                    Some(None)
-                } else if *nskip > 0 {
-                    *nskip -= 1;
-                    Some(None)
-                } else {
-                    Some(Some(part))
-                }
-            })
-            .filter_map(|x| x)
-            .filter(|x| !x.is_empty())
-            .collect::<Vec<_>>();
-        parts.iter().rev().fold(String::new(), |mut string, &part| {
-            string.push_str(part);
-            string.push('/');
-            string
-        })
-    };
-    result.pop(); // remove extra '/'
-
-    // replace with the root of the scheme if it's empty
-    Some(if result.is_empty() {
-        let pos = canon.find(':').map_or(canon.len(), |p| p + 1);
-        canon.truncate(pos);
-        canon
-    } else {
-        result
-    })
-}
-
 // XXX: chdir is not marked thread-safe (MT-safe) by POSIX. But on Linux it is simply run as a
 // syscall and is therefore atomic, which is presumably why Rust's libstd doesn't synchronize
 // access to this.