diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs
index 55928ae4c9bfc16a71a64bc7f24e5f6ef46026bd..f8ef8e049025049e137d11340ca801c4d5691519 100644
--- a/src/stdlib/src/lib.rs
+++ b/src/stdlib/src/lib.rs
@@ -62,11 +62,7 @@ pub unsafe extern "C" fn abort() {
 
 #[no_mangle]
 pub extern "C" fn abs(i: c_int) -> c_int {
-    if i < 0 {
-        -i
-    } else {
-        i
-    }
+    if i < 0 { -i } else { i }
 }
 
 #[no_mangle]
@@ -139,13 +135,14 @@ unsafe extern "C" fn void_cmp(a: *const c_void, b: *const c_void) -> c_int {
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn bsearch(
-    key: *const c_void,
-    base: *const c_void,
-    nel: size_t,
-    width: size_t,
-    compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
-) -> *mut c_void {
+pub unsafe extern "C" fn bsearch(key: *const c_void,
+                                 base: *const c_void,
+                                 nel: size_t,
+                                 width: size_t,
+                                 compar: Option<unsafe extern "C" fn(*const c_void,
+                                                                     *const c_void)
+                                                                     -> c_int>)
+                                 -> *mut c_void {
     let mut start = base;
     let mut len = nel;
     let cmp_fn = compar.unwrap_or(void_cmp);
@@ -195,12 +192,11 @@ pub extern "C" fn drand48() -> c_double {
 }
 
 #[no_mangle]
-pub extern "C" fn ecvt(
-    value: c_double,
-    ndigit: c_int,
-    decpt: *mut c_int,
-    sign: *mut c_int,
-) -> *mut c_char {
+pub extern "C" fn ecvt(value: c_double,
+                       ndigit: c_int,
+                       decpt: *mut c_int,
+                       sign: *mut c_int)
+                       -> *mut c_char {
     unimplemented!();
 }
 
@@ -221,12 +217,11 @@ pub unsafe extern "C" fn exit(status: c_int) {
 }
 
 #[no_mangle]
-pub extern "C" fn fcvt(
-    value: c_double,
-    ndigit: c_int,
-    decpt: *mut c_int,
-    sign: *mut c_int,
-) -> *mut c_char {
+pub extern "C" fn fcvt(value: c_double,
+                       ndigit: c_int,
+                       decpt: *mut c_int,
+                       sign: *mut c_int)
+                       -> *mut c_char {
     unimplemented!();
 }
 
@@ -249,11 +244,10 @@ pub extern "C" fn getenv(name: *const c_char) -> *mut c_char {
 }
 
 #[no_mangle]
-pub extern "C" fn getsubopt(
-    optionp: *mut *mut c_char,
-    tokens: *const *mut c_char,
-    valuep: *mut *mut c_char,
-) -> c_int {
+pub extern "C" fn getsubopt(optionp: *mut *mut c_char,
+                            tokens: *const *mut c_char,
+                            valuep: *mut *mut c_char)
+                            -> c_int {
     unimplemented!();
 }
 
@@ -279,11 +273,7 @@ pub extern "C" fn l64a(value: c_long) -> *mut c_char {
 
 #[no_mangle]
 pub extern "C" fn labs(i: c_long) -> c_long {
-    if i < 0 {
-        -i
-    } else {
-        i
-    }
+    if i < 0 { -i } else { i }
 }
 
 #[no_mangle]
@@ -382,12 +372,10 @@ pub extern "C" fn putenv(s: *mut c_char) -> c_int {
 }
 
 #[no_mangle]
-pub extern "C" fn qsort(
-    base: *mut c_void,
-    nel: size_t,
-    width: size_t,
-    compar: Option<extern "C" fn(*const c_void, *const c_void) -> c_int>,
-) {
+pub extern "C" fn qsort(base: *mut c_void,
+                        nel: size_t,
+                        width: size_t,
+                        compar: Option<extern "C" fn(*const c_void, *const c_void) -> c_int>) {
     unimplemented!();
 }
 
@@ -466,7 +454,7 @@ pub extern "C" fn srandom(seed: c_uint) {
 
 #[no_mangle]
 pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
-    //TODO: endptr
+    // TODO: endptr
 
     use core::str::FromStr;
 
@@ -480,90 +468,6 @@ pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c
     }
 }
 
-#[no_mangle]
-pub unsafe extern "C" fn strtol(
-    s: *const c_char,
-    endptr: *mut *const c_char,
-    base: c_int,
-) -> c_long {
-    let set_endptr = |idx: isize| {
-        if !endptr.is_null() {
-            *endptr = s.offset(idx);
-        }
-    };
-
-    let invalid_input = || {
-        platform::errno = EINVAL;
-        set_endptr(0);
-    };
-
-    // only valid bases are 2 through 36
-    if base != 0 && (base < 2 || base > 36) {
-        invalid_input();
-        return 0;
-    }
-
-    let mut idx = 0;
-
-    // skip any whitespace at the beginning of the string
-    while ctype::isspace(*s.offset(idx) as c_int) != 0 {
-        idx += 1;
-    }
-
-    // check for +/-
-    let positive = match is_positive(*s.offset(idx)) {
-        Some((pos, i)) => {
-            idx += i;
-            pos
-        }
-        None => {
-            invalid_input();
-            return 0;
-        }
-    };
-
-    // convert the string to a number
-    let num_str = s.offset(idx);
-    let res = match base {
-        0 => detect_base(num_str).and_then(|(base, i)| convert_integer(num_str.offset(i), base)),
-        8 => convert_octal(num_str),
-        16 => convert_hex(num_str),
-        _ => convert_integer(num_str, base),
-    };
-
-    // check for error parsing octal/hex prefix
-    // also check to ensure a number was indeed parsed
-    let (num, i, _) = match res {
-        Some(res) => res,
-        None => {
-            invalid_input();
-            return 0;
-        }
-    };
-    idx += i;
-
-    // account for the sign
-    let mut num = num as c_long;
-    num = if num.is_negative() {
-        platform::errno = ERANGE;
-        if positive {
-            c_long::max_value()
-        } else {
-            c_long::min_value()
-        }
-    } else {
-        if positive {
-            num
-        } else {
-            -num
-        }
-    };
-
-    set_endptr(idx);
-
-    num
-}
-
 fn is_positive(ch: c_char) -> Option<(bool, isize)> {
     match ch {
         0 => None,
@@ -606,8 +510,8 @@ unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> {
 }
 
 unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> {
-    if (*s != 0 && *s == b'0' as c_char)
-        && (*s.offset(1) != 0 && (*s.offset(1) == b'x' as c_char || *s.offset(1) == b'X' as c_char))
+    if (*s != 0 && *s == b'0' as c_char) &&
+       (*s.offset(1) != 0 && (*s.offset(1) == b'x' as c_char || *s.offset(1) == b'X' as c_char))
     {
         convert_integer(s.offset(2), 16).map(|(val, idx, overflow)| (val, idx + 2, overflow))
     } else {
@@ -669,11 +573,121 @@ fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, boo
     }
 }
 
-#[no_mangle]
-pub extern "C" fn strtoul(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulong {
-    unimplemented!();
+macro_rules! strto_impl {
+    ($funcname:ident, $rettype:ty, $signed:expr, $maxval:expr, $minval:expr) => {
+        #[no_mangle]
+        pub unsafe extern "C" fn $funcname(s: *const c_char,
+                                           endptr: *mut *const c_char,
+                                           base: c_int)
+                                           -> $rettype {
+            // ensure these are constants
+            const CHECK_SIGN: bool = $signed;
+            const MAX_VAL: $rettype = $maxval;
+            const MIN_VAL: $rettype = $minval;
+
+            let set_endptr = |idx: isize| {
+                if !endptr.is_null() {
+                    *endptr = s.offset(idx);
+                }
+            };
+
+            let invalid_input = || {
+                platform::errno = EINVAL;
+                set_endptr(0);
+            };
+
+            // only valid bases are 2 through 36
+            if base != 0 && (base < 2 || base > 36) {
+                invalid_input();
+                return 0;
+            }
+
+            let mut idx = 0;
+
+            // skip any whitespace at the beginning of the string
+            while ctype::isspace(*s.offset(idx) as c_int) != 0 {
+                idx += 1;
+            }
+
+            // check for +/-
+            let positive = match is_positive(*s.offset(idx)) {
+                Some((pos, i)) => {
+                    idx += i;
+                    pos
+                }
+                None => {
+                    invalid_input();
+                    return 0;
+                }
+            };
+
+            // convert the string to a number
+            let num_str = s.offset(idx);
+            let res = match base {
+                0 => detect_base(num_str).and_then(|(base, i)| {
+                    convert_integer(num_str.offset(i), base)
+                }),
+                8 => convert_octal(num_str),
+                16 => convert_hex(num_str),
+                _ => convert_integer(num_str, base),
+            };
+
+            // check for error parsing octal/hex prefix
+            // also check to ensure a number was indeed parsed
+            let (num, i, overflow) = match res {
+                Some(res) => res,
+                None => {
+                    invalid_input();
+                    return 0;
+                }
+            };
+            idx += i;
+
+            let overflow = if CHECK_SIGN {
+                overflow || (num as c_long).is_negative()
+            } else {
+                overflow
+            };
+            // account for the sign
+            let num = num as $rettype;
+            let num = if overflow {
+                platform::errno = ERANGE;
+                if CHECK_SIGN {
+                    if positive {
+                        MAX_VAL
+                    } else {
+                        MIN_VAL
+                    }
+                } else {
+                    MAX_VAL
+                }
+            } else {
+                if positive {
+                    num
+                } else {
+                    // not using -num to keep the compiler happy
+                    num.overflowing_neg().0
+                }
+            };
+
+            set_endptr(idx);
+
+            num
+        }
+    }
 }
 
+strto_impl!(strtoul,
+            c_ulong,
+            false,
+            c_ulong::max_value(),
+            c_ulong::min_value());
+strto_impl!(strtol,
+            c_long,
+            true,
+            c_long::max_value(),
+            c_long::min_value());
+
 #[no_mangle]
 pub extern "C" fn system(command: *const c_char) -> c_int {
     unimplemented!();
@@ -712,9 +726,8 @@ pub extern "C" fn wctomb(s: *mut c_char, wchar: wchar_t) -> c_int {
     unimplemented!();
 }
 
-/*
-#[no_mangle]
-pub extern "C" fn func(args) -> c_int {
-    unimplemented!();
-}
-*/
+// #[no_mangle]
+// pub extern "C" fn func(args) -> c_int {
+// unimplemented!();
+// }
+//
diff --git a/tests/Makefile b/tests/Makefile
index 1f83dd13946c6ac2cf800e584248467e7c2ee397..33b218c85426c4d401899902a80589406279d808 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -28,6 +28,7 @@ EXPECT_BINS=\
 	stdio/freopen \
 	stdlib/bsearch \
 	stdlib/strtol \
+	stdlib/strtoul \
 	stdlib/a64l \
 	stdlib/rand \
 	string/strncmp \
diff --git a/tests/stdlib/strtoul.c b/tests/stdlib/strtoul.c
new file mode 100644
index 0000000000000000000000000000000000000000..4431a720d231ef735c1b0d07d3264be4c9f38714
--- /dev/null
+++ b/tests/stdlib/strtoul.c
@@ -0,0 +1,30 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char* argv[]) {
+    printf("%ld\n", strtoul("         -42", NULL, 0));
+    printf("%ld\n", strtoul(" +555", NULL, 0));
+    printf("%ld\n", strtoul("   1234567890    ", NULL, 0));
+
+    printf("%ld\n", strtoul("         -42", NULL, 10));
+    printf("%ld\n", strtoul(" +555", NULL, 10));
+    printf("%ld\n", strtoul("   1234567890    ", NULL, 10));
+
+    printf("%lx\n", strtoul("  0x38Acfg", NULL, 0));
+    printf("%lx\n", strtoul("0Xabcdef12", NULL, 16));
+
+    printf("%lo\n", strtoul("  073189", NULL, 0));
+    printf("%lo\n", strtoul("     073189", NULL, 8));
+
+    printf("%lo\n", strtoul("  0b", NULL, 8));
+    if(errno != 0) {
+        printf("errno is not 0 (%d), something went wrong\n", errno);
+    }
+    printf("%lo\n", strtoul("  0b", NULL, 0));
+    if(errno != 0) {
+        printf("errno is not 0 (%d), something went wrong\n", errno);
+    }
+
+    return 0;
+}