diff --git a/libc-test/Cargo.lock b/libc-test/Cargo.lock
index 23d47b0a50e2cb8535792add692e42040b6e1e4c..1726c5370150b2099a6ad699a9327c8830a00bf3 100644
--- a/libc-test/Cargo.lock
+++ b/libc-test/Cargo.lock
@@ -2,7 +2,7 @@
 name = "libc-test"
 version = "0.1.0"
 dependencies = [
- "gcc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.13 (git+https://github.com/alexcrichton/gcc-rs)",
  "libc 0.1.10",
  "syntex_syntax 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 [[package]]
 name = "gcc"
 version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+source = "git+https://github.com/alexcrichton/gcc-rs#e429c775dcbb03eb049bcb7f7215cf8d9ee3b837"
 dependencies = [
  "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/libc-test/Cargo.toml b/libc-test/Cargo.toml
index e918ec21c125afba9f8bf0950b62387481316108..bab5699f5be3925618739699278b7f023466466d 100644
--- a/libc-test/Cargo.toml
+++ b/libc-test/Cargo.toml
@@ -9,7 +9,7 @@ libc = { path = ".." }
 
 [build-dependencies]
 syntex_syntax = "0.13.0"
-gcc = "0.3"
+gcc = { git = "https://github.com/alexcrichton/gcc-rs" }
 
 [lib]
 name = "libc_test"
diff --git a/libc-test/build.rs b/libc-test/build.rs
index d9aadc11f5d663d27981df6ff3e14076b40ef4d9..9b4527ec1658924b6c30b05b06aedf37e3a6f91d 100644
--- a/libc-test/build.rs
+++ b/libc-test/build.rs
@@ -8,10 +8,13 @@ use std::io::BufWriter;
 use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 
+use syntax::abi::Abi;
 use syntax::ast;
-use syntax::diagnostic::SpanHandler;
-use syntax::parse::token::InternedString;
 use syntax::attr::{self, ReprAttr};
+use syntax::diagnostic::SpanHandler;
+use syntax::ext::base::SyntaxExtension;
+use syntax::ext::expand;
+use syntax::parse::token::{intern, InternedString};
 use syntax::parse::{self, ParseSess};
 use syntax::visit::{self, Visitor};
 
@@ -28,6 +31,7 @@ struct TestGenerator<'a> {
     c: Box<Write>,
     sh: &'a SpanHandler,
     structs: HashSet<String>,
+    abi: Abi,
 }
 
 struct StructFinder {
@@ -37,12 +41,21 @@ struct StructFinder {
 impl<'a> TestGenerator<'a> {
     fn defines(&self) -> Vec<&'static str> {
         let mut ret = Vec::new();
+
+        // Pull in extra goodies on linux
         if self.target.contains("unknown-linux") {
             ret.push("_GNU_SOURCE");
         }
+
+        // MSVC doesn't have stdalign.h so get alignof ourselves
         if self.target.contains("msvc") {
             ret.push("alignof __alignof");
         }
+
+        // Pull in extra goodies on mingw
+        if self.target.contains("windows") {
+            ret.push("_WIN32_WINNT 0x8000");
+        }
         return ret
     }
 
@@ -82,6 +95,11 @@ impl<'a> TestGenerator<'a> {
             base.push("windows.h");
             base.push("process.h");
             base.push("ws2ipdef.h");
+
+            if self.target.contains("gnu") {
+                base.push("stdalign.h");
+                base.push("ws2tcpip.h");
+            }
         } else {
             base.push("ctype.h");
             base.push("dirent.h");
@@ -123,13 +141,19 @@ impl<'a> TestGenerator<'a> {
                     s => s.to_string(),
                 }
             }
+            // Perhaps this should be renamed in libc...
             "ip6_mreq" => "struct ipv6_mreq".to_string(),
+
+            // Just pass all these through, no need for a "struct" prefix
             "glob_t" |
             "FILE" |
             "DIR" |
             "fpos_t" => ty.to_string(),
             t if t.starts_with("pthread") => t.to_string(),
 
+            // Windows uppercase structs don't have `struct` in front, there's a
+            // few special cases for windows, and then otherwise put `struct` in
+            // front of everything.
             t if self.structs.contains(t) => {
                 if windows && ty.chars().next().unwrap().is_uppercase() {
                     t.to_string()
@@ -142,14 +166,18 @@ impl<'a> TestGenerator<'a> {
                 }
             }
 
+            // Fixup a few types on windows that don't actually exist.
             "time64_t" if windows => "__time64_t".to_string(),
             "ssize_t" if windows => "SSIZE_T".to_string(),
+
             t => t.to_string(),
         }
     }
 
     fn rust2cfield(&self, struct_: &str, field: &str) -> String {
         match field {
+            // Our stat *_nsec fields normally don't actually exist but are part
+            // of a timeval struct
             s if s.ends_with("_nsec") && struct_ == "stat" => {
                 if self.target.contains("apple-darwin") {
                     s.replace("_nsec", "spec.tv_nsec")
@@ -207,12 +235,21 @@ fn main() {
         c: Box::new(c_out),
         sh: &sess.span_diagnostic,
         structs: HashSet::new(),
+        abi: Abi::C,
     };
 
     // Parse the libc crate
     let src = Path::new("../src/lib.rs");
     let cfg = Vec::new();
-    let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
+    let krate = parse::parse_crate_from_file(src, cfg, &sess);
+
+    // expand macros
+    let ecfg = expand::ExpansionConfig::default("libc".to_string());
+    let exts = vec![
+        (intern("macro_rules"), SyntaxExtension::MacroRulesTT),
+    ];
+    let mut krate = expand::expand_crate(&sess, ecfg, Vec::new(),
+                                         exts, &mut Vec::new(), krate);
 
     // Strip the crate down to just what's configured for our target
     for (k, v) in tg.cfg_list() {
@@ -359,8 +396,22 @@ impl<'a> TestGenerator<'a> {
     }
 
     fn test_const(&mut self, name: &str, rust_ty: &str) {
+        let mingw = self.target.contains("windows-gnu");
+
+        // Apparently these don't exist in mingw headers?
+        match name {
+            "MEM_RESET_UNDO" |
+            "FILE_ATTRIBUTE_NO_SCRUB_DATA" |
+            "FILE_ATTRIBUTE_INTEGRITY_STREAM" |
+            "ERROR_NOTHING_TO_TERMINATE" if mingw => return,
+            _ => {}
+        }
+
         let cty = self.rust_ty_to_c_ty(rust_ty);
+
+        // SIG_IGN has weird types on platforms, just worry about it as a size_t
         let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
+
         t!(writeln!(self.c, r#"
             int __test_const_{name}({cty} *outptr) {{
                 *outptr = {cast}({name});
@@ -387,7 +438,7 @@ impl<'a> TestGenerator<'a> {
 
     fn test_extern_fn(&mut self, name: &str, cname: &str,
                       args: &[String], ret: &str,
-                      variadic: bool) {
+                      variadic: bool, abi: Abi) {
         match name {
             // manually verified
             "execv" |
@@ -408,11 +459,20 @@ impl<'a> TestGenerator<'a> {
                 .connect(", ") + if variadic {", ..."} else {""}
         };
         let cret = self.rust_ty_to_c_ty(ret);
+        let abi = match abi {
+            Abi::C => "",
+            Abi::Stdcall => "__stdcall ",
+            Abi::System if self.target.contains("i686-pc-windows") => {
+                "__stdcall "
+            }
+            Abi::System => "",
+            a => panic!("unknown ABI: {}", a),
+        };
         t!(writeln!(self.c, r#"
-            {ret} (*__test_fn_{name}(void))({args}) {{
+            {ret} ({abi}*__test_fn_{name}(void))({args}) {{
                 return {cname};
             }}
-        "#, name = name, cname = cname, args = args, ret = cret));
+        "#, name = name, cname = cname, args = args, ret = cret, abi = abi));
         t!(writeln!(self.rust, r#"
             #[test]
             #[cfg_attr(windows, ignore)] // FIXME -- dllimport weirdness?
@@ -473,6 +533,7 @@ impl<'a> TestGenerator<'a> {
 
 impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
     fn visit_item(&mut self, i: &'v ast::Item) {
+        let prev_abi = self.abi;
         match i.node {
             ast::ItemTy(_, ref generics) => {
                 self.assert_no_generics(i.ident, generics);
@@ -497,9 +558,14 @@ impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
                 self.test_const(&i.ident.to_string(), &ty);
             }
 
+            ast::ItemForeignMod(ref fm) => {
+                self.abi = fm.abi;
+            }
+
             _ => {}
         }
-        visit::walk_item(self, i)
+        visit::walk_item(self, i);
+        self.abi = prev_abi;
     }
 
     fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
@@ -514,8 +580,9 @@ impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
                     }
                     _ => i.ident.to_string(),
                 };
+                let abi = self.abi;
                 self.test_extern_fn(&i.ident.to_string(), &cname, &args, &ret,
-                                    variadic);
+                                    variadic, abi);
             }
             ast::ForeignItemStatic(_, _) => {
             }
diff --git a/src/lib.rs b/src/lib.rs
index 9c986d6a821548d86d36161bc1476247c83d21a4..fc2e6c2a2340e4d49c3fa3854d89acd4489da651 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -67,6 +67,9 @@
 
 #![allow(bad_style, raw_pointer_derive)]
 
+#[macro_use]
+mod macros;
+
 // Explicit export lists for the intersection (provided here) mean that
 // you can write more-platform-agnostic code if you stick to just these
 // symbols.
@@ -1829,10 +1832,13 @@ pub mod types {
 
                 pub type clock_t = i32;
 
-                #[cfg(target_arch = "x86")]
-                pub type time_t = i32;
-                #[cfg(target_arch = "x86_64")]
-                pub type time_t = i64;
+                cfg_if! {
+                    if #[cfg(all(target_arch = "x86", target_env = "gnu"))] {
+                        pub type time_t = i32;
+                    } else {
+                        pub type time_t = i64;
+                    }
+                }
 
                 pub type wchar_t = u16;
             }
@@ -2508,8 +2514,6 @@ pub mod consts {
             pub const BUFSIZ : c_uint = 512;
             pub const FOPEN_MAX : c_uint = 20;
             pub const FILENAME_MAX : c_uint = 260;
-            pub const L_tmpnam : c_uint = 260;
-            pub const TMP_MAX : c_uint = 0x7fff_ffff;
 
             pub const WSAEINTR: c_int = 10004;
             pub const WSAEBADF: c_int = 10009;
@@ -2563,6 +2567,16 @@ pub mod consts {
             pub const WSAEINVALIDPROCTABLE: c_int = 10104;
             pub const WSAEINVALIDPROVIDER: c_int = 10105;
             pub const WSAEPROVIDERFAILEDINIT: c_int = 10106;
+
+            cfg_if! {
+                if #[cfg(all(target_env = "gnu"))] {
+                    pub const L_tmpnam : c_uint = 14;
+                    pub const TMP_MAX : c_uint = 0x7fff;
+                } else {
+                    pub const L_tmpnam : c_uint = 260;
+                    pub const TMP_MAX : c_uint = 0x7fff_ffff;
+                }
+            }
         }
         pub mod c99 {
         }
@@ -6141,9 +6155,8 @@ pub mod funcs {
 
     #[cfg(windows)]
     pub mod bsd43 {
-        use types::common::c95::{c_void};
         use types::os::common::bsd44::{sockaddr, SOCKET};
-        use types::os::arch::c95::c_int;
+        use types::os::arch::c95::{c_int, c_char};
 
         extern "system" {
             pub fn socket(domain: c_int, ty: c_int, protocol: c_int) -> SOCKET;
@@ -6159,17 +6172,17 @@ pub mod funcs {
             pub fn getsockname(socket: SOCKET, address: *mut sockaddr,
                                address_len: *mut c_int) -> c_int;
             pub fn setsockopt(socket: SOCKET, level: c_int, name: c_int,
-                              value: *const c_void,
+                              value: *const c_char,
                               option_len: c_int) -> c_int;
             pub fn closesocket(socket: SOCKET) -> c_int;
-            pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int,
+            pub fn recv(socket: SOCKET, buf: *mut c_char, len: c_int,
                         flags: c_int) -> c_int;
-            pub fn send(socket: SOCKET, buf: *const c_void, len: c_int,
+            pub fn send(socket: SOCKET, buf: *const c_char, len: c_int,
                         flags: c_int) -> c_int;
-            pub fn recvfrom(socket: SOCKET, buf: *mut c_void, len: c_int,
+            pub fn recvfrom(socket: SOCKET, buf: *mut c_char, len: c_int,
                             flags: c_int, addr: *mut sockaddr,
                             addrlen: *mut c_int) -> c_int;
-            pub fn sendto(socket: SOCKET, buf: *const c_void, len: c_int,
+            pub fn sendto(socket: SOCKET, buf: *const c_char, len: c_int,
                           flags: c_int, addr: *const sockaddr,
                           addrlen: c_int) -> c_int;
             pub fn shutdown(socket: SOCKET, how: c_int) -> c_int;
@@ -6414,7 +6427,7 @@ pub mod funcs {
                                 lpNumberOfBytesRead: LPDWORD,
                                 lpOverlapped: LPOVERLAPPED) -> BOOL;
                 pub fn WriteFile(hFile: HANDLE,
-                                 lpBuffer: LPVOID,
+                                 lpBuffer: LPCVOID,
                                  nNumberOfBytesToWrite: DWORD,
                                  lpNumberOfBytesWritten: LPDWORD,
                                  lpOverlapped: LPOVERLAPPED) -> BOOL;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3b096a53f10155df81d0ed7c1e0bd35ca1b8e037
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,75 @@
+//! A macro for defining #[cfg] if-else statements.
+//!
+//! The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C
+//! preprocessor macro by allowing definition of a cascade of `#[cfg]` cases,
+//! emitting the implementation which matches first.
+//!
+//! This allows you to conveniently provide a long list #[cfg]'d blocks of code
+//! without having to rewrite each clause multiple times.
+
+macro_rules! cfg_if {
+    ($(
+        if #[cfg($($meta:meta),*)] { $($it:item)* }
+    ) else * else {
+        $($it2:item)*
+    }) => {
+        __cfg_if_items! {
+            () ;
+            $( ( ($($meta),*) ($($it)*) ), )*
+            ( () ($($it2)*) ),
+        }
+    }
+}
+
+#[doc(hidden)]
+macro_rules! __cfg_if_items {
+    (($($not:meta,)*) ; ) => {};
+    (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
+        __cfg_if_apply! { cfg(all($($m,)* not(any($($not),*)))), $($it)* }
+        __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
+    }
+}
+
+#[doc(hidden)]
+macro_rules! __cfg_if_apply {
+    ($m:meta, $($it:item)*) => {
+        $(#[$m] $it)*
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    cfg_if! {
+        if #[cfg(test)] {
+            use std::option::Option as Option2;
+            fn works1() -> Option2<u32> { Some(1) }
+        } else {
+            fn works1() -> Option<u32> { None }
+        }
+    }
+
+    cfg_if! {
+        if #[cfg(foo)] {
+            fn works2() -> bool { false }
+        } else if #[cfg(test)] {
+            fn works2() -> bool { true }
+        } else {
+            fn works2() -> bool { false }
+        }
+    }
+
+    cfg_if! {
+        if #[cfg(foo)] {
+            fn works3() -> bool { false }
+        } else {
+            fn works3() -> bool { true }
+        }
+    }
+
+    #[test]
+    fn it_works() {
+        assert!(works1().is_some());
+        assert!(works2());
+        assert!(works3());
+    }
+}