diff --git a/Cargo.lock b/Cargo.lock
index 55ac709b980cff5af21a853958b6703c2e45c34b..222e6749cacc31b2ed2f1c26df8e806fa4b6530d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,16 +33,16 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
 name = "cbindgen"
-version = "0.24.5"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d"
+checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
 dependencies = [
  "clap",
  "heck",
  "indexmap",
  "log",
- "proc-macro2 1.0.69",
- "quote 1.0.33",
+ "proc-macro2",
+ "quote",
  "serde",
  "serde_json",
  "syn 1.0.109",
@@ -52,9 +52,9 @@ dependencies = [
 
 [[package]]
 name = "cbitset"
-version = "0.1.0"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3a9afa72f63942dd7e7f01c67b863ce9df35c523ae10e3dddd3eec8f1e07eac"
+checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b"
 dependencies = [
  "num-traits",
 ]
@@ -102,7 +102,7 @@ dependencies = [
 name = "core_io"
 version = "0.1.20181107"
 dependencies = [
- "rustc_version 0.1.7",
+ "rustc_version",
 ]
 
 [[package]]
@@ -135,9 +135,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
 
 [[package]]
 name = "goblin"
-version = "0.0.21"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a4013e9182f2345c6b7829b9ef6e670bce0dfca12c6f974457ed2160c2c7fe9"
+checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134"
 dependencies = [
  "log",
  "plain",
@@ -181,15 +181,6 @@ version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-dependencies = [
- "spin 0.5.2",
-]
-
 [[package]]
 name = "ld_so"
 version = "0.1.0"
@@ -206,16 +197,6 @@ version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
 
-[[package]]
-name = "lock_api"
-version = "0.4.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
 [[package]]
 name = "log"
 version = "0.4.20"
@@ -230,9 +211,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
 [[package]]
 name = "memoffset"
-version = "0.5.6"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
 dependencies = [
  "autocfg",
 ]
@@ -263,13 +244,10 @@ name = "posix-regex"
 version = "0.1.0"
 
 [[package]]
-name = "proc-macro2"
-version = "0.4.30"
+name = "ppv-lite86"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
-dependencies = [
- "unicode-xid",
-]
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
@@ -280,22 +258,13 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "quote"
-version = "0.6.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
-dependencies = [
- "proc-macro2 0.4.30",
-]
-
 [[package]]
 name = "quote"
 version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
- "proc-macro2 1.0.69",
+ "proc-macro2",
 ]
 
 [[package]]
@@ -316,27 +285,59 @@ dependencies = [
 
 [[package]]
 name = "rand"
-version = "0.5.6"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 dependencies = [
- "rand_core 0.3.1",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.3.1"
+name = "rand_chacha"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 dependencies = [
- "rand_core 0.4.2",
+ "ppv-lite86",
+ "rand_core",
 ]
 
 [[package]]
 name = "rand_core"
-version = "0.4.2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_jitter"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a404fd88e0f817fc1c3351b9ba2207ffa65038cdde464405308a5f5d254835fe"
+dependencies = [
+ "libc",
+ "rand_core",
+ "winapi",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
+dependencies = [
+ "rand_core",
+]
 
 [[package]]
 name = "redox-exec"
@@ -353,15 +354,6 @@ version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
-[[package]]
-name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
-dependencies = [
- "bitflags 1.3.2",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.4.1"
@@ -374,13 +366,12 @@ dependencies = [
 name = "relibc"
 version = "0.2.5"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.1",
  "cbindgen",
  "cbitset",
  "cc",
  "core_io",
  "goblin",
- "lazy_static",
  "libc",
  "memchr",
  "memoffset",
@@ -388,10 +379,11 @@ dependencies = [
  "posix-regex",
  "ralloc",
  "rand",
+ "rand_jitter",
+ "rand_xorshift",
  "redox-exec",
  "redox_syscall 0.4.1",
  "sc",
- "spin 0.9.8",
  "unicode-width",
 ]
 
@@ -401,23 +393,14 @@ version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
 dependencies = [
- "semver 0.1.20",
-]
-
-[[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
-dependencies = [
- "semver 0.9.0",
+ "semver",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.20"
+version = "0.38.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
 dependencies = [
  "bitflags 2.4.1",
  "errno",
@@ -438,31 +421,24 @@ version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
 
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
 [[package]]
 name = "scroll"
-version = "0.9.2"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
 dependencies = [
- "rustc_version 0.2.3",
  "scroll_derive",
 ]
 
 [[package]]
 name = "scroll_derive"
-version = "0.9.5"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
+checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
 dependencies = [
- "proc-macro2 0.4.30",
- "quote 0.6.13",
- "syn 0.15.44",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.38",
 ]
 
 [[package]]
@@ -471,21 +447,6 @@ version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
 
-[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
 [[package]]
 name = "serde"
 version = "1.0.190"
@@ -501,8 +462,8 @@ version = "1.0.190"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
 dependencies = [
- "proc-macro2 1.0.69",
- "quote 1.0.33",
+ "proc-macro2",
+ "quote",
  "syn 2.0.38",
 ]
 
@@ -517,46 +478,20 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-dependencies = [
- "lock_api",
-]
-
 [[package]]
 name = "strsim"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
-[[package]]
-name = "syn"
-version = "0.15.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
-dependencies = [
- "proc-macro2 0.4.30",
- "quote 0.6.13",
- "unicode-xid",
-]
-
 [[package]]
 name = "syn"
 version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
- "proc-macro2 1.0.69",
- "quote 1.0.33",
+ "proc-macro2",
+ "quote",
  "unicode-ident",
 ]
 
@@ -566,20 +501,20 @@ version = "2.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
 dependencies = [
- "proc-macro2 1.0.69",
- "quote 1.0.33",
+ "proc-macro2",
+ "quote",
  "unicode-ident",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.8.0"
+version = "3.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall 0.3.5",
+ "redox_syscall 0.4.1",
  "rustix",
  "windows-sys",
 ]
@@ -626,12 +561,6 @@ version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
-[[package]]
-name = "unicode-xid"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
diff --git a/Cargo.toml b/Cargo.toml
index b2a3040a34194257a3244c268e2fe6d36d57d3dc..aa8cf18f3f91afb70250e2ffa8609a0f08c6923a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,25 +13,29 @@ members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "src/platform/redox/
 exclude = ["core_io", "ralloc", "tests"]
 
 [build-dependencies]
-cbindgen = "0.24.3"
+cbindgen = "0.26"
 cc = "1.0.25"
 #env_logger = "0.10"
 
 [dependencies]
-bitflags = "1"
-cbitset = "0.1.0"
+bitflags = "2"
+cbitset = "0.2"
 core_io = { path = "core_io", features = ["collections"] }
-lazy_static = { version = "1.4.0", default-features = false, features = ["spin_no_std"] }
-memoffset = "0.5.1"
+memoffset = "0.9"
 posix-regex = { path = "posix-regex", features = ["no_std"] }
-rand = { version = "0.5.5", default-features = false }
+
+# TODO: For some reason, rand_jitter hasn't been updated to use the latest rand_core
+rand = { version = "0.7", default-features = false }
+rand_xorshift = "0.2"
+rand_jitter = "0.3"
+
 memchr = { version = "2.2.0", default-features = false }
 plain = "0.2"
 unicode-width = "0.1"
 __libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true }
 
 [dependencies.goblin]
-version = "0.0.21"
+version = "0.7"
 default-features = false
 features = ["elf32", "elf64", "endian_fd"]
 
@@ -45,7 +49,6 @@ sc = "0.2.3"
 
 [target.'cfg(target_os = "redox")'.dependencies]
 redox_syscall = "0.4"
-spin = "0.9.0"
 redox-exec = { path = "src/platform/redox/redox-exec" }
 
 [features]
diff --git a/src/c_str.rs b/src/c_str.rs
index 3d28d49d00202c205f179c561d58c9e06d80b236..56f1b084f785477f195ae178b5e72e318ae1a4b3 100644
--- a/src/c_str.rs
+++ b/src/c_str.rs
@@ -1,1255 +1,89 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+use core::{marker::PhantomData, ptr::NonNull, str::Utf8Error};
 
 use alloc::{
-    borrow::{Borrow, Cow, ToOwned},
-    boxed::Box,
-    rc::Rc,
+    borrow::{Cow, ToOwned},
     string::String,
-    sync::Arc,
-    vec::Vec,
 };
-use core::{
-    ascii,
-    cmp::Ordering,
-    fmt::{self, Write},
-    mem, ops, ptr, slice,
-    str::{self, Utf8Error},
-};
-
-use crate::{header::string::strlen, platform::types::*};
-
-pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
-    use crate::header::string;
-
-    let p = unsafe {
-        string::memchr(
-            haystack.as_ptr() as *const c_void,
-            needle as c_int,
-            haystack.len(),
-        )
-    };
-    if p.is_null() {
-        None
-    } else {
-        Some(p as usize - (haystack.as_ptr() as usize))
-    }
-}
-
-/// A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the
-/// middle.
-///
-/// This type serves the purpose of being able to safely generate a
-/// C-compatible string from a Rust byte slice or vector. An instance of this
-/// type is a static guarantee that the underlying bytes contain no interior 0
-/// bytes ("nul characters") and that the final byte is 0 ("nul terminator").
-///
-/// `CString` is to [`CStr`] as [`String`] is to [`&str`]: the former
-/// in each pair are owned strings; the latter are borrowed
-/// references.
-///
-/// # Creating a `CString`
-///
-/// A `CString` is created from either a byte slice or a byte vector,
-/// or anything that implements [`Into`]`<`[`Vec`]`<`[`u8`]`>>` (for
-/// example, you can build a `CString` straight out of a [`String`] or
-/// a [`&str`], since both implement that trait).
-///
-/// The [`new`] method will actually check that the provided `&[u8]`
-/// does not have 0 bytes in the middle, and return an error if it
-/// finds one.
-///
-/// # Extracting a raw pointer to the whole C string
-///
-/// `CString` implements a [`as_ptr`] method through the [`Deref`]
-/// trait. This method will give you a `*const c_char` which you can
-/// feed directly to extern functions that expect a nul-terminated
-/// string, like C's `strdup()`.
-///
-/// # Extracting a slice of the whole C string
-///
-/// Alternatively, you can obtain a `&[`[`u8`]`]` slice from a
-/// `CString` with the [`as_bytes`] method. Slices produced in this
-/// way do *not* contain the trailing nul terminator. This is useful
-/// when you will be calling an extern function that takes a `*const
-/// u8` argument which is not necessarily nul-terminated, plus another
-/// argument with the length of the string — like C's `strndup()`.
-/// You can of course get the slice's length with its
-/// [`len`][slice.len] method.
-///
-/// If you need a `&[`[`u8`]`]` slice *with* the nul terminator, you
-/// can use [`as_bytes_with_nul`] instead.
-///
-/// Once you have the kind of slice you need (with or without a nul
-/// terminator), you can call the slice's own
-/// [`as_ptr`][slice.as_ptr] method to get a raw pointer to pass to
-/// extern functions. See the documentation for that function for a
-/// discussion on ensuring the lifetime of the raw pointer.
-///
-/// [`Into`]: ../convert/trait.Into.html
-/// [`Vec`]: ../vec/struct.Vec.html
-/// [`String`]: ../string/struct.String.html
-/// [`&str`]: ../primitive.str.html
-/// [`u8`]: ../primitive.u8.html
-/// [`new`]: #method.new
-/// [`as_bytes`]: #method.as_bytes
-/// [`as_bytes_with_nul`]: #method.as_bytes_with_nul
-/// [`as_ptr`]: #method.as_ptr
-/// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr
-/// [slice.len]: ../primitive.slice.html#method.len
-/// [`Deref`]: ../ops/trait.Deref.html
-/// [`CStr`]: struct.CStr.html
-///
-/// # Examples
-///
-/// ```ignore (extern-declaration)
-/// # fn main() {
-/// use std::ffi::CString;
-/// use std::os::raw::c_char;
-///
-/// extern {
-///     fn my_printer(s: *const c_char);
-/// }
-///
-/// // We are certain that our string doesn't have 0 bytes in the middle,
-/// // so we can .unwrap()
-/// let c_to_print = CString::new("Hello, world!").unwrap();
-/// unsafe {
-///     my_printer(c_to_print.as_ptr());
-/// }
-/// # }
-/// ```
-///
-/// # Safety
-///
-/// `CString` is intended for working with traditional C-style strings
-/// (a sequence of non-nul bytes terminated by a single nul byte); the
-/// primary use case for these kinds of strings is interoperating with C-like
-/// code. Often you will need to transfer ownership to/from that external
-/// code. It is strongly recommended that you thoroughly read through the
-/// documentation of `CString` before use, as improper ownership management
-/// of `CString` instances can lead to invalid memory accesses, memory leaks,
-/// and other memory errors.
-
-#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
-pub struct CString {
-    // Invariant 1: the slice ends with a zero byte and has a length of at least one.
-    // Invariant 2: the slice contains only one zero byte.
-    // Improper usage of unsafe function can break Invariant 2, but not Invariant 1.
-    inner: Box<[u8]>,
-}
-
-/// Representation of a borrowed C string.
-///
-/// This type represents a borrowed reference to a nul-terminated
-/// array of bytes. It can be constructed safely from a `&[`[`u8`]`]`
-/// slice, or unsafely from a raw `*const c_char`. It can then be
-/// converted to a Rust [`&str`] by performing UTF-8 validation, or
-/// into an owned [`CString`].
-///
-/// `CStr` is to [`CString`] as [`&str`] is to [`String`]: the former
-/// in each pair are borrowed references; the latter are owned
-/// strings.
-///
-/// Note that this structure is **not** `repr(C)` and is not recommended to be
-/// placed in the signatures of FFI functions. Instead, safe wrappers of FFI
-/// functions may leverage the unsafe [`from_ptr`] constructor to provide a safe
-/// interface to other consumers.
-///
-/// # Examples
-///
-/// Inspecting a foreign C string:
-///
-/// ```ignore (extern-declaration)
-/// use std::ffi::CStr;
-/// use std::os::raw::c_char;
-///
-/// extern { fn my_string() -> *const c_char; }
-///
-/// unsafe {
-///     let slice = CStr::from_ptr(my_string());
-///     println!("string buffer size without nul terminator: {}", slice.to_bytes().len());
-/// }
-/// ```
-///
-/// Passing a Rust-originating C string:
-///
-/// ```ignore (extern-declaration)
-/// use std::ffi::{CString, CStr};
-/// use std::os::raw::c_char;
-///
-/// fn work(data: &CStr) {
-///     extern { fn work_with(data: *const c_char); }
-///
-///     unsafe { work_with(data.as_ptr()) }
-/// }
-///
-/// let s = CString::new("data data data data").unwrap();
-/// work(&s);
-/// ```
-///
-/// Converting a foreign C string into a Rust [`String`]:
-///
-/// ```ignore (extern-declaration)
-/// use std::ffi::CStr;
-/// use std::os::raw::c_char;
-///
-/// extern { fn my_string() -> *const c_char; }
-///
-/// fn my_string_safe() -> String {
-///     unsafe {
-///         CStr::from_ptr(my_string()).to_string_lossy().into_owned()
-///     }
-/// }
-///
-/// println!("string: {}", my_string_safe());
-/// ```
-///
-/// [`u8`]: ../primitive.u8.html
-/// [`&str`]: ../primitive.str.html
-/// [`String`]: ../string/struct.String.html
-/// [`CString`]: struct.CString.html
-/// [`from_ptr`]: #method.from_ptr
-#[derive(Hash)]
-pub struct CStr {
-    // FIXME: this should not be represented with a DST slice but rather with
-    //        just a raw `c_char` along with some form of marker to make
-    //        this an unsized type. Essentially `sizeof(&CStr)` should be the
-    //        same as `sizeof(&c_char)` but `CStr` should be an unsized type.
-    inner: [c_char],
-}
-
-/// An error indicating that an interior nul byte was found.
-///
-/// While Rust strings may contain nul bytes in the middle, C strings
-/// can't, as that byte would effectively truncate the string.
-///
-/// This error is created by the [`new`][`CString::new`] method on
-/// [`CString`]. See its documentation for more.
-///
-/// [`CString`]: struct.CString.html
-/// [`CString::new`]: struct.CString.html#method.new
-///
-/// # Examples
-///
-/// ```
-/// use std::ffi::{CString, NulError};
-///
-/// let _: NulError = CString::new(b"f\0oo".to_vec()).unwrap_err();
-/// ```
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct NulError(usize, Vec<u8>);
-
-/// An error indicating that a nul byte was not in the expected position.
-///
-/// The slice used to create a [`CStr`] must have one and only one nul
-/// byte at the end of the slice.
-///
-/// This error is created by the
-/// [`from_bytes_with_nul`][`CStr::from_bytes_with_nul`] method on
-/// [`CStr`]. See its documentation for more.
-///
-/// [`CStr`]: struct.CStr.html
-/// [`CStr::from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul
-///
-/// # Examples
-///
-/// ```
-/// use std::ffi::{CStr, FromBytesWithNulError};
-///
-/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err();
-/// ```
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct FromBytesWithNulError {
-    kind: FromBytesWithNulErrorKind,
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-enum FromBytesWithNulErrorKind {
-    InteriorNul(usize),
-    NotNulTerminated,
-}
-
-impl FromBytesWithNulError {
-    fn interior_nul(pos: usize) -> FromBytesWithNulError {
-        FromBytesWithNulError {
-            kind: FromBytesWithNulErrorKind::InteriorNul(pos),
-        }
-    }
-    fn not_nul_terminated() -> FromBytesWithNulError {
-        FromBytesWithNulError {
-            kind: FromBytesWithNulErrorKind::NotNulTerminated,
-        }
-    }
 
-    fn description(&self) -> &str {
-        match self.kind {
-            FromBytesWithNulErrorKind::InteriorNul(..) => {
-                "data provided contains an interior nul byte"
-            }
-            FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated",
-        }
-    }
-}
+use crate::{header::string::strlen, platform::types::c_char};
 
-/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`].
-///
-/// `CString` is just a wrapper over a buffer of bytes with a nul
-/// terminator; [`into_string`][`CString::into_string`] performs UTF-8
-/// validation on those bytes and may return this error.
-///
-/// This `struct` is created by the
-/// [`into_string`][`CString::into_string`] method on [`CString`]. See
-/// its documentation for more.
-///
-/// [`String`]: ../string/struct.String.html
-/// [`CString`]: struct.CString.html
-/// [`CString::into_string`]: struct.CString.html#method.into_string
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct IntoStringError {
-    inner: CString,
-    error: Utf8Error,
+/// C string wrapper, guaranteed to be
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct CStr<'a> {
+    ptr: NonNull<c_char>,
+    _marker: PhantomData<&'a [u8]>,
 }
 
-impl CString {
-    /// Creates a new C-compatible string from a container of bytes.
-    ///
-    /// This function will consume the provided data and use the
-    /// underlying bytes to construct a new string, ensuring that
-    /// there is a trailing 0 byte. This trailing 0 byte will be
-    /// appended by this function; the provided data should *not*
-    /// contain any 0 bytes in it.
-    ///
-    /// # Examples
-    ///
-    /// ```ignore (extern-declaration)
-    /// use std::ffi::CString;
-    /// use std::os::raw::c_char;
-    ///
-    /// extern { fn puts(s: *const c_char); }
-    ///
-    /// let to_print = CString::new("Hello!").unwrap();
-    /// unsafe {
-    ///     puts(to_print.as_ptr());
-    /// }
-    /// ```
-    ///
-    /// # Errors
+impl<'a> CStr<'a> {
+    /// Safety
     ///
-    /// This function will return an error if the supplied bytes contain an
-    /// internal 0 byte. The [`NulError`] returned will contain the bytes as well as
-    /// the position of the nul byte.
-    ///
-    /// [`NulError`]: struct.NulError.html
-    pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> {
-        Self::_new(t.into())
-    }
-
-    fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
-        match memchr(0, &bytes) {
-            Some(i) => Err(NulError(i, bytes)),
-            None => Ok(unsafe { CString::from_vec_unchecked(bytes) }),
+    /// The ptr must be valid up to and including the first NUL byte from the base ptr.
+    pub const unsafe fn from_ptr(ptr: *const c_char) -> Self {
+        Self {
+            ptr: NonNull::new_unchecked(ptr as *mut c_char),
+            _marker: PhantomData,
         }
     }
-
-    /// Creates a C-compatible string by consuming a byte vector,
-    /// without checking for interior 0 bytes.
-    ///
-    /// This method is equivalent to [`new`] except that no runtime assertion
-    /// is made that `v` contains no 0 bytes, and it requires an actual
-    /// byte vector, not anything that can be converted to one with Into.
-    ///
-    /// [`new`]: #method.new
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let raw = b"foo".to_vec();
-    /// unsafe {
-    ///     let c_string = CString::from_vec_unchecked(raw);
-    /// }
-    /// ```
-    pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString {
-        v.reserve_exact(1);
-        v.push(0);
-        CString {
-            inner: v.into_boxed_slice(),
-        }
-    }
-
-    /// Retakes ownership of a `CString` that was transferred to C via [`into_raw`].
-    ///
-    /// Additionally, the length of the string will be recalculated from the pointer.
-    ///
-    /// # Safety
-    ///
-    /// This should only ever be called with a pointer that was earlier
-    /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g. trying to take
-    /// ownership of a string that was allocated by foreign code) is likely to lead
-    /// to undefined behavior or allocator corruption.
-    ///
-    /// > **Note:** If you need to borrow a string that was allocated by
-    /// > foreign code, use [`CStr`]. If you need to take ownership of
-    /// > a string that was allocated by foreign code, you will need to
-    /// > make your own provisions for freeing it appropriately, likely
-    /// > with the foreign code's API to do that.
-    ///
-    /// [`into_raw`]: #method.into_raw
-    /// [`CStr`]: struct.CStr.html
-    ///
-    /// # Examples
-    ///
-    /// Create a `CString`, pass ownership to an `extern` function (via raw pointer), then retake
-    /// ownership with `from_raw`:
-    ///
-    /// ```ignore (extern-declaration)
-    /// use std::ffi::CString;
-    /// use std::os::raw::c_char;
-    ///
-    /// extern {
-    ///     fn some_extern_function(s: *mut c_char);
-    /// }
-    ///
-    /// let c_string = CString::new("Hello!").unwrap();
-    /// let raw = c_string.into_raw();
-    /// unsafe {
-    ///     some_extern_function(raw);
-    ///     let c_string = CString::from_raw(raw);
-    /// }
-    /// ```
-    pub unsafe fn from_raw(ptr: *mut c_char) -> CString {
-        let len = strlen(ptr) + 1; // Including the NUL byte
-        let slice = slice::from_raw_parts_mut(ptr, len as usize);
-        CString {
-            inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]),
-        }
-    }
-
-    /// Consumes the `CString` and transfers ownership of the string to a C caller.
-    ///
-    /// The pointer which this function returns must be returned to Rust and reconstituted using
-    /// [`from_raw`] to be properly deallocated. Specifically, one
-    /// should *not* use the standard C `free()` function to deallocate
-    /// this string.
-    ///
-    /// Failure to call [`from_raw`] will lead to a memory leak.
-    ///
-    /// [`from_raw`]: #method.from_raw
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new("foo").unwrap();
-    ///
-    /// let ptr = c_string.into_raw();
-    ///
-    /// unsafe {
-    ///     assert_eq!(b'f', *ptr as u8);
-    ///     assert_eq!(b'o', *ptr.offset(1) as u8);
-    ///     assert_eq!(b'o', *ptr.offset(2) as u8);
-    ///     assert_eq!(b'\0', *ptr.offset(3) as u8);
-    ///
-    ///     // retake pointer to free memory
-    ///     let _ = CString::from_raw(ptr);
-    /// }
-    /// ```
-    #[inline]
-    pub fn into_raw(self) -> *mut c_char {
-        Box::into_raw(self.into_inner()) as *mut c_char
-    }
-
-    /// Converts the `CString` into a [`String`] if it contains valid UTF-8 data.
-    ///
-    /// On failure, ownership of the original `CString` is returned.
-    ///
-    /// [`String`]: ../string/struct.String.html
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let valid_utf8 = vec![b'f', b'o', b'o'];
-    /// let cstring = CString::new(valid_utf8).unwrap();
-    /// assert_eq!(cstring.into_string().unwrap(), "foo");
-    ///
-    /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o'];
-    /// let cstring = CString::new(invalid_utf8).unwrap();
-    /// let err = cstring.into_string().err().unwrap();
-    /// assert_eq!(err.utf8_error().valid_up_to(), 1);
-    /// ```
-
-    pub fn into_string(self) -> Result<String, IntoStringError> {
-        String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError {
-            error: e.utf8_error(),
-            inner: unsafe { CString::from_vec_unchecked(e.into_bytes()) },
-        })
-    }
-
-    /// Consumes the `CString` and returns the underlying byte buffer.
-    ///
-    /// The returned buffer does **not** contain the trailing nul
-    /// terminator, and it is guaranteed to not have any interior nul
-    /// bytes.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new("foo").unwrap();
-    /// let bytes = c_string.into_bytes();
-    /// assert_eq!(bytes, vec![b'f', b'o', b'o']);
-    /// ```
-    pub fn into_bytes(self) -> Vec<u8> {
-        let mut vec = self.into_inner().into_vec();
-        let _nul = vec.pop();
-        debug_assert_eq!(_nul, Some(0u8));
-        vec
-    }
-
-    /// Equivalent to the [`into_bytes`] function except that the returned vector
-    /// includes the trailing nul terminator.
-    ///
-    /// [`into_bytes`]: #method.into_bytes
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new("foo").unwrap();
-    /// let bytes = c_string.into_bytes_with_nul();
-    /// assert_eq!(bytes, vec![b'f', b'o', b'o', b'\0']);
-    /// ```
-    pub fn into_bytes_with_nul(self) -> Vec<u8> {
-        self.into_inner().into_vec()
-    }
-
-    /// Returns the contents of this `CString` as a slice of bytes.
-    ///
-    /// The returned slice does **not** contain the trailing nul
-    /// terminator, and it is guaranteed to not have any interior nul
-    /// bytes. If you need the nul terminator, use
-    /// [`as_bytes_with_nul`] instead.
-    ///
-    /// [`as_bytes_with_nul`]: #method.as_bytes_with_nul
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new("foo").unwrap();
-    /// let bytes = c_string.as_bytes();
-    /// assert_eq!(bytes, &[b'f', b'o', b'o']);
-    /// ```
-    #[inline]
-    pub fn as_bytes(&self) -> &[u8] {
-        &self.inner[..self.inner.len() - 1]
-    }
-
-    /// Equivalent to the [`as_bytes`] function except that the returned slice
-    /// includes the trailing nul terminator.
-    ///
-    /// [`as_bytes`]: #method.as_bytes
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new("foo").unwrap();
-    /// let bytes = c_string.as_bytes_with_nul();
-    /// assert_eq!(bytes, &[b'f', b'o', b'o', b'\0']);
-    /// ```
-    #[inline]
-    pub fn as_bytes_with_nul(&self) -> &[u8] {
-        &self.inner
-    }
-
-    /// Extracts a [`CStr`] slice containing the entire string.
-    ///
-    /// [`CStr`]: struct.CStr.html
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::{CString, CStr};
-    ///
-    /// let c_string = CString::new(b"foo".to_vec()).unwrap();
-    /// let c_str = c_string.as_c_str();
-    /// assert_eq!(c_str, CStr::from_bytes_with_nul(b"foo\0").unwrap());
-    /// ```
-    #[inline]
-    pub fn as_c_str(&self) -> &CStr {
-        &*self
-    }
-
-    /// Converts this `CString` into a boxed [`CStr`].
-    ///
-    /// [`CStr`]: struct.CStr.html
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::{CString, CStr};
-    ///
-    /// let c_string = CString::new(b"foo".to_vec()).unwrap();
-    /// let boxed = c_string.into_boxed_c_str();
-    /// assert_eq!(&*boxed, CStr::from_bytes_with_nul(b"foo\0").unwrap());
-    /// ```
-    pub fn into_boxed_c_str(self) -> Box<CStr> {
-        unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) }
-    }
-
-    // Bypass "move out of struct which implements [`Drop`] trait" restriction.
-    ///
-    /// [`Drop`]: ../ops/trait.Drop.html
-    fn into_inner(self) -> Box<[u8]> {
-        unsafe {
-            let result = ptr::read(&self.inner);
-            mem::forget(self);
-            result
+    pub unsafe fn from_nullable_ptr(ptr: *const c_char) -> Option<Self> {
+        if ptr.is_null() {
+            None
+        } else {
+            Some(Self::from_ptr(ptr))
         }
     }
-}
-
-// Turns this `CString` into an empty string to prevent
-// memory unsafe code from working by accident. Inline
-// to prevent LLVM from optimizing it away in debug builds.
-impl Drop for CString {
-    #[inline]
-    fn drop(&mut self) {
+    pub fn to_bytes_with_nul(self) -> &'a [u8] {
         unsafe {
-            *self.inner.get_unchecked_mut(0) = 0;
-        }
-    }
-}
-
-impl ops::Deref for CString {
-    type Target = CStr;
-
-    #[inline]
-    fn deref(&self) -> &CStr {
-        unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
-    }
-}
-
-impl fmt::Debug for CString {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Debug::fmt(&**self, f)
-    }
-}
-
-impl From<CString> for Vec<u8> {
-    /// Converts a [`CString`] into a [`Vec`]`<u8>`.
-    ///
-    /// The conversion consumes the [`CString`], and removes the terminating NUL byte.
-    ///
-    /// [`Vec`]: ../vec/struct.Vec.html
-    /// [`CString`]: ../ffi/struct.CString.html
-    #[inline]
-    fn from(s: CString) -> Vec<u8> {
-        s.into_bytes()
-    }
-}
-
-impl fmt::Debug for CStr {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "\"")?;
-        for byte in self
-            .to_bytes()
-            .iter()
-            .flat_map(|&b| ascii::escape_default(b))
-        {
-            f.write_char(byte as char)?;
+            // SAFETY: The string must be valid at least until (and including) the NUL byte.
+            let len = strlen(self.ptr.as_ptr());
+            core::slice::from_raw_parts(self.ptr.as_ptr().cast(), len + 1)
         }
-        write!(f, "\"")
     }
-}
-
-impl<'a> Default for &'a CStr {
-    fn default() -> &'a CStr {
-        const SLICE: &[c_char] = &[0];
-        unsafe { CStr::from_ptr(SLICE.as_ptr()) }
-    }
-}
-
-impl Default for CString {
-    /// Creates an empty `CString`.
-    fn default() -> CString {
-        let a: &CStr = Default::default();
-        a.to_owned()
+    pub fn to_bytes(self) -> &'a [u8] {
+        let s = self.to_bytes_with_nul();
+        &s[..s.len() - 1]
     }
-}
-
-impl Borrow<CStr> for CString {
-    #[inline]
-    fn borrow(&self) -> &CStr {
-        self
-    }
-}
-
-impl<'a> From<Cow<'a, CStr>> for CString {
-    #[inline]
-    fn from(s: Cow<'a, CStr>) -> Self {
-        s.into_owned()
-    }
-}
-
-impl<'a> From<&'a CStr> for Box<CStr> {
-    fn from(s: &'a CStr) -> Box<CStr> {
-        let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
-        unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
-    }
-}
-
-impl From<Box<CStr>> for CString {
-    /// Converts a [`Box`]`<CStr>` into a [`CString`] without copying or allocating.
-    ///
-    /// [`Box`]: ../boxed/struct.Box.html
-    /// [`CString`]: ../ffi/struct.CString.html
-    #[inline]
-    fn from(s: Box<CStr>) -> CString {
-        s.into_c_string()
-    }
-}
-
-impl Clone for Box<CStr> {
-    #[inline]
-    fn clone(&self) -> Self {
-        (**self).into()
-    }
-}
-
-impl From<CString> for Box<CStr> {
-    /// Converts a [`CString`] into a [`Box`]`<CStr>` without copying or allocating.
-    ///
-    /// [`CString`]: ../ffi/struct.CString.html
-    /// [`Box`]: ../boxed/struct.Box.html
-    #[inline]
-    fn from(s: CString) -> Box<CStr> {
-        s.into_boxed_c_str()
+    pub fn to_str(self) -> Result<&'a str, Utf8Error> {
+        core::str::from_utf8(self.to_bytes())
     }
-}
-
-impl<'a> From<CString> for Cow<'a, CStr> {
-    #[inline]
-    fn from(s: CString) -> Cow<'a, CStr> {
-        Cow::Owned(s)
-    }
-}
-
-impl<'a> From<&'a CStr> for Cow<'a, CStr> {
-    #[inline]
-    fn from(s: &'a CStr) -> Cow<'a, CStr> {
-        Cow::Borrowed(s)
-    }
-}
-
-impl<'a> From<&'a CString> for Cow<'a, CStr> {
-    #[inline]
-    fn from(s: &'a CString) -> Cow<'a, CStr> {
-        Cow::Borrowed(s.as_c_str())
-    }
-}
-
-impl From<CString> for Arc<CStr> {
-    /// Converts a [`CString`] into a [`Arc`]`<CStr>` without copying or allocating.
-    ///
-    /// [`CString`]: ../ffi/struct.CString.html
-    /// [`Arc`]: ../sync/struct.Arc.html
-    #[inline]
-    fn from(s: CString) -> Arc<CStr> {
-        let arc: Arc<[u8]> = Arc::from(s.into_inner());
-        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
-    }
-}
-
-impl<'a> From<&'a CStr> for Arc<CStr> {
-    #[inline]
-    fn from(s: &CStr) -> Arc<CStr> {
-        let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul());
-        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
-    }
-}
-
-impl From<CString> for Rc<CStr> {
-    /// Converts a [`CString`] into a [`Rc`]`<CStr>` without copying or allocating.
-    ///
-    /// [`CString`]: ../ffi/struct.CString.html
-    /// [`Rc`]: ../rc/struct.Rc.html
-    #[inline]
-    fn from(s: CString) -> Rc<CStr> {
-        let rc: Rc<[u8]> = Rc::from(s.into_inner());
-        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
-    }
-}
-
-impl<'a> From<&'a CStr> for Rc<CStr> {
-    #[inline]
-    fn from(s: &CStr) -> Rc<CStr> {
-        let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul());
-        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
-    }
-}
-
-impl Default for Box<CStr> {
-    fn default() -> Box<CStr> {
-        let boxed: Box<[u8]> = Box::from([0]);
-        unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
-    }
-}
-
-impl NulError {
-    /// Returns the position of the nul byte in the slice that caused
-    /// [`CString::new`] to fail.
-    ///
-    /// [`CString::new`]: struct.CString.html#method.new
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let nul_error = CString::new("foo\0bar").unwrap_err();
-    /// assert_eq!(nul_error.nul_position(), 3);
-    ///
-    /// let nul_error = CString::new("foo bar\0").unwrap_err();
-    /// assert_eq!(nul_error.nul_position(), 7);
-    /// ```
-    pub fn nul_position(&self) -> usize {
-        self.0
-    }
-
-    /// Consumes this error, returning the underlying vector of bytes which
-    /// generated the error in the first place.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let nul_error = CString::new("foo\0bar").unwrap_err();
-    /// assert_eq!(nul_error.into_vec(), b"foo\0bar");
-    /// ```
-    pub fn into_vec(self) -> Vec<u8> {
-        self.1
-    }
-}
-
-impl fmt::Display for NulError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "nul byte found in provided data at position: {}", self.0)
-    }
-}
-
-impl fmt::Display for FromBytesWithNulError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.write_str(self.description())?;
-        if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind {
-            write!(f, " at byte pos {}", pos)?;
-        }
-        Ok(())
-    }
-}
-
-impl IntoStringError {
-    /// Consumes this error, returning original [`CString`] which generated the
-    /// error.
-    ///
-    /// [`CString`]: struct.CString.html
-    pub fn into_cstring(self) -> CString {
-        self.inner
+    pub fn to_string_lossy(self) -> Cow<'a, str> {
+        String::from_utf8_lossy(self.to_bytes())
     }
-
-    /// Access the underlying UTF-8 error that was the cause of this error.
-    pub fn utf8_error(&self) -> Utf8Error {
-        self.error
+    pub fn as_ptr(self) -> *const c_char {
+        self.ptr.as_ptr()
     }
-
-    fn description(&self) -> &str {
-        "C string contained non-utf8 bytes"
-    }
-}
-
-impl fmt::Display for IntoStringError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.description().fmt(f)
+    pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &'a [u8]) -> Self {
+        Self::from_ptr(bytes.as_ptr().cast())
     }
-}
-
-impl CStr {
-    /// Wraps a raw C string with a safe C string wrapper.
-    ///
-    /// This function will wrap the provided `ptr` with a `CStr` wrapper, which
-    /// allows inspection and interoperation of non-owned C strings. This method
-    /// is unsafe for a number of reasons:
-    ///
-    /// * There is no guarantee to the validity of `ptr`.
-    /// * The returned lifetime is not guaranteed to be the actual lifetime of
-    ///   `ptr`.
-    /// * There is no guarantee that the memory pointed to by `ptr` contains a
-    ///   valid nul terminator byte at the end of the string.
-    /// * It is not guaranteed that the memory pointed by `ptr` won't change
-    ///   before the `CStr` has been destroyed.
-    ///
-    /// > **Note**: This operation is intended to be a 0-cost cast but it is
-    /// > currently implemented with an up-front calculation of the length of
-    /// > the string. This is not guaranteed to always be the case.
-    ///
-    /// # Examples
-    ///
-    /// ```ignore (extern-declaration)
-    /// # fn main() {
-    /// use std::ffi::CStr;
-    /// use std::os::raw::c_char;
-    ///
-    /// extern {
-    ///     fn my_string() -> *const c_char;
-    /// }
-    ///
-    /// unsafe {
-    ///     let slice = CStr::from_ptr(my_string());
-    ///     println!("string returned: {}", slice.to_str().unwrap());
-    /// }
-    /// # }
-    /// ```
-    pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
-        let len = strlen(ptr);
-        let ptr = ptr as *const u8;
-        CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
-    }
-
-    /// Creates a C string wrapper from a byte slice.
-    ///
-    /// This function will cast the provided `bytes` to a `CStr`
-    /// wrapper after ensuring that the byte slice is nul-terminated
-    /// and does not contain any interior nul bytes.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let cstr = CStr::from_bytes_with_nul(b"hello\0");
-    /// assert!(cstr.is_ok());
-    /// ```
-    ///
-    /// Creating a `CStr` without a trailing nul terminator is an error:
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"hello");
-    /// assert!(c_str.is_err());
-    /// ```
-    ///
-    /// Creating a `CStr` with an interior nul byte is an error:
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"he\0llo\0");
-    /// assert!(c_str.is_err());
-    /// ```
-    pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> {
-        let nul_pos = memchr(0, bytes);
-        if let Some(nul_pos) = nul_pos {
-            if nul_pos + 1 != bytes.len() {
-                return Err(FromBytesWithNulError::interior_nul(nul_pos));
-            }
-            Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
-        } else {
-            Err(FromBytesWithNulError::not_nul_terminated())
+    pub fn from_bytes_with_nul(bytes: &'a [u8]) -> Result<Self, FromBytesWithNulError> {
+        if bytes.last() != Some(&b'\0') || bytes[..bytes.len() - 1].contains(&b'\0') {
+            return Err(FromBytesWithNulError);
         }
-    }
 
-    /// Unsafely creates a C string wrapper from a byte slice.
-    ///
-    /// This function will cast the provided `bytes` to a `CStr` wrapper without
-    /// performing any sanity checks. The provided slice **must** be nul-terminated
-    /// and not contain any interior nul bytes.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::{CStr, CString};
-    ///
-    /// unsafe {
-    ///     let cstring = CString::new("hello").unwrap();
-    ///     let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul());
-    ///     assert_eq!(cstr, &*cstring);
-    /// }
-    /// ```
-    #[inline]
-    pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
-        &*(bytes as *const [u8] as *const CStr)
-    }
-
-    /// Returns the inner pointer to this C string.
-    ///
-    /// The returned pointer will be valid for as long as `self` is, and points
-    /// to a contiguous region of memory terminated with a 0 byte to represent
-    /// the end of the string.
-    ///
-    /// **WARNING**
-    ///
-    /// It is your responsibility to make sure that the underlying memory is not
-    /// freed too early. For example, the following code will cause undefined
-    /// behavior when `ptr` is used inside the `unsafe` block:
-    ///
-    /// ```no_run
-    /// # #![allow(unused_must_use)]
-    /// use std::ffi::{CString};
-    ///
-    /// let ptr = CString::new("Hello").unwrap().as_ptr();
-    /// unsafe {
-    ///     // `ptr` is dangling
-    ///     *ptr;
-    /// }
-    /// ```
-    ///
-    /// This happens because the pointer returned by `as_ptr` does not carry any
-    /// lifetime information and the [`CString`] is deallocated immediately after
-    /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated.
-    /// To fix the problem, bind the `CString` to a local variable:
-    ///
-    /// ```no_run
-    /// # #![allow(unused_must_use)]
-    /// use std::ffi::{CString};
-    ///
-    /// let hello = CString::new("Hello").unwrap();
-    /// let ptr = hello.as_ptr();
-    /// unsafe {
-    ///     // `ptr` is valid because `hello` is in scope
-    ///     *ptr;
-    /// }
-    /// ```
-    ///
-    /// This way, the lifetime of the `CString` in `hello` encompasses
-    /// the lifetime of `ptr` and the `unsafe` block.
-    ///
-    /// [`CString`]: struct.CString.html
-    #[inline]
-    pub fn as_ptr(&self) -> *const c_char {
-        self.inner.as_ptr()
-    }
-
-    /// Converts this C string to a byte slice.
-    ///
-    /// The returned slice will **not** contain the trailing nul terminator that this C
-    /// string has.
-    ///
-    /// > **Note**: This method is currently implemented as a constant-time
-    /// > cast, but it is planned to alter its definition in the future to
-    /// > perform the length calculation whenever this method is called.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap();
-    /// assert_eq!(c_str.to_bytes(), b"foo");
-    /// ```
-    #[inline]
-    pub fn to_bytes(&self) -> &[u8] {
-        let bytes = self.to_bytes_with_nul();
-        &bytes[..bytes.len() - 1]
-    }
-
-    /// Converts this C string to a byte slice containing the trailing 0 byte.
-    ///
-    /// This function is the equivalent of [`to_bytes`] except that it will retain
-    /// the trailing nul terminator instead of chopping it off.
-    ///
-    /// > **Note**: This method is currently implemented as a 0-cost cast, but
-    /// > it is planned to alter its definition in the future to perform the
-    /// > length calculation whenever this method is called.
-    ///
-    /// [`to_bytes`]: #method.to_bytes
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap();
-    /// assert_eq!(c_str.to_bytes_with_nul(), b"foo\0");
-    /// ```
-    #[inline]
-    pub fn to_bytes_with_nul(&self) -> &[u8] {
-        unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
-    }
-
-    /// Yields a [`&str`] slice if the `CStr` contains valid UTF-8.
-    ///
-    /// If the contents of the `CStr` are valid UTF-8 data, this
-    /// function will return the corresponding [`&str`] slice. Otherwise,
-    /// it will return an error with details of where UTF-8 validation failed.
-    ///
-    /// > **Note**: This method is currently implemented to check for validity
-    /// > after a constant-time cast, but it is planned to alter its definition
-    /// > in the future to perform the length calculation in addition to the
-    /// > UTF-8 check whenever this method is called.
-    ///
-    /// [`&str`]: ../primitive.str.html
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap();
-    /// assert_eq!(c_str.to_str(), Ok("foo"));
-    /// ```
-    pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
-        // NB: When CStr is changed to perform the length check in .to_bytes()
-        // instead of in from_ptr(), it may be worth considering if this should
-        // be rewritten to do the UTF-8 check inline with the length calculation
-        // instead of doing it afterwards.
-        str::from_utf8(self.to_bytes())
+        Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
     }
-
-    /// Converts a `CStr` into a [`Cow`]`<`[`str`]`>`.
-    ///
-    /// If the contents of the `CStr` are valid UTF-8 data, this
-    /// function will return a [`Cow`]`::`[`Borrowed`]`(`[`&str`]`)`
-    /// with the the corresponding [`&str`] slice. Otherwise, it will
-    /// replace any invalid UTF-8 sequences with
-    /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
-    /// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result.
-    ///
-    /// > **Note**: This method is currently implemented to check for validity
-    /// > after a constant-time cast, but it is planned to alter its definition
-    /// > in the future to perform the length calculation in addition to the
-    /// > UTF-8 check whenever this method is called.
-    ///
-    /// [`Cow`]: ../borrow/enum.Cow.html
-    /// [`Borrowed`]: ../borrow/enum.Cow.html#variant.Borrowed
-    /// [`Owned`]: ../borrow/enum.Cow.html#variant.Owned
-    /// [`str`]: ../primitive.str.html
-    /// [`String`]: ../string/struct.String.html
-    /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html
-    ///
-    /// # Examples
-    ///
-    /// Calling `to_string_lossy` on a `CStr` containing valid UTF-8:
-    ///
-    /// ```
-    /// use std::borrow::Cow;
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"Hello World\0").unwrap();
-    /// assert_eq!(c_str.to_string_lossy(), Cow::Borrowed("Hello World"));
-    /// ```
-    ///
-    /// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8:
-    ///
-    /// ```
-    /// use std::borrow::Cow;
-    /// use std::ffi::CStr;
-    ///
-    /// let c_str = CStr::from_bytes_with_nul(b"Hello \xF0\x90\x80World\0").unwrap();
-    /// assert_eq!(
-    ///     c_str.to_string_lossy(),
-    ///     Cow::Owned(String::from("Hello �World")) as Cow<str>
-    /// );
-    /// ```
-    pub fn to_string_lossy(&self) -> Cow<str> {
-        String::from_utf8_lossy(self.to_bytes())
-    }
-
-    /// Converts a [`Box`]`<CStr>` into a [`CString`] without copying or allocating.
-    ///
-    /// [`Box`]: ../boxed/struct.Box.html
-    /// [`CString`]: struct.CString.html
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::ffi::CString;
-    ///
-    /// let c_string = CString::new(b"foo".to_vec()).unwrap();
-    /// let boxed = c_string.into_boxed_c_str();
-    /// assert_eq!(boxed.into_c_string(), CString::new("foo").unwrap());
-    /// ```
-    pub fn into_c_string(self: Box<CStr>) -> CString {
-        let raw = Box::into_raw(self) as *mut [u8];
-        CString {
-            inner: unsafe { Box::from_raw(raw) },
+    pub fn from_bytes_until_nul(bytes: &'a [u8]) -> Result<Self, FromBytesUntilNulError> {
+        if !bytes.contains(&b'\0') {
+            return Err(FromBytesUntilNulError);
         }
-    }
-}
 
-impl PartialEq for CStr {
-    fn eq(&self, other: &CStr) -> bool {
-        self.to_bytes().eq(other.to_bytes())
+        Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
     }
-}
-
-impl Eq for CStr {}
-
-impl PartialOrd for CStr {
-    fn partial_cmp(&self, other: &CStr) -> Option<Ordering> {
-        self.to_bytes().partial_cmp(&other.to_bytes())
+    pub fn to_owned_cstring(self) -> CString {
+        CString::from(unsafe { core::ffi::CStr::from_ptr(self.ptr.as_ptr()) })
     }
-}
-
-impl Ord for CStr {
-    fn cmp(&self, other: &CStr) -> Ordering {
-        self.to_bytes().cmp(&other.to_bytes())
+    pub fn borrow(string: &'a CString) -> Self {
+        unsafe { Self::from_ptr(string.as_ptr()) }
     }
 }
 
-impl ToOwned for CStr {
-    type Owned = CString;
+unsafe impl Send for CStr<'_> {}
+unsafe impl Sync for CStr<'_> {}
 
-    fn to_owned(&self) -> CString {
-        CString {
-            inner: self.to_bytes_with_nul().into(),
-        }
-    }
-}
+#[derive(Debug)]
+pub struct FromBytesWithNulError;
 
-impl<'a> From<&'a CStr> for CString {
-    fn from(s: &'a CStr) -> CString {
-        s.to_owned()
-    }
-}
+#[derive(Debug)]
+pub struct FromBytesUntilNulError;
 
-impl ops::Index<ops::RangeFull> for CString {
-    type Output = CStr;
-
-    #[inline]
-    fn index(&self, _index: ops::RangeFull) -> &CStr {
-        self
-    }
-}
-
-impl AsRef<CStr> for CStr {
-    #[inline]
-    fn as_ref(&self) -> &CStr {
-        self
-    }
-}
-
-impl AsRef<CStr> for CString {
-    #[inline]
-    fn as_ref(&self) -> &CStr {
-        self
-    }
-}
+pub use alloc::ffi::CString;
diff --git a/src/db.rs b/src/db.rs
index 6fa6b8b325dd996af58c730f8a1cf4c66952ae78..777fbba8d8468b9e4eb5ed1fa351e5a8acc04a38 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -44,7 +44,7 @@ impl<R: BufRead> Db<R> {
 pub type FileDb = Db<BufReader<File>>;
 
 impl FileDb {
-    pub fn open(path: &CStr, separator: Separator) -> io::Result<Self> {
+    pub fn open(path: CStr, separator: Separator) -> io::Result<Self> {
         let file = File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)?;
         Ok(Db::new(BufReader::new(file), separator))
     }
diff --git a/src/fs.rs b/src/fs.rs
index 47f7c7422e9f82676da2873d04ba1f06a2533c75..37ad5174fb90f5b3ecc502fa924c48724d140967 100644
--- a/src/fs.rs
+++ b/src/fs.rs
@@ -24,14 +24,14 @@ impl File {
         }
     }
 
-    pub fn open(path: &CStr, oflag: c_int) -> io::Result<Self> {
+    pub fn open(path: CStr, oflag: c_int) -> io::Result<Self> {
         match Sys::open(path, oflag, 0) {
             -1 => Err(io::last_os_error()),
             ok => Ok(Self::new(ok)),
         }
     }
 
-    pub fn create(path: &CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> {
+    pub fn create(path: CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> {
         match Sys::open(path, oflag | O_CREAT, mode) {
             -1 => Err(io::last_os_error()),
             ok => Ok(Self::new(ok)),
diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs
index 0f7ab20ec84d9f52b1bf70be838adbf8957de611..b592e7efe4c732fbbd5ed85f51a32481770dad51 100644
--- a/src/header/dlfcn/mod.rs
+++ b/src/header/dlfcn/mod.rs
@@ -12,7 +12,7 @@ pub const RTLD_NOW: c_int = 0x0002;
 pub const RTLD_GLOBAL: c_int = 0x0100;
 pub const RTLD_LOCAL: c_int = 0x0000;
 
-static ERROR_NOT_SUPPORTED: &'static CStr = c_str!("dlfcn not supported");
+static ERROR_NOT_SUPPORTED: CStr = c_str!("dlfcn not supported");
 
 #[thread_local]
 static ERROR: AtomicUsize = AtomicUsize::new(0);
diff --git a/src/header/grp/mod.rs b/src/header/grp/mod.rs
index 282ffb6cf588bfb4ca7df8f5060a021529e08f8f..8dc0e53436597ead12390edd21bdc1ac19b25fbf 100644
--- a/src/header/grp/mod.rs
+++ b/src/header/grp/mod.rs
@@ -1,8 +1,9 @@
-//! grp implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html
+//! grp implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html
 
 use core::{
+    cell::SyncUnsafeCell,
     convert::{TryFrom, TryInto},
-    mem,
+    mem::{self, MaybeUninit},
     num::ParseIntError,
     ops::{Deref, DerefMut},
     pin::Pin,
@@ -11,8 +12,6 @@ use core::{
     str::Matches,
 };
 
-use lazy_static::lazy_static;
-
 use alloc::{
     borrow::ToOwned,
     string::{FromUtf8Error, String},
@@ -80,9 +79,7 @@ static mut GROUP: group = group {
     gr_mem: ptr::null_mut(),
 };
 
-lazy_static! {
-    static ref LINE_READER: Mutex<Option<Lines<BufReader<File>>>> = Mutex::new(None);
-}
+static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
 
 #[repr(C)]
 #[derive(Debug)]
@@ -307,7 +304,7 @@ pub unsafe extern "C" fn getgrnam_r(
 // MT-Unsafe race:grent race:grentbuf locale
 #[no_mangle]
 pub unsafe extern "C" fn getgrent() -> *mut group {
-    let mut line_reader = LINE_READER.lock();
+    let mut line_reader = &mut *LINE_READER.get();
 
     if line_reader.is_none() {
         let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return ptr::null_mut() };
@@ -331,60 +328,74 @@ pub unsafe extern "C" fn getgrent() -> *mut group {
 // MT-Unsafe race:grent locale
 #[no_mangle]
 pub unsafe extern "C" fn endgrent() {
-    let mut line_reader = LINE_READER.lock();
-    *line_reader = None;
+    *(&mut *LINE_READER.get()) = None;
 }
 
 // MT-Unsafe race:grent locale
 #[no_mangle]
 pub unsafe extern "C" fn setgrent() {
-    let mut line_reader = LINE_READER.lock();
+    let mut line_reader = &mut *LINE_READER.get();
     let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return };
     *line_reader = Some(BufReader::new(db).lines());
 }
 
+// MT-Safe locale
+// Not POSIX
 #[no_mangle]
 pub unsafe extern "C" fn getgrouplist(
     user: *const c_char,
     group: gid_t,
     groups: *mut gid_t,
-    ngroups: i32,
-) -> i32 {
-    let mut grps = Vec::<gid_t>::from_raw_parts(groups, 0, ngroups as usize);
-    let Ok(usr) = (crate::c_str::CStr::from_ptr(user).to_str()) else { return 0 };
+    ngroups: *mut c_int,
+) -> c_int {
+    let mut grps =
+        slice::from_raw_parts_mut(groups.cast::<MaybeUninit<gid_t>>(), ngroups.read() as usize);
+
+    // FIXME: This API probably expects the group database to already exist in memory, as it
+    // doesn't seem to have any documented error handling.
+
+    let Ok(user) = (crate::c_str::CStr::from_ptr(user).to_str()) else { return 0 };
 
     let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return 0; };
 
+    let mut groups_found: c_int = 0;
+
     for line in BufReader::new(db).lines() {
-        if grps.len() >= ngroups as usize {
-            return ngroups;
+        let Ok(line) = line else {
+            return 0;
+        };
+
+        let mut parts = line.split(SEPARATOR);
+
+        let group_name = parts.next().unwrap_or("");
+        let group_password = parts.next().unwrap_or("");
+        let group_id = parts.next().unwrap_or("-1").parse::<c_int>().unwrap();
+        let members = parts
+            .next()
+            .unwrap_or("")
+            .split(",")
+            .map(|i| i.trim())
+            .collect::<Vec<_>>();
+
+        if !members.iter().any(|i| *i == user) {
+            continue;
         }
 
-        match line {
-            Err(_) => return 0,
-            Ok(line) => {
-                let mut parts = line.split(SEPARATOR);
-
-                let group_name = parts.next().unwrap_or("");
-                let group_password = parts.next().unwrap_or("");
-                let group_id = parts.next().unwrap_or("-1").parse::<i32>().unwrap();
-                let members = parts
-                    .next()
-                    .unwrap_or("")
-                    .split(",")
-                    .map(|i| i.trim())
-                    .collect::<Vec<_>>();
-
-                if members.iter().any(|i| *i == usr) {
-                    grps.push(group_id);
-                }
-            }
+        if let Some(dst) = grps.get_mut(groups_found as usize) {
+            dst.write(group_id);
+        }
+
+        groups_found = match groups_found.checked_add(1) {
+            Some(g) => g,
+            None => break,
         };
     }
 
-    if grps.len() <= 0 {
-        grps.push(group);
-    }
+    ngroups.write(groups_found);
 
-    return grps.len() as i32;
+    if groups_found as usize > grps.len() {
+        -1
+    } else {
+        grps.len() as c_int
+    }
 }
diff --git a/src/header/netdb/host.rs b/src/header/netdb/host.rs
index 1b4ba101632ea849513f94c85f4fdbe6258fcf9e..c80327fec1bd50575f5ee9360132bfa1b1aafe60 100644
--- a/src/header/netdb/host.rs
+++ b/src/header/netdb/host.rs
@@ -45,7 +45,7 @@ pub unsafe extern "C" fn endhostent() {
 pub unsafe extern "C" fn sethostent(stayopen: c_int) {
     HOST_STAYOPEN = stayopen;
     if HOSTDB < 0 {
-        HOSTDB = Sys::open(&CString::new("/etc/hosts").unwrap(), O_RDONLY, 0)
+        HOSTDB = Sys::open(c_str!("/etc/hosts"), O_RDONLY, 0)
     } else {
         Sys::lseek(HOSTDB, 0, SEEK_SET);
     }
@@ -55,7 +55,7 @@ pub unsafe extern "C" fn sethostent(stayopen: c_int) {
 #[no_mangle]
 pub unsafe extern "C" fn gethostent() -> *mut hostent {
     if HOSTDB < 0 {
-        HOSTDB = Sys::open(&CString::new("/etc/hosts").unwrap(), O_RDONLY, 0);
+        HOSTDB = Sys::open(c_str!("/etc/hosts"), O_RDONLY, 0);
     }
     let mut rlb = RawLineBuffer::new(HOSTDB);
     rlb.seek(H_POS);
diff --git a/src/header/netdb/linux.rs b/src/header/netdb/linux.rs
index f174170164cc34f3f0ecd3bcfb1f59eb5bacd588..e7455c6e56c8f0b03048d92449e01b5453a3c95e 100644
--- a/src/header/netdb/linux.rs
+++ b/src/header/netdb/linux.rs
@@ -7,7 +7,7 @@ use crate::{
 use alloc::string::String;
 
 pub fn get_dns_server() -> String {
-    let file = match File::open(&CString::new("/etc/resolv.conf").unwrap(), fcntl::O_RDONLY) {
+    let file = match File::open(c_str!("/etc/resolv.conf"), fcntl::O_RDONLY) {
         Ok(file) => file,
         Err(_) => return String::new(), // TODO: better error handling
     };
diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs
index 4f7f47e75eb9b66cf479cc890d77fe5799984823..9bd4dfb780d3e6fd91c71852bd38f83000451ca2 100644
--- a/src/header/netdb/mod.rs
+++ b/src/header/netdb/mod.rs
@@ -370,7 +370,7 @@ pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut netent {
 #[no_mangle]
 pub unsafe extern "C" fn getnetent() -> *mut netent {
     if NETDB == 0 {
-        NETDB = Sys::open(&CString::new("/etc/networks").unwrap(), O_RDONLY, 0);
+        NETDB = Sys::open(c_str!("/etc/networks"), O_RDONLY, 0);
     }
 
     let mut rlb = RawLineBuffer::new(NETDB);
@@ -484,7 +484,7 @@ pub unsafe extern "C" fn getprotobynumber(number: c_int) -> *mut protoent {
 #[no_mangle]
 pub unsafe extern "C" fn getprotoent() -> *mut protoent {
     if PROTODB == 0 {
-        PROTODB = Sys::open(&CString::new("/etc/protocols").unwrap(), O_RDONLY, 0);
+        PROTODB = Sys::open(c_str!("/etc/protocols"), O_RDONLY, 0);
     }
 
     let mut rlb = RawLineBuffer::new(PROTODB);
@@ -603,7 +603,7 @@ pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mu
 #[no_mangle]
 pub unsafe extern "C" fn getservent() -> *mut servent {
     if SERVDB == 0 {
-        SERVDB = Sys::open(&CString::new("/etc/services").unwrap(), O_RDONLY, 0);
+        SERVDB = Sys::open(c_str!("/etc/services"), O_RDONLY, 0);
     }
     let mut rlb = RawLineBuffer::new(SERVDB);
     rlb.seek(S_POS);
@@ -690,7 +690,7 @@ pub unsafe extern "C" fn getservent() -> *mut servent {
 pub unsafe extern "C" fn setnetent(stayopen: c_int) {
     NET_STAYOPEN = stayopen;
     if NETDB == 0 {
-        NETDB = Sys::open(&CString::new("/etc/networks").unwrap(), O_RDONLY, 0)
+        NETDB = Sys::open(c_str!("/etc/networks"), O_RDONLY, 0)
     } else {
         Sys::lseek(NETDB, 0, SEEK_SET);
         N_POS = 0;
@@ -701,7 +701,7 @@ pub unsafe extern "C" fn setnetent(stayopen: c_int) {
 pub unsafe extern "C" fn setprotoent(stayopen: c_int) {
     PROTO_STAYOPEN = stayopen;
     if PROTODB == 0 {
-        PROTODB = Sys::open(&CString::new("/etc/protocols").unwrap(), O_RDONLY, 0)
+        PROTODB = Sys::open(c_str!("/etc/protocols"), O_RDONLY, 0)
     } else {
         Sys::lseek(PROTODB, 0, SEEK_SET);
         P_POS = 0;
@@ -712,7 +712,7 @@ pub unsafe extern "C" fn setprotoent(stayopen: c_int) {
 pub unsafe extern "C" fn setservent(stayopen: c_int) {
     SERV_STAYOPEN = stayopen;
     if SERVDB == 0 {
-        SERVDB = Sys::open(&CString::new("/etc/services").unwrap(), O_RDONLY, 0)
+        SERVDB = Sys::open(c_str!("/etc/services"), O_RDONLY, 0)
     } else {
         Sys::lseek(SERVDB, 0, SEEK_SET);
         S_POS = 0;
@@ -727,17 +727,8 @@ pub unsafe extern "C" fn getaddrinfo(
     res: *mut *mut addrinfo,
 ) -> c_int {
     //TODO: getaddrinfo
-    let node_opt = if node.is_null() {
-        None
-    } else {
-        Some(CStr::from_ptr(node))
-    };
-
-    let service_opt = if service.is_null() {
-        None
-    } else {
-        Some(CStr::from_ptr(service))
-    };
+    let node_opt = CStr::from_nullable_ptr(node);
+    let service_opt = CStr::from_nullable_ptr(service);
 
     let hints_opt = if hints.is_null() { None } else { Some(&*hints) };
 
@@ -791,7 +782,7 @@ pub unsafe extern "C" fn getaddrinfo(
 
             let ai_canonname = if ai_flags & AI_CANONNAME > 0 {
                 ai_flags &= !AI_CANONNAME;
-                node.to_owned().into_raw()
+                node.to_owned_cstring().into_raw()
             } else {
                 ptr::null_mut()
             };
@@ -859,7 +850,7 @@ pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) {
     while !ai.is_null() {
         let bai = Box::from_raw(ai);
         if !bai.ai_canonname.is_null() {
-            CString::from_raw(bai.ai_canonname);
+            drop(CString::from_raw(bai.ai_canonname));
         }
         if !bai.ai_addr.is_null() {
             if bai.ai_addrlen == mem::size_of::<sockaddr_in>() {
diff --git a/src/header/netdb/redox.rs b/src/header/netdb/redox.rs
index d52628b186ee6a7eee90081af25a89222f12e4c7..5c307eb894e0dbd2deb3196fb26bc5113108bf3a 100644
--- a/src/header/netdb/redox.rs
+++ b/src/header/netdb/redox.rs
@@ -1,9 +1,9 @@
-use crate::{c_str::CString, fs::File, header::fcntl, io::Read};
+use crate::{fs::File, header::fcntl, io::Read};
 use alloc::string::String;
 
 pub fn get_dns_server() -> String {
     let mut string = String::new();
-    let mut file = File::open(&CString::new("/etc/net/dns").unwrap(), fcntl::O_RDONLY).unwrap(); // TODO: error handling
+    let mut file = File::open(c_str!("/etc/net/dns"), fcntl::O_RDONLY).unwrap(); // TODO: error handling
     file.read_to_string(&mut string).unwrap(); // TODO: error handling
     string
 }
diff --git a/src/header/stdio/default.rs b/src/header/stdio/default.rs
index 341d76bd161e870467f89affe3dc5d739e68191e..624efeadf5855e993d467d4e896c02d4ac43a219 100644
--- a/src/header/stdio/default.rs
+++ b/src/header/stdio/default.rs
@@ -1,10 +1,17 @@
 use super::{constants, Buffer, BUFSIZ, FILE};
 use core::{cell::UnsafeCell, ptr};
 
-use crate::{fs::File, io::LineWriter, platform::types::*, sync::Mutex};
+use crate::{
+    fs::File,
+    io::LineWriter,
+    platform::types::*,
+    sync::{Mutex, Once},
+};
 use alloc::{boxed::Box, vec::Vec};
 
+// TODO: Change FILE to allow const fn initialization?
 pub struct GlobalFile(UnsafeCell<FILE>);
+
 impl GlobalFile {
     fn new(file: c_int, flags: c_int) -> Self {
         let file = File::new(file);
@@ -32,15 +39,19 @@ impl GlobalFile {
 // statics need to be Sync
 unsafe impl Sync for GlobalFile {}
 
-lazy_static! {
-    #[allow(non_upper_case_globals)]
-    pub static ref default_stdin: GlobalFile = GlobalFile::new(0, constants::F_NOWR);
-
-    #[allow(non_upper_case_globals)]
-    pub static ref default_stdout: GlobalFile = GlobalFile::new(1, constants::F_NORD);
+// TODO: Allow const fn initialization of FILE
+static DEFAULT_STDIN: Once<GlobalFile> = Once::new();
+static DEFAULT_STDOUT: Once<GlobalFile> = Once::new();
+static DEFAULT_STDERR: Once<GlobalFile> = Once::new();
 
-    #[allow(non_upper_case_globals)]
-    pub static ref default_stderr: GlobalFile = GlobalFile::new(2, constants::F_NORD);
+pub fn default_stdin() -> &'static GlobalFile {
+    DEFAULT_STDIN.call_once(|| GlobalFile::new(0, constants::F_NOWR))
+}
+pub fn default_stdout() -> &'static GlobalFile {
+    DEFAULT_STDOUT.call_once(|| GlobalFile::new(1, constants::F_NORD))
+}
+pub fn default_stderr() -> &'static GlobalFile {
+    DEFAULT_STDERR.call_once(|| GlobalFile::new(2, constants::F_NORD))
 }
 
 #[no_mangle]
diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs
index 5a43bd8d35145ff39c32ee66a6062e644d9a61ba..b8bb2b5b8f33076ffda2447f88e0cc64ecabc267 100644
--- a/src/header/stdio/mod.rs
+++ b/src/header/stdio/mod.rs
@@ -107,11 +107,16 @@ pub struct FILE {
     file: File,
     // pub for stdio_ext
     pub(crate) flags: c_int,
+
+    // TODO: Is the read_buf dropped?
     read_buf: Buffer<'static>,
+
     read_pos: usize,
     read_size: usize,
     unget: Vec<u8>,
     // pub for stdio_ext
+
+    // TODO: To support const fn initialization, use static dispatch (perhaps partially)?
     pub(crate) writer: Box<dyn Writer + Send>,
 
     // Optional pid for use with popen/pclose
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 7ce198255975fc1711fb7a37101c3c4023cbbef1..921faf6f5320ec72f59ad3c4bf053bca0e5106c8 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -3,10 +3,10 @@
 use core::{convert::TryFrom, intrinsics, iter, mem, ptr, slice};
 use rand::{
     distributions::{Alphanumeric, Distribution, Uniform},
-    prng::XorShiftRng,
-    rngs::JitterRng,
     Rng, SeedableRng,
 };
+use rand_jitter::JitterRng;
+use rand_xorshift::XorShiftRng;
 
 use crate::{
     c_str::CStr,
@@ -25,6 +25,7 @@ use crate::{
     },
     ld_so,
     platform::{self, types::*, Pal, Sys},
+    sync::Once,
 };
 
 mod rand48;
@@ -44,8 +45,11 @@ static mut ATEXIT_FUNCS: [Option<extern "C" fn()>; 32] = [None; 32];
 static mut L64A_BUFFER: [c_char; 7] = [0; 7]; // up to 6 digits plus null terminator
 static mut RNG: Option<XorShiftRng> = None;
 
-lazy_static! {
-    static ref RNG_SAMPLER: Uniform<c_int> = Uniform::new_inclusive(0, RAND_MAX);
+// TODO: This could be const fn, but the trait system won't allow that.
+static RNG_SAMPLER: Once<Uniform<c_int>> = Once::new();
+
+fn rng_sampler() -> &'static Uniform<c_int> {
+    RNG_SAMPLER.call_once(|| Uniform::new_inclusive(0, RAND_MAX))
 }
 
 #[no_mangle]
@@ -131,55 +135,53 @@ pub unsafe extern "C" fn atof(s: *const c_char) -> c_double {
 }
 
 macro_rules! dec_num_from_ascii {
-    ($s:expr, $t:ty) => {
-        unsafe {
-            let mut s = $s;
-            // Iterate past whitespace
-            while ctype::isspace(*s as c_int) != 0 {
+    ($s:expr, $t:ty) => {{
+        let mut s = $s;
+        // Iterate past whitespace
+        while ctype::isspace(*s as c_int) != 0 {
+            s = s.offset(1);
+        }
+
+        // Find out if there is a - sign
+        let neg_sign = match *s {
+            0x2d => {
                 s = s.offset(1);
+                true
             }
-
-            // Find out if there is a - sign
-            let neg_sign = match *s {
-                0x2d => {
-                    s = s.offset(1);
-                    true
-                }
-                // '+' increment s and continue parsing
-                0x2b => {
-                    s = s.offset(1);
-                    false
-                }
-                _ => false,
-            };
-
-            let mut n: $t = 0;
-            while ctype::isdigit(*s as c_int) != 0 {
-                n = 10 * n - (*s as $t - 0x30);
+            // '+' increment s and continue parsing
+            0x2b => {
                 s = s.offset(1);
+                false
             }
+            _ => false,
+        };
 
-            if neg_sign {
-                n
-            } else {
-                -n
-            }
+        let mut n: $t = 0;
+        while ctype::isdigit(*s as c_int) != 0 {
+            n = 10 * n - (*s as $t - 0x30);
+            s = s.offset(1);
         }
-    };
+
+        if neg_sign {
+            n
+        } else {
+            -n
+        }
+    }};
 }
 
 #[no_mangle]
-pub extern "C" fn atoi(s: *const c_char) -> c_int {
+pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int {
     dec_num_from_ascii!(s, c_int)
 }
 
 #[no_mangle]
-pub extern "C" fn atol(s: *const c_char) -> c_long {
+pub unsafe extern "C" fn atol(s: *const c_char) -> c_long {
     dec_num_from_ascii!(s, c_long)
 }
 
 #[no_mangle]
-pub extern "C" fn atoll(s: *const c_char) -> c_longlong {
+pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong {
     dec_num_from_ascii!(s, c_longlong)
 }
 
@@ -643,12 +645,16 @@ fn get_nstime() -> u64 {
 }
 
 #[no_mangle]
-pub extern "C" fn mkostemps(name: *mut c_char, suffix_len: c_int, mut flags: c_int) -> c_int {
+pub unsafe extern "C" fn mkostemps(
+    name: *mut c_char,
+    suffix_len: c_int,
+    mut flags: c_int,
+) -> c_int {
     flags &= !O_ACCMODE;
     flags |= O_RDWR | O_CREAT | O_EXCL;
 
     inner_mktemp(name, suffix_len, || {
-        let name = unsafe { CStr::from_ptr(name) };
+        let name = CStr::from_ptr(name);
         let fd = Sys::open(name, flags, 0o600);
 
         if fd >= 0 {
@@ -661,15 +667,15 @@ pub extern "C" fn mkostemps(name: *mut c_char, suffix_len: c_int, mut flags: c_i
 }
 
 #[no_mangle]
-pub extern "C" fn mkstemp(name: *mut c_char) -> c_int {
+pub unsafe extern "C" fn mkstemp(name: *mut c_char) -> c_int {
     mkostemps(name, 0, 0)
 }
 #[no_mangle]
-pub extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int {
+pub unsafe extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int {
     mkostemps(name, 0, flags)
 }
 #[no_mangle]
-pub extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int {
+pub unsafe extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int {
     mkostemps(name, suffix_len, 0)
 }
 
@@ -744,7 +750,7 @@ pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int {
 }
 
 #[no_mangle]
-pub extern "C" fn qsort(
+pub unsafe extern "C" fn qsort(
     base: *mut c_void,
     nel: size_t,
     width: size_t,
@@ -763,10 +769,10 @@ pub extern "C" fn qsort(
 #[no_mangle]
 pub unsafe extern "C" fn rand() -> c_int {
     match RNG {
-        Some(ref mut rng) => RNG_SAMPLER.sample(rng),
+        Some(ref mut rng) => rng_sampler().sample(rng),
         None => {
             let mut rng = XorShiftRng::from_seed([1; 16]);
-            let ret = RNG_SAMPLER.sample(&mut rng);
+            let ret = rng_sampler().sample(&mut rng);
             RNG = Some(rng);
             ret
         }
@@ -782,7 +788,7 @@ pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
         let seed_arr: [u8; 16] = mem::transmute([*seed; 16 / mem::size_of::<c_uint>()]);
 
         let mut rng = XorShiftRng::from_seed(seed_arr);
-        let ret = RNG_SAMPLER.sample(&mut rng);
+        let ret = rng_sampler().sample(&mut rng);
 
         *seed = ret as _;
 
@@ -861,7 +867,7 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char
 
     let out = slice::from_raw_parts_mut(ptr as *mut u8, limits::PATH_MAX);
     {
-        let file = match File::open(&CStr::from_ptr(pathname), O_PATH | O_CLOEXEC) {
+        let file = match File::open(CStr::from_ptr(pathname), O_PATH | O_CLOEXEC) {
             Ok(file) => file,
             Err(_) => return ptr::null_mut(),
         };
@@ -945,7 +951,7 @@ pub unsafe extern "C" fn setenv(
 }
 
 // #[no_mangle]
-pub extern "C" fn setkey(key: *const c_char) {
+pub unsafe extern "C" fn setkey(key: *const c_char) {
     unimplemented!();
 }
 
diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs
index 3161c3af9a79b42380ade46ca395fc3c250644f3..7dfb5482c520660f7631fa3ce815557e4c13ac32 100644
--- a/src/ld_so/dso.rs
+++ b/src/ld_so/dso.rs
@@ -18,17 +18,17 @@ use core::{
 };
 #[cfg(target_pointer_width = "32")]
 use goblin::elf32::{
+    dynamic::{Dyn, DT_DEBUG, DT_RUNPATH},
     header::ET_DYN,
     program_header,
-    r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
     section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY},
     sym,
 };
 #[cfg(target_pointer_width = "64")]
 use goblin::elf64::{
+    dynamic::{Dyn, DT_DEBUG, DT_RUNPATH},
     header::ET_DYN,
     program_header,
-    r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
     section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY},
     sym,
 };
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
index acf22736aef5b1fb1c97565d3fef63e67da9cee0..6e76e53b848405f100cf0ebd4c357316ca2cbdb4 100644
--- a/src/ld_so/linker.rs
+++ b/src/ld_so/linker.rs
@@ -11,7 +11,7 @@ use goblin::{
 };
 
 use crate::{
-    c_str::CString,
+    c_str::{CStr, CString},
     fs::File,
     header::{
         dl_tls::{__tls_get_addr, dl_tls_index},
@@ -293,7 +293,7 @@ impl Linker {
         let path_c = CString::new(path)
             .map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?;
         let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
-        let mut file = File::open(&path_c, flags)
+        let mut file = File::open(CStr::borrow(&path_c), flags)
             .map_err(|err| Error::Malformed(format!("failed to open '{}': {}", path, err)))?;
         file.read_to_end(&mut data)
             .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?;
diff --git a/src/lib.rs b/src/lib.rs
index fcddb73f0cdea430c738cde56a59cd0b1235b76b..7b04c82c52cbf842d945b06c78bb163de49ce9be 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(linkage)]
 #![feature(stmt_expr_attributes)]
 #![feature(str_internals)]
+#![feature(sync_unsafe_cell)]
 #![feature(thread_local)]
 #![feature(vec_into_raw_parts)]
 #![allow(clippy::cast_lossless)]
@@ -28,8 +29,6 @@ extern crate alloc;
 extern crate cbitset;
 extern crate core_io;
 extern crate goblin;
-#[macro_use]
-extern crate lazy_static;
 extern crate memchr;
 #[macro_use]
 extern crate memoffset;
@@ -43,9 +42,6 @@ extern crate sc;
 #[cfg(target_os = "redox")]
 extern crate syscall;
 
-#[cfg(target_os = "redox")]
-extern crate spin;
-
 #[macro_use]
 mod macros;
 pub mod c_str;
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index 5bdb26f87a97dba2e72db6c100d3996eec60ea1e..5d28143139a8153e3aa4ae14fd1239ff15dd6b49 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -89,7 +89,7 @@ impl Sys {
 }
 
 impl Pal for Sys {
-    fn access(path: &CStr, mode: c_int) -> c_int {
+    fn access(path: CStr, mode: c_int) -> c_int {
         e(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }) as c_int
     }
 
@@ -97,15 +97,15 @@ impl Pal for Sys {
         unsafe { syscall!(BRK, addr) as *mut c_void }
     }
 
-    fn chdir(path: &CStr) -> c_int {
+    fn chdir(path: CStr) -> c_int {
         e(unsafe { syscall!(CHDIR, path.as_ptr()) }) as c_int
     }
 
-    fn chmod(path: &CStr, mode: mode_t) -> c_int {
+    fn chmod(path: CStr, mode: mode_t) -> c_int {
         e(unsafe { syscall!(FCHMODAT, AT_FDCWD, path.as_ptr(), mode, 0) }) as c_int
     }
 
-    fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
         e(unsafe {
             syscall!(
                 FCHOWNAT,
@@ -141,7 +141,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(DUP3, fildes, fildes2, 0) }) as c_int
     }
 
-    unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
+    unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
         e(syscall!(EXECVE, path.as_ptr(), argv, envp)) as c_int
     }
     unsafe fn fexecve(fildes: c_int, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
@@ -252,7 +252,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(UTIMENSAT, fd, ptr::null::<c_char>(), times, 0) }) as c_int
     }
 
-    fn utimens(path: &CStr, times: *const timespec) -> c_int {
+    fn utimens(path: CStr, times: *const timespec) -> c_int {
         e(unsafe { syscall!(UTIMENSAT, AT_FDCWD, path.as_ptr(), times, 0) }) as c_int
     }
 
@@ -332,11 +332,11 @@ impl Pal for Sys {
         e(unsafe { syscall!(GETUID) }) as uid_t
     }
 
-    fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
         e(unsafe { syscall!(LCHOWN, path.as_ptr(), owner, group) }) as c_int
     }
 
-    fn link(path1: &CStr, path2: &CStr) -> c_int {
+    fn link(path1: CStr, path2: CStr) -> c_int {
         e(unsafe {
             syscall!(
                 LINKAT,
@@ -353,11 +353,11 @@ impl Pal for Sys {
         e(unsafe { syscall!(LSEEK, fildes, offset, whence) }) as off_t
     }
 
-    fn mkdir(path: &CStr, mode: mode_t) -> c_int {
+    fn mkdir(path: CStr, mode: mode_t) -> c_int {
         e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }) as c_int
     }
 
-    fn mkfifo(path: &CStr, mode: mode_t) -> c_int {
+    fn mkfifo(path: CStr, mode: mode_t) -> c_int {
         e(unsafe { syscall!(MKNODAT, AT_FDCWD, path.as_ptr(), mode | S_IFIFO, 0) }) as c_int
     }
 
@@ -408,7 +408,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(NANOSLEEP, rqtp, rmtp) }) as c_int
     }
 
-    fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int {
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int {
         e(unsafe { syscall!(OPENAT, AT_FDCWD, path.as_ptr(), oflag, mode) }) as c_int
     }
 
@@ -491,7 +491,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) }) as ssize_t
     }
 
-    fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t {
+    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t {
         e(unsafe {
             syscall!(
                 READLINKAT,
@@ -503,11 +503,11 @@ impl Pal for Sys {
         }) as ssize_t
     }
 
-    fn rename(old: &CStr, new: &CStr) -> c_int {
+    fn rename(old: CStr, new: CStr) -> c_int {
         e(unsafe { syscall!(RENAMEAT, AT_FDCWD, old.as_ptr(), AT_FDCWD, new.as_ptr()) }) as c_int
     }
 
-    fn rmdir(path: &CStr) -> c_int {
+    fn rmdir(path: CStr) -> c_int {
         e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), AT_REMOVEDIR) }) as c_int
     }
 
@@ -539,7 +539,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(SETSID) }) as c_int
     }
 
-    fn symlink(path1: &CStr, path2: &CStr) -> c_int {
+    fn symlink(path1: CStr, path2: CStr) -> c_int {
         e(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }) as c_int
     }
 
@@ -555,7 +555,7 @@ impl Pal for Sys {
         e(unsafe { syscall!(UNAME, utsname, 0) }) as c_int
     }
 
-    fn unlink(path: &CStr) -> c_int {
+    fn unlink(path: CStr) -> c_int {
         e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }) as c_int
     }
 
diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs
index d4268820d941a18a95c2cf8cb0dd62395d1f7f33..5de748d85d2287e5071c82c140b044ec26b03e8a 100644
--- a/src/platform/pal/mod.rs
+++ b/src/platform/pal/mod.rs
@@ -25,15 +25,15 @@ pub use self::socket::PalSocket;
 mod socket;
 
 pub trait Pal {
-    fn access(path: &CStr, mode: c_int) -> c_int;
+    fn access(path: CStr, mode: c_int) -> c_int;
 
     fn brk(addr: *mut c_void) -> *mut c_void;
 
-    fn chdir(path: &CStr) -> c_int;
+    fn chdir(path: CStr) -> c_int;
 
-    fn chmod(path: &CStr, mode: mode_t) -> c_int;
+    fn chmod(path: CStr, mode: mode_t) -> c_int;
 
-    fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int;
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int;
 
     fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int;
 
@@ -47,7 +47,7 @@ pub trait Pal {
 
     fn dup2(fildes: c_int, fildes2: c_int) -> c_int;
 
-    unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
+    unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
     unsafe fn fexecve(fildes: c_int, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
 
     fn exit(status: c_int) -> !;
@@ -87,7 +87,7 @@ pub trait Pal {
 
     fn futimens(fd: c_int, times: *const timespec) -> c_int;
 
-    fn utimens(path: &CStr, times: *const timespec) -> c_int;
+    fn utimens(path: CStr, times: *const timespec) -> c_int;
 
     fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
 
@@ -128,15 +128,15 @@ pub trait Pal {
 
     fn getuid() -> uid_t;
 
-    fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int;
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int;
 
-    fn link(path1: &CStr, path2: &CStr) -> c_int;
+    fn link(path1: CStr, path2: CStr) -> c_int;
 
     fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t;
 
-    fn mkdir(path: &CStr, mode: mode_t) -> c_int;
+    fn mkdir(path: CStr, mode: mode_t) -> c_int;
 
-    fn mkfifo(path: &CStr, mode: mode_t) -> c_int;
+    fn mkfifo(path: CStr, mode: mode_t) -> c_int;
 
     unsafe fn mlock(addr: *const c_void, len: usize) -> c_int;
 
@@ -165,7 +165,7 @@ pub trait Pal {
 
     fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int;
 
-    fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int;
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int;
 
     fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int;
 
@@ -179,11 +179,11 @@ pub trait Pal {
 
     fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t;
 
-    fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t;
+    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t;
 
-    fn rename(old: &CStr, new: &CStr) -> c_int;
+    fn rename(old: CStr, new: CStr) -> c_int;
 
-    fn rmdir(path: &CStr) -> c_int;
+    fn rmdir(path: CStr) -> c_int;
 
     fn sched_yield() -> c_int;
 
@@ -199,7 +199,7 @@ pub trait Pal {
 
     fn setsid() -> c_int;
 
-    fn symlink(path1: &CStr, path2: &CStr) -> c_int;
+    fn symlink(path1: CStr, path2: CStr) -> c_int;
 
     fn sync() -> c_int;
 
@@ -207,7 +207,7 @@ pub trait Pal {
 
     fn uname(utsname: *mut utsname) -> c_int;
 
-    fn unlink(path: &CStr) -> c_int;
+    fn unlink(path: CStr) -> c_int;
 
     fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t;
 
diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs
index f0375437300a5891905893efe17d55d9b36a0963..5379ea788285f416a424444ba2780a892f3dbbe1 100644
--- a/src/platform/redox/exec.rs
+++ b/src/platform/redox/exec.rs
@@ -81,7 +81,7 @@ pub enum ArgEnv<'a> {
 }
 
 pub enum Executable<'a> {
-    AtPath(&'a CStr),
+    AtPath(CStr<'a>),
     InFd { file: File, arg0: &'a [u8] },
 }
 
@@ -187,7 +187,8 @@ pub fn execve(
             interpreter.pop().unwrap();
         }
         let cstring = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?;
-        image_file = File::open(&cstring, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?;
+        image_file = File::open(CStr::borrow(&cstring), O_RDONLY as c_int)
+            .map_err(|_| Error::new(ENOENT))?;
 
         // Make sure path is kept alive long enough, and push it to the arguments
         _interpreter_path = Some(cstring);
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index a532de7fbbbc7f5b44038e1975d390311660e93d..56b2053796e24b25b79e4485e0edf146ff660356 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -80,7 +80,7 @@ pub fn e(sys: Result<usize>) -> usize {
 pub struct Sys;
 
 impl Pal for Sys {
-    fn access(path: &CStr, mode: c_int) -> c_int {
+    fn access(path: CStr, mode: c_int) -> c_int {
         let fd = match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
             Ok(fd) => fd,
             Err(_) => return -1,
@@ -165,19 +165,19 @@ impl Pal for Sys {
         }
     }
 
-    fn chdir(path: &CStr) -> c_int {
+    fn chdir(path: CStr) -> c_int {
         let path = path_from_c_str!(path);
         e(path::chdir(path).map(|()| 0)) as c_int
     }
 
-    fn chmod(path: &CStr, mode: mode_t) -> c_int {
+    fn chmod(path: CStr, mode: mode_t) -> c_int {
         match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
             Ok(file) => Self::fchmod(*file, mode),
             Err(_) => -1,
         }
     }
 
-    fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
+    fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
         match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
             Ok(file) => Self::fchown(*file, owner, group),
             Err(_) => -1,
@@ -232,7 +232,7 @@ impl Pal for Sys {
         loop {}
     }
 
-    unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
+    unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int {
         e(self::exec::execve(
             Executable::AtPath(path),
             self::exec::ArgEnv::C { argv, envp },
@@ -257,7 +257,7 @@ impl Pal for Sys {
             !0
         } else {
             match str::from_utf8(&buf[..res]) {
-                Ok(path) => Sys::chdir(&CString::new(path).unwrap()),
+                Ok(path) => e(path::chdir(path).map(|()| 0)) as c_int,
                 Err(_) => {
                     unsafe { errno = EINVAL };
                     return -1;
@@ -387,7 +387,7 @@ impl Pal for Sys {
         e(syscall::futimens(fd as usize, &times)) as c_int
     }
 
-    fn utimens(path: &CStr, times: *const timespec) -> c_int {
+    fn utimens(path: CStr, times: *const timespec) -> c_int {
         match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
             Ok(file) => Self::futimens(*file, times),
             Err(_) => -1,
@@ -637,7 +637,7 @@ impl Pal for Sys {
         e(syscall::getuid()) as pid_t
     }
 
-    fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
+    fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int {
         // TODO: Is it correct for regular chown to use O_PATH? On Linux the meaning of that flag
         // is to forbid file operations, including fchown.
 
@@ -648,7 +648,7 @@ impl Pal for Sys {
         }
     }
 
-    fn link(path1: &CStr, path2: &CStr) -> c_int {
+    fn link(path1: CStr, path2: CStr) -> c_int {
         e(unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8) })
             as c_int
     }
@@ -661,7 +661,7 @@ impl Pal for Sys {
         )) as off_t
     }
 
-    fn mkdir(path: &CStr, mode: mode_t) -> c_int {
+    fn mkdir(path: CStr, mode: mode_t) -> c_int {
         match File::create(
             path,
             fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC,
@@ -672,7 +672,7 @@ impl Pal for Sys {
         }
     }
 
-    fn mkfifo(path: &CStr, mode: mode_t) -> c_int {
+    fn mkfifo(path: CStr, mode: mode_t) -> c_int {
         match File::create(
             path,
             fcntl::O_CREAT | fcntl::O_CLOEXEC,
@@ -788,7 +788,7 @@ impl Pal for Sys {
         }
     }
 
-    fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int {
+    fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int {
         let path = path_from_c_str!(path);
 
         e(libredox::open(path, oflag, mode)) as c_int
@@ -828,7 +828,7 @@ impl Pal for Sys {
         e(syscall::fpath(fildes as usize, out)) as ssize_t
     }
 
-    fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t {
+    fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t {
         match File::open(
             pathname,
             fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
@@ -838,7 +838,7 @@ impl Pal for Sys {
         }
     }
 
-    fn rename(oldpath: &CStr, newpath: &CStr) -> c_int {
+    fn rename(oldpath: CStr, newpath: CStr) -> c_int {
         let newpath = path_from_c_str!(newpath);
         match File::open(oldpath, fcntl::O_PATH | fcntl::O_CLOEXEC) {
             Ok(file) => e(syscall::frename(*file as usize, newpath)) as c_int,
@@ -846,7 +846,7 @@ impl Pal for Sys {
         }
     }
 
-    fn rmdir(path: &CStr) -> c_int {
+    fn rmdir(path: CStr) -> c_int {
         let path = path_from_c_str!(path);
         e(canonicalize(path).and_then(|path| syscall::rmdir(&path))) as c_int
     }
@@ -891,7 +891,7 @@ impl Pal for Sys {
         e(syscall::setreuid(ruid as usize, euid as usize)) as c_int
     }
 
-    fn symlink(path1: &CStr, path2: &CStr) -> c_int {
+    fn symlink(path1: CStr, path2: CStr) -> c_int {
         let mut file = match File::create(
             path2,
             fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
@@ -922,10 +922,7 @@ impl Pal for Sys {
                 return Ok(());
             }
 
-            let mut file = File::open(
-                &CString::new("/etc/hostname").unwrap(),
-                fcntl::O_RDONLY | fcntl::O_CLOEXEC,
-            )?;
+            let mut file = File::open(c_str!("/etc/hostname"), fcntl::O_RDONLY | fcntl::O_CLOEXEC)?;
 
             let mut read = 0;
             let name_len = name.len();
@@ -1001,7 +998,7 @@ impl Pal for Sys {
         }
     }
 
-    fn unlink(path: &CStr) -> c_int {
+    fn unlink(path: CStr) -> c_int {
         let path = path_from_c_str!(path);
         e(canonicalize(path).and_then(|path| syscall::unlink(&path))) as c_int
     }
diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs
index 3d8dba4f329d4cd6e1103d439e3dbcbbf49e9501..7013afd07d3e81baebbdc77cd2eff6142496cb6a 100644
--- a/src/platform/redox/ptrace.rs
+++ b/src/platform/redox/ptrace.rs
@@ -10,7 +10,7 @@ use crate::header::arch_aarch64_user::user_regs_struct;
 #[cfg(target_arch = "x86_64")]
 use crate::header::arch_x64_user::user_regs_struct;
 use crate::{
-    c_str::CString,
+    c_str::{CStr, CString},
     fs::File,
     header::{errno as errnoh, fcntl, signal, sys_ptrace},
     io::{self, prelude::*},
@@ -57,7 +57,7 @@ pub fn is_traceme(pid: pid_t) -> bool {
         return false;
     }
     File::open(
-        &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
+        CStr::borrow(&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap()),
         fcntl::O_PATH,
     )
     .is_ok()
@@ -74,19 +74,19 @@ pub fn get_session(
                 Ok(entry.insert(Session {
                     first: true,
                     tracer: File::open(
-                        &CString::new(format!("proc:{}/trace", pid)).unwrap(),
+                        CStr::borrow(&CString::new(format!("proc:{}/trace", pid)).unwrap()),
                         NEW_FLAGS,
                     )?,
                     mem: File::open(
-                        &CString::new(format!("proc:{}/mem", pid)).unwrap(),
+                        CStr::borrow(&CString::new(format!("proc:{}/mem", pid)).unwrap()),
                         NEW_FLAGS,
                     )?,
                     regs: File::open(
-                        &CString::new(format!("proc:{}/regs/int", pid)).unwrap(),
+                        CStr::borrow(&CString::new(format!("proc:{}/regs/int", pid)).unwrap()),
                         NEW_FLAGS,
                     )?,
                     fpregs: File::open(
-                        &CString::new(format!("proc:{}/regs/float", pid)).unwrap(),
+                        CStr::borrow(&CString::new(format!("proc:{}/regs/float", pid)).unwrap()),
                         NEW_FLAGS,
                     )?,
                 }))
@@ -136,7 +136,7 @@ fn inner_ptrace(
         // Mark this child as traced, parent will check for this marker file
         let pid = Sys::getpid();
         mem::forget(File::open(
-            &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
+            CStr::borrow(&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap()),
             fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL,
         )?);
         return Ok(0);
diff --git a/src/platform/redox/redox-exec/Cargo.toml b/src/platform/redox/redox-exec/Cargo.toml
index 1a47f79812f1e2925bf8ad5fcacfa34c54a47589..b2f9570d7d2c262a245ac75d9e8a981e66933941 100644
--- a/src/platform/redox/redox-exec/Cargo.toml
+++ b/src/platform/redox/redox-exec/Cargo.toml
@@ -9,6 +9,5 @@ license = "MIT"
 
 [dependencies]
 redox_syscall = "0.4"
-# TODO: Update
-goblin = { version = "0.0.21", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
+goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
 plain = "0.2"
diff --git a/src/start.rs b/src/start.rs
index 360b54ff60a82eef88fbc3a99977ab6c74203745..5c3d0fd203734074bfbf547ab5169d0cc4b02659 100644
--- a/src/start.rs
+++ b/src/start.rs
@@ -119,10 +119,11 @@ extern "C" fn init_array() {
 }
 fn io_init() {
     unsafe {
-        // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718
-        stdio::stdin = stdio::default_stdin.get();
-        stdio::stdout = stdio::default_stdout.get();
-        stdio::stderr = stdio::default_stderr.get();
+        // Initialize stdin/stdout/stderr.
+        // TODO: const fn initialization of FILE
+        stdio::stdin = stdio::default_stdin().get();
+        stdio::stdout = stdio::default_stdout().get();
+        stdio::stderr = stdio::default_stderr().get();
     }
 }