diff --git a/Cargo.lock b/Cargo.lock
index 9bf6efb87daac03a40baea146a417a6a2571b336..0ae5091493020bf1859e01e6db229f4bb7d6f35b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -491,6 +491,7 @@ dependencies = [
  "cbindgen 0.5.2",
  "errno 0.1.0",
  "platform 0.1.0",
+ "signal 0.1.0",
 ]
 
 [[package]]
diff --git a/include/malloc.h b/include/malloc.h
new file mode 100644
index 0000000000000000000000000000000000000000..d7c21badfbb844d90bcddb8c1941936999f573dc
--- /dev/null
+++ b/include/malloc.h
@@ -0,0 +1,16 @@
+#ifndef _MALLOC_H
+#define _MALLOC_H
+
+#include <stddef.h>
+
+// Generated from:
+// `grep "malloc\|calloc\|realloc\|free\|valloc\|memalign" target/include/stdlib.h`
+
+void *calloc(size_t nelem, size_t elsize);
+void free(void *ptr);
+void *malloc(size_t size);
+void *memalign(size_t alignment, size_t size);
+void *realloc(void *ptr, size_t size);
+void *valloc(size_t size);
+
+#endif
diff --git a/src/dirent/src/lib.rs b/src/dirent/src/lib.rs
index 90549e01342dc5f0cce27682fe5762660f07c2f5..62edcf9a399f0079434d991fd7410f9d8fbc6134 100644
--- a/src/dirent/src/lib.rs
+++ b/src/dirent/src/lib.rs
@@ -44,7 +44,7 @@ pub extern "C" fn opendir(path: *const c_char) -> *mut DIR {
     let fd = platform::open(
         path,
         fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
-        0o755,
+        0,
     );
 
     if fd < 0 {
diff --git a/src/platform/src/linux/mod.rs b/src/platform/src/linux/mod.rs
index e7018fbfcfa238b67d8da1e58d5686d374c200db..99b2d6e08de653a35ef0476357e017a3b9b8d454 100644
--- a/src/platform/src/linux/mod.rs
+++ b/src/platform/src/linux/mod.rs
@@ -443,6 +443,10 @@ pub fn times(out: *mut tms) -> clock_t {
     unsafe { syscall!(TIMES, out) as clock_t }
 }
 
+pub fn umask(mask: mode_t) -> mode_t {
+    unsafe { syscall!(UMASK, mask) as mode_t }
+}
+
 pub fn uname(utsname: *mut utsname) -> c_int {
     e(unsafe { syscall!(UNAME, utsname, 0) }) as c_int
 }
diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs
index 691bf3c746459b54484a32913cbb8c3c790990be..48585668f3e9d4b7ebc5a20f884ea256d10e4c12 100644
--- a/src/platform/src/redox/mod.rs
+++ b/src/platform/src/redox/mod.rs
@@ -415,7 +415,7 @@ pub fn getgid() -> gid_t {
 }
 
 pub fn getrusage(who: c_int, r_usage: *mut rusage) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: getrusage({}, {:p})",
         who,
@@ -475,7 +475,7 @@ unsafe fn inner_get_name(
 }
 
 pub fn getitimer(which: c_int, out: *mut itimerval) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: getitimer({}, {:p})",
         which,
@@ -523,7 +523,7 @@ pub fn getsockopt(
     option_value: *mut c_void,
     option_len: *mut socklen_t,
 ) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: getsockopt({}, {}, {}, {:p}, {:p})",
         socket,
@@ -838,7 +838,7 @@ pub unsafe fn sendto(
 }
 
 pub fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: setitimer({}, {:p}, {:p})",
         which,
@@ -871,7 +871,7 @@ pub fn setsockopt(
     option_value: *const c_void,
     option_len: socklen_t,
 ) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: setsockopt({}, {}, {}, {:p}, {})",
         socket,
@@ -884,7 +884,7 @@ pub fn setsockopt(
 }
 
 pub fn shutdown(socket: c_int, how: c_int) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: shutdown({}, {})",
         socket,
@@ -896,14 +896,14 @@ pub fn shutdown(socket: c_int, how: c_int) -> c_int {
 pub unsafe fn sigaction(sig: c_int, act: *const sigaction, oact: *mut sigaction) -> c_int {
     if !oact.is_null() {
         // Assumes the last sigaction() call was made by relibc and not a different one
-        if let Some(callback) = SIG_HANDLER {
-            (*oact).sa_handler = callback;
+        if SIG_HANDLER.is_some() {
+            (*oact).sa_handler = SIG_HANDLER;
         }
     }
     let act = if act.is_null() {
         None
     } else {
-        SIG_HANDLER = Some((*act).sa_handler);
+        SIG_HANDLER = (*act).sa_handler;
         let m = (*act).sa_mask;
         Some(syscall::SigAction {
             sa_handler: sig_handler,
@@ -926,7 +926,7 @@ pub unsafe fn sigaction(sig: c_int, act: *const sigaction, oact: *mut sigaction)
 }
 
 pub fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: sigprocmask({}, {:p}, {:p})",
         how,
@@ -981,7 +981,7 @@ pub unsafe fn socket(domain: c_int, mut kind: c_int, protocol: c_int) -> c_int {
 }
 
 pub fn socketpair(domain: c_int, kind: c_int, protocol: c_int, socket_vector: *mut c_int) -> c_int {
-    let _ = write!(
+    let _ = writeln!(
         FileWriter(2),
         "unimplemented: socketpair({}, {}, {}, {:p})",
         domain,
@@ -1029,10 +1029,15 @@ pub fn tcsetattr(fd: c_int, _act: c_int, value: *const termios) -> c_int {
 }
 
 pub fn times(out: *mut tms) -> clock_t {
-    let _ = write!(FileWriter(2), "unimplemented: times({:p})", out);
+    let _ = writeln!(FileWriter(2), "unimplemented: times({:p})", out);
     !0
 }
 
+pub fn umask(mask: mode_t) -> mode_t {
+    let _ = writeln!(FileWriter(2), "unimplemented: umask({})", mask);
+    0
+}
+
 pub fn unlink(path: *const c_char) -> c_int {
     let path = unsafe { c_str(path) };
     e(syscall::unlink(path)) as c_int
diff --git a/src/platform/src/types.rs b/src/platform/src/types.rs
index bb09f4e22790df6e589f5a2f957c5c6dd2006e4f..bff380a3b873c3873486d2ae17477e0a9cf6a722 100644
--- a/src/platform/src/types.rs
+++ b/src/platform/src/types.rs
@@ -165,9 +165,9 @@ pub struct sockaddr_in {
 
 #[repr(C)]
 pub struct sigaction {
-    pub sa_handler: extern "C" fn(c_int),
+    pub sa_handler: Option<extern "C" fn(c_int)>,
     pub sa_flags: c_ulong,
-    pub sa_restorer: unsafe extern "C" fn(),
+    pub sa_restorer: Option<unsafe extern "C" fn()>,
     pub sa_mask: sigset_t,
 }
 
diff --git a/src/pwd/src/lib.rs b/src/pwd/src/lib.rs
index 300325c62a99cc87ec6c2332f46e136114bbebc1..6d82a1f9dd3b420c7fe200851597b5cb00860ae0 100644
--- a/src/pwd/src/lib.rs
+++ b/src/pwd/src/lib.rs
@@ -53,7 +53,7 @@ where
     let file = match RawFile::open(
         "/etc/passwd\0".as_ptr() as *const c_char,
         fcntl::O_RDONLY,
-        0o644,
+        0,
     ) {
         Ok(file) => file,
         Err(_) => return OptionPasswd::Error,
diff --git a/src/signal/src/lib.rs b/src/signal/src/lib.rs
index 49cf7b884bff1eab8769068a694c3a621f4ba6c7..1a463beeffc068689603baa1698eaa93bfc9c191 100644
--- a/src/signal/src/lib.rs
+++ b/src/signal/src/lib.rs
@@ -29,9 +29,14 @@ pub const SIG_SETMASK: c_int = 2;
 #[repr(C)]
 #[derive(Clone)]
 pub struct sigaction {
-    pub sa_handler: extern "C" fn(c_int),
+    // I don't actually want these to be optional. They can have more than just
+    // one invalid value. But because of rust's non-null optimization, this
+    // causes Some(sigaction) with a null sa_handler to become None.  Maybe
+    // these should be usizes and transmuted when needed... However, then I
+    // couldn't let cbindgen do its job.
+    pub sa_handler: Option<extern "C" fn(c_int)>,
     pub sa_flags: c_ulong,
-    pub sa_restorer: unsafe extern "C" fn(),
+    pub sa_restorer: Option<unsafe extern "C" fn()>,
     pub sa_mask: sigset_t,
 }
 
@@ -86,9 +91,20 @@ pub extern "C" fn sigaddset(set: *mut sigset_t, mut signo: c_int) -> c_int {
     0
 }
 
-// #[no_mangle]
+#[no_mangle]
 pub extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int {
-    unimplemented!();
+    if signo <= 0 || signo as usize > NSIG {
+        unsafe {
+            platform::errno = errno::EINVAL;
+        }
+        return -1;
+    }
+
+    let signo = signo as usize - 1; // 0-indexed usize, please!
+    unsafe {
+        *set &= !(1 << (signo & (8 * mem::size_of::<sigset_t>() - 1)));
+    }
+    0
 }
 
 #[no_mangle]
@@ -133,11 +149,11 @@ extern "C" {
 }
 
 #[no_mangle]
-pub extern "C" fn signal(sig: c_int, func: extern "C" fn(c_int)) -> extern "C" fn(c_int) {
+pub extern "C" fn signal(sig: c_int, func: Option<extern "C" fn(c_int)>) -> Option<extern "C" fn(c_int)> {
     let sa = sigaction {
         sa_handler: func,
         sa_flags: SA_RESTART as c_ulong,
-        sa_restorer: __restore_rt,
+        sa_restorer: Some(__restore_rt),
         sa_mask: sigset_t::default(),
     };
     let mut old_sa = unsafe { mem::uninitialized() };
@@ -182,3 +198,38 @@ pub extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int {
 pub extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int {
     unimplemented!();
 }
+
+pub const _signal_strings: [&'static str; 32] = [
+    "Unknown signal\0",
+    "Hangup\0",
+    "Interrupt\0",
+    "Quit\0",
+    "Illegal instruction\0",
+    "Trace/breakpoint trap\0",
+    "Aborted\0",
+    "Bus error\0",
+    "Arithmetic exception\0",
+    "Killed\0",
+    "User defined signal 1\0",
+    "Segmentation fault\0",
+    "User defined signal 2\0",
+    "Broken pipe\0",
+    "Alarm clock\0",
+    "Terminated\0",
+    "Stack fault\0",
+    "Child process status\0",
+    "Continued\0",
+    "Stopped (signal)\0",
+    "Stopped\0",
+    "Stopped (tty input)\0",
+    "Stopped (tty output)\0",
+    "Urgent I/O condition\0",
+    "CPU time limit exceeded\0",
+    "File size limit exceeded\0",
+    "Virtual timer expired\0",
+    "Profiling timer expired\0",
+    "Window changed\0",
+    "I/O possible\0",
+    "Power failure\0",
+    "Bad system call\0"
+];
diff --git a/src/signal/src/linux.rs b/src/signal/src/linux.rs
index f2cb86074f31b41a94023c6ce9d375a37fdbc0d0..a874b2652791fd71a5e43d180ea0e9ac97b7d1d2 100644
--- a/src/signal/src/linux.rs
+++ b/src/signal/src/linux.rs
@@ -48,7 +48,7 @@ pub const SIGVTALRM: usize = 26;
 pub const SIGPROF: usize = 27;
 pub const SIGWINCH: usize = 28;
 pub const SIGIO: usize = 29;
-pub const SIGPOLL: usize = 29;
+pub const SIGPOLL: usize = SIGIO;
 pub const SIGPWR: usize = 30;
 pub const SIGSYS: usize = 31;
 pub const SIGUNUSED: usize = SIGSYS;
diff --git a/src/string/Cargo.toml b/src/string/Cargo.toml
index bed689a677fc520b9a97e3bb16742afbd8369b06..69a3f03d745c073286f4f85b055c7585c1c0ea99 100644
--- a/src/string/Cargo.toml
+++ b/src/string/Cargo.toml
@@ -8,5 +8,6 @@ build = "build.rs"
 cbindgen = { path = "../../cbindgen" }
 
 [dependencies]
-platform = { path = "../platform" }
 errno = { path = "../errno" }
+platform = { path = "../platform" }
+signal = { path = "../signal" }
diff --git a/src/string/src/lib.rs b/src/string/src/lib.rs
index 90a319c5049c7b2253eb10f3c1e3e65cde9de3c5..ec074f3ed8c974248f84433da3db916c57976eeb 100644
--- a/src/string/src/lib.rs
+++ b/src/string/src/lib.rs
@@ -3,6 +3,7 @@
 
 extern crate errno;
 extern crate platform;
+extern crate signal;
 
 use core::cmp;
 use core::mem;
@@ -308,6 +309,13 @@ pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char {
     ptr::null_mut()
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn strsignal(sig: c_int) -> *mut c_char {
+    // Mutating this is undefined behavior I believe. But I just can't create a
+    // &'static mut str. Alternative is allocating everything on the heap...
+    signal::_signal_strings[sig as usize].as_ptr() as *const c_char as *mut c_char
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn strspn(s1: *const c_char, s2: *const c_char) -> size_t {
     let s1 = s1 as *const u8;
diff --git a/src/sys_stat/src/lib.rs b/src/sys_stat/src/lib.rs
index ee31788bbeb3aa595108e9f1000fe1042dd4fbdf..a5b63af151d59fb337f43ca9345cd4376bcfe010 100644
--- a/src/sys_stat/src/lib.rs
+++ b/src/sys_stat/src/lib.rs
@@ -105,9 +105,9 @@ pub extern "C" fn stat(file: *const c_char, buf: *mut platform::types::stat) ->
     platform::stat(file, buf)
 }
 
-// #[no_mangle]
+#[no_mangle]
 pub extern "C" fn umask(mask: mode_t) -> mode_t {
-    unimplemented!();
+    platform::umask(mask)
 }
 
 /*