diff --git a/include/limits.h b/include/bits/limits.h
similarity index 93%
rename from include/limits.h
rename to include/bits/limits.h
index 1b2de46fc821197cdcd923a2459afa1ac1b2d76e..73d0bdc23b6af8699f98a662dcca682d108baf75 100644
--- a/include/limits.h
+++ b/include/bits/limits.h
@@ -1,3 +1,6 @@
+#ifndef _BITS_LIMIT_H
+#define _BITS_LIMIT_H
+
 #define MB_LEN_MAX 4 // unicode
 
 #define CHAR_BIT __CHAR_BIT__
@@ -28,4 +31,4 @@
 #define USHRT_MAX ((1 << 16) - 1)
 #define WORD_BIT 32
 
-#define PATH_MAX 4096
+#endif
diff --git a/src/header/limits/cbindgen.toml b/src/header/limits/cbindgen.toml
new file mode 100644
index 0000000000000000000000000000000000000000..e25e6c67054a296c1e56543597a80fa3eb5b3788
--- /dev/null
+++ b/src/header/limits/cbindgen.toml
@@ -0,0 +1,8 @@
+sys_includes = []
+include_guard = "_LIMITS_H"
+language = "C"
+style = "Tag"
+trailer = "#include <bits/limits.h>"
+
+[enum]
+prefix_with_name = true
diff --git a/src/header/limits/mod.rs b/src/header/limits/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aab89f6ed35790a88226c56cd588ba87de00ce6c
--- /dev/null
+++ b/src/header/limits/mod.rs
@@ -0,0 +1,3 @@
+//! limits.h implementation for relibc
+
+pub const PATH_MAX: usize = 4096;
diff --git a/src/header/mod.rs b/src/header/mod.rs
index e60e528f62f6845ea743ab57262098d2e3a1f3f8..c733f05d1d710e9a4738077e835d03f9881206cb 100644
--- a/src/header/mod.rs
+++ b/src/header/mod.rs
@@ -10,6 +10,7 @@ pub mod fnmatch;
 pub mod getopt;
 pub mod grp;
 pub mod inttypes;
+pub mod limits;
 pub mod locale;
 pub mod netdb;
 pub mod netinet_in;
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index a4b5fa6ffa1c33943ca044de5ecd47485e3fe6c0..6573ac50dd8d02cd487b7b0bda7d8e2dd56c25d3 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -1,6 +1,6 @@
 //! stdlib implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdlib.h.html
 
-use core::{intrinsics, iter, mem, ptr};
+use core::{intrinsics, iter, mem, ptr, slice};
 use rand::distributions::Alphanumeric;
 use rand::prng::XorShiftRng;
 use rand::rngs::JitterRng;
@@ -12,6 +12,7 @@ use header::fcntl::*;
 use header::string::*;
 use header::time::constants::CLOCK_MONOTONIC;
 use header::time::timespec;
+use header::limits;
 use header::wchar::*;
 use header::{ctype, errno, unistd};
 use platform;
@@ -572,9 +573,24 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void
     platform::realloc(ptr, size)
 }
 
-// #[no_mangle]
-pub extern "C" fn realpath(file_name: *const c_char, resolved_name: *mut c_char) -> *mut c_char {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char) -> *mut c_char {
+    let mut path = [0; limits::PATH_MAX];
+    {
+        let slice = if resolved.is_null() {
+            &mut path
+        } else {
+            slice::from_raw_parts_mut(resolved as *mut u8, 4096)
+        };
+        if Sys::realpath(CStr::from_ptr(pathname), slice) < 0 {
+            return ptr::null_mut();
+        }
+    }
+    if !resolved.is_null() {
+        resolved
+    } else {
+        strdup(path.as_ptr() as *const i8)
+    }
 }
 
 // #[no_mangle]
diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs
index 816c2aed41d82684ac541c334f8d8d0455e65b16..80a6385544f4526ef95d3f35a8ec1c1ec52c7030 100644
--- a/src/header/unistd/mod.rs
+++ b/src/header/unistd/mod.rs
@@ -3,7 +3,7 @@
 use core::{ptr, slice};
 
 use c_str::CStr;
-use header::sys_time;
+use header::{limits, sys_time};
 use header::time::timespec;
 use platform;
 use platform::types::*;
@@ -35,8 +35,6 @@ pub const STDIN_FILENO: c_int = 0;
 pub const STDOUT_FILENO: c_int = 1;
 pub const STDERR_FILENO: c_int = 2;
 
-const PATH_MAX: usize = 4096;
-
 #[no_mangle]
 pub extern "C" fn _exit(status: c_int) {
     Sys::exit(status)
@@ -190,7 +188,7 @@ pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int {
 #[no_mangle]
 pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char {
     let alloc = buf.is_null();
-    let mut stack_buf = [0; PATH_MAX];
+    let mut stack_buf = [0; limits::PATH_MAX];
     if alloc {
         buf = stack_buf.as_mut_ptr();
         size = stack_buf.len();
@@ -305,7 +303,7 @@ pub extern "C" fn getuid() -> uid_t {
 
 #[no_mangle]
 pub extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
-    getcwd(path_name, PATH_MAX)
+    getcwd(path_name, limits::PATH_MAX)
 }
 
 #[no_mangle]
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index 5b3d8957335a5d1199dd1b72342ff0db1183fdde..a194e041d56bd7bd540b7e80f77b2201c9635682 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -1,11 +1,15 @@
-use core::fmt::Write;
+use alloc::vec::Vec;
+use core::fmt::Write as _WriteFmt;
 use core::{mem, ptr};
+use core_io::Write;
 
 use super::types::*;
 use super::{errno, FileWriter, Pal};
 use c_str::CStr;
+use fs::File;
 use header::dirent::dirent;
 use header::errno::{EINVAL, ENOSYS};
+use header::fcntl;
 use header::signal::SIGCHLD;
 use header::sys_ioctl::{winsize, TCGETS, TCSETS, TIOCGWINSZ};
 // use header::sys_resource::rusage;
@@ -302,6 +306,39 @@ impl Pal for Sys {
         e(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) }) as ssize_t
     }
 
+    fn realpath(pathname: &CStr, out: &mut [u8]) -> c_int {
+        fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t {
+            e(unsafe { syscall!(READLINKAT, AT_FDCWD, pathname.as_ptr(), out.as_mut_ptr(), out.len()) }) as ssize_t
+        }
+
+        let file = match File::open(pathname, fcntl::O_PATH) {
+            Ok(file) => file,
+            Err(_) => return -1
+        };
+
+        if out.is_empty() {
+            return 0;
+        }
+
+        let mut proc_path = b"/proc/self/fd/".to_vec();
+        write!(proc_path, "{}", *file).unwrap();
+        proc_path.push(0);
+
+        let len = out.len() - 1;
+        let read = readlink(CStr::from_bytes_with_nul(&proc_path).unwrap(), &mut out[..len]);
+        if read < 0 {
+            return -1;
+        }
+        out[read as usize] = 0;
+
+        // TODO: Should these checks from musl be ported?
+        // https://gitlab.com/bminor/musl/blob/master/src/misc/realpath.c#L33-38
+        // I'm not exactly sure what they're checking...
+        // Seems to be a sanity check whether or not it's still the same file?
+
+        0
+    }
+
     fn rename(old: &CStr, new: &CStr) -> c_int {
         e(unsafe { syscall!(RENAMEAT, AT_FDCWD, old.as_ptr(), AT_FDCWD, new.as_ptr()) }) as c_int
     }
diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs
index bd18cbabe1f8af8c066de3488eab114e632ba29c..f4fdd25f6bcf2362f18b50b0e8e329ff87c571be 100644
--- a/src/platform/pal/mod.rs
+++ b/src/platform/pal/mod.rs
@@ -112,6 +112,10 @@ pub trait Pal {
 
     fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t;
 
+    //fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t;
+
+    fn realpath(pathname: &CStr, out: &mut [u8]) -> c_int;
+
     fn rename(old: &CStr, new: &CStr) -> c_int;
 
     fn rmdir(path: &CStr) -> c_int;
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 7dcd9484fe10801c5e66b6a36472c7ad56703029..9767de086780aab772ff4673030895b5ab91d9aa 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -616,6 +616,15 @@ impl Pal for Sys {
         e(syscall::read(fd as usize, buf)) as ssize_t
     }
 
+    fn realpath(pathname: &CStr, out: &mut [u8]) -> c_int {
+        let file = match File::open(pathname, fcntl::O_PATH) {
+            Ok(fd) => fd,
+            Err(_) => return -1
+        };
+
+        e(syscall::fpath(*file as usize, out)) as c_int
+    }
+
     fn rename(oldpath: &CStr, newpath: &CStr) -> c_int {
         match syscall::open(oldpath.to_bytes(), O_WRONLY | O_CLOEXEC) {
             Ok(fd) => {
diff --git a/tests/Makefile b/tests/Makefile
index fb77b2d5ef8f53ebf7332815b8f299ad1745c3c1..71ee5e05da928b2225e39c6a0aabfdd6ed6c130c 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -85,6 +85,7 @@ BINS=\
 	stdlib/alloc \
 	stdlib/bsearch \
 	stdlib/mktemp \
+	stdlib/realpath \
 	time/gettimeofday \
 	unistd/chdir \
 	unistd/getcwd \
diff --git a/tests/stdlib/realpath.c b/tests/stdlib/realpath.c
new file mode 100644
index 0000000000000000000000000000000000000000..c6a679a4d508c28661492032ae33dfd94aa95bbd
--- /dev/null
+++ b/tests/stdlib/realpath.c
@@ -0,0 +1,29 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main() {
+    char* path = realpath("stdlib/realpath.c", NULL);
+    if (!path) {
+        perror("realpath");
+        return -1;
+    }
+    puts(path);
+
+    free(path);
+
+    path = malloc(PATH_MAX);
+    memset(path, 0, PATH_MAX);
+
+    realpath("stdlib/realpath.c", path);
+    if (!path) {
+        perror("realpath");
+        free(path);
+        return -1;
+    }
+    puts(path);
+
+    free(path);
+}