diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs
index 5f07cfef1335e..dfd9bd3207616 100644
--- a/compiler/rustc_data_structures/src/marker.rs
+++ b/compiler/rustc_data_structures/src/marker.rs
@@ -39,8 +39,15 @@ impls_dyn_send_neg!(
     [std::io::StderrLock<'_>]
 );
 
-#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))]
-// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms
+#[cfg(any(
+    unix,
+    target_os = "hermit",
+    all(target_vendor = "fortanix", target_env = "sgx"),
+    target_os = "solid_asp3",
+    target_os = "wasi",
+    target_os = "xous"
+))]
+// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms
 impl !DynSend for std::env::VarsOs {}
 
 macro_rules! already_send {
@@ -106,8 +113,15 @@ impls_dyn_sync_neg!(
     [std::sync::mpsc::Sender<T> where T]
 );
 
-#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))]
-// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms
+#[cfg(any(
+    unix,
+    target_os = "hermit",
+    all(target_vendor = "fortanix", target_env = "sgx"),
+    target_os = "solid_asp3",
+    target_os = "wasi",
+    target_os = "xous"
+))]
+// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms
 impl !DynSync for std::env::VarsOs {}
 
 macro_rules! already_sync {
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index c84a72c4fad02..1593969e114aa 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -13,7 +13,7 @@
 use crate::error::Error;
 use crate::ffi::{OsStr, OsString};
 use crate::path::{Path, PathBuf};
-use crate::sys::os as os_imp;
+use crate::sys::{env as env_imp, os as os_imp};
 use crate::{fmt, io, sys};
 
 /// Returns the current working directory as a [`PathBuf`].
@@ -96,7 +96,7 @@ pub struct Vars {
 /// [`env::vars_os()`]: vars_os
 #[stable(feature = "env", since = "1.0.0")]
 pub struct VarsOs {
-    inner: os_imp::Env,
+    inner: env_imp::Env,
 }
 
 /// Returns an iterator of (variable, value) pairs of strings, for all the
@@ -150,7 +150,7 @@ pub fn vars() -> Vars {
 #[must_use]
 #[stable(feature = "env", since = "1.0.0")]
 pub fn vars_os() -> VarsOs {
-    VarsOs { inner: os_imp::env() }
+    VarsOs { inner: env_imp::env() }
 }
 
 #[stable(feature = "env", since = "1.0.0")]
@@ -259,7 +259,7 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
 }
 
 fn _var_os(key: &OsStr) -> Option<OsString> {
-    os_imp::getenv(key)
+    env_imp::getenv(key)
 }
 
 /// The error type for operations interacting with environment variables.
@@ -363,7 +363,7 @@ impl Error for VarError {
 #[stable(feature = "env", since = "1.0.0")]
 pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
     let (key, value) = (key.as_ref(), value.as_ref());
-    unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| {
+    unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| {
         panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}")
     })
 }
@@ -434,7 +434,7 @@ pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
 #[stable(feature = "env", since = "1.0.0")]
 pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) {
     let key = key.as_ref();
-    unsafe { os_imp::unsetenv(key) }
+    unsafe { env_imp::unsetenv(key) }
         .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}"))
 }
 
diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs
index 6a37b32d2293f..0011f55dc14ee 100644
--- a/library/std/src/sys/args/mod.rs
+++ b/library/std/src/sys/args/mod.rs
@@ -2,6 +2,16 @@
 
 #![forbid(unsafe_op_in_unsafe_fn)]
 
+#[cfg(any(
+    all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))),
+    target_family = "windows",
+    target_os = "hermit",
+    target_os = "uefi",
+    target_os = "wasi",
+    target_os = "xous",
+))]
+mod common;
+
 cfg_if::cfg_if! {
     if #[cfg(any(
         all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))),
diff --git a/library/std/src/sys/args/uefi.rs b/library/std/src/sys/args/uefi.rs
index 84406c7f69df2..02dada382eff0 100644
--- a/library/std/src/sys/args/uefi.rs
+++ b/library/std/src/sys/args/uefi.rs
@@ -1,14 +1,11 @@
 use r_efi::protocols::loaded_image;
 
+pub use super::common::Args;
 use crate::env::current_exe;
 use crate::ffi::OsString;
 use crate::iter::Iterator;
 use crate::sys::pal::helpers;
 
-#[path = "common.rs"]
-mod common;
-pub use common::Args;
-
 pub fn args() -> Args {
     let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]);
 
diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs
index c087fd6296513..a7b79ad396e79 100644
--- a/library/std/src/sys/args/unix.rs
+++ b/library/std/src/sys/args/unix.rs
@@ -5,16 +5,13 @@
 
 #![allow(dead_code)] // runtime init functions not used during testing
 
+pub use super::common::Args;
 use crate::ffi::CStr;
 #[cfg(target_os = "hermit")]
 use crate::os::hermit::ffi::OsStringExt;
 #[cfg(not(target_os = "hermit"))]
 use crate::os::unix::ffi::OsStringExt;
 
-#[path = "common.rs"]
-mod common;
-pub use common::Args;
-
 /// One-time global initialization.
 pub unsafe fn init(argc: isize, argv: *const *const u8) {
     unsafe { imp::init(argc, argv) }
diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasi.rs
index 4795789e4c717..72063a87dc9f5 100644
--- a/library/std/src/sys/args/wasi.rs
+++ b/library/std/src/sys/args/wasi.rs
@@ -1,12 +1,9 @@
 #![forbid(unsafe_op_in_unsafe_fn)]
 
+pub use super::common::Args;
 use crate::ffi::{CStr, OsStr, OsString};
 use crate::os::wasi::ffi::OsStrExt;
 
-#[path = "common.rs"]
-mod common;
-pub use common::Args;
-
 /// Returns the command line arguments
 pub fn args() -> Args {
     Args::new(maybe_args().unwrap_or(Vec::new()))
diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs
index 47f0e5f2d05f8..81c44fabdcc67 100644
--- a/library/std/src/sys/args/windows.rs
+++ b/library/std/src/sys/args/windows.rs
@@ -6,6 +6,7 @@
 #[cfg(test)]
 mod tests;
 
+pub use super::common::Args;
 use crate::ffi::{OsStr, OsString};
 use crate::num::NonZero;
 use crate::os::windows::prelude::*;
@@ -18,10 +19,6 @@ use crate::sys_common::AsInner;
 use crate::sys_common::wstr::WStrUnits;
 use crate::{io, iter, ptr};
 
-#[path = "common.rs"]
-mod common;
-pub use common::Args;
-
 pub fn args() -> Args {
     // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16
     // string so it's safe for `WStrUnits` to use.
diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs
index 09a47283d6573..2010bad14d1fb 100644
--- a/library/std/src/sys/args/xous.rs
+++ b/library/std/src/sys/args/xous.rs
@@ -1,10 +1,7 @@
+pub use super::common::Args;
 use crate::sys::pal::os::get_application_parameters;
 use crate::sys::pal::os::params::ArgumentList;
 
-#[path = "common.rs"]
-mod common;
-pub use common::Args;
-
 pub fn args() -> Args {
     let Some(params) = get_application_parameters() else {
         return Args::new(vec![]);
diff --git a/library/std/src/sys/env/common.rs b/library/std/src/sys/env/common.rs
new file mode 100644
index 0000000000000..f161ff073f3d5
--- /dev/null
+++ b/library/std/src/sys/env/common.rs
@@ -0,0 +1,48 @@
+use crate::ffi::OsString;
+use crate::{fmt, vec};
+
+pub struct Env {
+    iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+pub struct EnvStrDebug<'a> {
+    slice: &'a [(OsString, OsString)],
+}
+
+impl fmt::Debug for EnvStrDebug<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list()
+            .entries(self.slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
+            .finish()
+    }
+}
+
+impl Env {
+    pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self {
+        Env { iter: env.into_iter() }
+    }
+
+    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+        EnvStrDebug { slice: self.iter.as_slice() }
+    }
+}
+
+impl fmt::Debug for Env {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.iter.as_slice()).finish()
+    }
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        self.iter.next()
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
diff --git a/library/std/src/sys/env/hermit.rs b/library/std/src/sys/env/hermit.rs
new file mode 100644
index 0000000000000..445ecdeb6a39f
--- /dev/null
+++ b/library/std/src/sys/env/hermit.rs
@@ -0,0 +1,72 @@
+use core::slice::memchr;
+
+pub use super::common::Env;
+use crate::collections::HashMap;
+use crate::ffi::{CStr, OsStr, OsString, c_char};
+use crate::io;
+use crate::os::hermit::ffi::OsStringExt;
+use crate::sync::Mutex;
+
+static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None);
+
+pub fn init(env: *const *const c_char) {
+    let mut guard = ENV.lock().unwrap();
+    let map = guard.insert(HashMap::new());
+
+    if env.is_null() {
+        return;
+    }
+
+    unsafe {
+        let mut environ = env;
+        while !(*environ).is_null() {
+            if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                map.insert(key, value);
+            }
+            environ = environ.add(1);
+        }
+    }
+
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        // Strategy (copied from glibc): Variable name and value are separated
+        // by an ASCII equals sign '='. Since a variable name must not be
+        // empty, allow variable names starting with an equals sign. Skip all
+        // malformed lines.
+        if input.is_empty() {
+            return None;
+        }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| {
+            (
+                OsStringExt::from_vec(input[..p].to_vec()),
+                OsStringExt::from_vec(input[p + 1..].to_vec()),
+            )
+        })
+    }
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+    let guard = ENV.lock().unwrap();
+    let env = guard.as_ref().unwrap();
+
+    let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect();
+
+    Env::new(result)
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    ENV.lock().unwrap().as_ref().unwrap().get(k).cloned()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let (k, v) = (k.to_owned(), v.to_owned());
+    ENV.lock().unwrap().as_mut().unwrap().insert(k, v);
+    Ok(())
+}
+
+pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
+    ENV.lock().unwrap().as_mut().unwrap().remove(k);
+    Ok(())
+}
diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs
new file mode 100644
index 0000000000000..d81ff875c830f
--- /dev/null
+++ b/library/std/src/sys/env/mod.rs
@@ -0,0 +1,48 @@
+//! Platform-dependent environment variables abstraction.
+
+#![forbid(unsafe_op_in_unsafe_fn)]
+
+#[cfg(any(
+    target_family = "unix",
+    target_os = "hermit",
+    all(target_vendor = "fortanix", target_env = "sgx"),
+    target_os = "solid_asp3",
+    target_os = "uefi",
+    target_os = "wasi",
+    target_os = "xous",
+))]
+mod common;
+
+cfg_if::cfg_if! {
+    if #[cfg(target_family = "unix")] {
+        mod unix;
+        pub use unix::*;
+    } else if #[cfg(target_family = "windows")] {
+        mod windows;
+        pub use windows::*;
+    } else if #[cfg(target_os = "hermit")] {
+        mod hermit;
+        pub use hermit::*;
+    } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
+        mod sgx;
+        pub use sgx::*;
+    } else if #[cfg(target_os = "solid_asp3")] {
+        mod solid;
+        pub use solid::*;
+    } else if #[cfg(target_os = "uefi")] {
+        mod uefi;
+        pub use uefi::*;
+    } else if #[cfg(target_os = "wasi")] {
+        mod wasi;
+        pub use wasi::*;
+    } else if #[cfg(target_os = "xous")] {
+        mod xous;
+        pub use xous::*;
+    } else if #[cfg(target_os = "zkvm")] {
+        mod zkvm;
+        pub use zkvm::*;
+    } else {
+        mod unsupported;
+        pub use unsupported::*;
+    }
+}
diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs
new file mode 100644
index 0000000000000..85be9cd6ad418
--- /dev/null
+++ b/library/std/src/sys/env/sgx.rs
@@ -0,0 +1,55 @@
+#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers
+
+pub use super::common::Env;
+use crate::collections::HashMap;
+use crate::ffi::{OsStr, OsString};
+use crate::io;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sync::{Mutex, Once};
+
+// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
+#[cfg_attr(test, linkage = "available_externally")]
+#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")]
+static ENV: AtomicUsize = AtomicUsize::new(0);
+// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
+#[cfg_attr(test, linkage = "available_externally")]
+#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")]
+static ENV_INIT: Once = Once::new();
+type EnvStore = Mutex<HashMap<OsString, OsString>>;
+
+fn get_env_store() -> Option<&'static EnvStore> {
+    unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
+}
+
+fn create_env_store() -> &'static EnvStore {
+    ENV_INIT.call_once(|| {
+        ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
+    });
+    unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) }
+}
+
+pub fn env() -> Env {
+    let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
+        map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
+    };
+
+    let env = get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default();
+    Env::new(env)
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let (k, v) = (k.to_owned(), v.to_owned());
+    create_env_store().lock().unwrap().insert(k, v);
+    Ok(())
+}
+
+pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
+    if let Some(env) = get_env_store() {
+        env.lock().unwrap().remove(k);
+    }
+    Ok(())
+}
diff --git a/library/std/src/sys/env/solid.rs b/library/std/src/sys/env/solid.rs
new file mode 100644
index 0000000000000..ea77fc3c11930
--- /dev/null
+++ b/library/std/src/sys/env/solid.rs
@@ -0,0 +1,96 @@
+use core::slice::memchr;
+
+pub use super::common::Env;
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::io;
+use crate::os::raw::{c_char, c_int};
+use crate::os::solid::ffi::{OsStrExt, OsStringExt};
+use crate::sync::{PoisonError, RwLock};
+use crate::sys::common::small_c_string::run_with_cstr;
+
+static ENV_LOCK: RwLock<()> = RwLock::new(());
+
+pub fn env_read_lock() -> impl Drop {
+    ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+    unsafe extern "C" {
+        static mut environ: *const *const c_char;
+    }
+
+    unsafe {
+        let _guard = env_read_lock();
+        let mut result = Vec::new();
+        if !environ.is_null() {
+            while !(*environ).is_null() {
+                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                    result.push(key_value);
+                }
+                environ = environ.add(1);
+            }
+        }
+        return Env::new(result);
+    }
+
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        // Strategy (copied from glibc): Variable name and value are separated
+        // by an ASCII equals sign '='. Since a variable name must not be
+        // empty, allow variable names starting with an equals sign. Skip all
+        // malformed lines.
+        if input.is_empty() {
+            return None;
+        }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| {
+            (
+                OsStringExt::from_vec(input[..p].to_vec()),
+                OsStringExt::from_vec(input[p + 1..].to_vec()),
+            )
+        })
+    }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    // environment variables with a nul byte can't be set, so their value is
+    // always None as well
+    run_with_cstr(k.as_bytes(), &|k| {
+        let _guard = env_read_lock();
+        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
+
+        if v.is_null() {
+            Ok(None)
+        } else {
+            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
+            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
+
+            Ok(Some(OsStringExt::from_vec(bytes)))
+        }
+    })
+    .ok()
+    .flatten()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    run_with_cstr(k.as_bytes(), &|k| {
+        run_with_cstr(v.as_bytes(), &|v| {
+            let _guard = ENV_LOCK.write();
+            cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
+        })
+    })
+}
+
+pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
+    run_with_cstr(n.as_bytes(), &|nbuf| {
+        let _guard = ENV_LOCK.write();
+        cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
+    })
+}
+
+/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
+/// function just returns a generic error.
+fn cvt_env(t: c_int) -> io::Result<c_int> {
+    if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
+}
diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs
new file mode 100644
index 0000000000000..1561df41cac3f
--- /dev/null
+++ b/library/std/src/sys/env/uefi.rs
@@ -0,0 +1,102 @@
+pub use super::common::Env;
+use crate::ffi::{OsStr, OsString};
+use crate::io;
+
+pub fn env() -> Env {
+    let env = uefi_env::get_all().expect("not supported on this platform");
+    Env::new(env)
+}
+
+pub fn getenv(key: &OsStr) -> Option<OsString> {
+    uefi_env::get(key)
+}
+
+pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> {
+    uefi_env::set(key, val)
+}
+
+pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> {
+    uefi_env::unset(key)
+}
+
+mod uefi_env {
+    use crate::ffi::{OsStr, OsString};
+    use crate::io;
+    use crate::os::uefi::ffi::OsStringExt;
+    use crate::ptr::NonNull;
+    use crate::sys::{helpers, unsupported_err};
+
+    pub(crate) fn get(key: &OsStr) -> Option<OsString> {
+        let shell = helpers::open_shell()?;
+        let mut key_ptr = helpers::os_string_to_raw(key)?;
+        unsafe { get_raw(shell, key_ptr.as_mut_ptr()) }
+    }
+
+    pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> {
+        let mut key_ptr = helpers::os_string_to_raw(key)
+            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
+        let mut val_ptr = helpers::os_string_to_raw(val)
+            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
+        unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) }
+    }
+
+    pub(crate) fn unset(key: &OsStr) -> io::Result<()> {
+        let mut key_ptr = helpers::os_string_to_raw(key)
+            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
+        unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) }
+    }
+
+    pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> {
+        let shell = helpers::open_shell().ok_or(unsupported_err())?;
+
+        let mut vars = Vec::new();
+        let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) };
+
+        if val.is_null() {
+            return Ok(vars);
+        }
+
+        let mut start = 0;
+
+        // UEFI Shell returns all keys separated by NULL.
+        // End of string is denoted by two NULLs
+        for i in 0.. {
+            if unsafe { *val.add(i) } == 0 {
+                // Two NULL signal end of string
+                if i == start {
+                    break;
+                }
+
+                let key = OsString::from_wide(unsafe {
+                    crate::slice::from_raw_parts(val.add(start), i - start)
+                });
+                // SAFETY: val.add(start) is always NULL terminated
+                let val = unsafe { get_raw(shell, val.add(start)) }
+                    .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
+
+                vars.push((key, val));
+                start = i + 1;
+            }
+        }
+
+        Ok(vars)
+    }
+
+    unsafe fn get_raw(
+        shell: NonNull<r_efi::efi::protocols::shell::Protocol>,
+        key_ptr: *mut r_efi::efi::Char16,
+    ) -> Option<OsString> {
+        let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) };
+        helpers::os_string_from_raw(val)
+    }
+
+    unsafe fn set_raw(
+        key_ptr: *mut r_efi::efi::Char16,
+        val_ptr: *mut r_efi::efi::Char16,
+    ) -> io::Result<()> {
+        let shell = helpers::open_shell().ok_or(unsupported_err())?;
+        let r =
+            unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) };
+        if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
+    }
+}
diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs
new file mode 100644
index 0000000000000..78c7af65f9e38
--- /dev/null
+++ b/library/std/src/sys/env/unix.rs
@@ -0,0 +1,126 @@
+use core::slice::memchr;
+
+use libc::c_char;
+
+pub use super::common::Env;
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::io;
+use crate::os::unix::prelude::*;
+use crate::sync::{PoisonError, RwLock};
+use crate::sys::common::small_c_string::run_with_cstr;
+use crate::sys::cvt;
+
+// Use `_NSGetEnviron` on Apple platforms.
+//
+// `_NSGetEnviron` is the documented alternative (see `man environ`), and has
+// been available since the first versions of both macOS and iOS.
+//
+// Nowadays, specifically since macOS 10.8, `environ` has been exposed through
+// `libdyld.dylib`, which is linked via. `libSystem.dylib`:
+// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913>
+//
+// So in the end, it likely doesn't really matter which option we use, but the
+// performance cost of using `_NSGetEnviron` is extremely miniscule, and it
+// might be ever so slightly more supported, so let's just use that.
+//
+// NOTE: The header where this is defined (`crt_externs.h`) was added to the
+// iOS 13.0 SDK, which has been the source of a great deal of confusion in the
+// past about the availability of this API.
+//
+// NOTE(madsmtm): Neither this nor using `environ` has been verified to not
+// cause App Store rejections; if this is found to be the case, an alternative
+// implementation of this is possible using `[NSProcessInfo environment]`
+// - which internally uses `_NSGetEnviron` and a system-wide lock on the
+// environment variables to protect against `setenv`, so using that might be
+// desirable anyhow? Though it also means that we have to link to Foundation.
+#[cfg(target_vendor = "apple")]
+pub unsafe fn environ() -> *mut *const *const c_char {
+    unsafe { libc::_NSGetEnviron() as *mut *const *const c_char }
+}
+
+// Use the `environ` static which is part of POSIX.
+#[cfg(not(target_vendor = "apple"))]
+pub unsafe fn environ() -> *mut *const *const c_char {
+    unsafe extern "C" {
+        static mut environ: *const *const c_char;
+    }
+    &raw mut environ
+}
+
+static ENV_LOCK: RwLock<()> = RwLock::new(());
+
+pub fn env_read_lock() -> impl Drop {
+    ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+    unsafe {
+        let _guard = env_read_lock();
+        let mut environ = *environ();
+        let mut result = Vec::new();
+        if !environ.is_null() {
+            while !(*environ).is_null() {
+                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                    result.push(key_value);
+                }
+                environ = environ.add(1);
+            }
+        }
+        return Env::new(result);
+    }
+
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        // Strategy (copied from glibc): Variable name and value are separated
+        // by an ASCII equals sign '='. Since a variable name must not be
+        // empty, allow variable names starting with an equals sign. Skip all
+        // malformed lines.
+        if input.is_empty() {
+            return None;
+        }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| {
+            (
+                OsStringExt::from_vec(input[..p].to_vec()),
+                OsStringExt::from_vec(input[p + 1..].to_vec()),
+            )
+        })
+    }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    // environment variables with a nul byte can't be set, so their value is
+    // always None as well
+    run_with_cstr(k.as_bytes(), &|k| {
+        let _guard = env_read_lock();
+        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
+
+        if v.is_null() {
+            Ok(None)
+        } else {
+            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
+            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
+
+            Ok(Some(OsStringExt::from_vec(bytes)))
+        }
+    })
+    .ok()
+    .flatten()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    run_with_cstr(k.as_bytes(), &|k| {
+        run_with_cstr(v.as_bytes(), &|v| {
+            let _guard = ENV_LOCK.write();
+            cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
+        })
+    })
+}
+
+pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
+    run_with_cstr(n.as_bytes(), &|nbuf| {
+        let _guard = ENV_LOCK.write();
+        cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
+    })
+}
diff --git a/library/std/src/sys/env/unsupported.rs b/library/std/src/sys/env/unsupported.rs
new file mode 100644
index 0000000000000..98905e6482747
--- /dev/null
+++ b/library/std/src/sys/env/unsupported.rs
@@ -0,0 +1,40 @@
+use crate::ffi::{OsStr, OsString};
+use crate::{fmt, io};
+
+pub struct Env(!);
+
+impl Env {
+    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+        self.0
+    }
+}
+
+impl fmt::Debug for Env {
+    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        self.0
+    }
+}
+
+pub fn env() -> Env {
+    panic!("not supported on this platform")
+}
+
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+    None
+}
+
+pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
+}
+
+pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
+    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
+}
diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs
new file mode 100644
index 0000000000000..3719f9db51eb3
--- /dev/null
+++ b/library/std/src/sys/env/wasi.rs
@@ -0,0 +1,102 @@
+use core::slice::memchr;
+
+pub use super::common::Env;
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::io;
+use crate::os::wasi::prelude::*;
+use crate::sys::common::small_c_string::run_with_cstr;
+use crate::sys::pal::os::{cvt, libc};
+
+cfg_if::cfg_if! {
+    if #[cfg(target_feature = "atomics")] {
+        // Access to the environment must be protected by a lock in multi-threaded scenarios.
+        use crate::sync::{PoisonError, RwLock};
+        static ENV_LOCK: RwLock<()> = RwLock::new(());
+        pub fn env_read_lock() -> impl Drop {
+            ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
+        }
+        pub fn env_write_lock() -> impl Drop {
+            ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner)
+        }
+    } else {
+        // No need for a lock if we are single-threaded.
+        pub fn env_read_lock() -> impl Drop {
+            Box::new(())
+        }
+        pub fn env_write_lock() -> impl Drop {
+            Box::new(())
+        }
+    }
+}
+
+pub fn env() -> Env {
+    unsafe {
+        let _guard = env_read_lock();
+
+        // Use `__wasilibc_get_environ` instead of `environ` here so that we
+        // don't require wasi-libc to eagerly initialize the environment
+        // variables.
+        let mut environ = libc::__wasilibc_get_environ();
+
+        let mut result = Vec::new();
+        if !environ.is_null() {
+            while !(*environ).is_null() {
+                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                    result.push(key_value);
+                }
+                environ = environ.add(1);
+            }
+        }
+        return Env::new(result);
+    }
+
+    // See src/libstd/sys/pal/unix/os.rs, same as that
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        if input.is_empty() {
+            return None;
+        }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| {
+            (
+                OsStringExt::from_vec(input[..p].to_vec()),
+                OsStringExt::from_vec(input[p + 1..].to_vec()),
+            )
+        })
+    }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    // environment variables with a nul byte can't be set, so their value is
+    // always None as well
+    run_with_cstr(k.as_bytes(), &|k| {
+        let _guard = env_read_lock();
+        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
+
+        if v.is_null() {
+            Ok(None)
+        } else {
+            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
+            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
+
+            Ok(Some(OsStringExt::from_vec(bytes)))
+        }
+    })
+    .ok()
+    .flatten()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    run_with_cstr(k.as_bytes(), &|k| {
+        run_with_cstr(v.as_bytes(), &|v| unsafe {
+            let _guard = env_write_lock();
+            cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+        })
+    })
+}
+
+pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
+    run_with_cstr(n.as_bytes(), &|nbuf| unsafe {
+        let _guard = env_write_lock();
+        cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
+    })
+}
diff --git a/library/std/src/sys/env/windows.rs b/library/std/src/sys/env/windows.rs
new file mode 100644
index 0000000000000..3c4d4a84cfd6b
--- /dev/null
+++ b/library/std/src/sys/env/windows.rs
@@ -0,0 +1,133 @@
+use crate::ffi::{OsStr, OsString};
+use crate::os::windows::prelude::*;
+use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s};
+use crate::{fmt, io, ptr, slice};
+
+pub struct Env {
+    base: *mut c::WCHAR,
+    iter: EnvIterator,
+}
+
+// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+pub struct EnvStrDebug<'a> {
+    iter: &'a EnvIterator,
+}
+
+impl fmt::Debug for EnvStrDebug<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self { iter } = self;
+        let iter: EnvIterator = (*iter).clone();
+        let mut list = f.debug_list();
+        for (a, b) in iter {
+            list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
+        }
+        list.finish()
+    }
+}
+
+impl Env {
+    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+        let Self { base: _, iter } = self;
+        EnvStrDebug { iter }
+    }
+}
+
+impl fmt::Debug for Env {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self { base: _, iter } = self;
+        f.debug_list().entries(iter.clone()).finish()
+    }
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        let Self { base: _, iter } = self;
+        iter.next()
+    }
+}
+
+#[derive(Clone)]
+struct EnvIterator(*mut c::WCHAR);
+
+impl Iterator for EnvIterator {
+    type Item = (OsString, OsString);
+
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        let Self(cur) = self;
+        loop {
+            unsafe {
+                if **cur == 0 {
+                    return None;
+                }
+                let p = *cur as *const u16;
+                let mut len = 0;
+                while *p.add(len) != 0 {
+                    len += 1;
+                }
+                let s = slice::from_raw_parts(p, len);
+                *cur = cur.add(len + 1);
+
+                // Windows allows environment variables to start with an equals
+                // symbol (in any other position, this is the separator between
+                // variable name and value). Since`s` has at least length 1 at
+                // this point (because the empty string terminates the array of
+                // environment variables), we can safely slice.
+                let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
+                    Some(p) => p,
+                    None => continue,
+                };
+                return Some((
+                    OsStringExt::from_wide(&s[..pos]),
+                    OsStringExt::from_wide(&s[pos + 1..]),
+                ));
+            }
+        }
+    }
+}
+
+impl Drop for Env {
+    fn drop(&mut self) {
+        unsafe {
+            c::FreeEnvironmentStringsW(self.base);
+        }
+    }
+}
+
+pub fn env() -> Env {
+    unsafe {
+        let ch = c::GetEnvironmentStringsW();
+        if ch.is_null() {
+            panic!("failure getting env string from OS: {}", io::Error::last_os_error());
+        }
+        Env { base: ch, iter: EnvIterator(ch) }
+    }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    let k = to_u16s(k).ok()?;
+    fill_utf16_buf(
+        |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
+        OsStringExt::from_wide,
+    )
+    .ok()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    // SAFETY: We ensure that k and v are null-terminated wide strings.
+    unsafe {
+        let k = to_u16s(k)?;
+        let v = to_u16s(v)?;
+
+        cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
+    }
+}
+
+pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
+    // SAFETY: We ensure that v is a null-terminated wide strings.
+    unsafe {
+        let v = to_u16s(n)?;
+        cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
+    }
+}
diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs
new file mode 100644
index 0000000000000..232a3dafb0be5
--- /dev/null
+++ b/library/std/src/sys/env/xous.rs
@@ -0,0 +1,54 @@
+pub use super::common::Env;
+use crate::collections::HashMap;
+use crate::ffi::{OsStr, OsString};
+use crate::io;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sync::{Mutex, Once};
+use crate::sys::pal::os::{get_application_parameters, params};
+
+static ENV: AtomicUsize = AtomicUsize::new(0);
+static ENV_INIT: Once = Once::new();
+type EnvStore = Mutex<HashMap<OsString, OsString>>;
+
+fn get_env_store() -> &'static EnvStore {
+    ENV_INIT.call_once(|| {
+        let env_store = EnvStore::default();
+        if let Some(params) = get_application_parameters() {
+            for param in params {
+                if let Ok(envs) = params::EnvironmentBlock::try_from(&param) {
+                    let mut env_store = env_store.lock().unwrap();
+                    for env in envs {
+                        env_store.insert(env.key.into(), env.value.into());
+                    }
+                    break;
+                }
+            }
+        }
+        ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed)
+    });
+    unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) }
+}
+
+pub fn env() -> Env {
+    let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
+        map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
+    };
+
+    let env = clone_to_vec(&*get_env_store().lock().unwrap());
+    Env::new(env)
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    get_env_store().lock().unwrap().get(k).cloned()
+}
+
+pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let (k, v) = (k.to_owned(), v.to_owned());
+    get_env_store().lock().unwrap().insert(k, v);
+    Ok(())
+}
+
+pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
+    get_env_store().lock().unwrap().remove(k);
+    Ok(())
+}
diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs
new file mode 100644
index 0000000000000..2eb7005ba1289
--- /dev/null
+++ b/library/std/src/sys/env/zkvm.rs
@@ -0,0 +1,32 @@
+#[expect(dead_code)]
+#[path = "unsupported.rs"]
+mod unsupported_env;
+pub use unsupported_env::{Env, env, setenv, unsetenv};
+
+use crate::ffi::{OsStr, OsString};
+use crate::sys::os_str;
+use crate::sys::pal::{WORD_SIZE, abi};
+use crate::sys_common::FromInner;
+
+pub fn getenv(varname: &OsStr) -> Option<OsString> {
+    let varname = varname.as_encoded_bytes();
+    let nbytes =
+        unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) };
+    if nbytes == usize::MAX {
+        return None;
+    }
+
+    let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE;
+    let words = unsafe { abi::sys_alloc_words(nwords) };
+
+    let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) };
+    debug_assert_eq!(nbytes, nbytes2);
+
+    // Convert to OsString.
+    //
+    // FIXME: We can probably get rid of the extra copy here if we
+    // reimplement "os_str" instead of just using the generic unix
+    // "os_str".
+    let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) };
+    Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() }))
+}
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index e7b631999e0da..f9a02b522e5e1 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -12,6 +12,7 @@ pub mod anonymous_pipe;
 pub mod args;
 pub mod backtrace;
 pub mod cmath;
+pub mod env;
 pub mod env_consts;
 pub mod exit_guard;
 pub mod fd;
diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs
index 70636760a83b6..ea636938d703f 100644
--- a/library/std/src/sys/pal/hermit/mod.rs
+++ b/library/std/src/sys/pal/hermit/mod.rs
@@ -16,7 +16,10 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 #![allow(missing_docs, nonstandard_style)]
 
+use crate::io::ErrorKind;
+use crate::os::hermit::hermit_abi;
 use crate::os::raw::c_char;
+use crate::sys::env;
 
 pub mod futex;
 pub mod os;
@@ -25,9 +28,6 @@ pub mod pipe;
 pub mod thread;
 pub mod time;
 
-use crate::io::ErrorKind;
-use crate::os::hermit::hermit_abi;
-
 pub fn unsupported<T>() -> crate::io::Result<T> {
     Err(unsupported_err())
 }
@@ -76,7 +76,7 @@ pub unsafe extern "C" fn runtime_entry(
     }
 
     // initialize environment
-    os::init_environment(env);
+    env::init(env);
 
     let result = unsafe { main(argc as isize, argv) };
 
diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs
index 791cdb1e57e7d..a998c3165e52f 100644
--- a/library/std/src/sys/pal/hermit/os.rs
+++ b/library/std/src/sys/pal/hermit/os.rs
@@ -1,15 +1,10 @@
-use core::slice::memchr;
-
 use super::hermit_abi;
-use crate::collections::HashMap;
 use crate::error::Error as StdError;
-use crate::ffi::{CStr, OsStr, OsString, c_char};
+use crate::ffi::{OsStr, OsString};
 use crate::marker::PhantomData;
-use crate::os::hermit::ffi::OsStringExt;
 use crate::path::{self, PathBuf};
-use crate::sync::Mutex;
 use crate::sys::unsupported;
-use crate::{fmt, io, str, vec};
+use crate::{fmt, io, str};
 
 pub fn errno() -> i32 {
     unsafe { hermit_abi::get_errno() }
@@ -68,115 +63,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None);
-
-pub fn init_environment(env: *const *const c_char) {
-    let mut guard = ENV.lock().unwrap();
-    let map = guard.insert(HashMap::new());
-
-    if env.is_null() {
-        return;
-    }
-
-    unsafe {
-        let mut environ = env;
-        while !(*environ).is_null() {
-            if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
-                map.insert(key, value);
-            }
-            environ = environ.add(1);
-        }
-    }
-
-    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
-        // Strategy (copied from glibc): Variable name and value are separated
-        // by an ASCII equals sign '='. Since a variable name must not be
-        // empty, allow variable names starting with an equals sign. Skip all
-        // malformed lines.
-        if input.is_empty() {
-            return None;
-        }
-        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
-        pos.map(|p| {
-            (
-                OsStringExt::from_vec(input[..p].to_vec()),
-                OsStringExt::from_vec(input[p + 1..].to_vec()),
-            )
-        })
-    }
-}
-
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-/// Returns a vector of (variable, value) byte-vector pairs for all the
-/// environment variables of the current process.
-pub fn env() -> Env {
-    let guard = ENV.lock().unwrap();
-    let env = guard.as_ref().unwrap();
-
-    let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::<Vec<_>>();
-
-    Env { iter: result.into_iter() }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    ENV.lock().unwrap().as_ref().unwrap().get(k).cloned()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    let (k, v) = (k.to_owned(), v.to_owned());
-    ENV.lock().unwrap().as_mut().unwrap().insert(k, v);
-    Ok(())
-}
-
-pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
-    ENV.lock().unwrap().as_mut().unwrap().remove(k);
-    Ok(())
-}
-
 pub fn temp_dir() -> PathBuf {
     PathBuf::from("/tmp")
 }
diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs
index 010634cf31063..70f838679c9ca 100644
--- a/library/std/src/sys/pal/sgx/os.rs
+++ b/library/std/src/sys/pal/sgx/os.rs
@@ -1,14 +1,11 @@
 use fortanix_sgx_abi::{Error, RESULT_SUCCESS};
 
-use crate::collections::HashMap;
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::marker::PhantomData;
 use crate::path::{self, PathBuf};
-use crate::sync::atomic::{AtomicUsize, Ordering};
-use crate::sync::{Mutex, Once};
 use crate::sys::{decode_error_kind, sgx_ineffective, unsupported};
-use crate::{fmt, io, str, vec};
+use crate::{fmt, io, str};
 
 pub fn errno() -> i32 {
     RESULT_SUCCESS
@@ -73,101 +70,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
-#[cfg_attr(test, linkage = "available_externally")]
-#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")]
-static ENV: AtomicUsize = AtomicUsize::new(0);
-// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
-#[cfg_attr(test, linkage = "available_externally")]
-#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")]
-static ENV_INIT: Once = Once::new();
-type EnvStore = Mutex<HashMap<OsString, OsString>>;
-
-fn get_env_store() -> Option<&'static EnvStore> {
-    unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
-}
-
-fn create_env_store() -> &'static EnvStore {
-    ENV_INIT.call_once(|| {
-        ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
-    });
-    unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) }
-}
-
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-pub fn env() -> Env {
-    let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
-        map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
-    };
-
-    let iter = get_env_store()
-        .map(|env| clone_to_vec(&env.lock().unwrap()))
-        .unwrap_or_default()
-        .into_iter();
-    Env { iter }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    let (k, v) = (k.to_owned(), v.to_owned());
-    create_env_store().lock().unwrap().insert(k, v);
-    Ok(())
-}
-
-pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
-    if let Some(env) = get_env_store() {
-        env.lock().unwrap().remove(k);
-    }
-    Ok(())
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem in SGX")
 }
diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs
index e3b2e0aa50f4a..8f5976b0592ec 100644
--- a/library/std/src/sys/pal/solid/os.rs
+++ b/library/std/src/sys/pal/solid/os.rs
@@ -1,14 +1,8 @@
-use core::slice::memchr;
-
 use super::{error, itron, unsupported};
 use crate::error::Error as StdError;
-use crate::ffi::{CStr, OsStr, OsString};
-use crate::os::raw::{c_char, c_int};
-use crate::os::solid::ffi::{OsStrExt, OsStringExt};
+use crate::ffi::{OsStr, OsString};
 use crate::path::{self, PathBuf};
-use crate::sync::{PoisonError, RwLock};
-use crate::sys::common::small_c_string::run_with_cstr;
-use crate::{fmt, io, vec};
+use crate::{fmt, io};
 
 // `solid` directly maps `errno`s to μITRON error codes.
 impl itron::error::ItronError {
@@ -75,138 +69,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-static ENV_LOCK: RwLock<()> = RwLock::new(());
-
-pub fn env_read_lock() -> impl Drop {
-    ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
-}
-
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-/// Returns a vector of (variable, value) byte-vector pairs for all the
-/// environment variables of the current process.
-pub fn env() -> Env {
-    unsafe extern "C" {
-        static mut environ: *const *const c_char;
-    }
-
-    unsafe {
-        let _guard = env_read_lock();
-        let mut result = Vec::new();
-        if !environ.is_null() {
-            while !(*environ).is_null() {
-                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
-                    result.push(key_value);
-                }
-                environ = environ.add(1);
-            }
-        }
-        return Env { iter: result.into_iter() };
-    }
-
-    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
-        // Strategy (copied from glibc): Variable name and value are separated
-        // by an ASCII equals sign '='. Since a variable name must not be
-        // empty, allow variable names starting with an equals sign. Skip all
-        // malformed lines.
-        if input.is_empty() {
-            return None;
-        }
-        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
-        pos.map(|p| {
-            (
-                OsStringExt::from_vec(input[..p].to_vec()),
-                OsStringExt::from_vec(input[p + 1..].to_vec()),
-            )
-        })
-    }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    // environment variables with a nul byte can't be set, so their value is
-    // always None as well
-    run_with_cstr(k.as_bytes(), &|k| {
-        let _guard = env_read_lock();
-        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
-
-        if v.is_null() {
-            Ok(None)
-        } else {
-            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
-            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
-
-            Ok(Some(OsStringExt::from_vec(bytes)))
-        }
-    })
-    .ok()
-    .flatten()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    run_with_cstr(k.as_bytes(), &|k| {
-        run_with_cstr(v.as_bytes(), &|v| {
-            let _guard = ENV_LOCK.write();
-            cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
-        })
-    })
-}
-
-pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
-    run_with_cstr(n.as_bytes(), &|nbuf| {
-        let _guard = ENV_LOCK.write();
-        cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
-    })
-}
-
-/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
-/// function just returns a generic error.
-fn cvt_env(t: c_int) -> io::Result<c_int> {
-    if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no standard temporary directory on this platform")
 }
diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs
index bf6945811ab0e..03f3c72b0229a 100644
--- a/library/std/src/sys/pal/teeos/os.rs
+++ b/library/std/src/sys/pal/teeos/os.rs
@@ -73,47 +73,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-pub struct Env(!);
-
-impl Env {
-    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-pub fn env() -> Env {
-    panic!("not supported on this platform")
-}
-
-pub fn getenv(_: &OsStr) -> Option<OsString> {
-    None
-}
-
-pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
-}
-
-pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on this platform")
 }
diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs
index d26d61890c19e..bfd4dc81cb44f 100644
--- a/library/std/src/sys/pal/uefi/os.rs
+++ b/library/std/src/sys/pal/uefi/os.rs
@@ -131,60 +131,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     helpers::device_path_to_text(protocol).map(PathBuf::from)
 }
 
-pub struct EnvStrDebug<'a> {
-    iter: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut list = f.debug_list();
-        for (a, b) in self.iter {
-            list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
-        }
-        list.finish()
-    }
-}
-
-pub struct Env(crate::vec::IntoIter<(OsString, OsString)>);
-
-impl Env {
-    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        EnvStrDebug { iter: self.0.as_slice() }
-    }
-}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.0.next()
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-pub fn env() -> Env {
-    let env = uefi_env::get_all().expect("not supported on this platform");
-    Env(env.into_iter())
-}
-
-pub fn getenv(key: &OsStr) -> Option<OsString> {
-    uefi_env::get(key)
-}
-
-pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> {
-    uefi_env::set(key, val)
-}
-
-pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> {
-    uefi_env::unset(key)
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on this platform")
 }
@@ -213,85 +159,3 @@ pub fn exit(code: i32) -> ! {
 pub fn getpid() -> u32 {
     panic!("no pids on this platform")
 }
-
-mod uefi_env {
-    use crate::ffi::{OsStr, OsString};
-    use crate::io;
-    use crate::os::uefi::ffi::OsStringExt;
-    use crate::ptr::NonNull;
-    use crate::sys::{helpers, unsupported_err};
-
-    pub(crate) fn get(key: &OsStr) -> Option<OsString> {
-        let shell = helpers::open_shell()?;
-        let mut key_ptr = helpers::os_string_to_raw(key)?;
-        unsafe { get_raw(shell, key_ptr.as_mut_ptr()) }
-    }
-
-    pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> {
-        let mut key_ptr = helpers::os_string_to_raw(key)
-            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
-        let mut val_ptr = helpers::os_string_to_raw(val)
-            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
-        unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) }
-    }
-
-    pub(crate) fn unset(key: &OsStr) -> io::Result<()> {
-        let mut key_ptr = helpers::os_string_to_raw(key)
-            .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
-        unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) }
-    }
-
-    pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> {
-        let shell = helpers::open_shell().ok_or(unsupported_err())?;
-
-        let mut vars = Vec::new();
-        let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) };
-
-        if val.is_null() {
-            return Ok(vars);
-        }
-
-        let mut start = 0;
-
-        // UEFI Shell returns all keys separated by NULL.
-        // End of string is denoted by two NULLs
-        for i in 0.. {
-            if unsafe { *val.add(i) } == 0 {
-                // Two NULL signal end of string
-                if i == start {
-                    break;
-                }
-
-                let key = OsString::from_wide(unsafe {
-                    crate::slice::from_raw_parts(val.add(start), i - start)
-                });
-                // SAFETY: val.add(start) is always NULL terminated
-                let val = unsafe { get_raw(shell, val.add(start)) }
-                    .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
-
-                vars.push((key, val));
-                start = i + 1;
-            }
-        }
-
-        Ok(vars)
-    }
-
-    unsafe fn get_raw(
-        shell: NonNull<r_efi::efi::protocols::shell::Protocol>,
-        key_ptr: *mut r_efi::efi::Char16,
-    ) -> Option<OsString> {
-        let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) };
-        helpers::os_string_from_raw(val)
-    }
-
-    unsafe fn set_raw(
-        key_ptr: *mut r_efi::efi::Char16,
-        val_ptr: *mut r_efi::efi::Char16,
-    ) -> io::Result<()> {
-        let shell = helpers::open_shell().ok_or(unsupported_err())?;
-        let r =
-            unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) };
-        if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
-    }
-}
diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs
index f47421c67051b..4883303b88e27 100644
--- a/library/std/src/sys/pal/unix/os.rs
+++ b/library/std/src/sys/pal/unix/os.rs
@@ -5,20 +5,15 @@
 #[cfg(test)]
 mod tests;
 
-use core::slice::memchr;
-
 use libc::{c_char, c_int, c_void};
 
 use crate::error::Error as StdError;
-use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::ffi::{CStr, OsStr, OsString};
 use crate::os::unix::prelude::*;
 use crate::path::{self, PathBuf};
-use crate::sync::{PoisonError, RwLock};
-use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
-#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
-use crate::sys::weak::weak;
-use crate::sys::{cvt, fd};
-use crate::{fmt, io, iter, mem, ptr, slice, str, vec};
+use crate::sys::common::small_c_string::run_path_with_cstr;
+use crate::sys::cvt;
+use crate::{fmt, io, iter, mem, ptr, slice, str};
 
 const TMPBUF_SZ: usize = 128;
 
@@ -552,166 +547,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
 }
 
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-// Use `_NSGetEnviron` on Apple platforms.
-//
-// `_NSGetEnviron` is the documented alternative (see `man environ`), and has
-// been available since the first versions of both macOS and iOS.
-//
-// Nowadays, specifically since macOS 10.8, `environ` has been exposed through
-// `libdyld.dylib`, which is linked via. `libSystem.dylib`:
-// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913>
-//
-// So in the end, it likely doesn't really matter which option we use, but the
-// performance cost of using `_NSGetEnviron` is extremely miniscule, and it
-// might be ever so slightly more supported, so let's just use that.
-//
-// NOTE: The header where this is defined (`crt_externs.h`) was added to the
-// iOS 13.0 SDK, which has been the source of a great deal of confusion in the
-// past about the availability of this API.
-//
-// NOTE(madsmtm): Neither this nor using `environ` has been verified to not
-// cause App Store rejections; if this is found to be the case, an alternative
-// implementation of this is possible using `[NSProcessInfo environment]`
-// - which internally uses `_NSGetEnviron` and a system-wide lock on the
-// environment variables to protect against `setenv`, so using that might be
-// desirable anyhow? Though it also means that we have to link to Foundation.
-#[cfg(target_vendor = "apple")]
-pub unsafe fn environ() -> *mut *const *const c_char {
-    libc::_NSGetEnviron() as *mut *const *const c_char
-}
-
-// Use the `environ` static which is part of POSIX.
-#[cfg(not(target_vendor = "apple"))]
-pub unsafe fn environ() -> *mut *const *const c_char {
-    unsafe extern "C" {
-        static mut environ: *const *const c_char;
-    }
-    &raw mut environ
-}
-
-static ENV_LOCK: RwLock<()> = RwLock::new(());
-
-pub fn env_read_lock() -> impl Drop {
-    ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
-}
-
-/// Returns a vector of (variable, value) byte-vector pairs for all the
-/// environment variables of the current process.
-pub fn env() -> Env {
-    unsafe {
-        let _guard = env_read_lock();
-        let mut environ = *environ();
-        let mut result = Vec::new();
-        if !environ.is_null() {
-            while !(*environ).is_null() {
-                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
-                    result.push(key_value);
-                }
-                environ = environ.add(1);
-            }
-        }
-        return Env { iter: result.into_iter() };
-    }
-
-    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
-        // Strategy (copied from glibc): Variable name and value are separated
-        // by an ASCII equals sign '='. Since a variable name must not be
-        // empty, allow variable names starting with an equals sign. Skip all
-        // malformed lines.
-        if input.is_empty() {
-            return None;
-        }
-        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
-        pos.map(|p| {
-            (
-                OsStringExt::from_vec(input[..p].to_vec()),
-                OsStringExt::from_vec(input[p + 1..].to_vec()),
-            )
-        })
-    }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    // environment variables with a nul byte can't be set, so their value is
-    // always None as well
-    run_with_cstr(k.as_bytes(), &|k| {
-        let _guard = env_read_lock();
-        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
-
-        if v.is_null() {
-            Ok(None)
-        } else {
-            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
-            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
-
-            Ok(Some(OsStringExt::from_vec(bytes)))
-        }
-    })
-    .ok()
-    .flatten()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    run_with_cstr(k.as_bytes(), &|k| {
-        run_with_cstr(v.as_bytes(), &|v| {
-            let _guard = ENV_LOCK.write();
-            cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
-        })
-    })
-}
-
-pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
-    run_with_cstr(n.as_bytes(), &|nbuf| {
-        let _guard = ENV_LOCK.write();
-        cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
-    })
-}
-
 #[cfg(not(target_os = "espidf"))]
 pub fn page_size() -> usize {
     unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs
index 48de4312885fe..a8ef97ecf67ac 100644
--- a/library/std/src/sys/pal/unsupported/os.rs
+++ b/library/std/src/sys/pal/unsupported/os.rs
@@ -62,47 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-pub struct Env(!);
-
-impl Env {
-    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-pub fn env() -> Env {
-    panic!("not supported on this platform")
-}
-
-pub fn getenv(_: &OsStr) -> Option<OsString> {
-    None
-}
-
-pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
-}
-
-pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on this platform")
 }
diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs
index ba2b65a1f40dc..672cf70d1a5b2 100644
--- a/library/std/src/sys/pal/wasi/os.rs
+++ b/library/std/src/sys/pal/wasi/os.rs
@@ -1,19 +1,16 @@
 #![forbid(unsafe_op_in_unsafe_fn)]
 
-use core::slice::memchr;
-
 use crate::error::Error as StdError;
 use crate::ffi::{CStr, OsStr, OsString};
 use crate::marker::PhantomData;
-use crate::ops::Drop;
 use crate::os::wasi::prelude::*;
 use crate::path::{self, PathBuf};
-use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
+use crate::sys::common::small_c_string::run_path_with_cstr;
 use crate::sys::unsupported;
-use crate::{fmt, io, str, vec};
+use crate::{fmt, io, str};
 
 // Add a few symbols not in upstream `libc` just yet.
-mod libc {
+pub mod libc {
     pub use libc::*;
 
     unsafe extern "C" {
@@ -23,28 +20,6 @@ mod libc {
     }
 }
 
-cfg_if::cfg_if! {
-    if #[cfg(target_feature = "atomics")] {
-        // Access to the environment must be protected by a lock in multi-threaded scenarios.
-        use crate::sync::{PoisonError, RwLock};
-        static ENV_LOCK: RwLock<()> = RwLock::new(());
-        pub fn env_read_lock() -> impl Drop {
-            ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
-        }
-        pub fn env_write_lock() -> impl Drop {
-            ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner)
-        }
-    } else {
-        // No need for a lock if we are single-threaded.
-        pub fn env_read_lock() -> impl Drop {
-            Box::new(())
-        }
-        pub fn env_write_lock() -> impl Drop {
-            Box::new(())
-        }
-    }
-}
-
 pub fn errno() -> i32 {
     unsafe extern "C" {
         #[thread_local]
@@ -141,123 +116,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-pub fn env() -> Env {
-    unsafe {
-        let _guard = env_read_lock();
-
-        // Use `__wasilibc_get_environ` instead of `environ` here so that we
-        // don't require wasi-libc to eagerly initialize the environment
-        // variables.
-        let mut environ = libc::__wasilibc_get_environ();
-
-        let mut result = Vec::new();
-        if !environ.is_null() {
-            while !(*environ).is_null() {
-                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
-                    result.push(key_value);
-                }
-                environ = environ.add(1);
-            }
-        }
-        return Env { iter: result.into_iter() };
-    }
-
-    // See src/libstd/sys/pal/unix/os.rs, same as that
-    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
-        if input.is_empty() {
-            return None;
-        }
-        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
-        pos.map(|p| {
-            (
-                OsStringExt::from_vec(input[..p].to_vec()),
-                OsStringExt::from_vec(input[p + 1..].to_vec()),
-            )
-        })
-    }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    // environment variables with a nul byte can't be set, so their value is
-    // always None as well
-    run_with_cstr(k.as_bytes(), &|k| {
-        let _guard = env_read_lock();
-        let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
-
-        if v.is_null() {
-            Ok(None)
-        } else {
-            // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
-            let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
-
-            Ok(Some(OsStringExt::from_vec(bytes)))
-        }
-    })
-    .ok()
-    .flatten()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    run_with_cstr(k.as_bytes(), &|k| {
-        run_with_cstr(v.as_bytes(), &|v| unsafe {
-            let _guard = env_write_lock();
-            cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
-        })
-    })
-}
-
-pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
-    run_with_cstr(n.as_bytes(), &|nbuf| unsafe {
-        let _guard = env_write_lock();
-        cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
-    })
-}
-
 #[allow(dead_code)]
 pub fn page_size() -> usize {
     unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
@@ -294,6 +152,6 @@ macro_rules! impl_is_minus_one {
 
 impl_is_minus_one! { i8 i16 i32 i64 isize }
 
-fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
     if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) }
 }
diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs
index 044dc2e8cd8fa..f331282d2d72a 100644
--- a/library/std/src/sys/pal/windows/os.rs
+++ b/library/std/src/sys/pal/windows/os.rs
@@ -5,16 +5,16 @@
 #[cfg(test)]
 mod tests;
 
+use super::api;
 #[cfg(not(target_vendor = "uwp"))]
 use super::api::WinError;
-use super::{api, to_u16s};
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::os::windows::ffi::EncodeWide;
 use crate::os::windows::prelude::*;
 use crate::path::{self, PathBuf};
-use crate::sys::{c, cvt};
-use crate::{fmt, io, ptr, slice};
+use crate::sys::pal::{c, cvt};
+use crate::{fmt, io, ptr};
 
 pub fn errno() -> i32 {
     api::get_last_error().code as i32
@@ -76,108 +76,6 @@ pub fn error_string(mut errnum: i32) -> String {
     }
 }
 
-pub struct Env {
-    base: *mut c::WCHAR,
-    iter: EnvIterator,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    iter: &'a EnvIterator,
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        let iter: EnvIterator = (*iter).clone();
-        let mut list = f.debug_list();
-        for (a, b) in iter {
-            list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
-        }
-        list.finish()
-    }
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { base: _, iter } = self;
-        EnvStrDebug { iter }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { base: _, iter } = self;
-        f.debug_list().entries(iter.clone()).finish()
-    }
-}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        let Self { base: _, iter } = self;
-        iter.next()
-    }
-}
-
-#[derive(Clone)]
-struct EnvIterator(*mut c::WCHAR);
-
-impl Iterator for EnvIterator {
-    type Item = (OsString, OsString);
-
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        let Self(cur) = self;
-        loop {
-            unsafe {
-                if **cur == 0 {
-                    return None;
-                }
-                let p = *cur as *const u16;
-                let mut len = 0;
-                while *p.add(len) != 0 {
-                    len += 1;
-                }
-                let s = slice::from_raw_parts(p, len);
-                *cur = cur.add(len + 1);
-
-                // Windows allows environment variables to start with an equals
-                // symbol (in any other position, this is the separator between
-                // variable name and value). Since`s` has at least length 1 at
-                // this point (because the empty string terminates the array of
-                // environment variables), we can safely slice.
-                let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
-                    Some(p) => p,
-                    None => continue,
-                };
-                return Some((
-                    OsStringExt::from_wide(&s[..pos]),
-                    OsStringExt::from_wide(&s[pos + 1..]),
-                ));
-            }
-        }
-    }
-}
-
-impl Drop for Env {
-    fn drop(&mut self) {
-        unsafe {
-            c::FreeEnvironmentStringsW(self.base);
-        }
-    }
-}
-
-pub fn env() -> Env {
-    unsafe {
-        let ch = c::GetEnvironmentStringsW();
-        if ch.is_null() {
-            panic!("failure getting env string from OS: {}", io::Error::last_os_error());
-        }
-        Env { base: ch, iter: EnvIterator(ch) }
-    }
-}
-
 pub struct SplitPaths<'a> {
     data: EncodeWide<'a>,
     must_yield: bool,
@@ -290,33 +188,6 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
     cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
 }
 
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    let k = to_u16s(k).ok()?;
-    super::fill_utf16_buf(
-        |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
-        OsStringExt::from_wide,
-    )
-    .ok()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    // SAFETY: We ensure that k and v are null-terminated wide strings.
-    unsafe {
-        let k = to_u16s(k)?;
-        let v = to_u16s(v)?;
-
-        cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
-    }
-}
-
-pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
-    // SAFETY: We ensure that v is a null-terminated wide strings.
-    unsafe {
-        let v = to_u16s(n)?;
-        cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
-    }
-}
-
 pub fn temp_dir() -> PathBuf {
     super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
 }
diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs
index 2c87e7d91f27d..1b41575358f1e 100644
--- a/library/std/src/sys/pal/xous/os.rs
+++ b/library/std/src/sys/pal/xous/os.rs
@@ -1,13 +1,11 @@
 use super::unsupported;
-use crate::collections::HashMap;
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::marker::PhantomData;
 use crate::os::xous::ffi::Error as XousError;
 use crate::path::{self, PathBuf};
-use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
-use crate::sync::{Mutex, Once};
-use crate::{fmt, io, vec};
+use crate::sync::atomic::{AtomicPtr, Ordering};
+use crate::{fmt, io};
 
 pub(crate) mod params;
 
@@ -136,100 +134,6 @@ pub(crate) fn get_application_parameters() -> Option<params::ApplicationParamete
     unsafe { params::ApplicationParameters::new_from_ptr(params_address) }
 }
 
-// ---------- Environment handling ---------- //
-static ENV: AtomicUsize = AtomicUsize::new(0);
-static ENV_INIT: Once = Once::new();
-type EnvStore = Mutex<HashMap<OsString, OsString>>;
-
-fn get_env_store() -> &'static EnvStore {
-    ENV_INIT.call_once(|| {
-        let env_store = EnvStore::default();
-        if let Some(params) = get_application_parameters() {
-            for param in params {
-                if let Ok(envs) = params::EnvironmentBlock::try_from(&param) {
-                    let mut env_store = env_store.lock().unwrap();
-                    for env in envs {
-                        env_store.insert(env.key.into(), env.value.into());
-                    }
-                    break;
-                }
-            }
-        }
-        ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed)
-    });
-    unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) }
-}
-
-pub struct Env {
-    iter: vec::IntoIter<(OsString, OsString)>,
-}
-
-// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-pub struct EnvStrDebug<'a> {
-    slice: &'a [(OsString, OsString)],
-}
-
-impl fmt::Debug for EnvStrDebug<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { slice } = self;
-        f.debug_list()
-            .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
-            .finish()
-    }
-}
-
-impl Env {
-    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self { iter } = self;
-        EnvStrDebug { slice: iter.as_slice() }
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self { iter } = self;
-        f.debug_list().entries(iter.as_slice()).finish()
-    }
-}
-
-impl !Send for Env {}
-impl !Sync for Env {}
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-pub fn env() -> Env {
-    let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
-        map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
-    };
-
-    let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter();
-    Env { iter }
-}
-
-pub fn getenv(k: &OsStr) -> Option<OsString> {
-    get_env_store().lock().unwrap().get(k).cloned()
-}
-
-pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
-    let (k, v) = (k.to_owned(), v.to_owned());
-    get_env_store().lock().unwrap().insert(k, v);
-    Ok(())
-}
-
-pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
-    get_env_store().lock().unwrap().remove(k);
-    Ok(())
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on this platform")
 }
diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs
index 868b19e33b672..a8ef97ecf67ac 100644
--- a/library/std/src/sys/pal/zkvm/os.rs
+++ b/library/std/src/sys/pal/zkvm/os.rs
@@ -1,10 +1,8 @@
-use super::{WORD_SIZE, abi, unsupported};
+use super::unsupported;
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::marker::PhantomData;
 use crate::path::{self, PathBuf};
-use crate::sys::os_str;
-use crate::sys_common::FromInner;
 use crate::{fmt, io};
 
 pub fn errno() -> i32 {
@@ -64,64 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-pub struct Env(!);
-
-impl Iterator for Env {
-    type Item = (OsString, OsString);
-    fn next(&mut self) -> Option<(OsString, OsString)> {
-        self.0
-    }
-}
-
-pub fn env() -> Env {
-    panic!("not supported on this platform")
-}
-
-impl Env {
-    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-impl fmt::Debug for Env {
-    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Self(inner) = self;
-        match *inner {}
-    }
-}
-
-pub fn getenv(varname: &OsStr) -> Option<OsString> {
-    let varname = varname.as_encoded_bytes();
-    let nbytes =
-        unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) };
-    if nbytes == usize::MAX {
-        return None;
-    }
-
-    let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE;
-    let words = unsafe { abi::sys_alloc_words(nwords) };
-
-    let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) };
-    debug_assert_eq!(nbytes, nbytes2);
-
-    // Convert to OsString.
-    //
-    // FIXME: We can probably get rid of the extra copy here if we
-    // reimplement "os_str" instead of just using the generic unix
-    // "os_str".
-    let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) };
-    Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() }))
-}
-
-pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
-}
-
-pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
-    Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
-}
-
 pub fn temp_dir() -> PathBuf {
     panic!("no filesystem on this platform")
 }
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index 3b04ec50db30e..f594ba5c200a6 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -88,7 +88,7 @@ impl Command {
         // in its own process. Thus the parent drops the lock guard immediately.
         // The child calls `mem::forget` to leak the lock, which is crucial because
         // releasing a lock is not async-signal-safe.
-        let env_lock = sys::os::env_read_lock();
+        let env_lock = sys::env::env_read_lock();
         let pid = unsafe { self.do_fork()? };
 
         if pid == 0 {
@@ -237,7 +237,7 @@ impl Command {
                     // Similar to when forking, we want to ensure that access to
                     // the environment is synchronized, so make sure to grab the
                     // environment lock before we try to exec.
-                    let _lock = sys::os::env_read_lock();
+                    let _lock = sys::env::env_read_lock();
 
                     let Err(e) = self.do_exec(theirs, envp.as_ref());
                     e
@@ -386,13 +386,13 @@ impl Command {
             impl Drop for Reset {
                 fn drop(&mut self) {
                     unsafe {
-                        *sys::os::environ() = self.0;
+                        *sys::env::environ() = self.0;
                     }
                 }
             }
 
-            _reset = Some(Reset(*sys::os::environ()));
-            *sys::os::environ() = envp.as_ptr();
+            _reset = Some(Reset(*sys::env::environ()));
+            *sys::env::environ() = envp.as_ptr();
         }
 
         libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
@@ -735,8 +735,8 @@ impl Command {
             cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
 
             // Make sure we synchronize access to the global `environ` resource
-            let _env_lock = sys::os::env_read_lock();
-            let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
+            let _env_lock = sys::env::env_read_lock();
+            let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _);
 
             #[cfg(not(target_os = "nto"))]
             let spawn_fn = libc::posix_spawnp;
diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs
index 5f1727789a1bc..b92446f0cf673 100644
--- a/library/std/src/sys/process/unix/vxworks.rs
+++ b/library/std/src/sys/process/unix/vxworks.rs
@@ -67,7 +67,7 @@ impl Command {
             let c_envp = envp
                 .as_ref()
                 .map(|c| c.as_ptr())
-                .unwrap_or_else(|| *sys::os::environ() as *const _);
+                .unwrap_or_else(|| *sys::env::environ() as *const _);
             let stack_size = crate::cmp::max(
                 crate::env::var_os("RUST_MIN_STACK")
                     .and_then(|s| s.to_str().and_then(|s| s.parse().ok()))
@@ -76,7 +76,7 @@ impl Command {
             );
 
             // ensure that access to the environment is synchronized
-            let _lock = sys::os::env_read_lock();
+            let _lock = sys::env::env_read_lock();
 
             let ret = libc::rtpSpawn(
                 self.get_program_cstr().as_ptr(),