diff --git a/mk/crates.mk b/mk/crates.mk
index d7667bef788..af2a663b61d 100644
--- a/mk/crates.mk
+++ b/mk/crates.mk
@@ -52,23 +52,23 @@
 TARGET_CRATES := libc std flate arena term \
                  serialize getopts collections test rand \
                  log graphviz core rbml alloc \
-                 rustc_unicode rustc_bitflags
+                 rustc_unicode rustc_bitflags \
+		 alloc_system
 RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \
                 rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
                 rustc_data_structures
 HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
-CRATES := $(TARGET_CRATES) $(HOST_CRATES)
 TOOLS := compiletest rustdoc rustc rustbook error-index-generator
 
 DEPS_core :=
 DEPS_libc := core
 DEPS_rustc_unicode := core
-DEPS_alloc := core libc native:jemalloc
+DEPS_alloc := core libc alloc_system
 DEPS_std := core libc rand alloc collections rustc_unicode \
 	native:rust_builtin native:backtrace \
-	rustc_bitflags
+	alloc_system
 DEPS_graphviz := std
-DEPS_syntax := std term serialize log fmt_macros arena libc
+DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags
 DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
                      rustc_typeck rustc_resolve log syntax serialize rustc_llvm \
 		     rustc_trans rustc_privacy rustc_lint
@@ -82,7 +82,7 @@ DEPS_rustc_privacy := rustc log syntax
 DEPS_rustc_lint := rustc log syntax
 DEPS_rustc := syntax flate arena serialize getopts rbml \
               log graphviz rustc_llvm rustc_back rustc_data_structures
-DEPS_rustc_llvm := native:rustllvm libc std
+DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
 DEPS_rustc_back := std syntax rustc_llvm flate log libc
 DEPS_rustc_data_structures := std log serialize
 DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
@@ -102,6 +102,7 @@ DEPS_test := std getopts serialize rbml term native:rust_test_helpers
 DEPS_rand := core
 DEPS_log := std
 DEPS_fmt_macros = std
+DEPS_alloc_system := core libc
 
 TOOL_DEPS_compiletest := test getopts
 TOOL_DEPS_rustdoc := rustdoc
@@ -121,14 +122,26 @@ ONLY_RLIB_rand := 1
 ONLY_RLIB_collections := 1
 ONLY_RLIB_rustc_unicode := 1
 ONLY_RLIB_rustc_bitflags := 1
+ONLY_RLIB_alloc_system := 1
 
 # Documented-by-default crates
 DOC_CRATES := std alloc collections core libc rustc_unicode
 
+ifeq ($(CFG_DISABLE_JEMALLOC),)
+TARGET_CRATES += alloc_jemalloc
+DEPS_std += alloc_jemalloc
+DEPS_alloc_jemalloc := core libc native:jemalloc
+ONLY_RLIB_alloc_jemalloc := 1
+else
+RUSTFLAGS_rustc_back := --cfg disable_jemalloc
+endif
+
 ################################################################################
 # You should not need to edit below this line
 ################################################################################
 
+CRATES := $(TARGET_CRATES) $(HOST_CRATES)
+
 # This macro creates some simple definitions for each crate being built, just
 # some munging of all of the parameters above.
 #
diff --git a/mk/rt.mk b/mk/rt.mk
index b8f345699f4..75051f9184f 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -184,8 +184,6 @@ $$(JEMALLOC_LOCAL_$(1)): $$(JEMALLOC_DEPS) $$(MKFILE_DEPS)
 		EXTRA_CFLAGS="-g1 -ffunction-sections -fdata-sections"
 	$$(Q)$$(MAKE) -C "$$(JEMALLOC_BUILD_DIR_$(1))" build_lib_static
 
-ifeq ($$(CFG_DISABLE_JEMALLOC),)
-RUSTFLAGS_alloc := --cfg jemalloc
 ifeq ($(1),$$(CFG_BUILD))
 ifneq ($$(CFG_JEMALLOC_ROOT),)
 $$(JEMALLOC_LIB_$(1)): $$(CFG_JEMALLOC_ROOT)/libjemalloc_pic.a
@@ -199,10 +197,6 @@ else
 $$(JEMALLOC_LIB_$(1)): $$(JEMALLOC_LOCAL_$(1))
 	$$(Q)cp $$< $$@
 endif
-else
-$$(JEMALLOC_LIB_$(1)): $$(MKFILE_DEPS)
-	$$(Q)touch $$@
-endif
 
 ################################################################################
 # compiler-rt
diff --git a/mk/tests.mk b/mk/tests.mk
index d4206acd274..e0984cfe86f 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -22,7 +22,8 @@ $(eval $(call RUST_CRATE,coretest))
 DEPS_collectionstest :=
 $(eval $(call RUST_CRATE,collectionstest))
 
-TEST_TARGET_CRATES = $(filter-out core rustc_unicode,$(TARGET_CRATES)) \
+TEST_TARGET_CRATES = $(filter-out core rustc_unicode alloc_system \
+		     		  alloc_jemalloc,$(TARGET_CRATES)) \
 			collectionstest coretest
 TEST_DOC_CRATES = $(DOC_CRATES)
 TEST_HOST_CRATES = $(filter-out rustc_typeck rustc_borrowck rustc_resolve \
diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs
index 14797d7f4b5..fad8308f0f4 100644
--- a/src/liballoc/heap.rs
+++ b/src/liballoc/heap.rs
@@ -16,6 +16,18 @@
 
 use core::{isize, usize};
 
+#[allow(improper_ctypes)]
+extern {
+    #[allocator]
+    fn __rust_allocate(size: usize, align: usize) -> *mut u8;
+    fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize);
+    fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize,
+                         align: usize) -> *mut u8;
+    fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
+                               align: usize) -> usize;
+    fn __rust_usable_size(size: usize, align: usize) -> usize;
+}
+
 #[inline(always)]
 fn check_size_and_alignment(size: usize, align: usize) {
     debug_assert!(size != 0);
@@ -35,7 +47,7 @@ fn check_size_and_alignment(size: usize, align: usize) {
 #[inline]
 pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
     check_size_and_alignment(size, align);
-    imp::allocate(size, align)
+    __rust_allocate(size, align)
 }
 
 /// Resize the allocation referenced by `ptr` to `size` bytes.
@@ -55,7 +67,7 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
 #[inline]
 pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
     check_size_and_alignment(size, align);
-    imp::reallocate(ptr, old_size, size, align)
+    __rust_reallocate(ptr, old_size, size, align)
 }
 
 /// Resize the allocation referenced by `ptr` to `size` bytes.
@@ -74,7 +86,7 @@ pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usiz
 pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
                                  align: usize) -> usize {
     check_size_and_alignment(size, align);
-    imp::reallocate_inplace(ptr, old_size, size, align)
+    __rust_reallocate_inplace(ptr, old_size, size, align)
 }
 
 /// Deallocates the memory referenced by `ptr`.
@@ -86,28 +98,20 @@ pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
 /// any value in range_inclusive(requested_size, usable_size).
 #[inline]
 pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) {
-    imp::deallocate(ptr, old_size, align)
+    __rust_deallocate(ptr, old_size, align)
 }
 
 /// Returns the usable size of an allocation created with the specified the
 /// `size` and `align`.
 #[inline]
 pub fn usable_size(size: usize, align: usize) -> usize {
-    imp::usable_size(size, align)
-}
-
-/// Prints implementation-defined allocator statistics.
-///
-/// These statistics may be inconsistent if other threads use the allocator
-/// during the call.
-pub fn stats_print() {
-    imp::stats_print();
+    unsafe { __rust_usable_size(size, align) }
 }
 
 /// An arbitrary non-null address to represent zero-size allocations.
 ///
-/// This preserves the non-null invariant for types like `Box<T>`. The address may overlap with
-/// non-zero-size memory allocations.
+/// This preserves the non-null invariant for types like `Box<T>`. The address
+/// may overlap with non-zero-size memory allocations.
 pub const EMPTY: *mut () = 0x1 as *mut ();
 
 /// The allocator for unique pointers.
@@ -131,362 +135,6 @@ unsafe fn exchange_free(ptr: *mut u8, old_size: usize, align: usize) {
     deallocate(ptr, old_size, align);
 }
 
-// The minimum alignment guaranteed by the architecture. This value is used to
-// add fast paths for low alignment values. In practice, the alignment is a
-// constant at the call site and the branch will be optimized out.
-#[cfg(all(not(feature = "external_funcs"),
-          not(feature = "external_crate"),
-          any(target_arch = "arm",
-              target_arch = "mips",
-              target_arch = "mipsel",
-              target_arch = "powerpc")))]
-const MIN_ALIGN: usize = 8;
-#[cfg(all(not(feature = "external_funcs"),
-          not(feature = "external_crate"),
-          any(target_arch = "x86",
-              target_arch = "x86_64",
-              target_arch = "aarch64")))]
-const MIN_ALIGN: usize = 16;
-
-#[cfg(feature = "external_funcs")]
-mod imp {
-    #[allow(improper_ctypes)]
-    extern {
-        fn rust_allocate(size: usize, align: usize) -> *mut u8;
-        fn rust_deallocate(ptr: *mut u8, old_size: usize, align: usize);
-        fn rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8;
-        fn rust_reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
-                                   align: usize) -> usize;
-        fn rust_usable_size(size: usize, align: usize) -> usize;
-        fn rust_stats_print();
-    }
-
-    #[inline]
-    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
-        rust_allocate(size, align)
-    }
-
-    #[inline]
-    pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) {
-        rust_deallocate(ptr, old_size, align)
-    }
-
-    #[inline]
-    pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
-        rust_reallocate(ptr, old_size, size, align)
-    }
-
-    #[inline]
-    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
-                                     align: usize) -> usize {
-        rust_reallocate_inplace(ptr, old_size, size, align)
-    }
-
-    #[inline]
-    pub fn usable_size(size: usize, align: usize) -> usize {
-        unsafe { rust_usable_size(size, align) }
-    }
-
-    #[inline]
-    pub fn stats_print() {
-        unsafe { rust_stats_print() }
-    }
-}
-
-#[cfg(feature = "external_crate")]
-mod imp {
-    extern crate external;
-    pub use self::external::{allocate, deallocate, reallocate_inplace, reallocate};
-    pub use self::external::{usable_size, stats_print};
-}
-
-#[cfg(all(not(feature = "external_funcs"),
-          not(feature = "external_crate"),
-          jemalloc))]
-mod imp {
-    use core::option::Option;
-    use core::option::Option::None;
-    use core::ptr::{null_mut, null};
-    use libc::{c_char, c_int, c_void, size_t};
-    use super::MIN_ALIGN;
-
-    #[link(name = "jemalloc", kind = "static")]
-    #[cfg(not(test))]
-    extern {}
-
-    extern {
-        #[allocator]
-        fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
-        fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
-        fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
-        fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
-        fn je_nallocx(size: size_t, flags: c_int) -> size_t;
-        fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
-                                                                *const c_char)>,
-                                 cbopaque: *mut c_void,
-                                 opts: *const c_char);
-    }
-
-    // -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough
-    #[cfg(all(not(windows),
-              not(target_os = "android"),
-              not(target_env = "musl")))]
-    #[link(name = "pthread")]
-    extern {}
-
-    // MALLOCX_ALIGN(a) macro
-    #[inline(always)]
-    fn mallocx_align(a: usize) -> c_int { a.trailing_zeros() as c_int }
-
-    #[inline(always)]
-    fn align_to_flags(align: usize) -> c_int {
-        if align <= MIN_ALIGN { 0 } else { mallocx_align(align) }
-    }
-
-    #[inline]
-    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
-        let flags = align_to_flags(align);
-        je_mallocx(size as size_t, flags) as *mut u8
-    }
-
-    #[inline]
-    pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize, align: usize) -> *mut u8 {
-        let flags = align_to_flags(align);
-        je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8
-    }
-
-    #[inline]
-    pub unsafe fn reallocate_inplace(ptr: *mut u8, _old_size: usize, size: usize,
-                                     align: usize) -> usize {
-        let flags = align_to_flags(align);
-        je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize
-    }
-
-    #[inline]
-    pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) {
-        let flags = align_to_flags(align);
-        je_sdallocx(ptr as *mut c_void, old_size as size_t, flags)
-    }
-
-    #[inline]
-    pub fn usable_size(size: usize, align: usize) -> usize {
-        let flags = align_to_flags(align);
-        unsafe { je_nallocx(size as size_t, flags) as usize }
-    }
-
-    pub fn stats_print() {
-        unsafe {
-            je_malloc_stats_print(None, null_mut(), null())
-        }
-    }
-}
-
-#[cfg(all(not(feature = "external_funcs"),
-          not(feature = "external_crate"),
-          not(jemalloc),
-          unix))]
-mod imp {
-    use core::cmp;
-    use core::ptr;
-    use libc;
-    use super::MIN_ALIGN;
-
-    extern {
-        fn posix_memalign(memptr: *mut *mut libc::c_void,
-                          align: libc::size_t,
-                          size: libc::size_t) -> libc::c_int;
-    }
-
-    #[inline]
-    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
-        if align <= MIN_ALIGN {
-            libc::malloc(size as libc::size_t) as *mut u8
-        } else {
-            let mut out = ptr::null_mut();
-            let ret = posix_memalign(&mut out,
-                                     align as libc::size_t,
-                                     size as libc::size_t);
-            if ret != 0 {
-                ptr::null_mut()
-            } else {
-                out as *mut u8
-            }
-        }
-    }
-
-    #[inline]
-    pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
-        if align <= MIN_ALIGN {
-            libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
-        } else {
-            let new_ptr = allocate(size, align);
-            ptr::copy(ptr, new_ptr, cmp::min(size, old_size));
-            deallocate(ptr, old_size, align);
-            new_ptr
-        }
-    }
-
-    #[inline]
-    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: usize, _size: usize,
-                                     _align: usize) -> usize {
-        old_size
-    }
-
-    #[inline]
-    pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, _align: usize) {
-        libc::free(ptr as *mut libc::c_void)
-    }
-
-    #[inline]
-    pub fn usable_size(size: usize, _align: usize) -> usize {
-        size
-    }
-
-    pub fn stats_print() {}
-}
-
-#[cfg(all(not(feature = "external_funcs"),
-          not(feature = "external_crate"),
-          not(jemalloc),
-          windows))]
-mod imp {
-    use core::mem::size_of;
-    use libc::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T, INVALID_HANDLE_VALUE};
-    use libc::{WriteFile};
-    use super::MIN_ALIGN;
-
-    extern "system" {
-        fn GetProcessHeap() -> HANDLE;
-        fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
-        fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
-        fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
-        fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
-        fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) -> BOOL;
-    }
-
-    #[repr(C)] #[allow(non_snake_case)]
-    struct HEAP_SUMMARY {
-        cb: DWORD,
-        cbAllocated: SIZE_T,
-        cbCommitted: SIZE_T,
-        cbReserved: SIZE_T,
-        cbMaxReserve: SIZE_T,
-    }
-    #[allow(non_camel_case_types)]
-    type LPHEAP_SUMMARY = *mut HEAP_SUMMARY;
-
-    #[repr(C)]
-    struct Header(*mut u8);
-
-    const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010;
-    const STD_OUTPUT_HANDLE: DWORD = -11i32 as u32;
-
-    #[inline]
-    unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
-        &mut *(ptr as *mut Header).offset(-1)
-    }
-
-    #[inline]
-    unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
-        let aligned = ptr.offset((align - (ptr as usize & (align - 1))) as isize);
-        *get_header(aligned) = Header(ptr);
-        aligned
-    }
-
-    #[inline]
-    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
-        if align <= MIN_ALIGN {
-            HeapAlloc(GetProcessHeap(), 0, size as SIZE_T) as *mut u8
-        } else {
-            let ptr = HeapAlloc(GetProcessHeap(), 0, (size + align) as SIZE_T) as *mut u8;
-            if ptr.is_null() { return ptr }
-            align_ptr(ptr, align)
-        }
-    }
-
-    #[inline]
-    pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize, align: usize) -> *mut u8 {
-        if align <= MIN_ALIGN {
-            HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, size as SIZE_T) as *mut u8
-        } else {
-            let header = get_header(ptr);
-            let new = HeapReAlloc(GetProcessHeap(), 0, header.0 as LPVOID,
-                                  (size + align) as SIZE_T) as *mut u8;
-            if new.is_null() { return new }
-            align_ptr(new, align)
-        }
-    }
-
-    #[inline]
-    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
-                                     align: usize) -> usize {
-        if align <= MIN_ALIGN {
-            let new = HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, ptr as LPVOID,
-                                  size as SIZE_T) as *mut u8;
-            if new.is_null() { old_size } else { size }
-        } else {
-            old_size
-        }
-    }
-
-    #[inline]
-    pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, align: usize) {
-        if align <= MIN_ALIGN {
-            let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
-            debug_assert!(err != 0);
-        } else {
-            let header = get_header(ptr);
-            let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
-            debug_assert!(err != 0);
-        }
-    }
-
-    #[inline]
-    pub fn usable_size(size: usize, _align: usize) -> usize {
-        size
-    }
-
-    pub fn stats_print() {
-        use core::fmt::{Error, Result, Write};
-        use core::ptr::null_mut;
-        use core::raw::Repr;
-        use core::result::Result::{Ok, Err};
-        struct Console(HANDLE);
-        impl Write for Console {
-            fn write_str(&mut self, s: &str) -> Result {
-                let repr = s.repr();
-                let mut written = 0;
-                let err = unsafe { WriteFile(self.0, repr.data as LPVOID, repr.len as DWORD,
-                                             &mut written, null_mut()) };
-                if written as usize != repr.len { return Err(Error) }
-                if err == 0 { return Err(Error) }
-                Ok(())
-            }
-        }
-        let mut hs = HEAP_SUMMARY {
-            cb: size_of::<HEAP_SUMMARY>() as DWORD,
-            cbAllocated: 0,
-            cbCommitted: 0,
-            cbReserved: 0,
-            cbMaxReserve: 0,
-        };
-        let err = unsafe { HeapSummary(GetProcessHeap(), 0, &mut hs) };
-        assert!(err != 0);
-        let handle = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
-        if handle.is_null() || handle == INVALID_HANDLE_VALUE { panic!("Failed to open stdout") }
-        let mut out = Console(handle);
-        writeln!(&mut out, "Allocated: {}", hs.cbAllocated).unwrap();
-        writeln!(&mut out, "Committed: {}", hs.cbCommitted).unwrap();
-        writeln!(&mut out, "Reserved: {}", hs.cbReserved).unwrap();
-        writeln!(&mut out, "MaxReserve: {}", hs.cbMaxReserve).unwrap();
-    }
-
-    #[test]
-    fn alignment_header_size() {
-        assert!(size_of::<Header>() <= MIN_ALIGN);
-    }
-}
-
 #[cfg(test)]
 mod tests {
     extern crate test;
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs
index 05863c2ee5c..ca86850f5df 100644
--- a/src/liballoc/lib.rs
+++ b/src/liballoc/lib.rs
@@ -61,6 +61,7 @@
 #![crate_name = "alloc"]
 #![crate_type = "rlib"]
 #![staged_api]
+#![allow(unused_attributes)]
 #![unstable(feature = "alloc",
             reason = "this library is unlikely to be stabilized in its current \
                       form or name")]
@@ -69,6 +70,7 @@
        html_root_url = "https://doc.rust-lang.org/nightly/",
        test(no_crate_inject))]
 #![no_std]
+#![cfg_attr(not(stage0), needs_allocator)]
 
 #![feature(allocator)]
 #![feature(box_syntax)]
@@ -92,13 +94,13 @@
 #![feature(unsize)]
 #![feature(core_slice_ext)]
 #![feature(core_str_ext)]
+#![cfg_attr(stage0, feature(alloc_system))]
+#![cfg_attr(not(stage0), feature(needs_allocator))]
 
 #![cfg_attr(test, feature(test, rustc_private, box_raw))]
-#![cfg_attr(all(not(feature = "external_funcs"), not(feature = "external_crate")),
-            feature(libc))]
 
-#[cfg(all(not(feature = "external_funcs"), not(feature = "external_crate")))]
-extern crate libc;
+#[cfg(stage0)]
+extern crate alloc_system;
 
 // Allow testing this library
 
diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs
new file mode 100644
index 00000000000..2a30f88df49
--- /dev/null
+++ b/src/liballoc_jemalloc/lib.rs
@@ -0,0 +1,96 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(stage0, feature(custom_attribute))]
+#![crate_name = "alloc_jemalloc"]
+#![crate_type = "rlib"]
+#![staged_api]
+#![no_std]
+#![cfg_attr(not(stage0), allocator)]
+#![unstable(feature = "alloc_jemalloc",
+            reason = "this library is unlikely to be stabilized in its current \
+                      form or name")]
+#![feature(allocator)]
+#![feature(libc)]
+#![feature(no_std)]
+#![feature(staged_api)]
+
+extern crate libc;
+
+use libc::{c_int, c_void, size_t};
+
+#[link(name = "jemalloc", kind = "static")]
+extern {
+    fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
+    fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
+    fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t,
+                  flags: c_int) -> size_t;
+    fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
+    fn je_nallocx(size: size_t, flags: c_int) -> size_t;
+}
+
+// -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough
+#[cfg(all(not(windows),
+          not(target_os = "android"),
+          not(target_env = "musl")))]
+#[link(name = "pthread")]
+extern {}
+
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values. In practice, the alignment is a
+// constant at the call site and the branch will be optimized out.
+#[cfg(all(any(target_arch = "arm",
+              target_arch = "mips",
+              target_arch = "mipsel",
+              target_arch = "powerpc")))]
+const MIN_ALIGN: usize = 8;
+#[cfg(all(any(target_arch = "x86",
+              target_arch = "x86_64",
+              target_arch = "aarch64")))]
+const MIN_ALIGN: usize = 16;
+
+// MALLOCX_ALIGN(a) macro
+fn mallocx_align(a: usize) -> c_int { a.trailing_zeros() as c_int }
+
+fn align_to_flags(align: usize) -> c_int {
+    if align <= MIN_ALIGN { 0 } else { mallocx_align(align) }
+}
+
+#[no_mangle]
+pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
+    let flags = align_to_flags(align);
+    unsafe { je_mallocx(size as size_t, flags) as *mut u8 }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize,
+                                align: usize) -> *mut u8 {
+    let flags = align_to_flags(align);
+    unsafe { je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate_inplace(ptr: *mut u8, _old_size: usize,
+                                        size: usize, align: usize) -> usize {
+    let flags = align_to_flags(align);
+    unsafe { je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
+}
+
+#[no_mangle]
+pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
+    let flags = align_to_flags(align);
+    unsafe { je_sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
+}
+
+#[no_mangle]
+pub extern fn __rust_usable_size(size: usize, align: usize) -> usize {
+    let flags = align_to_flags(align);
+    unsafe { je_nallocx(size as size_t, flags) as usize }
+}
diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs
new file mode 100644
index 00000000000..0687ced7da1
--- /dev/null
+++ b/src/liballoc_system/lib.rs
@@ -0,0 +1,212 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(stage0, feature(custom_attribute))]
+#![crate_name = "alloc_system"]
+#![crate_type = "rlib"]
+#![staged_api]
+#![no_std]
+#![cfg_attr(not(stage0), allocator)]
+#![unstable(feature = "alloc_system",
+            reason = "this library is unlikely to be stabilized in its current \
+                      form or name")]
+#![feature(allocator)]
+#![feature(libc)]
+#![feature(no_std)]
+#![feature(staged_api)]
+
+extern crate libc;
+
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values. In practice, the alignment is a
+// constant at the call site and the branch will be optimized out.
+#[cfg(all(any(target_arch = "arm",
+              target_arch = "mips",
+              target_arch = "mipsel",
+              target_arch = "powerpc")))]
+const MIN_ALIGN: usize = 8;
+#[cfg(all(any(target_arch = "x86",
+              target_arch = "x86_64",
+              target_arch = "aarch64")))]
+const MIN_ALIGN: usize = 16;
+
+#[no_mangle]
+pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
+    unsafe { imp::allocate(size, align) }
+}
+
+#[no_mangle]
+pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
+    unsafe { imp::deallocate(ptr, old_size, align) }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize,
+                                align: usize) -> *mut u8 {
+    unsafe { imp::reallocate(ptr, old_size, size, align) }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize,
+                                        size: usize, align: usize) -> usize {
+    unsafe { imp::reallocate_inplace(ptr, old_size, size, align) }
+}
+
+#[no_mangle]
+pub extern fn __rust_usable_size(size: usize, align: usize) -> usize {
+    imp::usable_size(size, align)
+}
+
+#[cfg(unix)]
+mod imp {
+    use core::cmp;
+    use core::ptr;
+    use libc;
+    use MIN_ALIGN;
+
+    extern {
+        // Apparently android doesn't have posix_memalign
+        #[cfg(target_os = "android")]
+        fn memalign(align: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
+
+        #[cfg(not(target_os = "android"))]
+        fn posix_memalign(memptr: *mut *mut libc::c_void,
+                          align: libc::size_t,
+                          size: libc::size_t) -> libc::c_int;
+    }
+
+    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
+        if align <= MIN_ALIGN {
+            libc::malloc(size as libc::size_t) as *mut u8
+        } else {
+            #[cfg(target_os = "android")]
+            unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
+                memalign(align as libc::size_t, size as libc::size_t) as *mut u8
+            }
+            #[cfg(not(target_os = "android"))]
+            unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
+                let mut out = ptr::null_mut();
+                let ret = posix_memalign(&mut out,
+                                         align as libc::size_t,
+                                         size as libc::size_t);
+                if ret != 0 {
+                    ptr::null_mut()
+                } else {
+                    out as *mut u8
+                }
+            }
+            more_aligned_malloc(size, align)
+        }
+    }
+
+    pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize,
+                             align: usize) -> *mut u8 {
+        if align <= MIN_ALIGN {
+            libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
+        } else {
+            let new_ptr = allocate(size, align);
+            ptr::copy(ptr, new_ptr, cmp::min(size, old_size));
+            deallocate(ptr, old_size, align);
+            new_ptr
+        }
+    }
+
+    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: usize, _size: usize,
+                                     _align: usize) -> usize {
+        old_size
+    }
+
+    pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, _align: usize) {
+        libc::free(ptr as *mut libc::c_void)
+    }
+
+    pub fn usable_size(size: usize, _align: usize) -> usize {
+        size
+    }
+}
+
+#[cfg(windows)]
+mod imp {
+    use libc::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T};
+    use MIN_ALIGN;
+
+    extern "system" {
+        fn GetProcessHeap() -> HANDLE;
+        fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+        fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID,
+                       dwBytes: SIZE_T) -> LPVOID;
+        fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+    }
+
+    #[repr(C)]
+    struct Header(*mut u8);
+
+    const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010;
+
+    unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
+        &mut *(ptr as *mut Header).offset(-1)
+    }
+
+    unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
+        let aligned = ptr.offset((align - (ptr as usize & (align - 1))) as isize);
+        *get_header(aligned) = Header(ptr);
+        aligned
+    }
+
+    pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
+        if align <= MIN_ALIGN {
+            HeapAlloc(GetProcessHeap(), 0, size as SIZE_T) as *mut u8
+        } else {
+            let ptr = HeapAlloc(GetProcessHeap(), 0,
+                                (size + align) as SIZE_T) as *mut u8;
+            if ptr.is_null() { return ptr }
+            align_ptr(ptr, align)
+        }
+    }
+
+    pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize,
+                             align: usize) -> *mut u8 {
+        if align <= MIN_ALIGN {
+            HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, size as SIZE_T) as *mut u8
+        } else {
+            let header = get_header(ptr);
+            let new = HeapReAlloc(GetProcessHeap(), 0, header.0 as LPVOID,
+                                  (size + align) as SIZE_T) as *mut u8;
+            if new.is_null() { return new }
+            align_ptr(new, align)
+        }
+    }
+
+    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
+                                     align: usize) -> usize {
+        if align <= MIN_ALIGN {
+            let new = HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
+                                  ptr as LPVOID, size as SIZE_T) as *mut u8;
+            if new.is_null() { old_size } else { size }
+        } else {
+            old_size
+        }
+    }
+
+    pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, align: usize) {
+        if align <= MIN_ALIGN {
+            let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
+            debug_assert!(err != 0);
+        } else {
+            let header = get_header(ptr);
+            let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
+            debug_assert!(err != 0);
+        }
+    }
+
+    pub fn usable_size(size: usize, _align: usize) -> usize {
+        size
+    }
+}
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index abcff6e78e2..3e0cf30fe94 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -78,12 +78,13 @@ pub const tag_crate_crate_name: usize = 0x104; // top-level only
 
 pub const tag_crate_dep_crate_name: usize = 0x36;
 pub const tag_crate_dep_hash: usize = 0x37;
+pub const tag_crate_dep_explicitly_linked: usize = 0x38; // top-level only
 
-pub const tag_mod_impl: usize = 0x38;
+pub const tag_mod_impl: usize = 0x39;
 
-pub const tag_item_trait_item: usize = 0x39;
+pub const tag_item_trait_item: usize = 0x3a;
 
-pub const tag_item_trait_ref: usize = 0x3a;
+pub const tag_item_trait_ref: usize = 0x3b;
 
 // discriminator value for variants
 pub const tag_disr_val: usize = 0x3c;
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index 3226a99c6b3..c34b2ea58dc 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -22,7 +22,7 @@ use metadata::loader;
 use metadata::loader::CratePaths;
 use util::nodemap::FnvHashMap;
 
-use std::cell::RefCell;
+use std::cell::{RefCell, Cell};
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::fs;
@@ -59,15 +59,16 @@ impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> {
 }
 
 fn dump_crates(cstore: &CStore) {
-    debug!("resolved crates:");
+    info!("resolved crates:");
     cstore.iter_crate_data_origins(|_, data, opt_source| {
-        debug!("  name: {}", data.name());
-        debug!("  cnum: {}", data.cnum);
-        debug!("  hash: {}", data.hash());
+        info!("  name: {}", data.name());
+        info!("  cnum: {}", data.cnum);
+        info!("  hash: {}", data.hash());
+        info!("  reqd: {}", data.explicitly_linked.get());
         opt_source.map(|cs| {
             let CrateSource { dylib, rlib, cnum: _ } = cs;
-            dylib.map(|dl| debug!("  dylib: {}", dl.0.display()));
-            rlib.map(|rl|  debug!("   rlib: {}", rl.0.display()));
+            dylib.map(|dl| info!("  dylib: {}", dl.0.display()));
+            rlib.map(|rl|  info!("   rlib: {}", rl.0.display()));
         });
     })
 }
@@ -241,7 +242,8 @@ impl<'a> CrateReader<'a> {
                       ident: &str,
                       name: &str,
                       span: Span,
-                      lib: loader::Library)
+                      lib: loader::Library,
+                      explicitly_linked: bool)
                       -> (ast::CrateNum, Rc<cstore::crate_metadata>,
                           cstore::CrateSource) {
         // Claim this crate number and cache it
@@ -266,15 +268,16 @@ impl<'a> CrateReader<'a> {
         let cnum_map = self.resolve_crate_deps(root, metadata.as_slice(), span);
         let staged_api = self.is_staged_api(metadata.as_slice());
 
-        let cmeta = Rc::new( cstore::crate_metadata {
+        let cmeta = Rc::new(cstore::crate_metadata {
             name: name.to_string(),
             local_path: RefCell::new(SmallVector::zero()),
             data: metadata,
-            cnum_map: cnum_map,
+            cnum_map: RefCell::new(cnum_map),
             cnum: cnum,
             codemap_import_info: RefCell::new(vec![]),
             span: span,
-            staged_api: staged_api
+            staged_api: staged_api,
+            explicitly_linked: Cell::new(explicitly_linked),
         });
 
         let source = cstore::CrateSource {
@@ -305,7 +308,8 @@ impl<'a> CrateReader<'a> {
                      name: &str,
                      hash: Option<&Svh>,
                      span: Span,
-                     kind: PathKind)
+                     kind: PathKind,
+                     explicitly_linked: bool)
                          -> (ast::CrateNum, Rc<cstore::crate_metadata>,
                              cstore::CrateSource) {
         match self.existing_match(name, hash, kind) {
@@ -326,11 +330,16 @@ impl<'a> CrateReader<'a> {
                     should_match_name: true,
                 };
                 let library = load_ctxt.load_library_crate();
-                self.register_crate(root, ident, name, span, library)
+                self.register_crate(root, ident, name, span, library,
+                                    explicitly_linked)
+            }
+            Some(cnum) => {
+                let data = self.sess.cstore.get_crate_data(cnum);
+                if explicitly_linked && !data.explicitly_linked.get() {
+                    data.explicitly_linked.set(explicitly_linked);
+                }
+                (cnum, data, self.sess.cstore.get_used_crate_source(cnum).unwrap())
             }
-            Some(cnum) => (cnum,
-                           self.sess.cstore.get_crate_data(cnum),
-                           self.sess.cstore.get_used_crate_source(cnum).unwrap())
         }
     }
 
@@ -349,7 +358,8 @@ impl<'a> CrateReader<'a> {
                                                    &dep.name,
                                                    Some(&dep.hash),
                                                    span,
-                                                   PathKind::Dependency);
+                                                   PathKind::Dependency,
+                                                   dep.explicitly_linked);
             (dep.cnum, local_cnum)
         }).collect()
     }
@@ -399,7 +409,8 @@ impl<'a> CrateReader<'a> {
         let metadata = if register {
             // Register crate now to avoid double-reading metadata
             let (_, cmd, _) = self.register_crate(&None, &info.ident,
-                                &info.name, span, library);
+                                                  &info.name, span, library,
+                                                  true);
             PMDSource::Registered(cmd)
         } else {
             // Not registering the crate; just hold on to the metadata
@@ -507,6 +518,124 @@ impl<'a> CrateReader<'a> {
             }
         }
     }
+
+    fn inject_allocator_crate(&mut self) {
+        // Make sure that we actually need an allocator, if none of our
+        // dependencies need one then we definitely don't!
+        //
+        // Also, if one of our dependencies has an explicit allocator, then we
+        // also bail out as we don't need to implicitly inject one.
+        let mut needs_allocator = false;
+        let mut found_required_allocator = false;
+        self.sess.cstore.iter_crate_data(|cnum, data| {
+            needs_allocator = needs_allocator || data.needs_allocator();
+            if data.is_allocator() {
+                debug!("{} required by rlib and is an allocator", data.name());
+                self.inject_allocator_dependency(cnum);
+                found_required_allocator = found_required_allocator ||
+                    data.explicitly_linked.get();
+            }
+        });
+        if !needs_allocator || found_required_allocator { return }
+
+        // At this point we've determined that we need an allocator and no
+        // previous allocator has been activated. We look through our outputs of
+        // crate types to see what kind of allocator types we may need.
+        //
+        // The main special output type here is that rlibs do **not** need an
+        // allocator linked in (they're just object files), only final products
+        // (exes, dylibs, staticlibs) need allocators.
+        let mut need_lib_alloc = false;
+        let mut need_exe_alloc = false;
+        for ct in self.sess.crate_types.borrow().iter() {
+            match *ct {
+                config::CrateTypeExecutable => need_exe_alloc = true,
+                config::CrateTypeDylib |
+                config::CrateTypeStaticlib => need_lib_alloc = true,
+                config::CrateTypeRlib => {}
+            }
+        }
+        if !need_lib_alloc && !need_exe_alloc { return }
+
+        // The default allocator crate comes from the custom target spec, and we
+        // choose between the standard library allocator or exe allocator. This
+        // distinction exists because the default allocator for binaries (where
+        // the world is Rust) is different than library (where the world is
+        // likely *not* Rust).
+        //
+        // If a library is being produced, but we're also flagged with `-C
+        // prefer-dynamic`, then we interpret this as a *Rust* dynamic library
+        // is being produced so we use the exe allocator instead.
+        //
+        // What this boils down to is:
+        //
+        // * Binaries use jemalloc
+        // * Staticlibs and Rust dylibs use system malloc
+        // * Rust dylibs used as dependencies to rust use jemalloc
+        let name = if need_lib_alloc && !self.sess.opts.cg.prefer_dynamic {
+            &self.sess.target.target.options.lib_allocation_crate
+        } else {
+            &self.sess.target.target.options.exe_allocation_crate
+        };
+        let (cnum, data, _) = self.resolve_crate(&None, name, name, None,
+                                                 codemap::DUMMY_SP,
+                                                 PathKind::Crate, false);
+
+        // To ensure that the `-Z allocation-crate=foo` option isn't abused, and
+        // to ensure that the allocator is indeed an allocator, we verify that
+        // the crate loaded here is indeed tagged #![allocator].
+        if !data.is_allocator() {
+            self.sess.err(&format!("the allocator crate `{}` is not tagged \
+                                    with #![allocator]", data.name()));
+        }
+
+        self.sess.injected_allocator.set(Some(cnum));
+        self.inject_allocator_dependency(cnum);
+    }
+
+    fn inject_allocator_dependency(&self, allocator: ast::CrateNum) {
+        // Before we inject any dependencies, make sure we don't inject a
+        // circular dependency by validating that this allocator crate doesn't
+        // transitively depend on any `#![needs_allocator]` crates.
+        validate(self, allocator, allocator);
+
+        // All crates tagged with `needs_allocator` do not explicitly depend on
+        // the allocator selected for this compile, but in order for this
+        // compilation to be successfully linked we need to inject a dependency
+        // (to order the crates on the command line correctly).
+        //
+        // Here we inject a dependency from all crates with #![needs_allocator]
+        // to the crate tagged with #![allocator] for this compilation unit.
+        self.sess.cstore.iter_crate_data(|cnum, data| {
+            if !data.needs_allocator() {
+                return
+            }
+
+            info!("injecting a dep from {} to {}", cnum, allocator);
+            let mut cnum_map = data.cnum_map.borrow_mut();
+            let remote_cnum = cnum_map.len() + 1;
+            let prev = cnum_map.insert(remote_cnum as ast::CrateNum, allocator);
+            assert!(prev.is_none());
+        });
+
+        fn validate(me: &CrateReader, krate: ast::CrateNum,
+                    allocator: ast::CrateNum) {
+            let data = me.sess.cstore.get_crate_data(krate);
+            if data.needs_allocator() {
+                let krate_name = data.name();
+                let data = me.sess.cstore.get_crate_data(allocator);
+                let alloc_name = data.name();
+                me.sess.err(&format!("the allocator crate `{}` cannot depend \
+                                      on a crate that needs an allocator, but \
+                                      it depends on `{}`", alloc_name,
+                                      krate_name));
+            }
+
+            for (_, &dep) in data.cnum_map.borrow().iter() {
+                validate(me, dep, allocator);
+            }
+        }
+    }
 }
 
 impl<'a, 'b> LocalCrateReader<'a, 'b> {
@@ -524,8 +653,9 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
     pub fn read_crates(&mut self, krate: &ast::Crate) {
         self.process_crate(krate);
         visit::walk_crate(self, krate);
+        self.creader.inject_allocator_crate();
 
-        if log_enabled!(log::DEBUG) {
+        if log_enabled!(log::INFO) {
             dump_crates(&self.sess.cstore);
         }
 
@@ -558,7 +688,8 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
                                                               &info.name,
                                                               None,
                                                               i.span,
-                                                              PathKind::Crate);
+                                                              PathKind::Crate,
+                                                              true);
                         self.ast_map.with_path(i.id, |path| {
                             cmeta.update_local_path(path)
                         });
diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs
index ae5e797a029..9179a0a1871 100644
--- a/src/librustc/metadata/cstore.rs
+++ b/src/librustc/metadata/cstore.rs
@@ -22,11 +22,12 @@ use metadata::{creader, decoder, loader};
 use session::search_paths::PathKind;
 use util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
-use std::cell::{RefCell, Ref};
+use std::cell::{RefCell, Ref, Cell};
 use std::rc::Rc;
 use std::path::PathBuf;
 use flate::Bytes;
 use syntax::ast;
+use syntax::attr;
 use syntax::codemap;
 use syntax::parse::token;
 use syntax::parse::token::IdentInterner;
@@ -59,11 +60,17 @@ pub struct crate_metadata {
     pub name: String,
     pub local_path: RefCell<SmallVector<ast_map::PathElem>>,
     pub data: MetadataBlob,
-    pub cnum_map: cnum_map,
+    pub cnum_map: RefCell<cnum_map>,
     pub cnum: ast::CrateNum,
     pub codemap_import_info: RefCell<Vec<ImportedFileMap>>,
     pub span: codemap::Span,
-    pub staged_api: bool
+    pub staged_api: bool,
+
+    /// Flag if this crate is required by an rlib version of this crate, or in
+    /// other words whether it was explicitly linked to. An example of a crate
+    /// where this is false is when an allocator crate is injected into the
+    /// dependency list, and therefore isn't actually needed to link an rlib.
+    pub explicitly_linked: Cell<bool>,
 }
 
 #[derive(Copy, Debug, PartialEq, Clone)]
@@ -132,10 +139,10 @@ impl CStore {
     }
 
     pub fn iter_crate_data<I>(&self, mut i: I) where
-        I: FnMut(ast::CrateNum, &crate_metadata),
+        I: FnMut(ast::CrateNum, &Rc<crate_metadata>),
     {
         for (&k, v) in self.metas.borrow().iter() {
-            i(k, &**v);
+            i(k, v);
         }
     }
 
@@ -188,7 +195,7 @@ impl CStore {
                  ordering: &mut Vec<ast::CrateNum>) {
             if ordering.contains(&cnum) { return }
             let meta = cstore.get_crate_data(cnum);
-            for (_, &dep) in &meta.cnum_map {
+            for (_, &dep) in meta.cnum_map.borrow().iter() {
                 visit(cstore, dep, ordering);
             }
             ordering.push(cnum);
@@ -196,6 +203,7 @@ impl CStore {
         for (&num, _) in self.metas.borrow().iter() {
             visit(self, num, &mut ordering);
         }
+        info!("topological ordering: {:?}", ordering);
         ordering.reverse();
         let mut libs = self.used_crate_sources.borrow()
             .iter()
@@ -271,8 +279,10 @@ impl crate_metadata {
             filemaps
         }
     }
+
     pub fn with_local_path<T, F>(&self, f: F) -> T
-    where F: Fn(&[ast_map::PathElem]) -> T {
+        where F: Fn(&[ast_map::PathElem]) -> T
+    {
         let cpath = self.local_path.borrow();
         if cpath.is_empty() {
             let name = ast_map::PathMod(token::intern(&self.name));
@@ -281,6 +291,7 @@ impl crate_metadata {
             f(cpath.as_slice())
         }
     }
+
     pub fn update_local_path<'a, 'b>(&self, candidate: ast_map::PathElems<'a, 'b>) {
         let mut cpath = self.local_path.borrow_mut();
         let cap = cpath.len();
@@ -295,6 +306,16 @@ impl crate_metadata {
             },
         }
     }
+
+    pub fn is_allocator(&self) -> bool {
+        let attrs = decoder::get_crate_attributes(self.data());
+        attr::contains_name(&attrs, "allocator")
+    }
+
+    pub fn needs_allocator(&self) -> bool {
+        let attrs = decoder::get_crate_attributes(self.data());
+        attr::contains_name(&attrs, "needs_allocator")
+    }
 }
 
 impl MetadataBlob {
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index c6c18fa14a3..fdd45251711 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -1174,6 +1174,7 @@ pub struct CrateDep {
     pub cnum: ast::CrateNum,
     pub name: String,
     pub hash: Svh,
+    pub explicitly_linked: bool,
 }
 
 pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
@@ -1188,10 +1189,13 @@ pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
     reader::tagged_docs(depsdoc, tag_crate_dep).enumerate().map(|(crate_num, depdoc)| {
         let name = docstr(depdoc, tag_crate_dep_crate_name);
         let hash = Svh::new(&docstr(depdoc, tag_crate_dep_hash));
+        let doc = reader::get_doc(depdoc, tag_crate_dep_explicitly_linked);
+        let explicitly_linked = reader::doc_as_u8(doc) != 0;
         CrateDep {
             cnum: crate_num as u32 + 1,
             name: name,
             hash: hash,
+            explicitly_linked: explicitly_linked,
         }
     }).collect()
 }
@@ -1252,7 +1256,7 @@ pub fn translate_def_id(cdata: Cmd, did: ast::DefId) -> ast::DefId {
         return ast::DefId { krate: cdata.cnum, node: did.node };
     }
 
-    match cdata.cnum_map.get(&did.krate) {
+    match cdata.cnum_map.borrow().get(&did.krate) {
         Some(&n) => {
             ast::DefId {
                 krate: n,
@@ -1270,7 +1274,7 @@ fn reverse_translate_def_id(cdata: Cmd, did: ast::DefId) -> Option<ast::DefId> {
         return Some(ast::DefId { krate: ast::LOCAL_CRATE, node: did.node });
     }
 
-    for (&local, &global) in &cdata.cnum_map {
+    for (&local, &global) in cdata.cnum_map.borrow().iter() {
         if global == did.krate {
             return Some(ast::DefId { krate: local, node: did.node });
         }
@@ -1385,7 +1389,7 @@ pub fn get_dylib_dependency_formats(cdata: Cmd)
         let cnum = spec.split(':').nth(0).unwrap();
         let link = spec.split(':').nth(1).unwrap();
         let cnum: ast::CrateNum = cnum.parse().unwrap();
-        let cnum = match cdata.cnum_map.get(&cnum) {
+        let cnum = match cdata.cnum_map.borrow().get(&cnum) {
             Some(&n) => n,
             None => panic!("didn't find a crate in the cnum_map")
         };
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index e0f35b6817b..cc723d94f74 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -23,8 +23,9 @@ use metadata::cstore;
 use metadata::decoder;
 use metadata::tyencode;
 use middle::def;
-use middle::ty::{self, Ty};
+use middle::dependency_format::Linkage;
 use middle::stability;
+use middle::ty::{self, Ty};
 use util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
 use serialize::Encodable;
@@ -32,6 +33,7 @@ use std::cell::RefCell;
 use std::hash::{Hash, Hasher, SipHasher};
 use std::io::prelude::*;
 use std::io::{Cursor, SeekFrom};
+use std::rc::Rc;
 use syntax::abi;
 use syntax::ast::{self, DefId, NodeId};
 use syntax::ast_util::*;
@@ -1751,25 +1753,21 @@ fn encode_polarity(rbml_w: &mut Encoder, polarity: ast::ImplPolarity) {
 }
 
 fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
-    fn get_ordered_deps(cstore: &cstore::CStore) -> Vec<decoder::CrateDep> {
+    fn get_ordered_deps(cstore: &cstore::CStore)
+                        -> Vec<(ast::CrateNum, Rc<cstore::crate_metadata>)> {
         // Pull the cnums and name,vers,hash out of cstore
         let mut deps = Vec::new();
-        cstore.iter_crate_data(|key, val| {
-            let dep = decoder::CrateDep {
-                cnum: key,
-                name: decoder::get_crate_name(val.data()),
-                hash: decoder::get_crate_hash(val.data()),
-            };
-            deps.push(dep);
+        cstore.iter_crate_data(|cnum, val| {
+            deps.push((cnum, val.clone()));
         });
 
         // Sort by cnum
-        deps.sort_by(|kv1, kv2| kv1.cnum.cmp(&kv2.cnum));
+        deps.sort_by(|kv1, kv2| kv1.0.cmp(&kv2.0));
 
         // Sanity-check the crate numbers
         let mut expected_cnum = 1;
-        for n in &deps {
-            assert_eq!(n.cnum, expected_cnum);
+        for &(n, _) in &deps {
+            assert_eq!(n, expected_cnum);
             expected_cnum += 1;
         }
 
@@ -1781,8 +1779,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
     // FIXME (#2166): This is not nearly enough to support correct versioning
     // but is enough to get transitive crate dependencies working.
     rbml_w.start_tag(tag_crate_deps);
-    for dep in &get_ordered_deps(cstore) {
-        encode_crate_dep(rbml_w, dep);
+    for (_cnum, dep) in get_ordered_deps(cstore) {
+        encode_crate_dep(rbml_w, &dep);
     }
     rbml_w.end_tag();
 }
@@ -1985,10 +1983,13 @@ fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) {
 }
 
 fn encode_crate_dep(rbml_w: &mut Encoder,
-                    dep: &decoder::CrateDep) {
+                    dep: &cstore::crate_metadata) {
     rbml_w.start_tag(tag_crate_dep);
-    rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name);
-    rbml_w.wr_tagged_str(tag_crate_dep_hash, dep.hash.as_str());
+    rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name());
+    let hash = decoder::get_crate_hash(dep.data());
+    rbml_w.wr_tagged_str(tag_crate_dep_hash, hash.as_str());
+    rbml_w.wr_tagged_u8(tag_crate_dep_explicitly_linked,
+                        dep.explicitly_linked.get() as u8);
     rbml_w.end_tag();
 }
 
@@ -2006,13 +2007,16 @@ fn encode_crate_triple(rbml_w: &mut Encoder, triple: &str) {
 
 fn encode_dylib_dependency_formats(rbml_w: &mut Encoder, ecx: &EncodeContext) {
     let tag = tag_dylib_dependency_formats;
-    match ecx.tcx.dependency_formats.borrow().get(&config::CrateTypeDylib) {
+    match ecx.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) {
         Some(arr) => {
             let s = arr.iter().enumerate().filter_map(|(i, slot)| {
-                slot.map(|kind| (format!("{}:{}", i + 1, match kind {
-                    cstore::RequireDynamic => "d",
-                    cstore::RequireStatic => "s",
-                })).to_string())
+                let kind = match *slot {
+                    Linkage::NotLinked |
+                    Linkage::IncludedFromDylib => return None,
+                    Linkage::Dynamic => "d",
+                    Linkage::Static => "s",
+                };
+                Some(format!("{}:{}", i + 1, kind))
             }).collect::<Vec<String>>();
             rbml_w.wr_tagged_str(tag, &s.join(","));
         }
diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs
index ff5178fbefc..125e9285b52 100644
--- a/src/librustc/middle/dependency_format.rs
+++ b/src/librustc/middle/dependency_format.rs
@@ -67,7 +67,6 @@ use session;
 use session::config;
 use metadata::cstore;
 use metadata::csearch;
-use middle::ty;
 use util::nodemap::FnvHashMap;
 
 /// A list of dependencies for a certain crate type.
@@ -76,19 +75,29 @@ use util::nodemap::FnvHashMap;
 /// The value is None if the crate does not need to be linked (it was found
 /// statically in another dylib), or Some(kind) if it needs to be linked as
 /// `kind` (either static or dynamic).
-pub type DependencyList = Vec<Option<cstore::LinkagePreference>>;
+pub type DependencyList = Vec<Linkage>;
 
 /// A mapping of all required dependencies for a particular flavor of output.
 ///
 /// This is local to the tcx, and is generally relevant to one session.
 pub type Dependencies = FnvHashMap<config::CrateType, DependencyList>;
 
-pub fn calculate(tcx: &ty::ctxt) {
-    let mut fmts = tcx.dependency_formats.borrow_mut();
-    for &ty in tcx.sess.crate_types.borrow().iter() {
-        fmts.insert(ty, calculate_type(&tcx.sess, ty));
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Linkage {
+    NotLinked,
+    IncludedFromDylib,
+    Static,
+    Dynamic,
+}
+
+pub fn calculate(sess: &session::Session) {
+    let mut fmts = sess.dependency_formats.borrow_mut();
+    for &ty in sess.crate_types.borrow().iter() {
+        let linkage = calculate_type(sess, ty);
+        verify_ok(sess, &linkage);
+        fmts.insert(ty, linkage);
     }
-    tcx.sess.abort_if_errors();
+    sess.abort_if_errors();
 }
 
 fn calculate_type(sess: &session::Session,
@@ -145,12 +154,12 @@ fn calculate_type(sess: &session::Session,
     sess.cstore.iter_crate_data(|cnum, data| {
         let src = sess.cstore.get_used_crate_source(cnum).unwrap();
         if src.dylib.is_some() {
-            debug!("adding dylib: {}", data.name);
+            info!("adding dylib: {}", data.name);
             add_library(sess, cnum, cstore::RequireDynamic, &mut formats);
             let deps = csearch::get_dylib_dependency_formats(&sess.cstore, cnum);
             for &(depnum, style) in &deps {
-                debug!("adding {:?}: {}", style,
-                       sess.cstore.get_crate_data(depnum).name.clone());
+                info!("adding {:?}: {}", style,
+                      sess.cstore.get_crate_data(depnum).name.clone());
                 add_library(sess, depnum, style, &mut formats);
             }
         }
@@ -158,24 +167,36 @@ fn calculate_type(sess: &session::Session,
 
     // Collect what we've got so far in the return vector.
     let mut ret = (1..sess.cstore.next_crate_num()).map(|i| {
-        match formats.get(&i).cloned() {
-            v @ Some(cstore::RequireDynamic) => v,
-            _ => None,
+        match formats.get(&i) {
+            Some(&cstore::RequireDynamic) => Linkage::Dynamic,
+            Some(&cstore::RequireStatic) => Linkage::IncludedFromDylib,
+            None => Linkage::NotLinked,
         }
     }).collect::<Vec<_>>();
 
     // Run through the dependency list again, and add any missing libraries as
     // static libraries.
+    //
+    // If the crate hasn't been included yet and it's not actually required
+    // (e.g. it's an allocator) then we skip it here as well.
     sess.cstore.iter_crate_data(|cnum, data| {
         let src = sess.cstore.get_used_crate_source(cnum).unwrap();
-        if src.dylib.is_none() && !formats.contains_key(&cnum) {
+        if src.dylib.is_none() &&
+           !formats.contains_key(&cnum) &&
+           data.explicitly_linked.get() {
             assert!(src.rlib.is_some());
-            debug!("adding staticlib: {}", data.name);
+            info!("adding staticlib: {}", data.name);
             add_library(sess, cnum, cstore::RequireStatic, &mut formats);
-            ret[cnum as usize - 1] = Some(cstore::RequireStatic);
+            ret[cnum as usize - 1] = Linkage::Static;
         }
     });
 
+    // We've gotten this far because we're emitting some form of a final
+    // artifact which means that we're going to need an allocator of some form.
+    // No allocator may have been required or linked so far, so activate one
+    // here if one isn't set.
+    activate_allocator(sess, &mut ret);
+
     // When dylib B links to dylib A, then when using B we must also link to A.
     // It could be the case, however, that the rlib for A is present (hence we
     // found metadata), but the dylib for A has since been removed.
@@ -183,21 +204,22 @@ fn calculate_type(sess: &session::Session,
     // For situations like this, we perform one last pass over the dependencies,
     // making sure that everything is available in the requested format.
     for (cnum, kind) in ret.iter().enumerate() {
-        let cnum = cnum as ast::CrateNum;
-        let src = sess.cstore.get_used_crate_source(cnum + 1).unwrap();
+        let cnum = (cnum + 1) as ast::CrateNum;
+        let src = sess.cstore.get_used_crate_source(cnum).unwrap();
         match *kind {
-            None => continue,
-            Some(cstore::RequireStatic) if src.rlib.is_some() => continue,
-            Some(cstore::RequireDynamic) if src.dylib.is_some() => continue,
-            Some(kind) => {
-                let data = sess.cstore.get_crate_data(cnum + 1);
+            Linkage::NotLinked |
+            Linkage::IncludedFromDylib => {}
+            Linkage::Static if src.rlib.is_some() => continue,
+            Linkage::Dynamic if src.dylib.is_some() => continue,
+            kind => {
+                let kind = match kind {
+                    Linkage::Static => "rlib",
+                    _ => "dylib",
+                };
+                let data = sess.cstore.get_crate_data(cnum);
                 sess.err(&format!("crate `{}` required to be available in {}, \
                                   but it was not available in this form",
-                                 data.name,
-                                 match kind {
-                                     cstore::RequireStatic => "rlib",
-                                     cstore::RequireDynamic => "dylib",
-                                 }));
+                                 data.name, kind));
             }
         }
     }
@@ -221,8 +243,7 @@ fn add_library(sess: &session::Session,
             if link2 != link || link == cstore::RequireStatic {
                 let data = sess.cstore.get_crate_data(cnum);
                 sess.err(&format!("cannot satisfy dependencies so `{}` only \
-                                  shows up once",
-                                 data.name));
+                                   shows up once", data.name));
                 sess.help("having upstream crates all available in one format \
                            will likely make this go away");
             }
@@ -233,9 +254,79 @@ fn add_library(sess: &session::Session,
 
 fn attempt_static(sess: &session::Session) -> Option<DependencyList> {
     let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
-    if crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) {
-        Some(crates.into_iter().map(|_| Some(cstore::RequireStatic)).collect())
-    } else {
-        None
+    if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) {
+        return None
+    }
+
+    // All crates are available in an rlib format, so we're just going to link
+    // everything in explicitly so long as it's actually required.
+    let mut ret = (1..sess.cstore.next_crate_num()).map(|cnum| {
+        if sess.cstore.get_crate_data(cnum).explicitly_linked.get() {
+            Linkage::Static
+        } else {
+            Linkage::NotLinked
+        }
+    }).collect::<Vec<_>>();
+
+    // Our allocator may not have been activated as it's not flagged with
+    // explicitly_linked, so flag it here if necessary.
+    activate_allocator(sess, &mut ret);
+
+    Some(ret)
+}
+
+// Given a list of how to link upstream dependencies so far, ensure that an
+// allocator is activated. This will not do anything if one was transitively
+// included already (e.g. via a dylib or explicitly so).
+//
+// If an allocator was not found then we're guaranteed the metadata::creader
+// module has injected an allocator dependency (not listed as a required
+// dependency) in the session's `injected_allocator` field. If this field is not
+// set then this compilation doesn't actually need an allocator and we can also
+// skip this step entirely.
+fn activate_allocator(sess: &session::Session, list: &mut DependencyList) {
+    let mut allocator_found = false;
+    for (i, slot) in list.iter().enumerate() {
+        let cnum = (i + 1) as ast::CrateNum;
+        if !sess.cstore.get_crate_data(cnum).is_allocator() {
+            continue
+        }
+        if let Linkage::NotLinked = *slot {
+            continue
+        }
+        allocator_found = true;
+    }
+    if !allocator_found {
+        if let Some(injected_allocator) = sess.injected_allocator.get() {
+            let idx = injected_allocator as usize - 1;
+            assert_eq!(list[idx], Linkage::NotLinked);
+            list[idx] = Linkage::Static;
+        }
+    }
+}
+
+// After the linkage for a crate has been determined we need to verify that
+// there's only going to be one allocator in the output.
+fn verify_ok(sess: &session::Session, list: &[Linkage]) {
+    if list.len() == 0 {
+        return
+    }
+    let mut allocator = None;
+    for (i, linkage) in list.iter().enumerate() {
+        let cnum = (i + 1) as ast::CrateNum;
+        let data = sess.cstore.get_crate_data(cnum);
+        if !data.is_allocator() {
+            continue
+        }
+        if let Linkage::NotLinked = *linkage {
+            continue
+        }
+        if let Some(prev_alloc) = allocator {
+            let prev = sess.cstore.get_crate_data(prev_alloc);
+            sess.err(&format!("cannot link together two \
+                               allocators: {} and {}",
+                              prev.name(), data.name()));
+        }
+        allocator = Some(cnum);
     }
 }
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 49bf0e580b9..fefd7856045 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -47,7 +47,6 @@ use middle::check_const;
 use middle::const_eval::{self, ConstVal, ErrKind};
 use middle::const_eval::EvalHint::UncheckedExprHint;
 use middle::def::{self, DefMap, ExportMap};
-use middle::dependency_format;
 use middle::fast_reject;
 use middle::free_region::FreeRegionMap;
 use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
@@ -840,8 +839,6 @@ pub struct ctxt<'tcx> {
     pub extern_const_variants: RefCell<DefIdMap<ast::NodeId>>,
     pub extern_const_fns: RefCell<DefIdMap<ast::NodeId>>,
 
-    pub dependency_formats: RefCell<dependency_format::Dependencies>,
-
     pub node_lint_levels: RefCell<FnvHashMap<(ast::NodeId, lint::LintId),
                                               lint::LevelSource>>,
 
@@ -3837,7 +3834,6 @@ impl<'tcx> ctxt<'tcx> {
             extern_const_statics: RefCell::new(DefIdMap()),
             extern_const_variants: RefCell::new(DefIdMap()),
             extern_const_fns: RefCell::new(DefIdMap()),
-            dependency_formats: RefCell::new(FnvHashMap()),
             node_lint_levels: RefCell::new(FnvHashMap()),
             transmute_restrictions: RefCell::new(Vec::new()),
             stability: RefCell::new(stability),
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index ffb3a8ccb36..18982215f58 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -11,8 +11,9 @@
 use lint;
 use metadata::cstore::CStore;
 use metadata::filesearch;
+use middle::dependency_format;
 use session::search_paths::PathKind;
-use util::nodemap::NodeMap;
+use util::nodemap::{NodeMap, FnvHashMap};
 
 use syntax::ast::NodeId;
 use syntax::codemap::Span;
@@ -57,6 +58,7 @@ pub struct Session {
     pub plugin_llvm_passes: RefCell<Vec<String>>,
     pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
     pub crate_types: RefCell<Vec<config::CrateType>>,
+    pub dependency_formats: RefCell<dependency_format::Dependencies>,
     pub crate_metadata: RefCell<Vec<String>>,
     pub features: RefCell<feature_gate::Features>,
 
@@ -68,7 +70,11 @@ pub struct Session {
 
     pub can_print_warnings: bool,
 
-    next_node_id: Cell<ast::NodeId>
+    /// The metadata::creader module may inject an allocator dependency if it
+    /// didn't already find one, and this tracks what was injected.
+    pub injected_allocator: Cell<Option<ast::CrateNum>>,
+
+    next_node_id: Cell<ast::NodeId>,
 }
 
 impl Session {
@@ -447,12 +453,14 @@ pub fn build_session_(sopts: config::Options,
         plugin_llvm_passes: RefCell::new(Vec::new()),
         plugin_attributes: RefCell::new(Vec::new()),
         crate_types: RefCell::new(Vec::new()),
+        dependency_formats: RefCell::new(FnvHashMap()),
         crate_metadata: RefCell::new(Vec::new()),
         delayed_span_bug: RefCell::new(None),
         features: RefCell::new(feature_gate::Features::new()),
         recursion_limit: Cell::new(64),
         can_print_warnings: can_print_warnings,
-        next_node_id: Cell::new(1)
+        next_node_id: Cell::new(1),
+        injected_allocator: Cell::new(None),
     };
 
     sess
diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs
index beb95697201..2314368177c 100644
--- a/src/librustc/util/common.rs
+++ b/src/librustc/util/common.rs
@@ -32,11 +32,11 @@ pub const FN_OUTPUT_NAME: &'static str = "Output";
 #[derive(Clone, Copy, Debug)]
 pub struct ErrorReported;
 
-pub fn time<T, U, F>(do_it: bool, what: &str, u: U, f: F) -> T where
-    F: FnOnce(U) -> T,
+pub fn time<T, F>(do_it: bool, what: &str, f: F) -> T where
+    F: FnOnce() -> T,
 {
     thread_local!(static DEPTH: Cell<usize> = Cell::new(0));
-    if !do_it { return f(u); }
+    if !do_it { return f(); }
 
     let old = DEPTH.with(|slot| {
         let r = slot.get();
@@ -49,7 +49,7 @@ pub fn time<T, U, F>(do_it: bool, what: &str, u: U, f: F) -> T where
         let ref mut rvp = rv;
 
         Duration::span(move || {
-            *rvp = Some(f(u))
+            *rvp = Some(f())
         })
     };
     let rv = rv.unwrap();
diff --git a/src/librustc_back/target/apple_base.rs b/src/librustc_back/target/apple_base.rs
index a1cbf838d0a..0c4e28d9700 100644
--- a/src/librustc_back/target/apple_base.rs
+++ b/src/librustc_back/target/apple_base.rs
@@ -24,6 +24,7 @@ pub fn opts() -> TargetOptions {
         dll_suffix: ".dylib".to_string(),
         archive_format: "bsd".to_string(),
         pre_link_args: Vec::new(),
+        exe_allocation_crate: super::best_allocator(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs
index 9e163f7b64f..96680dc3759 100644
--- a/src/librustc_back/target/bitrig_base.rs
+++ b/src/librustc_back/target/bitrig_base.rs
@@ -20,6 +20,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         position_independent_executables: true,
         archive_format: "gnu".to_string(),
+        exe_allocation_crate: super::best_allocator(),
 
         .. Default::default()
     }
diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs
index 422aabd8c98..736035c5389 100644
--- a/src/librustc_back/target/dragonfly_base.rs
+++ b/src/librustc_back/target/dragonfly_base.rs
@@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions {
         ),
         position_independent_executables: true,
         archive_format: "bsd".to_string(),
+        exe_allocation_crate: super::best_allocator(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs
index 28512ba08fa..a5807d2787f 100644
--- a/src/librustc_back/target/freebsd_base.rs
+++ b/src/librustc_back/target/freebsd_base.rs
@@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions {
         executables: true,
         has_rpath: true,
         archive_format: "gnu".to_string(),
+        exe_allocation_crate: super::best_allocator(),
 
         .. Default::default()
     }
diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs
index a70cbc5797c..d6b50a955b6 100644
--- a/src/librustc_back/target/linux_base.rs
+++ b/src/librustc_back/target/linux_base.rs
@@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions {
         ],
         position_independent_executables: true,
         archive_format: "gnu".to_string(),
+        exe_allocation_crate: super::best_allocator(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index 7ca46a1d169..d9cfdaacc90 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -173,6 +173,10 @@ pub struct TargetOptions {
     /// defined in libgcc.  If this option is enabled, the target must provide
     /// `eh_unwind_resume` lang item.
     pub custom_unwind_resume: bool,
+
+    /// Default crate for allocation symbols to link against
+    pub lib_allocation_crate: String,
+    pub exe_allocation_crate: String,
 }
 
 impl Default for TargetOptions {
@@ -211,6 +215,8 @@ impl Default for TargetOptions {
             post_link_objects: Vec::new(),
             archive_format: String::new(),
             custom_unwind_resume: false,
+            lib_allocation_crate: "alloc_system".to_string(),
+            exe_allocation_crate: "alloc_system".to_string(),
         }
     }
 }
@@ -424,3 +430,11 @@ impl Target {
         Err(format!("Could not find specification for target {:?}", target))
     }
 }
+
+fn best_allocator() -> String {
+    if cfg!(disable_jemalloc) {
+        "alloc_system".to_string()
+    } else {
+        "alloc_jemalloc".to_string()
+    }
+}
diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs
index 361f71f6995..3b02111d934 100644
--- a/src/librustc_back/target/openbsd_base.rs
+++ b/src/librustc_back/target/openbsd_base.rs
@@ -27,6 +27,7 @@ pub fn opts() -> TargetOptions {
         ),
         position_independent_executables: true,
         archive_format: "gnu".to_string(),
+        exe_allocation_crate: super::best_allocator(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs
index d2217f902ea..fedae51e0e8 100644
--- a/src/librustc_back/target/windows_base.rs
+++ b/src/librustc_back/target/windows_base.rs
@@ -60,6 +60,7 @@ pub fn opts() -> TargetOptions {
             // Always enable DEP (NX bit) when it is available
             "-Wl,--nxcompat".to_string(),
         ),
+        exe_allocation_crate: super::best_allocator(),
 
         .. Default::default()
     }
diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs
index 2466c254e05..fe9ac32ee8f 100644
--- a/src/librustc_back/target/windows_msvc_base.rs
+++ b/src/librustc_back/target/windows_msvc_base.rs
@@ -60,6 +60,7 @@ pub fn opts() -> TargetOptions {
             "/NXCOMPAT".to_string(),
         ],
         archive_format: "gnu".to_string(),
+        exe_allocation_crate: super::best_allocator(),
 
         .. Default::default()
     }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 80db6426917..263a8e14807 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -356,7 +356,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
     syntax::ext::mtwt::reset_tables();
     token::reset_ident_interner();
 
-    let krate = time(sess.time_passes(), "parsing", (), |_| {
+    let krate = time(sess.time_passes(), "parsing", || {
         match *input {
             Input::File(ref file) => {
                 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
@@ -406,7 +406,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     //
     // baz! should not use this definition unless foo is enabled.
 
-    krate = time(time_passes, "configuration 1", krate, |krate|
+    krate = time(time_passes, "configuration 1", move ||
                  syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
 
     *sess.crate_types.borrow_mut() =
@@ -414,11 +414,11 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     *sess.crate_metadata.borrow_mut() =
         collect_crate_metadata(sess, &krate.attrs);
 
-    time(time_passes, "recursion limit", (), |_| {
+    time(time_passes, "recursion limit", || {
         middle::recursion_limit::update_recursion_limit(sess, &krate);
     });
 
-    time(time_passes, "gated macro checking", (), |_| {
+    time(time_passes, "gated macro checking", || {
         let features =
             syntax::feature_gate::check_crate_macros(sess.codemap(),
                                                      &sess.parse_sess.span_diagnostic,
@@ -430,20 +430,20 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     });
 
 
-    krate = time(time_passes, "crate injection", krate, |krate|
+    krate = time(time_passes, "crate injection", ||
                  syntax::std_inject::maybe_inject_crates_ref(krate,
                                                              sess.opts.alt_std_name.clone()));
 
-    let macros = time(time_passes, "macro loading", (), |_|
+    let macros = time(time_passes, "macro loading", ||
         metadata::macro_import::read_macro_defs(sess, &krate));
 
     let mut addl_plugins = Some(addl_plugins);
-    let registrars = time(time_passes, "plugin loading", (), |_|
+    let registrars = time(time_passes, "plugin loading", ||
         plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap()));
 
     let mut registry = Registry::new(sess, &krate);
 
-    time(time_passes, "plugin registration", registrars, |registrars| {
+    time(time_passes, "plugin registration", || {
         if sess.features.borrow().rustc_diagnostic_macros {
             registry.register_macro("__diagnostic_used",
                 diagnostics::plugin::expand_diagnostic_used);
@@ -486,45 +486,43 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // Abort if there are errors from lint processing or a plugin registrar.
     sess.abort_if_errors();
 
-    krate = time(time_passes, "expansion", (krate, macros, syntax_exts),
-        |(krate, macros, syntax_exts)| {
-            // Windows dlls do not have rpaths, so they don't know how to find their
-            // dependencies. It's up to us to tell the system where to find all the
-            // dependent dlls. Note that this uses cfg!(windows) as opposed to
-            // targ_cfg because syntax extensions are always loaded for the host
-            // compiler, not for the target.
-            let mut _old_path = OsString::new();
-            if cfg!(windows) {
-                _old_path = env::var_os("PATH").unwrap_or(_old_path);
-                let mut new_path = sess.host_filesearch(PathKind::All)
-                                       .get_dylib_search_paths();
-                new_path.extend(env::split_paths(&_old_path));
-                env::set_var("PATH", &env::join_paths(new_path).unwrap());
-            }
-            let features = sess.features.borrow();
-            let cfg = syntax::ext::expand::ExpansionConfig {
-                crate_name: crate_name.to_string(),
-                features: Some(&features),
-                recursion_limit: sess.recursion_limit.get(),
-                trace_mac: sess.opts.debugging_opts.trace_macros,
-            };
-            let ret = syntax::ext::expand::expand_crate(&sess.parse_sess,
-                                              cfg,
-                                              macros,
-                                              syntax_exts,
-                                              krate);
-            if cfg!(windows) {
-                env::set_var("PATH", &_old_path);
-            }
-            ret
+    krate = time(time_passes, "expansion", || {
+        // Windows dlls do not have rpaths, so they don't know how to find their
+        // dependencies. It's up to us to tell the system where to find all the
+        // dependent dlls. Note that this uses cfg!(windows) as opposed to
+        // targ_cfg because syntax extensions are always loaded for the host
+        // compiler, not for the target.
+        let mut _old_path = OsString::new();
+        if cfg!(windows) {
+            _old_path = env::var_os("PATH").unwrap_or(_old_path);
+            let mut new_path = sess.host_filesearch(PathKind::All)
+                                   .get_dylib_search_paths();
+            new_path.extend(env::split_paths(&_old_path));
+            env::set_var("PATH", &env::join_paths(new_path).unwrap());
         }
-    );
+        let features = sess.features.borrow();
+        let cfg = syntax::ext::expand::ExpansionConfig {
+            crate_name: crate_name.to_string(),
+            features: Some(&features),
+            recursion_limit: sess.recursion_limit.get(),
+            trace_mac: sess.opts.debugging_opts.trace_macros,
+        };
+        let ret = syntax::ext::expand::expand_crate(&sess.parse_sess,
+                                          cfg,
+                                          macros,
+                                          syntax_exts,
+                                          krate);
+        if cfg!(windows) {
+            env::set_var("PATH", &_old_path);
+        }
+        ret
+    });
 
     // Needs to go *after* expansion to be able to check the results
     // of macro expansion.  This runs before #[cfg] to try to catch as
     // much as possible (e.g. help the programmer avoid platform
     // specific differences)
-    time(time_passes, "complete gated feature checking 1", (), |_| {
+    time(time_passes, "complete gated feature checking 1", || {
         let features =
             syntax::feature_gate::check_crate(sess.codemap(),
                                               &sess.parse_sess.span_diagnostic,
@@ -537,25 +535,25 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // JBC: make CFG processing part of expansion to avoid this problem:
 
     // strip again, in case expansion added anything with a #[cfg].
-    krate = time(time_passes, "configuration 2", krate, |krate|
+    krate = time(time_passes, "configuration 2", ||
                  syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
 
-    krate = time(time_passes, "maybe building test harness", krate, |krate|
+    krate = time(time_passes, "maybe building test harness", ||
                  syntax::test::modify_for_testing(&sess.parse_sess,
                                                   &sess.opts.cfg,
                                                   krate,
                                                   sess.diagnostic()));
 
-    krate = time(time_passes, "prelude injection", krate, |krate|
+    krate = time(time_passes, "prelude injection", ||
                  syntax::std_inject::maybe_inject_prelude(&sess.parse_sess, krate));
 
-    time(time_passes, "checking that all macro invocations are gone", &krate, |krate|
-         syntax::ext::expand::check_for_macros(&sess.parse_sess, krate));
+    time(time_passes, "checking that all macro invocations are gone", ||
+         syntax::ext::expand::check_for_macros(&sess.parse_sess, &krate));
 
     // One final feature gating of the true AST that gets compiled
     // later, to make sure we've got everything (e.g. configuration
     // can insert new attributes via `cfg_attr`)
-    time(time_passes, "complete gated feature checking 2", (), |_| {
+    time(time_passes, "complete gated feature checking 2", || {
         let features =
             syntax::feature_gate::check_crate(sess.codemap(),
                                               &sess.parse_sess.span_diagnostic,
@@ -582,7 +580,7 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session,
         }
     }
 
-    let map = time(sess.time_passes(), "assigning node ids and indexing ast", forest, |forest|
+    let map = time(sess.time_passes(), "assigning node ids and indexing ast", move ||
                    ast_map::map_crate(forest, NodeIdAssigner { sess: sess }));
 
     if sess.opts.debugging_opts.ast_json {
@@ -608,10 +606,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
     let time_passes = sess.time_passes();
     let krate = ast_map.krate();
 
-    time(time_passes, "external crate/lib resolution", (), |_|
+    time(time_passes, "external crate/lib resolution", ||
          LocalCrateReader::new(&sess, &ast_map).read_crates(krate));
 
-    let lang_items = time(time_passes, "language item collection", (), |_|
+    let lang_items = time(time_passes, "language item collection", ||
                           middle::lang_items::collect_language_items(krate, &sess));
 
     let resolve::CrateMap {
@@ -622,30 +620,30 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
         external_exports,
         glob_map,
     } =
-        time(time_passes, "resolution", (),
-             |_| resolve::resolve_crate(&sess, &ast_map, make_glob_map));
+        time(time_passes, "resolution",
+             || resolve::resolve_crate(&sess, &ast_map, make_glob_map));
 
     // Discard MTWT tables that aren't required past resolution.
     syntax::ext::mtwt::clear_tables();
 
-    let named_region_map = time(time_passes, "lifetime resolution", (),
-                                |_| middle::resolve_lifetime::krate(&sess, krate, &def_map));
+    let named_region_map = time(time_passes, "lifetime resolution",
+                                || middle::resolve_lifetime::krate(&sess, krate, &def_map));
 
-    time(time_passes, "looking for entry point", (),
-         |_| middle::entry::find_entry_point(&sess, &ast_map));
+    time(time_passes, "looking for entry point",
+         || middle::entry::find_entry_point(&sess, &ast_map));
 
     sess.plugin_registrar_fn.set(
-        time(time_passes, "looking for plugin registrar", (), |_|
+        time(time_passes, "looking for plugin registrar", ||
             plugin::build::find_plugin_registrar(
                 sess.diagnostic(), krate)));
 
-    let region_map = time(time_passes, "region resolution", (), |_|
+    let region_map = time(time_passes, "region resolution", ||
                           middle::region::resolve_crate(&sess, krate));
 
-    time(time_passes, "loop checking", (), |_|
+    time(time_passes, "loop checking", ||
          middle::check_loop::check_crate(&sess, krate));
 
-    time(time_passes, "static item recursion checking", (), |_|
+    time(time_passes, "static item recursion checking", ||
          middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map));
 
     ty::ctxt::create_and_enter(sess,
@@ -662,33 +660,33 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
         // passes are timed inside typeck
         typeck::check_crate(tcx, trait_map);
 
-        time(time_passes, "const checking", (), |_|
+        time(time_passes, "const checking", ||
             middle::check_const::check_crate(tcx));
 
         let (exported_items, public_items) =
-                time(time_passes, "privacy checking", (), |_|
+                time(time_passes, "privacy checking", ||
                     rustc_privacy::check_crate(tcx, &export_map, external_exports));
 
         // Do not move this check past lint
-        time(time_passes, "stability index", (), |_|
+        time(time_passes, "stability index", ||
             tcx.stability.borrow_mut().build(tcx, krate, &public_items));
 
-        time(time_passes, "intrinsic checking", (), |_|
+        time(time_passes, "intrinsic checking", ||
             middle::intrinsicck::check_crate(tcx));
 
-        time(time_passes, "effect checking", (), |_|
+        time(time_passes, "effect checking", ||
             middle::effect::check_crate(tcx));
 
-        time(time_passes, "match checking", (), |_|
+        time(time_passes, "match checking", ||
             middle::check_match::check_crate(tcx));
 
-        time(time_passes, "liveness checking", (), |_|
+        time(time_passes, "liveness checking", ||
             middle::liveness::check_crate(tcx));
 
-        time(time_passes, "borrow checking", (), |_|
+        time(time_passes, "borrow checking", ||
             borrowck::check_crate(tcx));
 
-        time(time_passes, "rvalue checking", (), |_|
+        time(time_passes, "rvalue checking", ||
             middle::check_rvalues::check_crate(tcx, krate));
 
         // Avoid overwhelming user with errors if type checking failed.
@@ -699,24 +697,24 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
         tcx.sess.abort_if_errors();
 
         let reachable_map =
-            time(time_passes, "reachability checking", (), |_|
+            time(time_passes, "reachability checking", ||
                 reachable::find_reachable(tcx, &exported_items));
 
-        time(time_passes, "death checking", (), |_| {
+        time(time_passes, "death checking", || {
             middle::dead::check_crate(tcx,
                                       &exported_items,
                                       &reachable_map)
         });
 
         let ref lib_features_used =
-            time(time_passes, "stability checking", (), |_|
+            time(time_passes, "stability checking", ||
                 stability::check_unstable_api_usage(tcx));
 
-        time(time_passes, "unused lib feature checking", (), |_|
+        time(time_passes, "unused lib feature checking", ||
             stability::check_unused_or_stable_features(
                 &tcx.sess, lib_features_used));
 
-        time(time_passes, "lint checking", (), |_|
+        time(time_passes, "lint checking", ||
             lint::check_crate(tcx, &exported_items));
 
         // The above three passes generate errors w/o aborting
@@ -739,11 +737,11 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis)
                                  -> trans::CrateTranslation {
     let time_passes = tcx.sess.time_passes();
 
-    time(time_passes, "resolving dependency formats", (), |_|
-         dependency_format::calculate(tcx));
+    time(time_passes, "resolving dependency formats", ||
+         dependency_format::calculate(&tcx.sess));
 
     // Option dance to work around the lack of stack once closures.
-    time(time_passes, "translation", analysis, |analysis|
+    time(time_passes, "translation", move ||
          trans::trans_crate(tcx, analysis))
 }
 
@@ -755,7 +753,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
     if sess.opts.cg.no_integrated_as {
         let output_type = config::OutputTypeAssembly;
 
-        time(sess.time_passes(), "LLVM passes", (), |_|
+        time(sess.time_passes(), "LLVM passes", ||
             write::run_passes(sess, trans, &[output_type], outputs));
 
         write::run_assembler(sess, outputs);
@@ -765,7 +763,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
             fs::remove_file(&outputs.temp_path(config::OutputTypeAssembly)).unwrap();
         }
     } else {
-        time(sess.time_passes(), "LLVM passes", (), |_|
+        time(sess.time_passes(), "LLVM passes", ||
             write::run_passes(sess,
                               trans,
                               &sess.opts.output_types,
@@ -780,7 +778,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
 pub fn phase_6_link_output(sess: &Session,
                            trans: &trans::CrateTranslation,
                            outputs: &OutputFilenames) {
-    time(sess.time_passes(), "linking", (), |_|
+    time(sess.time_passes(), "linking", ||
          link::link_binary(sess,
                            trans,
                            outputs,
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 230307f0b19..5bb013c5f96 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -383,10 +383,10 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
         if sess.opts.debugging_opts.save_analysis {
             control.after_analysis.callback = box |state| {
                 time(state.session.time_passes(),
-                     "save analysis", (),
-                     |_| save::process_crate(state.tcx.unwrap(),
-                                             state.analysis.unwrap(),
-                                             state.out_dir));
+                     "save analysis",
+                     || save::process_crate(state.tcx.unwrap(),
+                                            state.analysis.unwrap(),
+                                            state.out_dir));
             };
             control.make_glob_map = resolve::MakeGlobMap::Yes;
         }
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 46c7b80670f..91c0edb901f 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -23,6 +23,7 @@ use metadata::common::LinkMeta;
 use metadata::filesearch::FileDoesntMatch;
 use metadata::loader::METADATA_FILENAME;
 use metadata::{encoder, cstore, filesearch, csearch, creader};
+use middle::dependency_format::Linkage;
 use middle::ty::{self, Ty};
 use rustc::ast_map::{PathElem, PathElems, PathName};
 use trans::{CrateContext, CrateTranslation, gensym_name};
@@ -493,6 +494,31 @@ pub fn filename_for_input(sess: &Session,
     }
 }
 
+pub fn each_linked_rlib(sess: &Session,
+                        f: &mut FnMut(ast::CrateNum, &Path)) {
+    let crates = sess.cstore.get_used_crates(cstore::RequireStatic).into_iter();
+    let fmts = sess.dependency_formats.borrow();
+    let fmts = fmts.get(&config::CrateTypeExecutable).or_else(|| {
+        fmts.get(&config::CrateTypeStaticlib)
+    }).unwrap_or_else(|| {
+        sess.bug("could not find formats for rlibs")
+    });
+    for (cnum, path) in crates {
+        match fmts[cnum as usize - 1] {
+            Linkage::NotLinked | Linkage::IncludedFromDylib => continue,
+            _ => {}
+        }
+        let name = sess.cstore.get_crate_data(cnum).name.clone();
+        let path = match path {
+            Some(p) => p,
+            None => {
+                sess.fatal(&format!("could not find rlib for: `{}`", name));
+            }
+        };
+        f(cnum, &path);
+    }
+}
+
 fn link_binary_output(sess: &Session,
                       trans: &CrateTranslation,
                       crate_type: config::CrateType,
@@ -524,11 +550,11 @@ fn link_binary_output(sess: &Session,
             link_staticlib(sess, &objects, &out_filename, tmpdir.path());
         }
         config::CrateTypeExecutable => {
-            link_natively(sess, trans, false, &objects, &out_filename, outputs,
+            link_natively(sess, false, &objects, &out_filename, trans, outputs,
                           tmpdir.path());
         }
         config::CrateTypeDylib => {
-            link_natively(sess, trans, true, &objects, &out_filename, outputs,
+            link_natively(sess, true, &objects, &out_filename, trans, outputs,
                           tmpdir.path());
         }
     }
@@ -763,23 +789,15 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
         ab.add_native_library("compiler-rt").unwrap();
     }
 
-    let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
     let mut all_native_libs = vec![];
 
-    for &(cnum, ref path) in &crates {
-        let ref name = sess.cstore.get_crate_data(cnum).name;
-        let p = match *path {
-            Some(ref p) => p.clone(), None => {
-                sess.err(&format!("could not find rlib for: `{}`",
-                                 name));
-                continue
-            }
-        };
-        ab.add_rlib(&p, &name[..], sess.lto()).unwrap();
+    each_linked_rlib(sess, &mut |cnum, path| {
+        let name = sess.cstore.get_crate_data(cnum).name();
+        ab.add_rlib(path, &name, sess.lto()).unwrap();
 
         let native_libs = csearch::get_native_libraries(&sess.cstore, cnum);
         all_native_libs.extend(native_libs);
-    }
+    });
 
     ab.update_symbols();
     ab.build();
@@ -805,8 +823,9 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
 //
 // This will invoke the system linker/cc to create the resulting file. This
 // links to all upstream files as well.
-fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
+fn link_natively(sess: &Session, dylib: bool,
                  objects: &[PathBuf], out_filename: &Path,
+                 trans: &CrateTranslation,
                  outputs: &OutputFilenames,
                  tmpdir: &Path) {
     info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects,
@@ -829,7 +848,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
             Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
         };
         link_args(&mut *linker, sess, dylib, tmpdir,
-                  trans, objects, out_filename, outputs);
+                  objects, out_filename, trans, outputs);
         if !sess.target.target.options.no_compiler_rt {
             linker.link_staticlib("compiler-rt");
         }
@@ -848,7 +867,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
 
     // Invoke the system linker
     info!("{:?}", &cmd);
-    let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output());
+    let prog = time(sess.time_passes(), "running linker", || cmd.output());
     match prog {
         Ok(prog) => {
             if !prog.status.success() {
@@ -884,9 +903,9 @@ fn link_args(cmd: &mut Linker,
              sess: &Session,
              dylib: bool,
              tmpdir: &Path,
-             trans: &CrateTranslation,
              objects: &[PathBuf],
              out_filename: &Path,
+             trans: &CrateTranslation,
              outputs: &OutputFilenames) {
 
     // The default library location, we need this to find the runtime.
@@ -980,7 +999,7 @@ fn link_args(cmd: &mut Linker,
     // this kind of behavior is pretty platform specific and generally not
     // recommended anyway, so I don't think we're shooting ourself in the foot
     // much with that.
-    add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans);
+    add_upstream_rust_crates(cmd, sess, dylib, tmpdir);
     add_local_native_libraries(cmd, sess);
     add_upstream_native_libraries(cmd, sess);
 
@@ -1086,8 +1105,7 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
 // dependencies will be linked when producing the final output (instead of
 // the intermediate rlib version)
 fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
-                            dylib: bool, tmpdir: &Path,
-                            trans: &CrateTranslation) {
+                            dylib: bool, tmpdir: &Path) {
     // All of the heavy lifting has previously been accomplished by the
     // dependency_format module of the compiler. This is just crawling the
     // output of that module, adding crates as necessary.
@@ -1096,10 +1114,11 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
     // will slurp up the object files inside), and linking to a dynamic library
     // involves just passing the right -l flag.
 
+    let formats = sess.dependency_formats.borrow();
     let data = if dylib {
-        trans.crate_formats.get(&config::CrateTypeDylib).unwrap()
+        formats.get(&config::CrateTypeDylib).unwrap()
     } else {
-        trans.crate_formats.get(&config::CrateTypeExecutable).unwrap()
+        formats.get(&config::CrateTypeExecutable).unwrap()
     };
 
     // Invoke get_used_crates to ensure that we get a topological sorting of
@@ -1110,20 +1129,17 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
         // We may not pass all crates through to the linker. Some crates may
         // appear statically in an existing dylib, meaning we'll pick up all the
         // symbols from the dylib.
-        let kind = match data[cnum as usize - 1] {
-            Some(t) => t,
-            None => continue
-        };
         let src = sess.cstore.get_used_crate_source(cnum).unwrap();
-        match kind {
-            cstore::RequireDynamic => {
-                add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0)
-            }
-            cstore::RequireStatic => {
+        match data[cnum as usize - 1] {
+            Linkage::NotLinked |
+            Linkage::IncludedFromDylib => {}
+            Linkage::Static => {
                 add_static_crate(cmd, sess, tmpdir, dylib, &src.rlib.unwrap().0)
             }
+            Linkage::Dynamic => {
+                add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0)
+            }
         }
-
     }
 
     // Converts a library file-stem into a cc -l argument
@@ -1174,7 +1190,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
         let name = cratepath.file_name().unwrap().to_str().unwrap();
         let name = &name[3..name.len() - 5]; // chop off lib/.rlib
 
-        time(sess.time_passes(), &format!("altering {}.rlib", name), (), |()| {
+        time(sess.time_passes(), &format!("altering {}.rlib", name), || {
             let cfg = archive_config(sess, &dst, Some(cratepath));
             let mut archive = ArchiveBuilder::new(cfg);
             archive.remove_file(METADATA_FILENAME);
diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs
index 8bd86a3a34a..a4333dc10d6 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -17,7 +17,7 @@ use std::process::Command;
 
 use back::archive;
 use metadata::csearch;
-use metadata::cstore;
+use middle::dependency_format::Linkage;
 use session::Session;
 use session::config::DebugInfoLevel::{NoDebugInfo, LimitedDebugInfo, FullDebugInfo};
 use session::config::CrateTypeDylib;
@@ -347,9 +347,10 @@ impl<'a> Linker for MsvcLinker<'a> {
             // dynamic library. For all statically linked libraries we take all
             // their reachable symbols and emit them as well.
             let cstore = &sess.cstore;
-            let symbols = trans.crate_formats[&CrateTypeDylib].iter();
+            let formats = sess.dependency_formats.borrow();
+            let symbols = formats[&CrateTypeDylib].iter();
             let symbols = symbols.enumerate().filter_map(|(i, f)| {
-                if let Some(cstore::RequireStatic) = *f {
+                if *f == Linkage::Static {
                     Some((i + 1) as ast::CrateNum)
                 } else {
                     None
diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs
index 518add44b4c..00439c1fd58 100644
--- a/src/librustc_trans/back/lto.rs
+++ b/src/librustc_trans/back/lto.rs
@@ -14,7 +14,6 @@ use rustc::session::{self, config};
 use llvm;
 use llvm::archive_ro::ArchiveRO;
 use llvm::{ModuleRef, TargetMachineRef, True, False};
-use rustc::metadata::cstore;
 use rustc::util::common::time;
 use back::write::{ModuleConfig, with_llvm_pmb};
 
@@ -46,17 +45,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
     // For each of our upstream dependencies, find the corresponding rlib and
     // load the bitcode from the archive. Then merge it into the current LLVM
     // module that we've got.
-    let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
-    for (cnum, path) in crates {
-        let name = sess.cstore.get_crate_data(cnum).name.clone();
-        let path = match path {
-            Some(p) => p,
-            None => {
-                sess.fatal(&format!("could not find rlib for: `{}`",
-                                   name));
-            }
-        };
-
+    link::each_linked_rlib(sess, &mut |_, path| {
         let archive = ArchiveRO::open(&path).expect("wanted an rlib");
         let bytecodes = archive.iter().filter_map(|child| {
             child.name().map(|name| (name, child))
@@ -65,7 +54,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
             let bc_encoded = data.data();
 
             let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
-                time(sess.time_passes(), &format!("decode {}", name), (), |_| {
+                time(sess.time_passes(), &format!("decode {}", name), || {
                     // Read the version
                     let version = extract_bytecode_format_version(bc_encoded);
 
@@ -89,9 +78,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
                     }
                 })
             } else {
-                time(sess.time_passes(), &format!("decode {}", name), (), |_| {
-                // the object must be in the old, pre-versioning format, so simply
-                // inflate everything and let LLVM decide if it can make sense of it
+                time(sess.time_passes(), &format!("decode {}", name), || {
+                    // the object must be in the old, pre-versioning format, so
+                    // simply inflate everything and let LLVM decide if it can
+                    // make sense of it
                     match flate::inflate_bytes(bc_encoded) {
                         Ok(bc) => bc,
                         Err(_) => {
@@ -104,8 +94,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
 
             let ptr = bc_decoded.as_ptr();
             debug!("linking {}", name);
-            time(sess.time_passes(), &format!("ll link {}", name), (),
-                 |()| unsafe {
+            time(sess.time_passes(), &format!("ll link {}", name), || unsafe {
                 if !llvm::LLVMRustLinkInExternalBitcode(llmod,
                                                         ptr as *const libc::c_char,
                                                         bc_decoded.len() as libc::size_t) {
@@ -115,7 +104,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
                 }
             });
         }
-    }
+    });
 
     // Internalize everything but the reachable symbols of the current module
     let cstrs: Vec<CString> = reachable.iter().map(|s| {
@@ -154,7 +143,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
 
         llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
 
-        time(sess.time_passes(), "LTO passes", (), |()|
+        time(sess.time_passes(), "LTO passes", ||
              llvm::LLVMRunPassManager(pm, llmod));
 
         llvm::LLVMDisposePassManager(pm);
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index d1fb0c2e8d7..d75f2fcf126 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -484,9 +484,9 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         cgcx.handler.abort_if_errors();
 
         // Finally, run the actual optimization passes
-        time(config.time_passes, "llvm function passes", (), |()|
+        time(config.time_passes, "llvm function passes", ||
              llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
-        time(config.time_passes, "llvm module passes", (), |()|
+        time(config.time_passes, "llvm module passes", ||
              llvm::LLVMRunPassManager(mpm, llmod));
 
         // Deallocate managers that we're now done with
@@ -495,7 +495,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
 
         match cgcx.lto_ctxt {
             Some((sess, reachable)) if sess.lto() =>  {
-                time(sess.time_passes(), "all lto passes", (), |()|
+                time(sess.time_passes(), "all lto passes", ||
                      lto::run(sess, llmod, tm, reachable, &config));
 
                 if config.emit_lto_bc {
@@ -536,7 +536,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
     }
 
-    time(config.time_passes, "codegen passes", (), |()| {
+    time(config.time_passes, "codegen passes", || {
         if config.emit_ir {
             let ext = format!("{}.ll", name_extra);
             let out = output_names.with_extension(&ext);
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 947c902b2a9..1b4e14d7ad7 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -2822,7 +2822,6 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
         llcx: shared_ccx.metadata_llcx(),
         llmod: shared_ccx.metadata_llmod(),
     };
-    let formats = shared_ccx.tcx().dependency_formats.borrow().clone();
     let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
 
     CrateTranslation {
@@ -2831,7 +2830,6 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
         link: link_meta,
         metadata: metadata,
         reachable: reachable_symbols,
-        crate_formats: formats,
         no_builtins: no_builtins,
     }
 }
diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs
index 7d568ff90ea..04854501312 100644
--- a/src/librustc_trans/trans/mod.rs
+++ b/src/librustc_trans/trans/mod.rs
@@ -10,7 +10,6 @@
 
 use llvm::{ContextRef, ModuleRef};
 use metadata::common::LinkMeta;
-use middle::dependency_format;
 
 pub use self::base::trans_crate;
 pub use self::context::CrateContext;
@@ -74,6 +73,5 @@ pub struct CrateTranslation {
     pub link: LinkMeta,
     pub metadata: Vec<u8>,
     pub reachable: Vec<String>,
-    pub crate_formats: dependency_format::Dependencies,
     pub no_builtins: bool,
 }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index e6824c811c5..1eff224219e 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -332,34 +332,34 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) {
         tcx: tcx
     };
 
-    time(time_passes, "type collecting", (), |_|
+    time(time_passes, "type collecting", ||
          collect::collect_item_types(tcx));
 
     // this ensures that later parts of type checking can assume that items
     // have valid types and not error
     tcx.sess.abort_if_errors();
 
-    time(time_passes, "variance inference", (), |_|
+    time(time_passes, "variance inference", ||
          variance::infer_variance(tcx));
 
-    time(time_passes, "coherence checking", (), |_|
+    time(time_passes, "coherence checking", ||
         coherence::check_coherence(&ccx));
 
-    time(time_passes, "wf checking (old)", (), |_|
+    time(time_passes, "wf checking (old)", ||
         check::check_wf_old(&ccx));
 
-    time(time_passes, "item-types checking", (), |_|
+    time(time_passes, "item-types checking", ||
         check::check_item_types(&ccx));
 
-    time(time_passes, "item-bodies checking", (), |_|
+    time(time_passes, "item-bodies checking", ||
         check::check_item_bodies(&ccx));
 
-    time(time_passes, "drop-impl checking", (), |_|
+    time(time_passes, "drop-impl checking", ||
         check::check_drop_impls(&ccx));
 
     // Do this last so that if there are errors in the old code, they
     // get reported, and we don't get extra warnings.
-    time(time_passes, "wf checking (new)", (), |_|
+    time(time_passes, "wf checking (new)", ||
         check::check_wf_new(&ccx));
 
     check_for_entry_fn(&ccx);
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 77c634e8090..3dcb157233d 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -275,8 +275,6 @@ extern crate alloc;
 extern crate rustc_unicode;
 extern crate libc;
 
-#[macro_use] #[no_link] extern crate rustc_bitflags;
-
 // Make std testable by not duplicating lang items and other globals. See #2912
 #[cfg(test)] extern crate std as realstd;
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 87149ff81da..694a1a43f59 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -85,6 +85,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
     ("on_unimplemented", "1.0.0", Active),
     ("simd_ffi", "1.0.0", Active),
     ("allocator", "1.0.0", Active),
+    ("needs_allocator", "1.4.0", Active),
     ("linked_from", "1.3.0", Active),
 
     ("if_let", "1.0.0", Accepted),
@@ -253,6 +254,9 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
                                       is an experimental feature")),
     ("allocator", Gated("allocator",
                         "the `#[allocator]` attribute is an experimental feature")),
+    ("needs_allocator", Gated("needs_allocator", "the `#[needs_allocator]` \
+                                                  attribute is an experimental \
+                                                  feature")),
     ("rustc_variance", Gated("rustc_attrs",
                              "the `#[rustc_variance]` attribute \
                               is an experimental feature")),
diff --git a/src/test/auxiliary/allocator-dummy.rs b/src/test/auxiliary/allocator-dummy.rs
new file mode 100644
index 00000000000..0194fb1ddda
--- /dev/null
+++ b/src/test/auxiliary/allocator-dummy.rs
@@ -0,0 +1,55 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(allocator, no_std, core, core_intrinsics, libc)]
+#![allocator]
+#![crate_type = "rlib"]
+#![no_std]
+
+extern crate libc;
+
+pub static mut HITS: usize = 0;
+
+#[no_mangle]
+pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
+    unsafe {
+        HITS += 1;
+        libc::malloc(size as libc::size_t) as *mut u8
+    }
+}
+
+#[no_mangle]
+pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
+    unsafe {
+        HITS += 1;
+        libc::free(ptr as *mut _)
+    }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize,
+                                align: usize) -> *mut u8 {
+    unsafe {
+        libc::realloc(ptr as *mut _, size as libc::size_t) as *mut u8
+    }
+}
+
+#[no_mangle]
+pub extern fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize,
+                                        size: usize, align: usize) -> usize {
+    unsafe { core::intrinsics::abort() }
+}
+
+#[no_mangle]
+pub extern fn __rust_usable_size(size: usize, align: usize) -> usize {
+    unsafe { core::intrinsics::abort() }
+}
diff --git a/src/test/auxiliary/allocator-dylib.rs b/src/test/auxiliary/allocator-dylib.rs
new file mode 100644
index 00000000000..568b247ecdb
--- /dev/null
+++ b/src/test/auxiliary/allocator-dylib.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "dylib"]
+
+pub fn foo() {}
diff --git a/src/test/auxiliary/allocator-dylib2.rs b/src/test/auxiliary/allocator-dylib2.rs
new file mode 100644
index 00000000000..0d76c0e5eb8
--- /dev/null
+++ b/src/test/auxiliary/allocator-dylib2.rs
@@ -0,0 +1,12 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub fn foo() {}
+
diff --git a/src/test/auxiliary/allocator1.rs b/src/test/auxiliary/allocator1.rs
new file mode 100644
index 00000000000..4e9cef64348
--- /dev/null
+++ b/src/test/auxiliary/allocator1.rs
@@ -0,0 +1,16 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(allocator, no_std, core)]
+#![allocator]
+#![crate_type = "rlib"]
+#![no_std]
diff --git a/src/test/auxiliary/allocator2.rs b/src/test/auxiliary/allocator2.rs
new file mode 100644
index 00000000000..4e9cef64348
--- /dev/null
+++ b/src/test/auxiliary/allocator2.rs
@@ -0,0 +1,16 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(allocator, no_std, core)]
+#![allocator]
+#![crate_type = "rlib"]
+#![no_std]
diff --git a/src/test/auxiliary/allocator3.rs b/src/test/auxiliary/allocator3.rs
new file mode 100644
index 00000000000..1ee21bc7ddb
--- /dev/null
+++ b/src/test/auxiliary/allocator3.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(no_std, allocator)]
+#![no_std]
+#![allocator]
+#![crate_type = "rlib"]
+
+extern crate needs_allocator;
+
diff --git a/src/test/auxiliary/needs_allocator.rs b/src/test/auxiliary/needs_allocator.rs
new file mode 100644
index 00000000000..c09b153d921
--- /dev/null
+++ b/src/test/auxiliary/needs_allocator.rs
@@ -0,0 +1,16 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(no_std, needs_allocator)]
+#![no_std]
+#![needs_allocator]
+#![crate_type = "rlib"]
diff --git a/src/test/compile-fail/allocator-depends-on-needs-allocators.rs b/src/test/compile-fail/allocator-depends-on-needs-allocators.rs
new file mode 100644
index 00000000000..7f420ff735a
--- /dev/null
+++ b/src/test/compile-fail/allocator-depends-on-needs-allocators.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern: `allocator3` cannot depend on a crate that needs an allocator
+// aux-build:needs_allocator.rs
+// aux-build:allocator3.rs
+
+// The needs_allocator crate is a dependency of the allocator crate allocator3,
+// which is not allowed
+
+extern crate allocator3;
+
+fn main() {
+}
diff --git a/src/test/compile-fail/allocator-dylib-is-system.rs b/src/test/compile-fail/allocator-dylib-is-system.rs
new file mode 100644
index 00000000000..8fad2af42b9
--- /dev/null
+++ b/src/test/compile-fail/allocator-dylib-is-system.rs
@@ -0,0 +1,27 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-msvc everything is the system allocator on msvc
+// aux-build:allocator-dylib.rs
+// no-prefer-dynamic
+// error-pattern: cannot link together two allocators
+
+// Verify that the allocator for statically linked dynamic libraries is the
+// system allocator. Do this by linking in jemalloc and making sure that we get
+// an error.
+
+#![feature(alloc_jemalloc)]
+
+extern crate allocator_dylib;
+extern crate alloc_jemalloc;
+
+fn main() {
+    allocator_dylib::foo();
+}
diff --git a/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs
new file mode 100644
index 00000000000..224d9379ee1
--- /dev/null
+++ b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs
@@ -0,0 +1,26 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-msvc everything is the system allocator on msvc
+// aux-build:allocator-dylib2.rs
+// error-pattern: cannot link together two allocators
+
+// Ensure that rust dynamic libraries use jemalloc as their allocator, verifying
+// by linking in the system allocator here and ensuring that we get a complaint.
+
+#![feature(alloc_system)]
+
+extern crate allocator_dylib2;
+extern crate alloc_system;
+
+fn main() {
+    allocator_dylib2::foo();
+}
+
diff --git a/src/test/compile-fail/feature-gate-allocator.rs b/src/test/compile-fail/feature-gate-allocator.rs
new file mode 100644
index 00000000000..6490216d012
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-allocator.rs
@@ -0,0 +1,13 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allocator] //~ ERROR: experimental feature
+
+fn main() {}
diff --git a/src/test/compile-fail/feature-gate-needs-allocator.rs b/src/test/compile-fail/feature-gate-needs-allocator.rs
new file mode 100644
index 00000000000..1809564f5de
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-needs-allocator.rs
@@ -0,0 +1,14 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![needs_allocator] //~ ERROR the `#[needs_allocator]` attribute is
+
+fn main() {}
+
diff --git a/src/test/compile-fail/two-allocators-2.rs b/src/test/compile-fail/two-allocators-2.rs
new file mode 100644
index 00000000000..d6fcbcb513a
--- /dev/null
+++ b/src/test/compile-fail/two-allocators-2.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern: cannot link together two allocators: allocator1 and allocator2
+// aux-build:allocator1.rs
+// aux-build:allocator2.rs
+
+// Make sure we can't link together two explicit allocators.
+
+extern crate allocator1;
+extern crate allocator2;
+
+fn main() {}
+
diff --git a/src/test/compile-fail/two-allocators-3.rs b/src/test/compile-fail/two-allocators-3.rs
new file mode 100644
index 00000000000..70c9dfcafae
--- /dev/null
+++ b/src/test/compile-fail/two-allocators-3.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:allocator1.rs
+// error-pattern: cannot link together two allocators
+
+// We're linking std dynamically (via -C prefer-dynamic for this test) which
+// has an allocator and then we're also linking in a new allocator (allocator1)
+// and this should be an error
+
+extern crate allocator1;
+
+fn main() {
+}
diff --git a/src/test/compile-fail/two-allocators.rs b/src/test/compile-fail/two-allocators.rs
new file mode 100644
index 00000000000..a34f77de245
--- /dev/null
+++ b/src/test/compile-fail/two-allocators.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern: cannot link together two allocators
+
+// aux-build:allocator1.rs
+// aux-build:allocator2.rs
+
+extern crate allocator1;
+extern crate allocator2;
+
+fn main() {}
diff --git a/src/test/run-pass/allocator-default.rs b/src/test/run-pass/allocator-default.rs
new file mode 100644
index 00000000000..1bca39de663
--- /dev/null
+++ b/src/test/run-pass/allocator-default.rs
@@ -0,0 +1,20 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(alloc_jemalloc, alloc_system)]
+
+#[cfg(not(target_env = "msvc"))]
+extern crate alloc_jemalloc;
+#[cfg(target_env = "msvc")]
+extern crate alloc_system;
+
+fn main() {
+    println!("{:?}", Box::new(3));
+}
diff --git a/src/test/run-pass/allocator-jemalloc.rs b/src/test/run-pass/allocator-jemalloc.rs
new file mode 100644
index 00000000000..77fa64ec3db
--- /dev/null
+++ b/src/test/run-pass/allocator-jemalloc.rs
@@ -0,0 +1,20 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+// ignore-msvc no jemalloc on msvc
+
+#![feature(alloc_jemalloc)]
+
+extern crate alloc_jemalloc;
+
+fn main() {
+    println!("{:?}", Box::new(3));
+}
diff --git a/src/test/run-pass/allocator-override.rs b/src/test/run-pass/allocator-override.rs
new file mode 100644
index 00000000000..993ea414914
--- /dev/null
+++ b/src/test/run-pass/allocator-override.rs
@@ -0,0 +1,24 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+// aux-build:allocator-dummy.rs
+
+extern crate allocator_dummy;
+
+fn main() {
+    unsafe {
+        let before = allocator_dummy::HITS;
+        let b = Box::new(3);
+        assert_eq!(allocator_dummy::HITS - before, 1);
+        drop(b);
+        assert_eq!(allocator_dummy::HITS - before, 2);
+    }
+}
diff --git a/src/test/run-pass/allocator-system.rs b/src/test/run-pass/allocator-system.rs
new file mode 100644
index 00000000000..4585003d579
--- /dev/null
+++ b/src/test/run-pass/allocator-system.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(alloc_system)]
+
+extern crate alloc_system;
+
+fn main() {
+    println!("{:?}", Box::new(3));
+}