diff --git a/include/bits/stdio.h b/include/bits/stdio.h
index 16eb100bb47f46f9ddbab354ff7b689350df0c21..506ff549e369cfae666513b76698050a4966847d 100644
--- a/include/bits/stdio.h
+++ b/include/bits/stdio.h
@@ -2,6 +2,9 @@
 #define _BITS_STDIO_H
 
 #define EOF (-1)
+#define stdin __stdin()
+#define stdout __stdout()
+#define stderr __stderr()
 
 int fprintf(FILE * stream, const char * fmt, ...);
 int printf(const char * fmt, ...);
diff --git a/src/locale/src/lib.rs b/src/locale/src/lib.rs
index f71380b3533c2a3a388d891ed6cbb1a35ce0d842..34755f17208cf482c3f242f46864ca1f721284b8 100644
--- a/src/locale/src/lib.rs
+++ b/src/locale/src/lib.rs
@@ -9,6 +9,7 @@ use core::ptr;
 use platform::types::*;
 
 const EMPTY_PTR: *const c_char = "\0" as *const _ as *const c_char;
+// Can't use &str because of the mutability
 static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0];
 
 #[repr(C)]
@@ -64,7 +65,6 @@ pub extern "C" fn localeconv() -> *const lconv {
 #[no_mangle]
 pub unsafe extern "C" fn setlocale(_option: c_int, val: *const c_char) -> *mut c_char {
     if val.is_null() {
-        // Can't use string``
         return C_LOCALE.as_mut_ptr() as *mut c_char;
     }
     // TODO actually implement
diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs
index 840a6fa07ff1ef76d945ff7997403e6b153d1f40..25b5844719ec765fea972db7c62e146cc8d975ae 100644
--- a/src/stdio/src/default.rs
+++ b/src/stdio/src/default.rs
@@ -1,12 +1,25 @@
+use core::cell::UnsafeCell;
 use core::sync::atomic::AtomicBool;
 use core::ptr;
 use super::{constants, internal, BUFSIZ, FILE, UNGET};
 
+struct GlobalFile(UnsafeCell<FILE>);
+impl GlobalFile {
+    const fn new(file: FILE) -> Self {
+        GlobalFile(UnsafeCell::new(file))
+    }
+    fn get(&self) -> *mut FILE {
+        self.0.get()
+    }
+}
+// statics need to be Sync
+unsafe impl Sync for GlobalFile {}
+
 #[allow(non_upper_case_globals)]
 static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET];
 
 #[allow(non_upper_case_globals)]
-static mut default_stdin: FILE = FILE {
+static mut default_stdin: GlobalFile = GlobalFile::new(FILE {
     flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ,
     rpos: ptr::null_mut(),
     rend: ptr::null_mut(),
@@ -19,13 +32,13 @@ static mut default_stdin: FILE = FILE {
     buf_char: -1,
     unget: UNGET,
     lock: AtomicBool::new(false),
-};
+});
 
 #[allow(non_upper_case_globals)]
 static mut default_stdout_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize];
 
 #[allow(non_upper_case_globals)]
-static mut default_stdout: FILE = FILE {
+static mut default_stdout: GlobalFile = GlobalFile::new(FILE {
     flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ,
     rpos: ptr::null_mut(),
     rend: ptr::null_mut(),
@@ -38,13 +51,13 @@ static mut default_stdout: FILE = FILE {
     buf_char: b'\n' as i8,
     unget: 0,
     lock: AtomicBool::new(false),
-};
+});
 
 #[allow(non_upper_case_globals)]
 static mut default_stderr_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize];
 
 #[allow(non_upper_case_globals)]
-static mut default_stderr: FILE = FILE {
+static mut default_stderr: GlobalFile = GlobalFile::new(FILE {
     flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ,
     rpos: ptr::null_mut(),
     rend: ptr::null_mut(),
@@ -57,20 +70,19 @@ static mut default_stderr: FILE = FILE {
     buf_char: -1,
     unget: 0,
     lock: AtomicBool::new(false),
-};
+});
 
-// Don't ask me how the casting below works, I have no idea
-// " as *const FILE as *mut FILE" rust pls
-//
-// -- Tommoa
-#[allow(non_upper_case_globals)]
 #[no_mangle]
-pub static mut stdin: *mut FILE = unsafe { &default_stdin } as *const FILE as *mut FILE;
+pub extern "C" fn __stdin() -> *mut FILE {
+    unsafe { default_stdin.get() }
+}
 
-#[allow(non_upper_case_globals)]
 #[no_mangle]
-pub static mut stdout: *mut FILE = unsafe { &default_stdout } as *const FILE as *mut FILE;
+pub extern "C" fn __stdout() -> *mut FILE {
+    unsafe { default_stdout.get() }
+}
 
-#[allow(non_upper_case_globals)]
 #[no_mangle]
-pub static mut stderr: *mut FILE = unsafe { &default_stderr } as *const FILE as *mut FILE;
+pub extern "C" fn __stderr() -> *mut FILE {
+    unsafe { default_stderr.get() }
+}
diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs
index cec2032da6e6b62dc9b9d049fb8123c5e0c64d14..ff4bd1a860c4fabdbef65ac65e22e816a1333c2f 100644
--- a/src/stdio/src/lib.rs
+++ b/src/stdio/src/lib.rs
@@ -2,6 +2,7 @@
 
 #![no_std]
 #![feature(alloc)]
+#![feature(const_fn)]
 
 extern crate alloc;
 extern crate errno;
@@ -619,7 +620,7 @@ pub extern "C" fn getc(stream: &mut FILE) -> c_int {
 /// Get a single char from `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn getchar() -> c_int {
-    fgetc(&mut *stdin)
+    fgetc(&mut *__stdin())
 }
 
 /// Get a char from a stream without locking the stream
@@ -644,14 +645,14 @@ pub extern "C" fn getc_unlocked(stream: &mut FILE) -> c_int {
 /// Get a char from `stdin` without locking `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn getchar_unlocked() -> c_int {
-    getc_unlocked(&mut *stdin)
+    getc_unlocked(&mut *__stdin())
 }
 
 /// Get a string from `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
     use core::i32;
-    fgets(s, i32::MAX, &mut *stdin)
+    fgets(s, i32::MAX, &mut *__stdin())
 }
 
 /// Get an integer from `stream`
@@ -706,7 +707,7 @@ pub extern "C" fn putc(c: c_int, stream: &mut FILE) -> c_int {
 /// Put a character `c` into `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
-    fputc(c, &mut *stdout)
+    fputc(c, &mut *__stdout())
 }
 
 /// Put a character `c` into `stream` without locking `stream`
@@ -738,13 +739,13 @@ pub extern "C" fn putc_unlocked(c: c_int, stream: &mut FILE) -> c_int {
 /// Put a character `c` into `stdout` without locking `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
-    putc_unlocked(c, &mut *stdout)
+    putc_unlocked(c, &mut *__stdout())
 }
 
 /// Put a string `s` into `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
-    let ret = (fputs(s, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
+    let ret = (fputs(s, &mut *__stdout()) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
     if ret {
         0
     } else {
@@ -872,7 +873,7 @@ pub unsafe extern "C" fn vfprintf(file: &mut FILE, format: *const c_char, ap: va
 
 #[no_mangle]
 pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int {
-    vfprintf(&mut *stdout, format, ap)
+    vfprintf(&mut *__stdout(), format, ap)
 }
 
 #[no_mangle]
@@ -901,7 +902,7 @@ pub unsafe extern "C" fn vfscanf(file: &mut FILE, format: *const c_char, ap: va_
 
 #[no_mangle]
 pub unsafe extern "C" fn vscanf(format: *const c_char, ap: va_list) -> c_int {
-    vfscanf(&mut *stdin, format, ap)
+    vfscanf(&mut *__stdin(), format, ap)
 }
 
 #[no_mangle]
diff --git a/src/unistd/src/getopt.rs b/src/unistd/src/getopt.rs
index c93a5487b94d3be9ca9e0522811ab028a6b316fb..e1dac2dad77298c515b5f19fe164dd2fb06382c5 100644
--- a/src/unistd/src/getopt.rs
+++ b/src/unistd/src/getopt.rs
@@ -69,10 +69,10 @@ unsafe fn parse_arg(
 
     let print_error = |desc: &[u8]| {
         // NOTE: we don't use fprintf to get around the usage of va_list
-        stdio::fputs(*argv as _, &mut *stdio::stderr);
-        stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
-        stdio::fputc(*current_arg as _, &mut *stdio::stderr);
-        stdio::fputc(b'\n' as _, &mut *stdio::stderr);
+        stdio::fputs(*argv as _, &mut *stdio::__stderr());
+        stdio::fputs(desc.as_ptr() as _, &mut *stdio::__stderr());
+        stdio::fputc(*current_arg as _, &mut *stdio::__stderr());
+        stdio::fputc(b'\n' as _, &mut *stdio::__stderr());
     };
 
     match find_option(*current_arg, optstring) {