about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-11-30 14:41:40 -0800
committerbors <bors@rust-lang.org>2013-11-30 14:41:40 -0800
commit4252a24ae1236207a99c1d313d4b1b1eda3ebb58 (patch)
treec0b36a1a14bf504be40acb188934e65497745848
parentdfe46f788bd080fed67c63e7a04dad10468502b3 (diff)
parent56e4c82a38cfb2bcf7de2d6323dab0073a70726d (diff)
downloadrust-4252a24ae1236207a99c1d313d4b1b1eda3ebb58.tar.gz
rust-4252a24ae1236207a99c1d313d4b1b1eda3ebb58.zip
auto merge of #10528 : alexcrichton/rust/static-linking-v2, r=pcwalton
In this series of commits, I've implemented static linking for rust. The scheme I implemented was the same as my [mailing list post](https://mail.mozilla.org/pipermail/rust-dev/2013-November/006686.html).

The commits have more details to the nitty gritty of what went on. I've rebased this on top of my native mutex pull request (#10479), but I imagine that it will land before this lands, I just wanted to pre-emptively get all the rebase conflicts out of the way (becuase this is reorganizing building librustrt as well).

Some contentious points I want to make sure are all good:

* I've added more "compiler chooses a default" behavior than I would like, I want to make sure that this is all very clearly outlined in the code, and if not I would like to remove behavior or make it clearer.
* I want to make sure that the new "fancy suite" tests are ok (using make/python instead of another rust crate)

If we do indeed pursue this, I would be more than willing to write up a document describing how linking in rust works. I believe that this behavior should be very understandable, and the compiler should never hinder someone just because linking is a little fuzzy.
-rw-r--r--Makefile.in69
-rwxr-xr-xconfigure3
-rw-r--r--doc/rust.md12
-rw-r--r--mk/clean.mk10
-rw-r--r--mk/host.mk35
-rw-r--r--mk/install.mk12
-rw-r--r--mk/platform.mk1
-rw-r--r--mk/rt.mk6
-rw-r--r--mk/target.mk6
-rw-r--r--mk/tests.mk44
-rw-r--r--src/etc/maketest.py27
-rw-r--r--src/etc/snapshot.py4
-rw-r--r--src/libextra/flate.rs2
-rw-r--r--src/libextra/lib.rs5
-rw-r--r--src/librustc/back/archive.rs138
-rw-r--r--src/librustc/back/arm.rs2
-rw-r--r--src/librustc/back/link.rs540
-rw-r--r--src/librustc/back/mips.rs2
-rw-r--r--src/librustc/back/rpath.rs24
-rw-r--r--src/librustc/back/target_strs.rs2
-rw-r--r--src/librustc/back/x86.rs2
-rw-r--r--src/librustc/back/x86_64.rs2
-rw-r--r--src/librustc/driver/driver.rs85
-rw-r--r--src/librustc/driver/session.rs149
-rw-r--r--src/librustc/front/feature_gate.rs15
-rw-r--r--src/librustc/lib.rs5
-rw-r--r--src/librustc/lib/llvm.rs64
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/metadata/creader.rs153
-rw-r--r--src/librustc/metadata/csearch.rs6
-rw-r--r--src/librustc/metadata/cstore.rs72
-rw-r--r--src/librustc/metadata/decoder.rs10
-rw-r--r--src/librustc/metadata/encoder.rs25
-rw-r--r--src/librustc/metadata/loader.rs281
-rw-r--r--src/librustc/middle/lint.rs4
-rw-r--r--src/librustc/middle/trans/base.rs46
-rw-r--r--src/librustdoc/html/markdown.rs2
-rw-r--r--src/librustdoc/lib.rs3
-rw-r--r--src/librustpkg/lib.rs5
-rw-r--r--src/librustpkg/tests.rs4
-rw-r--r--src/librustpkg/util.rs7
-rw-r--r--src/librustuv/lib.rs5
-rw-r--r--src/librustuv/uvll.rs31
-rw-r--r--src/libstd/lib.rs15
-rw-r--r--src/libstd/rtdeps.rs54
-rw-r--r--src/libsyntax/lib.rs3
-rw-r--r--src/rt/rustrt.def.in51
-rw-r--r--src/rustllvm/rustllvm.def.in2
-rw-r--r--src/test/auxiliary/anon-extern-mod-cross-crate-1.rs5
-rw-r--r--src/test/auxiliary/extern-crosscrate-source.rs1
-rw-r--r--src/test/auxiliary/foreign_lib.rs1
-rw-r--r--src/test/run-make/bootstrap-from-c-with-uvio/Makefile9
-rw-r--r--src/test/run-make/bootstrap-from-c-with-uvio/lib.rs25
-rw-r--r--src/test/run-make/bootstrap-from-c-with-uvio/main.c16
-rw-r--r--src/test/run-make/c-dynamic-dylib/Makefile14
-rw-r--r--src/test/run-make/c-dynamic-dylib/bar.rs5
-rw-r--r--src/test/run-make/c-dynamic-dylib/cfoo.c1
-rw-r--r--src/test/run-make/c-dynamic-dylib/foo.rs10
-rw-r--r--src/test/run-make/c-dynamic-rlib/Makefile14
-rw-r--r--src/test/run-make/c-dynamic-rlib/bar.rs5
-rw-r--r--src/test/run-make/c-dynamic-rlib/cfoo.c1
-rw-r--r--src/test/run-make/c-dynamic-rlib/foo.rs10
-rw-r--r--src/test/run-make/c-link-to-rust-dylib/Makefile9
-rw-r--r--src/test/run-make/c-link-to-rust-dylib/bar.c6
-rw-r--r--src/test/run-make/c-link-to-rust-dylib/foo.rs4
-rw-r--r--src/test/run-make/c-link-to-rust-staticlib/Makefile13
-rw-r--r--src/test/run-make/c-link-to-rust-staticlib/bar.c6
-rw-r--r--src/test/run-make/c-link-to-rust-staticlib/foo.rs4
-rw-r--r--src/test/run-make/c-static-dylib/Makefile9
-rw-r--r--src/test/run-make/c-static-dylib/bar.rs5
-rw-r--r--src/test/run-make/c-static-dylib/cfoo.c1
-rw-r--r--src/test/run-make/c-static-dylib/foo.rs10
-rw-r--r--src/test/run-make/c-static-rlib/Makefile8
-rw-r--r--src/test/run-make/c-static-rlib/bar.rs5
-rw-r--r--src/test/run-make/c-static-rlib/cfoo.c1
-rw-r--r--src/test/run-make/c-static-rlib/foo.rs10
-rw-r--r--src/test/run-make/dylib-chain/Makefile12
-rw-r--r--src/test/run-make/dylib-chain/m1.rs2
-rw-r--r--src/test/run-make/dylib-chain/m2.rs4
-rw-r--r--src/test/run-make/dylib-chain/m3.rs4
-rw-r--r--src/test/run-make/dylib-chain/m4.rs3
-rw-r--r--src/test/run-make/mixing-deps/Makefile7
-rw-r--r--src/test/run-make/mixing-deps/both.rs4
-rw-r--r--src/test/run-make/mixing-deps/dylib.rs6
-rw-r--r--src/test/run-make/mixing-deps/prog.rs9
-rw-r--r--src/test/run-make/mixing-libs/Makefile9
-rw-r--r--src/test/run-make/mixing-libs/dylib.rs4
-rw-r--r--src/test/run-make/mixing-libs/prog.rs7
-rw-r--r--src/test/run-make/mixing-libs/rlib.rs2
-rw-r--r--src/test/run-make/prefer-dylib/Makefile8
-rw-r--r--src/test/run-make/prefer-dylib/bar.rs1
-rw-r--r--src/test/run-make/prefer-dylib/foo.rs5
-rw-r--r--src/test/run-make/prefer-rlib/Makefile8
-rw-r--r--src/test/run-make/prefer-rlib/bar.rs1
-rw-r--r--src/test/run-make/prefer-rlib/foo.rs5
-rw-r--r--src/test/run-make/rlib-chain/Makefile10
-rw-r--r--src/test/run-make/rlib-chain/m1.rs2
-rw-r--r--src/test/run-make/rlib-chain/m2.rs4
-rw-r--r--src/test/run-make/rlib-chain/m3.rs4
-rw-r--r--src/test/run-make/rlib-chain/m4.rs3
-rw-r--r--src/test/run-make/simple-dylib/Makefile5
-rw-r--r--src/test/run-make/simple-dylib/bar.rs1
-rw-r--r--src/test/run-make/simple-dylib/foo.rs5
-rw-r--r--src/test/run-make/simple-rlib/Makefile5
-rw-r--r--src/test/run-make/simple-rlib/bar.rs1
-rw-r--r--src/test/run-make/simple-rlib/foo.rs5
-rw-r--r--src/test/run-make/static-unwinding/Makefile6
-rw-r--r--src/test/run-make/static-unwinding/lib.rs15
-rw-r--r--src/test/run-make/static-unwinding/main.rs25
-rw-r--r--src/test/run-make/tools.mk27
-rw-r--r--src/test/run-pass/anon-extern-mod-cross-crate-2.rs4
-rw-r--r--src/test/run-pass/anon-extern-mod.rs2
-rw-r--r--src/test/run-pass/c-stack-as-value.rs1
-rw-r--r--src/test/run-pass/extern-call-deep.rs1
-rw-r--r--src/test/run-pass/extern-call-deep2.rs1
-rw-r--r--src/test/run-pass/extern-call-indirect.rs1
-rw-r--r--src/test/run-pass/extern-call-scrub.rs1
-rw-r--r--src/test/run-pass/extern-pass-TwoU16s.rs1
-rw-r--r--src/test/run-pass/extern-pass-TwoU32s.rs1
-rw-r--r--src/test/run-pass/extern-pass-TwoU64s.rs1
-rw-r--r--src/test/run-pass/extern-pass-TwoU8s.rs1
-rw-r--r--src/test/run-pass/extern-pass-char.rs1
-rw-r--r--src/test/run-pass/extern-pass-double.rs1
-rw-r--r--src/test/run-pass/extern-pass-u32.rs1
-rw-r--r--src/test/run-pass/extern-pass-u64.rs1
-rw-r--r--src/test/run-pass/extern-return-TwoU16s.rs1
-rw-r--r--src/test/run-pass/extern-return-TwoU32s.rs1
-rw-r--r--src/test/run-pass/extern-return-TwoU64s.rs1
-rw-r--r--src/test/run-pass/extern-return-TwoU8s.rs1
-rw-r--r--src/test/run-pass/extern-stress.rs1
-rw-r--r--src/test/run-pass/extern-yield.rs1
-rw-r--r--src/test/run-pass/foreign-call-no-runtime.rs1
-rw-r--r--src/test/run-pass/foreign-dupe.rs4
-rw-r--r--src/test/run-pass/foreign-no-abi.rs1
-rw-r--r--src/test/run-pass/invoke-external-foreign.rs4
-rw-r--r--src/test/run-pass/static-mut-foreign.rs2
-rw-r--r--src/test/run-pass/struct-return.rs2
137 files changed, 1714 insertions, 799 deletions
diff --git a/Makefile.in b/Makefile.in
index f5bb3cb2ed0..b575312c03b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -130,6 +130,14 @@ ifndef DEBUG_BORROWS
   RUSTFLAGS_STAGE2 += -Z no-debug-borrows
 endif
 
+# The executables crated during this compilation process have no need to include
+# static copies of libstd and libextra. We also generate dynamic versions of all
+# libraries, so in the interest of space, prefer dynamic linking throughout the
+# compilation process.
+RUSTFLAGS_STAGE1 += -Z prefer-dynamic
+RUSTFLAGS_STAGE2 += -Z prefer-dynamic
+RUSTFLAGS_STAGE3 += -Z prefer-dynamic
+
 # platform-specific auto-configuration
 include $(CFG_SRC_DIR)mk/platform.mk
 
@@ -214,7 +222,7 @@ GENERATED :=
 
 define DEF_LIBS
 
-CFG_RUNTIME_$(1) :=$(call CFG_LIB_NAME_$(1),rustrt)
+CFG_RUNTIME_$(1) :=$(call CFG_STATIC_LIB_NAME_$(1),rustrt)
 CFG_RUSTLLVM_$(1) :=$(call CFG_LIB_NAME_$(1),rustllvm)
 CFG_STDLIB_$(1) :=$(call CFG_LIB_NAME_$(1),std)
 CFG_EXTRALIB_$(1) :=$(call CFG_LIB_NAME_$(1),extra)
@@ -239,6 +247,10 @@ LIBRUSTPKG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustpkg)
 LIBRUSTDOC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc)
 LIBRUSTUV_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustuv)
 
+EXTRALIB_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,extra)
+STDLIB_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,std)
+LIBRUSTUV_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,rustuv)
+
 endef
 
 # $(1) is the path for directory to match against
@@ -392,42 +404,25 @@ TBIN$(1)_T_$(2)_H_$(3) = $$(TROOT$(1)_T_$(2)_H_$(3))/bin
 TLIB$(1)_T_$(2)_H_$(3) = $$(TROOT$(1)_T_$(2)_H_$(3))/$$(CFG_LIBDIR)
 
 # The name of the standard and extra libraries used by rustc
-ifdef CFG_DISABLE_SHAREDSTD
-  HSTDLIB_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/libstd.rlib
-  TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib
-
-  HEXTRALIB_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/libextra.rlib
-  TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/libextra.rlib
-
-  HLIBRUSTC_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/librustc.rlib
-  TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/librustc.rlib
-else
-  HSTDLIB_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/$(CFG_STDLIB_$(3))
-  TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2))
-
-  HEXTRALIB_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/$(CFG_EXTRALIB_$(3))
-  TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2))
-
-  HLIBRUSTC_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTC_$(3))
-  TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2))
-
-  HLIBRUSTUV_DEFAULT$(1)_H_$(3) = \
-    $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTUV_$(3))
-  TLIBRUSTUV_DEFAULT$(1)_T_$(2)_H_$(3) = \
-    $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2))
-endif
+HSTDLIB_DEFAULT$(1)_H_$(3) = \
+  $$(HLIB$(1)_H_$(3))/$(CFG_STDLIB_$(3))
+TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
+  $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2))
+
+HEXTRALIB_DEFAULT$(1)_H_$(3) = \
+  $$(HLIB$(1)_H_$(3))/$(CFG_EXTRALIB_$(3))
+TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \
+  $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2))
+
+HLIBRUSTC_DEFAULT$(1)_H_$(3) = \
+  $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTC_$(3))
+TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \
+  $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2))
+
+HLIBRUSTUV_DEFAULT$(1)_H_$(3) = \
+  $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTUV_$(3))
+TLIBRUSTUV_DEFAULT$(1)_T_$(2)_H_$(3) = \
+  $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2))
 
 # Preqrequisites for using the stageN compiler
 HSREQ$(1)_H_$(3) = \
diff --git a/configure b/configure
index 2390016b3f1..074e522a20c 100755
--- a/configure
+++ b/configure
@@ -364,7 +364,6 @@ fi
 BOOL_OPTIONS=""
 VAL_OPTIONS=""
 
-opt sharedstd 1 "build libstd as a shared library"
 opt valgrind 0 "run tests with valgrind (memcheck by default)"
 opt helgrind 0 "run tests with helgrind instead of memcheck"
 opt docs     1 "build documentation"
@@ -398,7 +397,7 @@ valopt sysconfdir "/etc" "install system configuration files"
 valopt datadir "${CFG_PREFIX}/share" "install data"
 valopt infodir "${CFG_PREFIX}/share/info" "install additional info"
 valopt mandir "${CFG_PREFIX}/share/man" "install man pages in PATH"
-valopt libdir "${CFG_PREFIX}/lib" "install libraries" 
+valopt libdir "${CFG_PREFIX}/lib" "install libraries"
 
 # Validate Options
 step_msg "validating $CFG_SELF args"
diff --git a/doc/rust.md b/doc/rust.md
index a25f19371bd..969e40e632a 100644
--- a/doc/rust.md
+++ b/doc/rust.md
@@ -1507,19 +1507,15 @@ an `abi` string, as shown here:
 extern "stdcall" { }
 ~~~~
 
-The `link_name` attribute allows the name of the library to be specified.
+The `link` attribute allows the name of the library to be specified. When
+specified the compiler will attempt to link against the native library of the
+specified name.
 
 ~~~~ {.xfail-test}
-#[link_name = "crypto"]
+#[link(name = "crypto")]
 extern { }
 ~~~~
 
-The `nolink` attribute tells the Rust compiler
-not to do any linking for the external block.
-This is particularly useful for creating external blocks for libc,
-which tends to not follow standard library naming conventions
-and is linked to all Rust programs anyway.
-
 The type of a function
 declared in an extern block
 is `extern "abi" fn(A1, ..., An) -> R`,
diff --git a/mk/clean.mk b/mk/clean.mk
index dc2aefeb865..1994fec0990 100644
--- a/mk/clean.mk
+++ b/mk/clean.mk
@@ -59,6 +59,7 @@ clean-generic-$(2)-$(1):
 	$(Q)find $(1)/rustllvm \
 	         $(1)/rt \
 		 $(1)/test \
+		 $(1)/stage* \
          -name '*.[odasS]' -o \
          -name '*.so' -o      \
          -name '*.dylib' -o   \
@@ -91,13 +92,16 @@ clean$(1)_H_$(2):
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTC_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBSYNTAX_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(STDLIB_GLOB_$(2))
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(STDLIB_RGLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(EXTRALIB_GLOB_$(2))
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(EXTRALIB_RGLOB_$(2))
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTUV_GLOB_$(2))
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTUV_RGLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTC_GLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBSYNTAX_GLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB_$(2))
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM_$(2))
-	$(Q)rm -f $$(HLIB$(1)_H_$(2))/libstd.rlib
 
 endef
 
@@ -122,14 +126,16 @@ clean$(1)_T_$(2)_H_$(3):
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBSYNTAX_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(STDLIB_GLOB_$(2))
+	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(STDLIB_RGLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(EXTRALIB_GLOB_$(2))
+	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(EXTRALIB_RGLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTUV_GLOB_$(2))
+	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTUV_RGLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTC_GLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBSYNTAX_GLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTPKG_GLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOC_GLOB_$(2))
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM_$(2))
-	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/librun_pass_stage* # For unix
 	$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/run_pass_stage* # For windows
diff --git a/mk/host.mk b/mk/host.mk
index 9ba2b978f10..7e8a3e8a6eb 100644
--- a/mk/host.mk
+++ b/mk/host.mk
@@ -50,7 +50,7 @@ $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTC_$(4)): \
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(4)),$$(notdir $$@))
 	$$(Q)cp $$< $$@
 	$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_GLOB_$(4)) \
-		$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_DSYM_GLOB_$(4))) \
+		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_DSYM_GLOB_$(4))) \
 	        $$(HLIB$(2)_H_$(4))
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(4)),$$(notdir $$@))
 
@@ -82,6 +82,7 @@ $$(HLIB$(2)_H_$(4))/$(CFG_STDLIB_$(4)): \
 	| $$(HLIB$(2)_H_$(4))/
 	@$$(call E, cp: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(4)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(4)),$$(notdir $$@))
 	$$(Q)cp $$< $$@
 # Subtle: We do not let the shell expand $$(STDLIB_DSYM_GLOB) directly rather
 # we use Make's $$(wildcard) facility. The reason is that, on mac, when using
@@ -91,9 +92,11 @@ $$(HLIB$(2)_H_$(4))/$(CFG_STDLIB_$(4)): \
 # Make instead expands the glob to nothing, which gives us the correct behavior.
 # (Copy .dsym file if it exists, but do nothing otherwise)
 	$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_GLOB_$(4)) \
+		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_RGLOB_$(4))) \
 		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_DSYM_GLOB_$(4))) \
 	        $$(HLIB$(2)_H_$(4))
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(4)),$$(notdir $$@))
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(4)),$$(notdir $$@))
 
 $$(HLIB$(2)_H_$(4))/$(CFG_EXTRALIB_$(4)): \
 	$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_EXTRALIB_$(4)) \
@@ -102,11 +105,14 @@ $$(HLIB$(2)_H_$(4))/$(CFG_EXTRALIB_$(4)): \
 	| $$(HLIB$(2)_H_$(4))/
 	@$$(call E, cp: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(4)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(4)),$$(notdir $$@))
 	$$(Q)cp $$< $$@
 	$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_GLOB_$(4)) \
+		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_RGLOB_$(4))) \
 		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_DSYM_GLOB_$(4))) \
 	        $$(HLIB$(2)_H_$(4))
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(4)),$$(notdir $$@))
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(4)),$$(notdir $$@))
 
 $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTUV_$(4)): \
 	$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTUV_$(4)) \
@@ -115,35 +121,14 @@ $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTUV_$(4)): \
 	| $$(HLIB$(2)_H_$(4))/
 	@$$(call E, cp: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(4)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(4)),$$(notdir $$@))
 	$$(Q)cp $$< $$@
 	$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_GLOB_$(4)) \
+		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_RGLOB_$(4))) \
 		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_DSYM_GLOB_$(4))) \
 	        $$(HLIB$(2)_H_$(4))
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(4)),$$(notdir $$@))
-
-$$(HLIB$(2)_H_$(4))/libstd.rlib: \
-	$$(TLIB$(1)_T_$(4)_H_$(3))/libstd.rlib \
-	$$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \
-	| $$(HLIB$(2)_H_$(4))/
-	@$$(call E, cp: $$@)
-	$$(Q)cp $$< $$@
-
-$$(HLIB$(2)_H_$(4))/libextra.rlib: \
-	$$(TLIB$(1)_T_$(4)_H_$(3))/libextra.rlib \
-	$$(HLIB$(2)_H_$(4))/libstd.rlib \
-	$$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \
-	| $$(HLIB$(2)_H_$(4))/
-	@$$(call E, cp: $$@)
-	$$(Q)cp $$< $$@
-
-$$(HLIB$(2)_H_$(4))/librustc.rlib: \
-	$$(TLIB$(1)_T_$(4)_H_$(3))/librustc.rlib \
-	$$(HLIB$(2)_H_$(4))/libstd.rlib \
-	$$(HLIB$(2)_H_$(4))/libextra.rlib \
-	$$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \
-	| $$(HLIB$(2)_H_$(4))/
-	@$$(call E, cp: $$@)
-	$$(Q)cp $$< $$@
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(4)),$$(notdir $$@))
 
 $$(HLIB$(2)_H_$(4))/$(CFG_RUSTLLVM_$(4)): \
 	$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_RUSTLLVM_$(4)) \
diff --git a/mk/install.mk b/mk/install.mk
index 49f1fdbf547..7d6c0ea5264 100644
--- a/mk/install.mk
+++ b/mk/install.mk
@@ -88,7 +88,6 @@ install-target-$(1)-host-$(2): LIB_SOURCE_DIR=$$(TL$(1)$(2))
 install-target-$(1)-host-$(2): LIB_DESTIN_DIR=$$(PTL$(1)$(2))
 install-target-$(1)-host-$(2): $$(TSREQ$$(ISTAGE)_T_$(1)_H_$(2)) $$(SREQ$$(ISTAGE)_T_$(1)_H_$(2))
 	$$(Q)$$(call MK_INSTALL_DIR,$$(PTL$(1)$(2)))
-	$$(Q)$$(call INSTALL_LIB,$$(CFG_RUNTIME_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(STDLIB_GLOB_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(EXTRALIB_GLOB_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTUV_GLOB_$(1)))
@@ -101,7 +100,6 @@ install-target-$(1)-host-$(2): LIB_SOURCE_DIR=$$(TL$(1)$(2))
 install-target-$(1)-host-$(2): LIB_DESTIN_DIR=$$(PTL$(1)$(2))
 install-target-$(1)-host-$(2): $$(CSREQ$$(ISTAGE)_T_$(1)_H_$(2))
 	$$(Q)$$(call MK_INSTALL_DIR,$$(PTL$(1)$(2)))
-	$$(Q)$$(call INSTALL_LIB,$$(CFG_RUNTIME_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(CFG_RUSTLLVM_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(STDLIB_GLOB_$(1)))
 	$$(Q)$$(call INSTALL_LIB,$$(EXTRALIB_GLOB_$(1)))
@@ -144,13 +142,15 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_BUILD_)_H_$(CFG_BUILD_))
 	$(Q)$(call INSTALL,$(HB2),$(PHB),rustpkg$(X_$(CFG_BUILD)))
 	$(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(STDLIB_GLOB_$(CFG_BUILD)))
+	$(Q)$(call INSTALL_LIB,$(STDLIB_RGLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(EXTRALIB_GLOB_$(CFG_BUILD)))
+	$(Q)$(call INSTALL_LIB,$(EXTRALIB_RGLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(LIBRUSTUV_GLOB_$(CFG_BUILD)))
+	$(Q)$(call INSTALL_LIB,$(LIBRUSTUV_RGLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(LIBRUSTC_GLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(LIBSYNTAX_GLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD)))
 	$(Q)$(call INSTALL_LIB,$(LIBRUSTDOC_GLOB_$(CFG_BUILD)))
-	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUNTIME_$(CFG_BUILD)))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUSTLLVM_$(CFG_BUILD)))
 	$(Q)$(call INSTALL,$(S)/man,$(CFG_MANDIR)/man1,rustc.1)
 	$(Q)$(call INSTALL,$(S)/man,$(CFG_MANDIR)/man1,rustdoc.1)
@@ -167,11 +167,13 @@ uninstall:
 	$(Q)rm -f $(PHB)/rustpkg$(X_$(CFG_BUILD))
 	$(Q)rm -f $(PHB)/rustdoc$(X_$(CFG_BUILD))
 	$(Q)rm -f $(PHL)/$(CFG_RUSTLLVM_$(CFG_BUILD))
-	$(Q)rm -f $(PHL)/$(CFG_RUNTIME_$(CFG_BUILD))
 	$(Q)for i in \
           $(call HOST_LIB_FROM_HL_GLOB,$(STDLIB_GLOB_$(CFG_BUILD))) \
+          $(call HOST_LIB_FROM_HL_GLOB,$(STDLIB_RGLOB_$(CFG_BUILD))) \
           $(call HOST_LIB_FROM_HL_GLOB,$(EXTRALIB_GLOB_$(CFG_BUILD))) \
+          $(call HOST_LIB_FROM_HL_GLOB,$(EXTRALIB_RGLOB_$(CFG_BUILD))) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTUV_GLOB_$(CFG_BUILD))) \
+          $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTUV_RGLOB_$(CFG_BUILD))) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTC_GLOB_$(CFG_BUILD))) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBSYNTAX_GLOB_$(CFG_BUILD))) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD))) \
@@ -232,7 +234,6 @@ endif
 define INSTALL_RUNTIME_TARGET_N
 install-runtime-target-$(1)-host-$(2): $$(TSREQ$$(ISTAGE)_T_$(1)_H_$(2)) $$(SREQ$$(ISTAGE)_T_$(1)_H_$(2))
 	$(Q)$(call ADB_SHELL,mkdir,$(CFG_RUNTIME_PUSH_DIR))
-	$(Q)$(call ADB_PUSH,$$(TL$(1)$(2))/$$(CFG_RUNTIME_$(1)),$(CFG_RUNTIME_PUSH_DIR))
 	$(Q)$(call ADB_PUSH,$$(TL$(1)$(2))/$$(STDLIB_GLOB_$(1)),$(CFG_RUNTIME_PUSH_DIR))
 	$(Q)$(call ADB_PUSH,$$(TL$(1)$(2))/$$(EXTRALIB_GLOB_$(1)),$(CFG_RUNTIME_PUSH_DIR))
 	$(Q)$(call ADB_PUSH,$$(TL$(1)$(2))/$$(LIBRUSTUV_GLOB_$(1)),$(CFG_RUNTIME_PUSH_DIR))
@@ -241,7 +242,6 @@ endef
 define INSTALL_RUNTIME_TARGET_CLEANUP_N
 install-runtime-target-$(1)-cleanup:
 	$(Q)$(call ADB,remount)
-	$(Q)$(call ADB_SHELL,rm,$(CFG_RUNTIME_PUSH_DIR)/$(CFG_RUNTIME_$(1)))
 	$(Q)$(call ADB_SHELL,rm,$(CFG_RUNTIME_PUSH_DIR)/$(STDLIB_GLOB_$(1)))
 	$(Q)$(call ADB_SHELL,rm,$(CFG_RUNTIME_PUSH_DIR)/$(EXTRALIB_GLOB_$(1)))
 	$(Q)$(call ADB_SHELL,rm,$(CFG_RUNTIME_PUSH_DIR)/$(LIBRUSTUV_GLOB_$(1)))
diff --git a/mk/platform.mk b/mk/platform.mk
index bc2536cce48..35d4279eaef 100644
--- a/mk/platform.mk
+++ b/mk/platform.mk
@@ -138,6 +138,7 @@ endif
 endif
 endif
 
+CFG_RLIB_GLOB=lib$(1)-*.rlib
 
 # x86_64-unknown-linux-gnu configuration
 CC_x86_64-unknown-linux-gnu=$(CC)
diff --git a/mk/rt.mk b/mk/rt.mk
index cc22d5781a6..f27ed8714f4 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -121,11 +121,9 @@ $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJS_
 	@$$(call E, link: $$@)
 	$$(Q)$(AR_$(1)) rcs $$@ $$^
 
-$$(RT_BUILD_DIR_$(1)_$(2))/$(CFG_RUNTIME_$(1)): $$(RUNTIME_OBJS_$(1)_$(2)) $$(MKFILE_DEPS) \
-                        $$(RUNTIME_DEF_$(1)_$(2))
+$$(RT_BUILD_DIR_$(1)_$(2))/$(CFG_RUNTIME_$(1)): $$(RUNTIME_OBJS_$(1)_$(2)) $$(MKFILE_DEPS)
 	@$$(call E, link: $$@)
-	$$(Q)$$(call CFG_LINK_CXX_$(1),$$@, $$(RUNTIME_OBJS_$(1)_$(2)) \
-	    $$(CFG_LIBUV_LINK_FLAGS_$(1)),$$(RUNTIME_DEF_$(1)_$(2)),$$(CFG_RUNTIME_$(1)))
+	$$(Q)$(AR_$(1)) rcs $$@ $$(RUNTIME_OBJS_$(1)_$(2))
 
 # These could go in rt.mk or rustllvm.mk, they're needed for both.
 
diff --git a/mk/target.mk b/mk/target.mk
index f7d8ec83a5a..a8606cbdbcb 100644
--- a/mk/target.mk
+++ b/mk/target.mk
@@ -60,8 +60,10 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)): \
 		| $$(TLIB$(1)_T_$(2)_H_$(3))/
 	@$$(call E, compile_and_link: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(2)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(2)),$$(notdir $$@))
 	$$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) --out-dir $$(@D) $$< && touch $$@
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(2)),$$(notdir $$@))
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(2)),$$(notdir $$@))
 
 $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)): \
 		$$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \
@@ -70,8 +72,10 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)): \
 		| $$(TLIB$(1)_T_$(2)_H_$(3))/
 	@$$(call E, compile_and_link: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(2)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(2)),$$(notdir $$@))
 	$$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) --out-dir $$(@D) $$< && touch $$@
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(2)),$$(notdir $$@))
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(2)),$$(notdir $$@))
 
 $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)): \
 		$$(LIBRUSTUV_CRATE) $$(LIBRUSTUV_INPUTS) \
@@ -82,11 +86,13 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)): \
 		| $$(TLIB$(1)_T_$(2)_H_$(3))/
 	@$$(call E, compile_and_link: $$@)
 	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(2)),$$(notdir $$@))
+	$$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(2)),$$(notdir $$@))
 	$$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) \
 		-L $$(UV_SUPPORT_DIR_$(2)) \
 		-L $$(dir $$(LIBUV_LIB_$(2))) \
 		--out-dir $$(@D) $$< && touch $$@
 	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(2)),$$(notdir $$@))
+	$$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(2)),$$(notdir $$@))
 
 $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBSYNTAX_$(3)): \
                 $$(LIBSYNTAX_CRATE) $$(LIBSYNTAX_INPUTS) \
diff --git a/mk/tests.mk b/mk/tests.mk
index a24791d76af..3b4a490f1ad 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -193,7 +193,7 @@ check-lite: cleantestlibs cleantmptestlogs \
 	check-stage2-std check-stage2-extra check-stage2-rpass \
 	check-stage2-rustuv \
 	check-stage2-rustpkg \
-	check-stage2-rfail check-stage2-cfail
+	check-stage2-rfail check-stage2-cfail check-stage2-rmake
 	$(Q)$(CFG_PYTHON) $(S)src/etc/check-summary.py tmp/*.log
 
 .PHONY: cleantmptestlogs cleantestlibs
@@ -284,7 +284,8 @@ check-stage$(1)-T-$(2)-H-$(3)-exec:     				\
 	check-stage$(1)-T-$(2)-H-$(3)-rfail-exec			\
 	check-stage$(1)-T-$(2)-H-$(3)-cfail-exec			\
 	check-stage$(1)-T-$(2)-H-$(3)-rpass-full-exec			\
-        check-stage$(1)-T-$(2)-H-$(3)-crates-exec                      \
+	check-stage$(1)-T-$(2)-H-$(3)-rmake-exec			\
+        check-stage$(1)-T-$(2)-H-$(3)-crates-exec                       \
 	check-stage$(1)-T-$(2)-H-$(3)-bench-exec			\
 	check-stage$(1)-T-$(2)-H-$(3)-debuginfo-exec \
 	check-stage$(1)-T-$(2)-H-$(3)-codegen-exec \
@@ -584,6 +585,10 @@ TEST_SREQ$(1)_T_$(2)_H_$(3) = \
 # remove directive, if present, from CFG_RUSTC_FLAGS (issue #7898).
 CTEST_RUSTC_FLAGS := $$(subst --cfg ndebug,,$$(CFG_RUSTC_FLAGS))
 
+# There's no need our entire test suite to take up gigabytes of space on disk
+# including copies of libstd/libextra all over the place
+CTEST_RUSTC_FLAGS := $$(CTEST_RUSTC_FLAGS) -Z prefer-dynamic
+
 # The tests can not be optimized while the rest of the compiler is optimized, so
 # filter out the optimization (if any) from rustc and then figure out if we need
 # to be optimized
@@ -766,6 +771,7 @@ TEST_GROUPS = \
 	cfail \
 	bench \
 	perf \
+	rmake \
 	debuginfo \
 	codegen \
 	doc \
@@ -896,3 +902,37 @@ endef
 
 $(foreach host,$(CFG_HOST),			\
  $(eval $(call DEF_CHECK_FAST_FOR_H,$(host))))
+
+RMAKE_TESTS := $(shell ls -d $(S)src/test/run-make/*/)
+RMAKE_TESTS := $(RMAKE_TESTS:$(S)src/test/run-make/%/=%)
+
+define DEF_RMAKE_FOR_T_H
+# $(1) the stage
+# $(2) target triple
+# $(3) host triple
+
+check-stage$(1)-T-$(2)-H-$(3)-rmake-exec: \
+		$$(call TEST_OK_FILE,$(1),$(2),$(3),rmake)
+
+$$(call TEST_OK_FILE,$(1),$(2),$(3),rmake): \
+		$$(RMAKE_TESTS:%=$(3)/test/run-make/%-$(1)-T-$(2)-H-$(3).ok)
+	@touch $$@
+
+$(3)/test/run-make/%-$(1)-T-$(2)-H-$(3).ok: \
+		$(S)src/test/run-make/%/Makefile \
+		$$(HBIN$(1)_H_$(3))/rustc$$(X_$(3))
+	@rm -rf $(3)/test/run-make/$$*
+	@mkdir -p $(3)/test/run-make/$$*
+	@echo maketest: $$*
+	$$(Q)python $(S)src/etc/maketest.py $$(dir $$<) \
+	    $$(HBIN$(1)_H_$(3))/rustc$$(X_$(3)) \
+	    $(3)/test/run-make/$$* \
+	    "$$(CC_$(3)) $$(CFG_GCCISH_CFLAGS_$(3))"
+	@touch $$@
+
+endef
+
+$(foreach stage,$(STAGES), \
+ $(foreach target,$(CFG_TARGET), \
+  $(foreach host,$(CFG_HOST), \
+   $(eval $(call DEF_RMAKE_FOR_T_H,$(stage),$(target),$(host))))))
diff --git a/src/etc/maketest.py b/src/etc/maketest.py
new file mode 100644
index 00000000000..96c658e5686
--- /dev/null
+++ b/src/etc/maketest.py
@@ -0,0 +1,27 @@
+# xfail-license
+
+import subprocess
+import os
+import sys
+
+os.putenv('RUSTC', os.path.abspath(sys.argv[2]))
+os.putenv('TMPDIR', os.path.abspath(sys.argv[3]))
+os.putenv('CC', sys.argv[4])
+
+proc = subprocess.Popen(['make', '-C', sys.argv[1]],
+                        stdout = subprocess.PIPE,
+                        stderr = subprocess.PIPE)
+out, err = proc.communicate()
+i = proc.wait()
+
+if i != 0:
+
+    print '----- ' + sys.argv[1] + """ --------------------
+------ stdout ---------------------------------------------
+""" + out + """
+------ stderr ---------------------------------------------
+""" + err + """
+------        ---------------------------------------------
+"""
+    sys.exit(i)
+
diff --git a/src/etc/snapshot.py b/src/etc/snapshot.py
index 99193d905f8..03ada0eef18 100644
--- a/src/etc/snapshot.py
+++ b/src/etc/snapshot.py
@@ -31,7 +31,6 @@ snapshot_files = {
               "lib/librustc-*.so",
               "lib/libsyntax-*.so",
               "lib/librustuv-*.so",
-              "lib/librustrt.so",
               "lib/librustllvm.so"],
     "macos": ["bin/rustc",
               "lib/libstd-*.dylib",
@@ -39,7 +38,6 @@ snapshot_files = {
               "lib/librustc-*.dylib",
               "lib/libsyntax-*.dylib",
               "lib/librustuv-*.dylib",
-              "lib/librustrt.dylib",
               "lib/librustllvm.dylib"],
     "winnt": ["bin/rustc.exe",
               "bin/std-*.dll",
@@ -47,7 +45,6 @@ snapshot_files = {
               "bin/rustc-*.dll",
               "bin/syntax-*.dll",
               "bin/rustuv-*.dll",
-              "bin/rustrt.dll",
               "bin/rustllvm.dll"],
     "freebsd": ["bin/rustc",
                 "lib/libstd-*.so",
@@ -55,7 +52,6 @@ snapshot_files = {
                 "lib/librustc-*.so",
                 "lib/libsyntax-*.so",
                 "lib/librustuv-*.so",
-                "lib/librustrt.so",
                 "lib/librustllvm.so"]
     }
 
diff --git a/src/libextra/flate.rs b/src/libextra/flate.rs
index 3d1d0c91e31..a4a10ccfa73 100644
--- a/src/libextra/flate.rs
+++ b/src/libextra/flate.rs
@@ -23,7 +23,7 @@ use std::vec;
 pub mod rustrt {
     use std::libc::{c_int, c_void, size_t};
 
-    #[link_name = "rustrt"]
+    #[link(name = "rustrt")]
     extern {
         pub fn tdefl_compress_mem_to_heap(psrc_buf: *c_void,
                                           src_buf_len: size_t,
diff --git a/src/libextra/lib.rs b/src/libextra/lib.rs
index a74c4993be3..ce504d8bf6f 100644
--- a/src/libextra/lib.rs
+++ b/src/libextra/lib.rs
@@ -32,12 +32,15 @@ Rust extras are part of the standard Rust distribution.
 
 #[comment = "Rust extras"];
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "rlib"];
+#[crate_type = "dylib"];
 
 #[feature(macro_rules, globs, managed_boxes)];
 
 #[deny(non_camel_case_types)];
 #[deny(missing_doc)];
+#[allow(attribute_usage)]; // NOTE: remove after the next snapshot
 
 use std::str::{StrSlice, OwnedStr};
 
diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs
new file mode 100644
index 00000000000..9f5aaf3a426
--- /dev/null
+++ b/src/librustc/back/archive.rs
@@ -0,0 +1,138 @@
+// Copyright 2013 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.
+
+//! A helper class for dealing with static archives
+
+use driver::session::Session;
+use metadata::filesearch;
+
+use std::io::fs;
+use std::os;
+use std::run::{ProcessOptions, Process, ProcessOutput};
+use std::str;
+use extra::tempfile::TempDir;
+use syntax::abi;
+
+pub struct Archive {
+    priv sess: Session,
+    priv dst: Path,
+}
+
+fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
+        paths: &[&Path]) -> ProcessOutput {
+    let ar = sess.opts.ar.clone().unwrap_or_else(|| ~"ar");
+    let mut args = ~[args.to_owned()];
+    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
+    args.extend(&mut paths);
+    let mut opts = ProcessOptions::new();
+    opts.dir = cwd;
+    debug!("{} {}", ar, args.connect(" "));
+    match cwd {
+        Some(p) => { debug!("inside {}", p.display()); }
+        None => {}
+    }
+    let o = Process::new(ar, args.as_slice(), opts).finish_with_output();
+    if !o.status.success() {
+        sess.err(format!("{} failed with: {}", ar, o.status));
+        sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
+        sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
+        sess.abort_if_errors();
+    }
+    o
+}
+
+impl Archive {
+    /// Initializes a new static archive with the given object file
+    pub fn create<'a>(sess: Session, dst: &'a Path,
+                      initial_object: &'a Path) -> Archive {
+        run_ar(sess, "crus", None, [dst, initial_object]);
+        Archive { sess: sess, dst: dst.clone() }
+    }
+
+    /// Opens an existing static archive
+    pub fn open(sess: Session, dst: Path) -> Archive {
+        assert!(dst.exists());
+        Archive { sess: sess, dst: dst }
+    }
+
+    /// Read a file in the archive
+    pub fn read(&self, file: &str) -> ~[u8] {
+        // Apparently if "ar p" is used on windows, it generates a corrupt file
+        // which has bad headers and LLVM will immediately choke on it
+        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
+            let loc = TempDir::new("rsar").unwrap();
+            let archive = os::make_absolute(&self.dst);
+            run_ar(self.sess, "x", Some(loc.path()), [&archive,
+                                                      &Path::init(file)]);
+            fs::File::open(&loc.path().join(file)).read_to_end()
+        } else {
+            run_ar(self.sess, "p", None, [&self.dst, &Path::init(file)]).output
+        }
+    }
+
+    /// Adds all of the contents of a native library to this archive. This will
+    /// search in the relevant locations for a library named `name`.
+    pub fn add_native_library(&mut self, name: &str) {
+        let location = self.find_library(name);
+        self.add_archive(&location, name);
+    }
+
+    /// Adds all of the contents of the rlib at the specified path to this
+    /// archive.
+    pub fn add_rlib(&mut self, rlib: &Path) {
+        let name = rlib.filename_str().unwrap().split('-').next().unwrap();
+        self.add_archive(rlib, name);
+    }
+
+    fn add_archive(&mut self, archive: &Path, name: &str) {
+        let loc = TempDir::new("rsar").unwrap();
+
+        // First, extract the contents of the archive to a temporary directory
+        let archive = os::make_absolute(archive);
+        run_ar(self.sess, "x", Some(loc.path()), [&archive]);
+
+        // Next, we must rename all of the inputs to "guaranteed unique names".
+        // The reason for this is that archives are keyed off the name of the
+        // files, so if two files have the same name they will override one
+        // another in the archive (bad).
+        let files = fs::readdir(loc.path());
+        let mut inputs = ~[];
+        for file in files.iter() {
+            let filename = file.filename_str().unwrap();
+            let filename = format!("r-{}-{}", name, filename);
+            let new_filename = file.with_filename(filename);
+            fs::rename(file, &new_filename);
+            inputs.push(new_filename);
+        }
+
+        // Finally, add all the renamed files to this archive
+        let mut args = ~[&self.dst];
+        args.extend(&mut inputs.iter());
+        run_ar(self.sess, "r", None, args.as_slice());
+    }
+
+    fn find_library(&self, name: &str) -> Path {
+        let (prefix, ext) = match self.sess.targ_cfg.os {
+            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
+        };
+        let libname = format!("{}{}.{}", prefix, name, ext);
+
+        let mut rustpath = filesearch::rust_path();
+        rustpath.push(self.sess.filesearch.get_target_lib_path());
+        let path = self.sess.opts.addl_lib_search_paths.iter();
+        for path in path.chain(rustpath.iter()) {
+            debug!("looking for {} inside {}", name, path.display());
+            let test = path.join(libname.clone());
+            if test.exists() { return test }
+        }
+        self.sess.fatal(format!("could not find native static library `{}`, \
+                                 perhaps an -L flag is missing?", name));
+    }
+}
diff --git a/src/librustc/back/arm.rs b/src/librustc/back/arm.rs
index a3ac468a5f0..c155f4bd15b 100644
--- a/src/librustc/back/arm.rs
+++ b/src/librustc/back/arm.rs
@@ -63,6 +63,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs::
 
         target_triple: target_triple,
 
-        cc_args: ~[~"-marm"]
+        cc_args: ~[~"-marm"],
     };
 }
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 9aba16422d3..8119618da57 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 
+use back::archive::Archive;
 use back::rpath;
 use driver::session::Session;
 use driver::session;
@@ -16,7 +17,7 @@ use lib::llvm::llvm;
 use lib::llvm::ModuleRef;
 use lib;
 use metadata::common::LinkMeta;
-use metadata::{encoder, cstore, filesearch};
+use metadata::{encoder, cstore, filesearch, csearch};
 use middle::trans::context::CrateContext;
 use middle::trans::common::gensym_name;
 use middle::ty;
@@ -30,7 +31,6 @@ use std::os::consts::{macos, freebsd, linux, android, win32};
 use std::ptr;
 use std::run;
 use std::str;
-use std::vec;
 use std::io::fs;
 use syntax::abi;
 use syntax::ast;
@@ -88,7 +88,6 @@ pub mod jit {
     use driver::session::Session;
     use lib::llvm::llvm;
     use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef};
-    use metadata::cstore;
 
     use std::c_str::ToCStr;
     use std::cast;
@@ -125,19 +124,6 @@ pub mod jit {
             // core linked into rustc. We don't want that,
             // incase the user wants to use an older extra library.
 
-            let cstore = sess.cstore;
-            let r = cstore::get_used_crate_files(cstore);
-            for cratepath in r.iter() {
-                debug!("linking: {}", cratepath.display());
-
-                cratepath.with_c_str(|buf_t| {
-                    if !llvm::LLVMRustLoadCrate(manager, buf_t) {
-                        llvm_err(sess, ~"Could not link");
-                    }
-                    debug!("linked: {}", cratepath.display());
-                })
-            }
-
             // We custom-build a JIT execution engine via some rust wrappers
             // first. This wrappers takes ownership of the module passed in.
             let ee = llvm::LLVMRustBuildJIT(manager, m, stacks);
@@ -368,20 +354,20 @@ pub mod write {
     }
 
     pub fn run_assembler(sess: Session, assembly: &Path, object: &Path) {
-        let cc_prog = super::get_cc_prog(sess);
+        let cc = super::get_cc_prog(sess);
 
         // FIXME (#9639): This needs to handle non-utf8 paths
-        let cc_args = ~[
+        let args = [
             ~"-c",
             ~"-o", object.as_str().unwrap().to_owned(),
             assembly.as_str().unwrap().to_owned()];
 
-        let prog = run::process_output(cc_prog, cc_args);
+        debug!("{} {}", cc, args.connect(" "));
+        let prog = run::process_output(cc, args);
 
         if !prog.status.success() {
-            sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
-            sess.note(format!("{} arguments: {}",
-                        cc_prog, cc_args.connect(" ")));
+            sess.err(format!("linking with `{}` failed: {}", cc, prog.status));
+            sess.note(format!("{} arguments: {}", cc, args.connect(" ")));
             sess.note(str::from_utf8(prog.error + prog.output));
             sess.abort_if_errors();
         }
@@ -876,90 +862,71 @@ pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str
     mangle(ccx.sess, path, None, None)
 }
 
-
-pub fn output_dll_filename(os: abi::Os, lm: LinkMeta) -> ~str {
-    let (dll_prefix, dll_suffix) = match os {
-        abi::OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
-        abi::OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
-        abi::OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
-        abi::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
-        abi::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
-    };
-    format!("{}{}-{}-{}{}", dll_prefix, lm.name, lm.extras_hash, lm.vers, dll_suffix)
+pub fn output_lib_filename(lm: LinkMeta) -> ~str {
+    format!("{}-{}-{}", lm.name, lm.extras_hash, lm.vers)
 }
 
 pub fn get_cc_prog(sess: Session) -> ~str {
+    match sess.opts.linker {
+        Some(ref linker) => return linker.to_owned(),
+        None => {}
+    }
+
     // In the future, FreeBSD will use clang as default compiler.
     // It would be flexible to use cc (system's default C compiler)
     // instead of hard-coded gcc.
-    // For win32, there is no cc command, so we add a condition to make it use g++.
-    // We use g++ rather than gcc because it automatically adds linker options required
-    // for generation of dll modules that correctly register stack unwind tables.
-    match sess.opts.linker {
-        Some(ref linker) => linker.to_str(),
-        None => match sess.targ_cfg.os {
-            abi::OsAndroid =>
-                match &sess.opts.android_cross_path {
-                    &Some(ref path) => {
-                        format!("{}/bin/arm-linux-androideabi-gcc", *path)
-                    }
-                    &None => {
-                        sess.fatal("need Android NDK path for linking \
-                                    (--android-cross-path)")
-                    }
-                },
-            abi::OsWin32 => ~"g++",
-            _ => ~"cc"
-        }
+    // For win32, there is no cc command, so we add a condition to make it use
+    // g++.  We use g++ rather than gcc because it automatically adds linker
+    // options required for generation of dll modules that correctly register
+    // stack unwind tables.
+    match sess.targ_cfg.os {
+        abi::OsAndroid => match sess.opts.android_cross_path {
+            Some(ref path) => format!("{}/bin/arm-linux-androideabi-gcc", *path),
+            None => {
+                sess.fatal("need Android NDK path for linking \
+                            (--android-cross-path)")
+            }
+        },
+        abi::OsWin32 => ~"g++",
+        _ => ~"cc",
     }
 }
 
-// If the user wants an exe generated we need to invoke
-// cc to link the object file with some libs
+/// Perform the linkage portion of the compilation phase. This will generate all
+/// of the requested outputs for this compilation session.
 pub fn link_binary(sess: Session,
+                   crate_types: &[~str],
                    obj_filename: &Path,
                    out_filename: &Path,
                    lm: LinkMeta) {
-
-    let cc_prog = get_cc_prog(sess);
-    // The invocations of cc share some flags across platforms
-
-    let output = if *sess.building_library {
-        let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
-        debug!("link_meta.name:  {}", lm.name);
-        debug!("long_libname: {}", long_libname);
-        debug!("out_filename: {}", out_filename.display());
-        let out_dirname = out_filename.dir_path();
-        debug!("dirname(out_filename): {}", out_dirname.display());
-
-        out_filename.with_filename(long_libname)
+    let outputs = if sess.opts.test {
+        // If we're generating a test executable, then ignore all other output
+        // styles at all other locations
+        ~[session::OutputExecutable]
     } else {
-        out_filename.clone()
+        // Always generate whatever was specified on the command line, but also
+        // look at what was in the crate file itself for generating output
+        // formats.
+        let mut outputs = sess.opts.outputs.clone();
+        for ty in crate_types.iter() {
+            if "bin" == *ty {
+                outputs.push(session::OutputExecutable);
+            } else if "dylib" == *ty || "lib" == *ty {
+                outputs.push(session::OutputDylib);
+            } else if "rlib" == *ty {
+                outputs.push(session::OutputRlib);
+            } else if "staticlib" == *ty {
+                outputs.push(session::OutputStaticlib);
+            }
+        }
+        if outputs.len() == 0 {
+            outputs.push(session::OutputExecutable);
+        }
+        outputs
     };
 
-    debug!("output: {}", output.display());
-    let cc_args = link_args(sess, obj_filename, out_filename, lm);
-    debug!("{} link args: {}", cc_prog, cc_args.connect(" "));
-    if (sess.opts.debugging_opts & session::print_link_args) != 0 {
-        println!("{} link args: {}", cc_prog, cc_args.connect(" "));
-    }
-
-    // We run 'cc' here
-    let prog = run::process_output(cc_prog, cc_args);
-
-    if !prog.status.success() {
-        sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
-        sess.note(format!("{} arguments: {}",
-                    cc_prog, cc_args.connect(" ")));
-        sess.note(str::from_utf8(prog.error + prog.output));
-        sess.abort_if_errors();
-    }
-
-    // On OSX, debuggers needs this utility to get run to do some munging of the
-    // symbols
-    if sess.targ_cfg.os == abi::OsMacos && sess.opts.debuginfo {
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        run::process_status("dsymutil", [output.as_str().unwrap().to_owned()]);
+    for output in outputs.move_iter() {
+        link_binary_output(sess, output, obj_filename, out_filename, lm);
     }
 
     // Remove the temporary object file if we aren't saving temps
@@ -971,34 +938,36 @@ pub fn link_binary(sess: Session,
 fn is_writeable(p: &Path) -> bool {
     use std::io;
 
-    !p.exists() ||
-        (match io::result(|| p.stat()) {
-            Err(..) => false,
-            Ok(m) => m.perm & io::UserWrite == io::UserWrite
-        })
+    match io::result(|| p.stat()) {
+        Err(..) => true,
+        Ok(m) => m.perm & io::UserWrite == io::UserWrite
+    }
 }
 
-pub fn link_args(sess: Session,
-                 obj_filename: &Path,
-                 out_filename: &Path,
-                 lm:LinkMeta) -> ~[~str] {
-
-    // Converts a library file-stem into a cc -l argument
-    fn unlib(config: @session::config, stem: ~str) -> ~str {
-        if stem.starts_with("lib") &&
-            config.os != abi::OsWin32 {
-            stem.slice(3, stem.len()).to_owned()
-        } else {
-            stem
+fn link_binary_output(sess: Session,
+                      output: session::OutputStyle,
+                      obj_filename: &Path,
+                      out_filename: &Path,
+                      lm: LinkMeta) {
+    let libname = output_lib_filename(lm);
+    let out_filename = match output {
+        session::OutputRlib => {
+            out_filename.with_filename(format!("lib{}.rlib", libname))
         }
-    }
-
-
-    let output = if *sess.building_library {
-        let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
-        out_filename.with_filename(long_libname)
-    } else {
-        out_filename.clone()
+        session::OutputDylib => {
+            let (prefix, suffix) = match sess.targ_cfg.os {
+                abi::OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
+                abi::OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
+                abi::OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
+                abi::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
+                abi::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
+            };
+            out_filename.with_filename(format!("{}{}{}", prefix, libname, suffix))
+        }
+        session::OutputStaticlib => {
+            out_filename.with_filename(format!("lib{}.a", libname))
+        }
+        session::OutputExecutable => out_filename.clone(),
     };
 
     // Make sure the output and obj_filename are both writeable.
@@ -1006,139 +975,294 @@ pub fn link_args(sess: Session,
     // however, the Linux linker will happily overwrite a read-only file.
     // We should be consistent.
     let obj_is_writeable = is_writeable(obj_filename);
-    let out_is_writeable = is_writeable(&output);
+    let out_is_writeable = is_writeable(&out_filename);
     if !out_is_writeable {
         sess.fatal(format!("Output file {} is not writeable -- check its permissions.",
-                           output.display()));
+                           out_filename.display()));
     }
     else if !obj_is_writeable {
         sess.fatal(format!("Object file {} is not writeable -- check its permissions.",
                            obj_filename.display()));
     }
 
-    // The default library location, we need this to find the runtime.
-    // The location of crates will be determined as needed.
-    // FIXME (#9639): This needs to handle non-utf8 paths
-    let lib_path = sess.filesearch.get_target_lib_path();
-    let stage: ~str = ~"-L" + lib_path.as_str().unwrap();
-
-    let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);
-
-    // FIXME (#9639): This needs to handle non-utf8 paths
-    args.push_all([
-        ~"-o", output.as_str().unwrap().to_owned(),
-        obj_filename.as_str().unwrap().to_owned()]);
-
-    let lib_cmd = match sess.targ_cfg.os {
-        abi::OsMacos => ~"-dynamiclib",
-        _ => ~"-shared"
-    };
+    match output {
+        session::OutputRlib => {
+            link_rlib(sess, obj_filename, &out_filename);
+        }
+        session::OutputStaticlib => {
+            link_staticlib(sess, obj_filename, &out_filename);
+        }
+        session::OutputExecutable => {
+            link_natively(sess, false, obj_filename, &out_filename);
+        }
+        session::OutputDylib => {
+            link_natively(sess, true, obj_filename, &out_filename);
+        }
+    }
+}
 
-    // # Crate linking
+// Create an 'rlib'
+//
+// An rlib in its current incarnation is essentially a renamed .a file. The
+// rlib primarily contains the object file of the crate, but it also contains
+// all of the object files from native libraries. This is done by unzipping
+// native libraries and inserting all of the contents into this archive.
+fn link_rlib(sess: Session, obj_filename: &Path,
+             out_filename: &Path) -> Archive {
+    let mut a = Archive::create(sess, out_filename, obj_filename);
+    for &(ref l, kind) in cstore::get_used_libraries(sess.cstore).iter() {
+        match kind {
+            cstore::NativeStatic => {
+                a.add_native_library(l.as_slice());
+            }
+            cstore::NativeUnknown => {}
+        }
+    }
+    return a;
+}
 
-    let cstore = sess.cstore;
-    let r = cstore::get_used_crate_files(cstore);
-    // FIXME (#9639): This needs to handle non-utf8 paths
-    for cratepath in r.iter() {
-        if cratepath.extension_str() == Some("rlib") {
-            args.push(cratepath.as_str().unwrap().to_owned());
-            continue;
+// Create a static archive
+//
+// This is essentially the same thing as an rlib, but it also involves adding
+// all of the upstream crates' objects into the the archive. This will slurp in
+// all of the native libraries of upstream dependencies as well.
+//
+// Additionally, there's no way for us to link dynamic libraries, so we warn
+// about all dynamic library dependencies that they're not linked in.
+fn link_staticlib(sess: Session, obj_filename: &Path, out_filename: &Path) {
+    let mut a = link_rlib(sess, obj_filename, out_filename);
+    a.add_native_library("morestack");
+
+    let crates = cstore::get_used_crates(sess.cstore, cstore::RequireStatic);
+    for &(cnum, ref path) in crates.iter() {
+        let p = match *path {
+            Some(ref p) => p.clone(), None => {
+                sess.err(format!("could not find rlib for: `{}`",
+                                 cstore::get_crate_data(sess.cstore, cnum).name));
+                continue
+            }
+        };
+        a.add_rlib(&p);
+        let native_libs = csearch::get_native_libraries(sess.cstore, cnum);
+        for lib in native_libs.iter() {
+            sess.warn(format!("unlinked native library: {}", *lib));
         }
-        let dir = cratepath.dirname_str().unwrap();
-        if !dir.is_empty() { args.push("-L" + dir); }
-        let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap().to_owned());
-        args.push("-l" + libarg);
     }
+}
 
-    let ula = cstore::get_used_link_args(cstore);
-    for arg in ula.iter() { args.push(arg.to_owned()); }
+// Create a dynamic library or executable
+//
+// 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, dylib: bool, obj_filename: &Path,
+                 out_filename: &Path) {
+    // The invocations of cc share some flags across platforms
+    let cc_prog = get_cc_prog(sess);
+    let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone();
+    cc_args.push_all_move(link_args(sess, dylib, obj_filename, out_filename));
+    if (sess.opts.debugging_opts & session::print_link_args) != 0 {
+        println!("{} link args: {}", cc_prog, cc_args.connect(" "));
+    }
 
-    // # Extern library linking
+    // May have not found libraries in the right formats.
+    sess.abort_if_errors();
 
-    // User-supplied library search paths (-L on the cammand line) These are
-    // the same paths used to find Rust crates, so some of them may have been
-    // added already by the previous crate linking code. This only allows them
-    // to be found at compile time so it is still entirely up to outside
-    // forces to make sure that library can be found at runtime.
+    // Invoke the system linker
+    debug!("{} {}", cc_prog, cc_args.connect(" "));
+    let prog = run::process_output(cc_prog, cc_args);
 
-    for path in sess.opts.addl_lib_search_paths.iter() {
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        args.push("-L" + path.as_str().unwrap().to_owned());
+    if !prog.status.success() {
+        sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
+        sess.note(format!("{} arguments: {}", cc_prog, cc_args.connect(" ")));
+        sess.note(str::from_utf8(prog.error + prog.output));
+        sess.abort_if_errors();
     }
 
-    let rustpath = filesearch::rust_path();
-    for path in rustpath.iter() {
+
+    // On OSX, debuggers need this utility to get run to do some munging of
+    // the symbols
+    if sess.targ_cfg.os == abi::OsMacos && sess.opts.debuginfo {
         // FIXME (#9639): This needs to handle non-utf8 paths
-        args.push("-L" + path.as_str().unwrap().to_owned());
+        run::process_status("dsymutil",
+                            [out_filename.as_str().unwrap().to_owned()]);
     }
+}
 
-    if sess.targ_cfg.os == abi::OsLinux {
-        // GNU-style linkers will use this to omit linking to libraries which don't actually fulfill
-        // any relocations, but only for libraries which follow this flag. Thus, use it before
-        // specifing libraries to link to.
-        args.push(~"-Wl,--as-needed");
-    }
+fn link_args(sess: Session,
+             dylib: bool,
+             obj_filename: &Path,
+             out_filename: &Path) -> ~[~str] {
 
-    // The names of the extern libraries
-    let used_libs = cstore::get_used_libraries(cstore);
-    for l in used_libs.iter() { args.push(~"-l" + *l); }
+    // The default library location, we need this to find the runtime.
+    // The location of crates will be determined as needed.
+    // FIXME (#9639): This needs to handle non-utf8 paths
+    let lib_path = sess.filesearch.get_target_lib_path();
+    let stage: ~str = ~"-L" + lib_path.as_str().unwrap();
 
-    if *sess.building_library {
-        args.push(lib_cmd);
+    let mut args = ~[stage];
 
-        // On mac we need to tell the linker to let this library
-        // be rpathed
-        if sess.targ_cfg.os == abi::OsMacos {
-            // FIXME (#9639): This needs to handle non-utf8 paths
-            args.push("-Wl,-install_name,@rpath/"
-                      + output.filename_str().unwrap());
-        }
-    }
+    // FIXME (#9639): This needs to handle non-utf8 paths
+    args.push_all([
+        ~"-o", out_filename.as_str().unwrap().to_owned(),
+        obj_filename.as_str().unwrap().to_owned()]);
 
-    // On linux librt and libdl are an indirect dependencies via rustrt,
-    // and binutils 2.22+ won't add them automatically
     if sess.targ_cfg.os == abi::OsLinux {
-        // GNU-style linkers supports optimization with -O. --gc-sections removes metadata and
-        // potentially other useful things, so don't include it. GNU ld doesn't need a numeric
-        // argument, but other linkers do.
-        if sess.opts.optimize == session::Default || sess.opts.optimize == session::Aggressive {
+        // GNU-style linkers will use this to omit linking to libraries which
+        // don't actually fulfill any relocations, but only for libraries which
+        // follow this flag. Thus, use it before specifing libraries to link to.
+        args.push(~"-Wl,--as-needed");
+
+        // GNU-style linkers support optimization with -O. --gc-sections
+        // removes metadata and potentially other useful things, so don't
+        // include it. GNU ld doesn't need a numeric argument, but other linkers
+        // do.
+        if sess.opts.optimize == session::Default ||
+           sess.opts.optimize == session::Aggressive {
             args.push(~"-Wl,-O1");
         }
+    }
 
-        args.push_all([~"-lrt", ~"-ldl"]);
+    add_upstream_rust_crates(&mut args, sess, dylib);
+    add_local_native_libraries(&mut args, sess);
 
-        // LLVM implements the `frem` instruction as a call to `fmod`,
-        // which lives in libm. Similar to above, on some linuxes we
-        // have to be explicit about linking to it. See #2510
-        args.push(~"-lm");
-    }
-    else if sess.targ_cfg.os == abi::OsAndroid {
-        args.push_all([~"-ldl", ~"-llog",  ~"-lsupc++", ~"-lgnustl_shared"]);
-        args.push(~"-lm");
+    // # Telling the linker what we're doing
+
+    if dylib {
+        // On mac we need to tell the linker to let this library be rpathed
+        if sess.targ_cfg.os == abi::OsMacos {
+            args.push(~"-dynamiclib");
+            args.push(~"-Wl,-dylib");
+            // FIXME (#9639): This needs to handle non-utf8 paths
+            args.push(~"-Wl,-install_name,@rpath/" +
+                      out_filename.filename_str().unwrap());
+        } else {
+            args.push(~"-shared")
+        }
     }
 
     if sess.targ_cfg.os == abi::OsFreebsd {
-        args.push_all([~"-pthread", ~"-lrt",
-                       ~"-L/usr/local/lib", ~"-lexecinfo",
+        args.push_all([~"-L/usr/local/lib",
                        ~"-L/usr/local/lib/gcc46",
-                       ~"-L/usr/local/lib/gcc44", ~"-lstdc++",
-                       ~"-Wl,-z,origin",
-                       ~"-Wl,-rpath,/usr/local/lib/gcc46",
-                       ~"-Wl,-rpath,/usr/local/lib/gcc44"]);
+                       ~"-L/usr/local/lib/gcc44"]);
     }
 
     // Stack growth requires statically linking a __morestack function
     args.push(~"-lmorestack");
 
-    // Always want the runtime linked in
-    args.push(~"-lrustrt");
+    // FIXME (#2397): At some point we want to rpath our guesses as to
+    // where extern libraries might live, based on the
+    // addl_lib_search_paths
+    args.push_all(rpath::get_rpath_flags(sess, out_filename));
 
-    // FIXME (#2397): At some point we want to rpath our guesses as to where
-    // extern libraries might live, based on the addl_lib_search_paths
-    args.push_all(rpath::get_rpath_flags(sess, &output));
-
-    // Finally add all the linker arguments provided on the command line
+    // Finally add all the linker arguments provided on the command line along
+    // with any #[link_args] attributes found inside the crate
     args.push_all(sess.opts.linker_args);
-
+    for arg in cstore::get_used_link_args(sess.cstore).iter() {
+        args.push(arg.clone());
+    }
     return args;
 }
+
+// # Rust Crate linking
+//
+// Rust crates are not considered at all when creating an rlib output. All
+// dependencies will be linked when producing the final output (instead of
+// the intermediate rlib version)
+fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
+                            dylib: bool) {
+    // Converts a library file-stem into a cc -l argument
+    fn unlib(config: @session::config, stem: &str) -> ~str {
+        if stem.starts_with("lib") &&
+            config.os != abi::OsWin32 {
+            stem.slice(3, stem.len()).to_owned()
+        } else {
+            stem.to_owned()
+        }
+    }
+
+    let cstore = sess.cstore;
+    if !dylib && !sess.prefer_dynamic() {
+        // With an executable, things get a little interesting. As a limitation
+        // of the current implementation, we require that everything must be
+        // static, or everything must be dynamic. The reasons for this are a
+        // little subtle, but as with the above two cases, the goal is to
+        // prevent duplicate copies of the same library showing up. For example,
+        // a static immediate dependency might show up as an upstream dynamic
+        // dependency and we currently have no way of knowing that. We know that
+        // all dynamic libaries require dynamic dependencies (see above), so
+        // it's satisfactory to include either all static libraries or all
+        // dynamic libraries.
+        let crates = cstore::get_used_crates(cstore,
+                                             cstore::RequireStatic);
+        if crates.iter().all(|&(_, ref p)| p.is_some()) {
+            for &(cnum, ref path) in crates.iter() {
+                let cratepath = path.clone().unwrap();
+
+                // If we're linking to the static version of the crate, then
+                // we're mostly good to go. The caveat here is that we need to
+                // pull in the static crate's native dependencies.
+                args.push(cratepath.as_str().unwrap().to_owned());
+
+                let libs = csearch::get_native_libraries(sess.cstore, cnum);
+                for lib in libs.iter() {
+                    args.push("-l" + *lib);
+                }
+            }
+            return;
+        }
+    }
+
+    // This is a fallback of three different  cases of linking:
+    //
+    // * When creating a dynamic library, all inputs are required to be dynamic
+    //   as well
+    // * If an executable is created with a preference on dynamic linking, then
+    //   this case is the fallback
+    // * If an executable is being created, and one of the inputs is missing as
+    //   a static library, then this is the fallback case.
+    let crates = cstore::get_used_crates(cstore, cstore::RequireDynamic);
+    for &(cnum, ref path) in crates.iter() {
+        let cratepath = match *path {
+            Some(ref p) => p.clone(),
+            None => {
+                sess.err(format!("could not find dynamic library for: `{}`",
+                                 cstore::get_crate_data(sess.cstore, cnum).name));
+                return
+            }
+        };
+        // Just need to tell the linker about where the library lives and what
+        // its name is
+        let dir = cratepath.dirname_str().unwrap();
+        if !dir.is_empty() { args.push("-L" + dir); }
+        let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap());
+        args.push("-l" + libarg);
+    }
+}
+
+// # Native library linking
+//
+// User-supplied library search paths (-L on the cammand line) These are
+// the same paths used to find Rust crates, so some of them may have been
+// added already by the previous crate linking code. This only allows them
+// to be found at compile time so it is still entirely up to outside
+// forces to make sure that library can be found at runtime.
+//
+// Also note that the native libraries linked here are only the ones located
+// in the current crate. Upstream crates with native library dependencies
+// may have their native library pulled in above.
+fn add_local_native_libraries(args: &mut ~[~str], sess: Session) {
+    for path in sess.opts.addl_lib_search_paths.iter() {
+        // FIXME (#9639): This needs to handle non-utf8 paths
+        args.push("-L" + path.as_str().unwrap().to_owned());
+    }
+
+    let rustpath = filesearch::rust_path();
+    for path in rustpath.iter() {
+        // FIXME (#9639): This needs to handle non-utf8 paths
+        args.push("-L" + path.as_str().unwrap().to_owned());
+    }
+
+    for &(ref l, _) in cstore::get_used_libraries(sess.cstore).iter() {
+        args.push(~"-l" + *l);
+    }
+}
diff --git a/src/librustc/back/mips.rs b/src/librustc/back/mips.rs
index cbe39efdf8c..ce716f6f94b 100644
--- a/src/librustc/back/mips.rs
+++ b/src/librustc/back/mips.rs
@@ -63,6 +63,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs::
 
         target_triple: target_triple,
 
-        cc_args: ~[]
+        cc_args: ~[],
     };
 }
diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs
index 5f01a57c23f..0853213a529 100644
--- a/src/librustc/back/rpath.rs
+++ b/src/librustc/back/rpath.rs
@@ -21,8 +21,7 @@ fn not_win32(os: abi::Os) -> bool {
   os != abi::OsWin32
 }
 
-pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
-                    -> ~[~str] {
+pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) -> ~[~str] {
     let os = sess.targ_cfg.os;
 
     // No rpath on windows
@@ -30,18 +29,28 @@ pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
         return ~[];
     }
 
+    let mut flags = ~[];
+
+    if sess.targ_cfg.os == abi::OsFreebsd {
+        flags.push_all([~"-Wl,-rpath,/usr/local/lib/gcc46",
+                        ~"-Wl,-rpath,/usr/local/lib/gcc44",
+                        ~"-Wl,-z,origin"]);
+    }
+
     debug!("preparing the RPATH!");
 
     let sysroot = sess.filesearch.sysroot();
     let output = out_filename;
-    let libs = cstore::get_used_crate_files(sess.cstore);
+    let libs = cstore::get_used_crates(sess.cstore, cstore::RequireDynamic);
+    let libs = libs.move_iter().filter_map(|(_, l)| l.map(|p| p.clone())).collect();
     // We don't currently rpath extern libraries, but we know
     // where rustrt is and we know every rust program needs it
     let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess));
 
     let rpaths = get_rpaths(os, sysroot, output, libs,
                             sess.opts.target_triple);
-    rpaths_to_flags(rpaths)
+    flags.push_all(rpaths_to_flags(rpaths));
+    flags
 }
 
 fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
@@ -52,8 +61,11 @@ fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
 }
 
 pub fn rpaths_to_flags(rpaths: &[~str]) -> ~[~str] {
-    // FIXME (#9639): This needs to handle non-utf8 paths
-    rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",*rpath)).collect()
+    let mut ret = ~[];
+    for rpath in rpaths.iter() {
+        ret.push("-Wl,-rpath," + *rpath);
+    }
+    return ret;
 }
 
 fn get_rpaths(os: abi::Os,
diff --git a/src/librustc/back/target_strs.rs b/src/librustc/back/target_strs.rs
index 7baaac4fc95..87133237878 100644
--- a/src/librustc/back/target_strs.rs
+++ b/src/librustc/back/target_strs.rs
@@ -14,5 +14,5 @@ pub struct t {
     meta_sect_name: ~str,
     data_layout: ~str,
     target_triple: ~str,
-    cc_args: ~[~str]
+    cc_args: ~[~str],
 }
diff --git a/src/librustc/back/x86.rs b/src/librustc/back/x86.rs
index b3c92bc4057..de0372b83b9 100644
--- a/src/librustc/back/x86.rs
+++ b/src/librustc/back/x86.rs
@@ -46,6 +46,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs::
 
         target_triple: target_triple,
 
-        cc_args: ~[~"-m32"]
+        cc_args: ~[~"-m32"],
     };
 }
diff --git a/src/librustc/back/x86_64.rs b/src/librustc/back/x86_64.rs
index 3237085dfb9..dce4de3dce3 100644
--- a/src/librustc/back/x86_64.rs
+++ b/src/librustc/back/x86_64.rs
@@ -54,6 +54,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs::
 
         target_triple: target_triple,
 
-        cc_args: ~[~"-m64"]
+        cc_args: ~[~"-m64"],
     };
 }
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 8821f5f6229..7d13cb2da65 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -11,7 +11,7 @@
 
 use back::link;
 use back::{arm, x86, x86_64, mips};
-use driver::session::{Aggressive};
+use driver::session::{Aggressive, OutputExecutable};
 use driver::session::{Session, Session_, No, Less, Default};
 use driver::session;
 use front;
@@ -164,8 +164,11 @@ pub fn phase_2_configure_and_expand(sess: Session,
                                     mut crate: ast::Crate) -> ast::Crate {
     let time_passes = sess.time_passes();
 
-    *sess.building_library = session::building_library(sess.opts.crate_type,
-                                                       &crate, sess.opts.test);
+    *sess.building_library = session::building_library(sess.opts, &crate);
+    let want_exe = sess.opts.outputs.iter().any(|&o| o == OutputExecutable);
+    if *sess.building_library && want_exe {
+        sess.err("cannot build both a library and an executable");
+    }
 
     time(time_passes, "gated feature checking", (), |_|
          front::feature_gate::check_crate(sess, &crate));
@@ -225,10 +228,8 @@ pub fn phase_3_run_analysis_passes(sess: Session,
                        syntax::ast_map::map_crate(sess.diagnostic(), crate));
 
     time(time_passes, "external crate/lib resolution", (), |_|
-         creader::read_crates(sess.diagnostic(), crate, sess.cstore,
-                              sess.filesearch,
+         creader::read_crates(sess, crate,
                               session::sess_os_to_meta_os(sess.targ_cfg.os),
-                              sess.opts.is_static,
                               token::get_ident_interner()));
 
     let lang_items = time(time_passes, "language item collection", (), |_|
@@ -330,7 +331,8 @@ pub fn phase_3_run_analysis_passes(sess: Session,
 pub struct CrateTranslation {
     context: ContextRef,
     module: ModuleRef,
-    link: LinkMeta
+    link: LinkMeta,
+    crate_types: ~[~str],
 }
 
 /// Run the translation phase to LLVM, after which the AST and analysis can
@@ -390,6 +392,7 @@ pub fn phase_6_link_output(sess: Session,
                            outputs: &OutputFilenames) {
     time(sess.time_passes(), "linking", (), |_|
          link::link_binary(sess,
+                           trans.crate_types,
                            &outputs.obj_filename,
                            &outputs.out_filename,
                            trans.link));
@@ -417,11 +420,6 @@ pub fn stop_after_phase_5(sess: Session) -> bool {
         return true;
     }
 
-    if sess.opts.is_static && *sess.building_library {
-        debug!("building static library, returning early from compile_input");
-        return true;
-    }
-
     if sess.opts.jit {
         debug!("running JIT, returning early from compile_input");
         return true;
@@ -652,13 +650,21 @@ pub fn build_session_options(binary: @str,
                              matches: &getopts::Matches,
                              demitter: @diagnostic::Emitter)
                              -> @session::options {
-    let crate_type = if matches.opt_present("lib") {
-        session::lib_crate
-    } else if matches.opt_present("bin") {
-        session::bin_crate
-    } else {
-        session::unknown_crate
-    };
+    let mut outputs = ~[];
+    if matches.opt_present("rlib") {
+        outputs.push(session::OutputRlib)
+    }
+    if matches.opt_present("staticlib") {
+        outputs.push(session::OutputStaticlib)
+    }
+    // dynamic libraries are the "compiler blesssed" default library
+    if matches.opt_present("dylib") || matches.opt_present("lib") {
+        outputs.push(session::OutputDylib)
+    }
+    if matches.opt_present("bin") {
+        outputs.push(session::OutputExecutable)
+    }
+
     let parse_only = matches.opt_present("parse-only");
     let no_trans = matches.opt_present("no-trans");
 
@@ -750,11 +756,10 @@ pub fn build_session_options(binary: @str,
     let debuginfo = debugging_opts & session::debug_info != 0 ||
         extra_debuginfo;
 
-    let statik = debugging_opts & session::statik != 0;
-
     let addl_lib_search_paths = matches.opt_strs("L").map(|s| {
-      Path::init(s.as_slice())
+        Path::init(s.as_slice())
     }).move_iter().collect();
+    let ar = matches.opt_str("ar");
     let linker = matches.opt_str("linker");
     let linker_args = matches.opt_strs("link-args").flat_map( |a| {
         a.split(' ').map(|arg| arg.to_owned()).collect()
@@ -782,8 +787,7 @@ pub fn build_session_options(binary: @str,
     };
 
     let sopts = @session::options {
-        crate_type: crate_type,
-        is_static: statik,
+        outputs: outputs,
         gc: gc,
         optimize: opt_level,
         custom_passes: custom_passes,
@@ -795,6 +799,7 @@ pub fn build_session_options(binary: @str,
         jit: jit,
         output_type: output_type,
         addl_lib_search_paths: @mut addl_lib_search_paths,
+        ar: ar,
         linker: linker,
         linker_args: linker_args,
         maybe_sysroot: sysroot_opt,
@@ -871,7 +876,6 @@ pub fn parse_pretty(sess: Session, name: &str) -> PpMode {
 // rustc command line options
 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
  ~[
-  optflag("",  "bin", "Compile an executable crate (default)"),
   optflag("c", "",    "Compile and assemble, but do not link"),
   optmulti("", "cfg", "Configure the compilation
                           environment", "SPEC"),
@@ -881,8 +885,13 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] {
   optflag("h", "help","Display this message"),
   optmulti("L", "",   "Add a directory to the library search path",
                               "PATH"),
-  optflag("",  "lib", "Compile a library crate"),
+  optflag("",  "bin", "Compile an executable crate (default)"),
+  optflag("",  "lib", "Compile a rust library crate using the compiler's default"),
+  optflag("",  "rlib", "Compile a rust library crate as an rlib file"),
+  optflag("",  "staticlib", "Compile a static library crate"),
+  optflag("",  "dylib", "Compile a dynamic library crate"),
   optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"),
+  optopt("", "ar", "Program to use for managing archives instead of the default.", "AR"),
   optmulti("",  "link-args", "FLAGS is a space-separated list of flags
                             passed to the linker", "FLAGS"),
   optflag("",  "ls",  "List the symbols defined by a library crate"),
@@ -957,19 +966,16 @@ pub fn build_output_filenames(input: &input,
     let obj_path;
     let out_path;
     let sopts = sess.opts;
-    let stop_after_codegen =
-        sopts.output_type != link::output_type_exe ||
-            sopts.is_static && *sess.building_library;
-
-    let obj_suffix =
-        match sopts.output_type {
-          link::output_type_none => ~"none",
-          link::output_type_bitcode => ~"bc",
-          link::output_type_assembly => ~"s",
-          link::output_type_llvm_assembly => ~"ll",
-          // Object and exe output both use the '.o' extension here
-          link::output_type_object | link::output_type_exe => ~"o"
-        };
+    let stop_after_codegen = sopts.output_type != link::output_type_exe;
+
+    let obj_suffix = match sopts.output_type {
+        link::output_type_none => ~"none",
+        link::output_type_bitcode => ~"bc",
+        link::output_type_assembly => ~"s",
+        link::output_type_llvm_assembly => ~"ll",
+        // Object and exe output both use the '.o' extension here
+        link::output_type_object | link::output_type_exe => ~"o"
+    };
 
     match *ofile {
       None => {
@@ -1047,6 +1053,7 @@ pub fn early_error(emitter: @diagnostic::Emitter, msg: &str) -> ! {
 
 pub fn list_metadata(sess: Session, path: &Path, out: @mut io::Writer) {
     metadata::loader::list_file_metadata(
+        sess,
         token::get_ident_interner(),
         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
 }
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index e497caa4946..e45ea533f79 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -13,7 +13,6 @@ use back::link;
 use back::target_strs;
 use back;
 use driver::driver::host_triple;
-use driver::session;
 use metadata::filesearch;
 use metadata;
 use middle::lint;
@@ -30,13 +29,6 @@ use syntax;
 
 use std::hashmap::{HashMap,HashSet};
 
-#[deriving(Clone)]
-pub enum crate_type {
-    bin_crate,
-    lib_crate,
-    unknown_crate,
-}
-
 pub struct config {
     os: abi::Os,
     arch: abi::Architecture,
@@ -66,16 +58,16 @@ pub static gc:                      uint = 1 << 17;
 pub static jit:                     uint = 1 << 18;
 pub static debug_info:              uint = 1 << 19;
 pub static extra_debug_info:        uint = 1 << 20;
-pub static statik:                  uint = 1 << 21;
-pub static print_link_args:         uint = 1 << 22;
-pub static no_debug_borrows:        uint = 1 << 23;
-pub static lint_llvm:               uint = 1 << 24;
-pub static print_llvm_passes:       uint = 1 << 25;
-pub static no_vectorize_loops:      uint = 1 << 26;
-pub static no_vectorize_slp:        uint = 1 << 27;
-pub static no_prepopulate_passes:   uint = 1 << 28;
-pub static use_softfp:              uint = 1 << 29;
-pub static gen_crate_map:           uint = 1 << 30;
+pub static print_link_args:         uint = 1 << 21;
+pub static no_debug_borrows:        uint = 1 << 22;
+pub static lint_llvm:               uint = 1 << 23;
+pub static print_llvm_passes:       uint = 1 << 24;
+pub static no_vectorize_loops:      uint = 1 << 25;
+pub static no_vectorize_slp:        uint = 1 << 26;
+pub static no_prepopulate_passes:   uint = 1 << 27;
+pub static use_softfp:              uint = 1 << 28;
+pub static gen_crate_map:           uint = 1 << 29;
+pub static prefer_dynamic:          uint = 1 << 30;
 
 pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] {
     ~[("verbose", "in general, enable more debug printouts", verbose),
@@ -107,7 +99,6 @@ pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] {
      ("extra-debug-info", "Extra debugging info (experimental)",
       extra_debug_info),
      ("debug-info", "Produce debug info (experimental)", debug_info),
-     ("static", "Use or produce static libraries or binaries (experimental)", statik),
      ("no-debug-borrows",
       "do not show where borrow checks fail",
       no_debug_borrows),
@@ -129,6 +120,7 @@ pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] {
       no_vectorize_slp),
      ("soft-float", "Generate software floating point library calls", use_softfp),
      ("gen-crate-map", "Force generation of a toplevel crate map", gen_crate_map),
+     ("prefer-dynamic", "Prefer dynamic linking to static linking", prefer_dynamic),
     ]
 }
 
@@ -144,8 +136,8 @@ pub enum OptLevel {
 pub struct options {
     // The crate config requested for the session, which may be combined
     // with additional crate configurations during the compile process
-    crate_type: crate_type,
-    is_static: bool,
+    outputs: ~[OutputStyle],
+
     gc: bool,
     optimize: OptLevel,
     custom_passes: ~[~str],
@@ -159,6 +151,7 @@ pub struct options {
     addl_lib_search_paths: @mut HashSet<Path>, // This is mutable for rustpkg, which
                                                // updates search paths based on the
                                                // parsed code
+    ar: Option<~str>,
     linker: Option<~str>,
     linker_args: ~[~str],
     maybe_sysroot: Option<@Path>,
@@ -194,6 +187,14 @@ pub enum EntryFnType {
     EntryNone,
 }
 
+#[deriving(Eq, Clone)]
+pub enum OutputStyle {
+    OutputExecutable,
+    OutputDylib,
+    OutputRlib,
+    OutputStaticlib,
+}
+
 pub struct Session_ {
     targ_cfg: @config,
     opts: @options,
@@ -337,6 +338,9 @@ impl Session_ {
     pub fn gen_crate_map(&self) -> bool {
         self.debugging_opt(gen_crate_map)
     }
+    pub fn prefer_dynamic(&self) -> bool {
+        self.debugging_opt(prefer_dynamic)
+    }
 
     // pointless function, now...
     pub fn str_of(&self, id: ast::Ident) -> @str {
@@ -357,8 +361,7 @@ impl Session_ {
 /// Some reasonable defaults
 pub fn basic_options() -> @options {
     @options {
-        crate_type: session::lib_crate,
-        is_static: false,
+        outputs: ~[],
         gc: false,
         optimize: No,
         custom_passes: ~[],
@@ -370,6 +373,7 @@ pub fn basic_options() -> @options {
         jit: false,
         output_type: link::output_type_exe,
         addl_lib_search_paths: @mut HashSet::new(),
+        ar: None,
         linker: None,
         linker_args: ~[],
         maybe_sysroot: None,
@@ -391,24 +395,17 @@ pub fn expect<T:Clone>(sess: Session, opt: Option<T>, msg: || -> ~str) -> T {
     diagnostic::expect(sess.diagnostic(), opt, msg)
 }
 
-pub fn building_library(req_crate_type: crate_type,
-                        crate: &ast::Crate,
-                        testing: bool) -> bool {
-    match req_crate_type {
-      bin_crate => false,
-      lib_crate => true,
-      unknown_crate => {
-        if testing {
-            false
-        } else {
-            match syntax::attr::first_attr_value_str_by_name(
-                crate.attrs,
-                "crate_type") {
-              Some(s) => "lib" == s,
-              _ => false
-            }
+pub fn building_library(options: &options, crate: &ast::Crate) -> bool {
+    for output in options.outputs.iter() {
+        match *output {
+            OutputExecutable => {}
+            OutputStaticlib | OutputDylib | OutputRlib => return true
         }
-      }
+    }
+    if options.test { return false }
+    match syntax::attr::first_attr_value_str_by_name(crate.attrs, "crate_type") {
+        Some(s) => "lib" == s || "rlib" == s || "dylib" == s || "staticlib" == s,
+        _ => false
     }
 }
 
@@ -423,75 +420,3 @@ pub fn sess_os_to_meta_os(os: abi::Os) -> metadata::loader::Os {
         abi::OsFreebsd => loader::OsFreebsd
     }
 }
-
-#[cfg(test)]
-mod test {
-    use driver::session::{bin_crate, building_library, lib_crate};
-    use driver::session::{unknown_crate};
-
-    use syntax::ast;
-    use syntax::attr;
-    use syntax::codemap;
-
-    fn make_crate_type_attr(t: @str) -> ast::Attribute {
-        attr::mk_attr(attr::mk_name_value_item_str(@"crate_type", t))
-    }
-
-    fn make_crate(with_bin: bool, with_lib: bool) -> @ast::Crate {
-        let mut attrs = ~[];
-        if with_bin {
-            attrs.push(make_crate_type_attr(@"bin"));
-        }
-        if with_lib {
-            attrs.push(make_crate_type_attr(@"lib"));
-        }
-        @ast::Crate {
-            module: ast::_mod { view_items: ~[], items: ~[] },
-            attrs: attrs,
-            config: ~[],
-            span: codemap::dummy_sp(),
-        }
-    }
-
-    #[test]
-    fn bin_crate_type_attr_results_in_bin_output() {
-        let crate = make_crate(true, false);
-        assert!(!building_library(unknown_crate, crate, false));
-    }
-
-    #[test]
-    fn lib_crate_type_attr_results_in_lib_output() {
-        let crate = make_crate(false, true);
-        assert!(building_library(unknown_crate, crate, false));
-    }
-
-    #[test]
-    fn bin_option_overrides_lib_crate_type() {
-        let crate = make_crate(false, true);
-        assert!(!building_library(bin_crate, crate, false));
-    }
-
-    #[test]
-    fn lib_option_overrides_bin_crate_type() {
-        let crate = make_crate(true, false);
-        assert!(building_library(lib_crate, crate, false));
-    }
-
-    #[test]
-    fn bin_crate_type_is_default() {
-        let crate = make_crate(false, false);
-        assert!(!building_library(unknown_crate, crate, false));
-    }
-
-    #[test]
-    fn test_option_overrides_lib_crate_type() {
-        let crate = make_crate(false, true);
-        assert!(!building_library(unknown_crate, crate, true));
-    }
-
-    #[test]
-    fn test_option_does_not_override_requested_lib_type() {
-        let crate = make_crate(false, false);
-        assert!(building_library(lib_crate, crate, true));
-    }
-}
diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs
index 82ddb9c2f97..d18a8306812 100644
--- a/src/librustc/front/feature_gate.rs
+++ b/src/librustc/front/feature_gate.rs
@@ -21,6 +21,7 @@
 use middle::lint;
 
 use syntax::ast;
+use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
 use syntax::visit;
@@ -41,6 +42,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
     ("managed_boxes", Active),
     ("non_ascii_idents", Active),
     ("thread_local", Active),
+    ("link_args", Active),
 
     // These are used to test this portion of the compiler, they don't actually
     // mean anything
@@ -111,7 +113,8 @@ impl Visitor<()> for Context {
 
     fn visit_item(&mut self, i: @ast::item, _:()) {
         for attr in i.attrs.iter() {
-            if "thread_local" == attr.name() {
+            if "thread_local" == attr.name() &&
+               cfg!(stage0, remove_this_on_next_snapshot) { // NOTE: snap rem
                 self.gate_feature("thread_local", i.span,
                                   "`#[thread_local]` is an experimental feature, and does not \
                                   currently handle destructors. There is no corresponding \
@@ -132,6 +135,16 @@ impl Visitor<()> for Context {
                 }
             }
 
+            ast::item_foreign_mod(..) => {
+                if attr::contains_name(i.attrs, "link_args") &&
+                    cfg!(stage0, remove_this_on_next_snapshot) { // NOTE: snap
+                    self.gate_feature("link_args", i.span,
+                                      "the `link_args` attribute is not portable \
+                                       across platforms, it is recommended to \
+                                       use `#[link(name = \"foo\")]` instead")
+                }
+            }
+
             _ => {}
         }
 
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index b5703a62a1f..e0073e7ce12 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -16,9 +16,11 @@
 
 #[comment = "The Rust compiler"];
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "dylib"];
 
 #[feature(macro_rules, globs, struct_variant, managed_boxes)];
+#[allow(attribute_usage)]; // NOTE: remove after the next snapshot
 
 extern mod extra;
 extern mod syntax;
@@ -87,6 +89,7 @@ pub mod front {
 }
 
 pub mod back {
+    pub mod archive;
     pub mod link;
     pub mod abi;
     pub mod upcall;
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index a6a96b25ff5..5e5cc242358 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -14,7 +14,6 @@ use std::c_str::ToCStr;
 use std::hashmap::HashMap;
 use std::libc::{c_uint, c_ushort, c_void, free};
 use std::str::raw::from_c_str;
-use std::option;
 
 use middle::trans::type_::Type;
 
@@ -304,9 +303,16 @@ pub mod llvm {
     use super::{ValueRef, TargetMachineRef, FileType};
     use super::{CodeGenModel, RelocMode, CodeGenOptLevel};
     use super::debuginfo::*;
-    use std::libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong};
+    use std::libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong,
+                    size_t};
 
+    #[cfg(stage0)]
     #[link_args = "-lrustllvm"]
+    extern {}
+    #[cfg(not(stage0))] // if you're deleting this, put this on the block below
+    #[link(name = "rustllvm")]
+    extern {}
+
     extern {
         /* Create and destroy contexts. */
         pub fn LLVMContextCreate() -> ContextRef;
@@ -1426,6 +1432,16 @@ pub mod llvm {
             LLVMDisposeMemoryBuffer() to get rid of it. */
         pub fn LLVMRustCreateMemoryBufferWithContentsOfFile(Path: *c_char)
             -> MemoryBufferRef;
+        /** Borrows the contents of the memory buffer (doesn't copy it) */
+        pub fn LLVMCreateMemoryBufferWithMemoryRange(InputData: *c_char,
+                                                     InputDataLength: size_t,
+                                                     BufferName: *c_char,
+                                                     RequiresNull: Bool)
+            -> MemoryBufferRef;
+        pub fn LLVMCreateMemoryBufferWithMemoryRangeCopy(InputData: *c_char,
+                                                         InputDataLength: size_t,
+                                                         BufferName: *c_char)
+            -> MemoryBufferRef;
 
         /** Returns a string describing the last error caused by an LLVMRust*
             call. */
@@ -1901,38 +1917,32 @@ pub fn mk_pass_manager() -> PassManager {
 
 /* Memory-managed interface to object files. */
 
-pub struct object_file_res {
-    ObjectFile: ObjectFileRef,
+pub struct ObjectFile {
+    llof: ObjectFileRef,
 }
 
-impl Drop for object_file_res {
-    fn drop(&mut self) {
+impl ObjectFile {
+    // This will take ownership of llmb
+    pub fn new(llmb: MemoryBufferRef) -> Option<ObjectFile> {
         unsafe {
-            llvm::LLVMDisposeObjectFile(self.ObjectFile);
+            let llof = llvm::LLVMCreateObjectFile(llmb);
+            if llof as int == 0 {
+                llvm::LLVMDisposeMemoryBuffer(llmb);
+                return None
+            }
+
+            Some(ObjectFile {
+                llof: llof,
+            })
         }
     }
 }
 
-pub fn object_file_res(ObjFile: ObjectFileRef) -> object_file_res {
-    object_file_res {
-        ObjectFile: ObjFile
-    }
-}
-
-pub struct ObjectFile {
-    llof: ObjectFileRef,
-    dtor: @object_file_res
-}
-
-pub fn mk_object_file(llmb: MemoryBufferRef) -> Option<ObjectFile> {
-    unsafe {
-        let llof = llvm::LLVMCreateObjectFile(llmb);
-        if llof as int == 0 { return option::None::<ObjectFile>; }
-
-        option::Some(ObjectFile {
-            llof: llof,
-            dtor: @object_file_res(llof)
-        })
+impl Drop for ObjectFile {
+    fn drop(&mut self) {
+        unsafe {
+            llvm::LLVMDisposeObjectFile(self.llof);
+        }
     }
 }
 
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 5e6d0f27615..5ed1eac746c 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -197,6 +197,8 @@ pub static tag_region_param_def: uint = 0x100;
 pub static tag_region_param_def_ident: uint = 0x101;
 pub static tag_region_param_def_def_id: uint = 0x102;
 
+pub static tag_native_libraries: uint = 0x103;
+pub static tag_native_libraries_lib: uint = 0x104;
 
 pub struct LinkMeta {
     name: @str,
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index f18357999a1..9d28a5abed2 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -10,10 +10,9 @@
 
 //! Validates all used crates and extern libraries and loads their metadata
 
-
+use driver::session::Session;
 use metadata::cstore;
 use metadata::decoder;
-use metadata::filesearch::FileSearch;
 use metadata::loader;
 
 use std::hashmap::HashMap;
@@ -29,19 +28,13 @@ use syntax::visit;
 
 // Traverses an AST, reading all the information about use'd crates and extern
 // libraries necessary for later resolving, typechecking, linking, etc.
-pub fn read_crates(diag: @mut span_handler,
+pub fn read_crates(sess: Session,
                    crate: &ast::Crate,
-                   cstore: @mut cstore::CStore,
-                   filesearch: @FileSearch,
                    os: loader::Os,
-                   statik: bool,
                    intr: @ident_interner) {
     let e = @mut Env {
-        diag: diag,
-        filesearch: filesearch,
-        cstore: cstore,
+        sess: sess,
         os: os,
-        statik: statik,
         crate_cache: @mut ~[],
         next_crate_num: 1,
         intr: intr
@@ -50,7 +43,7 @@ pub fn read_crates(diag: @mut span_handler,
     visit_crate(e, crate);
     visit::walk_crate(&mut v, crate, ());
     dump_crates(*e.crate_cache);
-    warn_if_multiple_versions(e, diag, *e.crate_cache);
+    warn_if_multiple_versions(e, sess.diagnostic(), *e.crate_cache);
 }
 
 struct ReadCrateVisitor { e:@mut Env }
@@ -113,18 +106,15 @@ fn warn_if_multiple_versions(e: @mut Env,
 }
 
 struct Env {
-    diag: @mut span_handler,
-    filesearch: @FileSearch,
-    cstore: @mut cstore::CStore,
+    sess: Session,
     os: loader::Os,
-    statik: bool,
     crate_cache: @mut ~[cache_entry],
     next_crate_num: ast::CrateNum,
     intr: @ident_interner
 }
 
 fn visit_crate(e: &Env, c: &ast::Crate) {
-    let cstore = e.cstore;
+    let cstore = e.sess.cstore;
 
     for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
         match a.value_str() {
@@ -146,7 +136,7 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) {
                   let p_path = Path::init(p);
                   match p_path.filestem_str() {
                       None|Some("") =>
-                          e.diag.span_bug(i.span, "Bad package path in `extern mod` item"),
+                          e.sess.span_bug(i.span, "Bad package path in `extern mod` item"),
                       Some(s) =>
                           vec::append(
                               ~[attr::mk_name_value_item_str(@"package_id", p),
@@ -162,7 +152,7 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) {
                                    meta_items,
                                    @"",
                                    i.span);
-          cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
+          cstore::add_extern_mod_stmt_cnum(e.sess.cstore, id, cnum);
       }
       _ => ()
   }
@@ -170,60 +160,62 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) {
 
 fn visit_item(e: &Env, i: @ast::item) {
     match i.node {
-      ast::item_foreign_mod(ref fm) => {
-        if fm.abis.is_rust() || fm.abis.is_intrinsic() {
-            return;
-        }
-
-        let cstore = e.cstore;
-        let link_args = i.attrs.iter()
-            .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
-            .collect::<~[&ast::Attribute]>();
-
-        // XXX: two whom it may concern, this was the old logic applied to the
-        //      ast's extern mod blocks which had names (we used to allow
-        //      "extern mod foo"). This code was never run for anonymous blocks,
-        //      and we now only have anonymous blocks. We're still in the midst
-        //      of figuring out what the exact operations we'd like to support
-        //      when linking external modules, but I wanted to leave this logic
-        //      here for the time beging to refer back to it
-
-        //let mut already_added = false;
-        //let link_name = i.attrs.iter()
-        //    .find(|at| "link_name" == at.name())
-        //    .and_then(|at| at.value_str());
+        ast::item_foreign_mod(ref fm) => {
+            if fm.abis.is_rust() || fm.abis.is_intrinsic() {
+                return;
+            }
 
-        //let foreign_name = match link_name {
-        //        Some(nn) => {
-        //            if nn.is_empty() {
-        //                e.diag.span_fatal(
-        //                    i.span,
-        //                    "empty #[link_name] not allowed; use \
-        //                     #[nolink].");
-        //            }
-        //            nn
-        //        }
-        //        None => token::ident_to_str(&i.ident)
-        //    };
-        //if !attr::contains_name(i.attrs, "nolink") {
-        //    already_added =
-        //        !cstore::add_used_library(cstore, foreign_name);
-        //}
-        //if !link_args.is_empty() && already_added {
-        //    e.diag.span_fatal(i.span, ~"library '" + foreign_name +
-        //               "' already added: can't specify link_args.");
-        //}
+            // First, add all of the custom link_args attributes
+            let cstore = e.sess.cstore;
+            let link_args = i.attrs.iter()
+                .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
+                .to_owned_vec();
+            for m in link_args.iter() {
+                match m.value_str() {
+                    Some(linkarg) => {
+                        cstore::add_used_link_args(cstore, linkarg);
+                    }
+                    None => { /* fallthrough */ }
+                }
+            }
 
-        for m in link_args.iter() {
-            match m.value_str() {
-                Some(linkarg) => {
-                    cstore::add_used_link_args(cstore, linkarg);
+            // Next, process all of the #[link(..)]-style arguments
+            let cstore = e.sess.cstore;
+            let link_args = i.attrs.iter()
+                .filter_map(|at| if "link" == at.name() {Some(at)} else {None})
+                .to_owned_vec();
+            for m in link_args.iter() {
+                match m.meta_item_list() {
+                    Some(items) => {
+                        let kind = items.iter().find(|k| {
+                            "kind" == k.name()
+                        }).and_then(|a| a.value_str());
+                        let kind = match kind {
+                            Some(k) if "static" == k => cstore::NativeStatic,
+                            Some(k) => {
+                                e.sess.span_fatal(i.span,
+                                    format!("unknown kind: `{}`", k));
+                            }
+                            None => cstore::NativeUnknown
+                        };
+                        let n = items.iter().find(|n| {
+                            "name" == n.name()
+                        }).and_then(|a| a.value_str());
+                        let n = match n {
+                            Some(n) => n,
+                            None => {
+                                e.sess.span_fatal(i.span,
+                                    "#[link(...)] specified without \
+                                     `name = \"foo\"`");
+                            }
+                        };
+                        cstore::add_used_library(cstore, n.to_owned(), kind);
+                    }
+                    None => {}
                 }
-                None => { /* fallthrough */ }
             }
         }
-      }
-      _ => { }
+        _ => { }
     }
 }
 
@@ -263,24 +255,21 @@ fn resolve_crate(e: @mut Env,
     match existing_match(e, metas, hash) {
       None => {
         let load_ctxt = loader::Context {
-            diag: e.diag,
-            filesearch: e.filesearch,
+            sess: e.sess,
             span: span,
             ident: ident,
             metas: metas,
             hash: hash,
             os: e.os,
-            is_static: e.statik,
             intr: e.intr
         };
-        let (lident, ldata) = loader::load_library_crate(&load_ctxt);
+        let loader::Library {
+            dylib, rlib, metadata
+        } = load_ctxt.load_library_crate();
 
-        let cfilename = Path::init(lident);
-        let cdata = ldata;
-
-        let attrs = decoder::get_crate_attributes(cdata);
+        let attrs = decoder::get_crate_attributes(metadata);
         let linkage_metas = attr::find_linkage_metas(attrs);
-        let hash = decoder::get_crate_hash(cdata);
+        let hash = decoder::get_crate_hash(metadata);
 
         // Claim this crate number and cache it
         let cnum = e.next_crate_num;
@@ -293,7 +282,7 @@ fn resolve_crate(e: @mut Env,
         e.next_crate_num += 1;
 
         // Now resolve the crates referenced by this crate
-        let cnum_map = resolve_crate_deps(e, cdata);
+        let cnum_map = resolve_crate_deps(e, metadata);
 
         let cname =
             match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
@@ -303,14 +292,18 @@ fn resolve_crate(e: @mut Env,
             };
         let cmeta = @cstore::crate_metadata {
             name: cname,
-            data: cdata,
+            data: metadata,
             cnum_map: cnum_map,
             cnum: cnum
         };
 
-        let cstore = e.cstore;
+        let cstore = e.sess.cstore;
         cstore::set_crate_data(cstore, cnum, cmeta);
-        cstore::add_used_crate_file(cstore, &cfilename);
+        cstore::add_used_crate_source(cstore, cstore::CrateSource {
+            dylib: dylib,
+            rlib: rlib,
+            cnum: cnum,
+        });
         return cnum;
       }
       Some(cnum) => {
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index 9c9dba52453..96250fd5ec8 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -262,6 +262,12 @@ pub fn get_item_visibility(cstore: @mut cstore::CStore,
     decoder::get_item_visibility(cdata, def_id.node)
 }
 
+pub fn get_native_libraries(cstore: @mut cstore::CStore,
+                            crate_num: ast::CrateNum) -> ~[~str] {
+    let cdata = cstore::get_crate_data(cstore, crate_num);
+    decoder::get_native_libraries(cdata)
+}
+
 pub fn each_impl(cstore: @mut cstore::CStore,
                  crate_num: ast::CrateNum,
                  callback: |ast::DefId|) {
diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs
index bc15312d98e..50da84d3895 100644
--- a/src/librustc/metadata/cstore.rs
+++ b/src/librustc/metadata/cstore.rs
@@ -34,12 +34,33 @@ pub struct crate_metadata {
     cnum: ast::CrateNum
 }
 
+#[deriving(Eq)]
+pub enum LinkagePreference {
+    RequireDynamic,
+    RequireStatic,
+}
+
+#[deriving(Eq)]
+pub enum NativeLibaryKind {
+    NativeStatic,
+    NativeUnknown,
+}
+
+// Where a crate came from on the local filesystem. One of these two options
+// must be non-None.
+#[deriving(Eq)]
+pub struct CrateSource {
+    dylib: Option<Path>,
+    rlib: Option<Path>,
+    cnum: ast::CrateNum,
+}
+
 pub struct CStore {
     priv metas: HashMap <ast::CrateNum, @crate_metadata>,
     priv extern_mod_crate_map: extern_mod_crate_map,
-    priv used_crate_files: ~[Path],
-    priv used_libraries: ~[@str],
-    priv used_link_args: ~[@str],
+    priv used_crate_sources: ~[CrateSource],
+    priv used_libraries: ~[(~str, NativeLibaryKind)],
+    priv used_link_args: ~[~str],
     intr: @ident_interner
 }
 
@@ -50,7 +71,7 @@ pub fn mk_cstore(intr: @ident_interner) -> CStore {
     return CStore {
         metas: HashMap::new(),
         extern_mod_crate_map: HashMap::new(),
-        used_crate_files: ~[],
+        used_crate_sources: ~[],
         used_libraries: ~[],
         used_link_args: ~[],
         intr: intr
@@ -88,39 +109,50 @@ pub fn iter_crate_data(cstore: &CStore, i: |ast::CrateNum, @crate_metadata|) {
     }
 }
 
-pub fn add_used_crate_file(cstore: &mut CStore, lib: &Path) {
-    if !cstore.used_crate_files.contains(lib) {
-        cstore.used_crate_files.push((*lib).clone());
+pub fn add_used_crate_source(cstore: &mut CStore, src: CrateSource) {
+    if !cstore.used_crate_sources.contains(&src) {
+        cstore.used_crate_sources.push(src);
     }
 }
 
-pub fn get_used_crate_files(cstore: &CStore) -> ~[Path] {
-    // XXX(pcwalton): Bad copy.
-    return cstore.used_crate_files.clone();
+pub fn get_used_crate_sources<'a>(cstore: &'a CStore) -> &'a [CrateSource] {
+    cstore.used_crate_sources.as_slice()
+}
+
+pub fn get_used_crates(cstore: &CStore, prefer: LinkagePreference)
+    -> ~[(ast::CrateNum, Option<Path>)]
+{
+    let mut ret = ~[];
+    for src in cstore.used_crate_sources.iter() {
+        ret.push((src.cnum, match prefer {
+            RequireDynamic => src.dylib.clone(),
+            RequireStatic => src.rlib.clone(),
+        }));
+    }
+    return ret;
 }
 
-pub fn add_used_library(cstore: &mut CStore, lib: @str) -> bool {
+pub fn add_used_library(cstore: &mut CStore,
+                        lib: ~str, kind: NativeLibaryKind) -> bool {
     assert!(!lib.is_empty());
 
-    if cstore.used_libraries.iter().any(|x| x == &lib) { return false; }
-    cstore.used_libraries.push(lib);
+    if cstore.used_libraries.iter().any(|&(ref x, _)| x == &lib) { return false; }
+    cstore.used_libraries.push((lib, kind));
     true
 }
 
-pub fn get_used_libraries<'a>(cstore: &'a CStore) -> &'a [@str] {
-    let slice: &'a [@str] = cstore.used_libraries;
-    slice
+pub fn get_used_libraries<'a>(cstore: &'a CStore) -> &'a [(~str, NativeLibaryKind)] {
+    cstore.used_libraries.as_slice()
 }
 
 pub fn add_used_link_args(cstore: &mut CStore, args: &str) {
     for s in args.split(' ') {
-        cstore.used_link_args.push(s.to_managed());
+        cstore.used_link_args.push(s.to_owned());
     }
 }
 
-pub fn get_used_link_args<'a>(cstore: &'a CStore) -> &'a [@str] {
-    let slice: &'a [@str] = cstore.used_link_args;
-    slice
+pub fn get_used_link_args<'a>(cstore: &'a CStore) -> &'a [~str] {
+    cstore.used_link_args.as_slice()
 }
 
 pub fn add_extern_mod_stmt_cnum(cstore: &mut CStore,
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 48621459228..441f1620e4d 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -1529,3 +1529,13 @@ pub fn get_trait_of_method(cdata: Cmd, id: ast::NodeId, tcx: ty::ctxt)
     }
 }
 
+
+pub fn get_native_libraries(cdata: Cmd) -> ~[~str] {
+    let libraries = reader::get_doc(reader::Doc(cdata.data), tag_native_libraries);
+    let mut result = ~[];
+    reader::tagged_docs(libraries, tag_native_libraries_lib, |lib_doc| {
+        result.push(lib_doc.as_str());
+        true
+    });
+    return result;
+}
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 6da75397002..269054c6fd2 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -75,6 +75,7 @@ struct Stats {
     attr_bytes: u64,
     dep_bytes: u64,
     lang_item_bytes: u64,
+    native_lib_bytes: u64,
     impl_bytes: u64,
     misc_bytes: u64,
     item_bytes: u64,
@@ -1633,6 +1634,23 @@ fn encode_lang_items(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) {
     ebml_w.end_tag();   // tag_lang_items
 }
 
+fn encode_native_libraries(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) {
+    ebml_w.start_tag(tag_native_libraries);
+
+    for &(ref lib, kind) in cstore::get_used_libraries(ecx.cstore).iter() {
+        match kind {
+            cstore::NativeStatic => {} // these libraries are not propagated
+            cstore::NativeUnknown => {
+                ebml_w.start_tag(tag_native_libraries_lib);
+                ebml_w.writer.write(lib.as_bytes());
+                ebml_w.end_tag();
+            }
+        }
+    }
+
+    ebml_w.end_tag();
+}
+
 struct ImplVisitor<'self> {
     ecx: &'self EncodeContext<'self>,
     ebml_w: &'self mut writer::Encoder,
@@ -1750,6 +1768,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
         attr_bytes: 0,
         dep_bytes: 0,
         lang_item_bytes: 0,
+        native_lib_bytes: 0,
         impl_bytes: 0,
         misc_bytes: 0,
         item_bytes: 0,
@@ -1806,6 +1825,11 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
     encode_lang_items(&ecx, &mut ebml_w);
     ecx.stats.lang_item_bytes = wr.tell() - i;
 
+    // Encode the native libraries used
+    i = wr.tell();
+    encode_native_libraries(&ecx, &mut ebml_w);
+    ecx.stats.native_lib_bytes = wr.tell() - i;
+
     // Encode the def IDs of impls, for coherence checking.
     i = wr.tell();
     encode_impls(&ecx, crate, &mut ebml_w);
@@ -1842,6 +1866,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
         println!(" attribute bytes: {}", ecx.stats.attr_bytes);
         println!("       dep bytes: {}", ecx.stats.dep_bytes);
         println!(" lang item bytes: {}", ecx.stats.lang_item_bytes);
+        println!("    native bytes: {}", ecx.stats.native_lib_bytes);
         println!("      impl bytes: {}", ecx.stats.impl_bytes);
         println!("      misc bytes: {}", ecx.stats.misc_bytes);
         println!("      item bytes: {}", ecx.stats.item_bytes);
diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs
index ecd1c8985bd..40fca0f42f1 100644
--- a/src/librustc/metadata/loader.rs
+++ b/src/librustc/metadata/loader.rs
@@ -10,11 +10,12 @@
 
 //! Finds crate binaries and loads their metadata
 
-
-use lib::llvm::{False, llvm, mk_object_file, mk_section_iter};
+use back::archive::Archive;
+use driver::session::Session;
+use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
 use metadata::decoder;
 use metadata::encoder;
-use metadata::filesearch::{FileSearch, FileMatch, FileMatches, FileDoesntMatch};
+use metadata::filesearch::{FileMatches, FileDoesntMatch};
 use metadata::filesearch;
 use syntax::codemap::Span;
 use syntax::diagnostic::span_handler;
@@ -26,6 +27,7 @@ use syntax::attr::AttrMetaMethods;
 use std::c_str::ToCStr;
 use std::cast;
 use std::io;
+use std::libc;
 use std::num;
 use std::option;
 use std::os::consts::{macos, freebsd, linux, android, win32};
@@ -43,103 +45,176 @@ pub enum Os {
 }
 
 pub struct Context {
-    diag: @mut span_handler,
-    filesearch: @FileSearch,
+    sess: Session,
     span: Span,
     ident: @str,
     metas: ~[@ast::MetaItem],
     hash: @str,
     os: Os,
-    is_static: bool,
     intr: @ident_interner
 }
 
-pub fn load_library_crate(cx: &Context) -> (~str, @~[u8]) {
-    match find_library_crate(cx) {
-      Some(t) => t,
-      None => {
-        cx.diag.span_fatal(cx.span,
-                           format!("can't find crate for `{}`",
-                                cx.ident));
-      }
-    }
+pub struct Library {
+    dylib: Option<Path>,
+    rlib: Option<Path>,
+    metadata: @~[u8],
 }
 
-fn find_library_crate(cx: &Context) -> Option<(~str, @~[u8])> {
-    attr::require_unique_names(cx.diag, cx.metas);
-    find_library_crate_aux(cx, libname(cx), cx.filesearch)
-}
+impl Context {
+    pub fn load_library_crate(&self) -> Library {
+        match self.find_library_crate() {
+            Some(t) => t,
+            None => {
+                self.sess.span_fatal(self.span,
+                                     format!("can't find crate for `{}`",
+                                             self.ident));
+            }
+        }
+    }
 
-fn libname(cx: &Context) -> (~str, ~str) {
-    if cx.is_static { return (~"lib", ~".rlib"); }
-    let (dll_prefix, dll_suffix) = match cx.os {
-        OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
-        OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
-        OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
-        OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
-        OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
-    };
+    fn find_library_crate(&self) -> Option<Library> {
+        attr::require_unique_names(self.sess.diagnostic(), self.metas);
+        let filesearch = self.sess.filesearch;
+        let crate_name = crate_name_from_metas(self.metas);
+        let (dyprefix, dysuffix) = self.dylibname();
 
-    (dll_prefix.to_owned(), dll_suffix.to_owned())
-}
+        // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
+        let dylib_prefix = format!("{}{}-", dyprefix, crate_name);
+        let rlib_prefix = format!("lib{}-", crate_name);
 
-fn find_library_crate_aux(
-    cx: &Context,
-    (prefix, suffix): (~str, ~str),
-    filesearch: @filesearch::FileSearch
-) -> Option<(~str, @~[u8])> {
-    let crate_name = crate_name_from_metas(cx.metas);
-    // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
-    let prefix = format!("{}{}-", prefix, crate_name);
-    let mut matches = ~[];
-    filesearch::search(filesearch, |path| -> FileMatch {
-      // FIXME (#9639): This needs to handle non-utf8 paths
-      let path_str = path.filename_str();
-      match path_str {
-          None => FileDoesntMatch,
-          Some(path_str) =>
-              if path_str.starts_with(prefix) && path_str.ends_with(suffix) {
-                  debug!("{} is a candidate", path.display());
-                  match get_metadata_section(cx.os, path) {
-                      Some(cvec) =>
-                          if !crate_matches(cvec, cx.metas, cx.hash) {
-                              debug!("skipping {}, metadata doesn't match",
-                                  path.display());
-                              FileDoesntMatch
-                          } else {
-                              debug!("found {} with matching metadata", path.display());
-                              // FIXME (#9639): This needs to handle non-utf8 paths
-                              matches.push((path.as_str().unwrap().to_owned(), cvec));
-                              FileMatches
-                          },
-                      _ => {
-                          debug!("could not load metadata for {}", path.display());
-                          FileDoesntMatch
-                      }
-                  }
-               }
-               else {
-                   FileDoesntMatch
-               }
-      }
-    });
+        let mut matches = ~[];
+        filesearch::search(filesearch, |path| {
+            match path.filename_str() {
+                None => FileDoesntMatch,
+                Some(file) => {
+                    let (candidate, existing) = if file.starts_with(rlib_prefix) &&
+                                                   file.ends_with(".rlib") {
+                        debug!("{} is an rlib candidate", path.display());
+                        (true, self.add_existing_rlib(matches, path, file))
+                    } else if file.starts_with(dylib_prefix) &&
+                              file.ends_with(dysuffix) {
+                        debug!("{} is a dylib candidate", path.display());
+                        (true, self.add_existing_dylib(matches, path, file))
+                    } else {
+                        (false, false)
+                    };
+
+                    if candidate && existing {
+                        FileMatches
+                    } else if candidate {
+                        match get_metadata_section(self.sess, self.os, path,
+                                                   crate_name) {
+                            Some(cvec) =>
+                                if crate_matches(cvec, self.metas, self.hash) {
+                                    debug!("found {} with matching metadata",
+                                           path.display());
+                                    let (rlib, dylib) = if file.ends_with(".rlib") {
+                                        (Some(path.clone()), None)
+                                    } else {
+                                        (None, Some(path.clone()))
+                                    };
+                                    matches.push(Library {
+                                        rlib: rlib,
+                                        dylib: dylib,
+                                        metadata: cvec,
+                                    });
+                                    FileMatches
+                                } else {
+                                    debug!("skipping {}, metadata doesn't match",
+                                           path.display());
+                                    FileDoesntMatch
+                                },
+                                _ => {
+                                    debug!("could not load metadata for {}",
+                                           path.display());
+                                    FileDoesntMatch
+                                }
+                        }
+                    } else {
+                        FileDoesntMatch
+                    }
+                }
+            }
+        });
 
-    match matches.len() {
-        0 => None,
-        1 => Some(matches[0]),
-        _ => {
-            cx.diag.span_err(
-                    cx.span, format!("multiple matching crates for `{}`", crate_name));
-                cx.diag.handler().note("candidates:");
-                for pair in matches.iter() {
-                    let ident = pair.first();
-                    let data = pair.second();
-                    cx.diag.handler().note(format!("path: {}", ident));
-                    let attrs = decoder::get_crate_attributes(data);
-                    note_linkage_attrs(cx.intr, cx.diag, attrs);
+        match matches.len() {
+            0 => None,
+            1 => Some(matches[0]),
+            _ => {
+                self.sess.span_err(self.span,
+                    format!("multiple matching crates for `{}`", crate_name));
+                self.sess.note("candidates:");
+                for lib in matches.iter() {
+                    match lib.dylib {
+                        Some(ref p) => {
+                            self.sess.note(format!("path: {}", p.display()));
+                        }
+                        None => {}
+                    }
+                    match lib.rlib {
+                        Some(ref p) => {
+                            self.sess.note(format!("path: {}", p.display()));
+                        }
+                        None => {}
+                    }
+                    let attrs = decoder::get_crate_attributes(lib.metadata);
+                    note_linkage_attrs(self.intr, self.sess.diagnostic(), attrs);
                 }
-                cx.diag.handler().abort_if_errors();
+                self.sess.abort_if_errors();
                 None
+            }
+        }
+    }
+
+    fn add_existing_rlib(&self, libs: &mut [Library],
+                         path: &Path, file: &str) -> bool {
+        let (prefix, suffix) = self.dylibname();
+        let file = file.slice_from(3); // chop off 'lib'
+        let file = file.slice_to(file.len() - 5); // chop off '.rlib'
+        let file = format!("{}{}{}", prefix, file, suffix);
+
+        for lib in libs.mut_iter() {
+            match lib.dylib {
+                Some(ref p) if p.filename_str() == Some(file.as_slice()) => {
+                    assert!(lib.rlib.is_none()); // XXX: legit compiler error
+                    lib.rlib = Some(path.clone());
+                    return true;
+                }
+                Some(..) | None => {}
+            }
+        }
+        return false;
+    }
+
+    fn add_existing_dylib(&self, libs: &mut [Library],
+                          path: &Path, file: &str) -> bool {
+        let (prefix, suffix) = self.dylibname();
+        let file = file.slice_from(prefix.len());
+        let file = file.slice_to(file.len() - suffix.len());
+        let file = format!("lib{}.rlib", file);
+
+        for lib in libs.mut_iter() {
+            match lib.rlib {
+                Some(ref p) if p.filename_str() == Some(file.as_slice()) => {
+                    assert!(lib.dylib.is_none()); // XXX: legit compiler error
+                    lib.dylib = Some(path.clone());
+                    return true;
+                }
+                Some(..) | None => {}
+            }
+        }
+        return false;
+    }
+
+    // Returns the corresponding (prefix, suffix) that files need to have for
+    // dynamic libraries
+    fn dylibname(&self) -> (&'static str, &'static str) {
+        match self.os {
+            OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
+            OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
+            OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
+            OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
+            OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
         }
     }
 }
@@ -196,16 +271,26 @@ pub fn metadata_matches(extern_metas: &[@ast::MetaItem],
     local_metas.iter().all(|needed| attr::contains(extern_metas, *needed))
 }
 
-fn get_metadata_section(os: Os,
-                        filename: &Path) -> Option<@~[u8]> {
+fn get_metadata_section(sess: Session, os: Os, filename: &Path,
+                        crate_name: &str) -> Option<@~[u8]> {
     unsafe {
-        let mb = filename.with_c_str(|buf| {
-            llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
-        });
-        if mb as int == 0 { return option::None::<@~[u8]>; }
-        let of = match mk_object_file(mb) {
-            option::Some(of) => of,
-            _ => return option::None::<@~[u8]>
+        let mb = if filename.filename_str().unwrap().ends_with(".rlib") {
+            let archive = Archive::open(sess, filename.clone());
+            let contents = archive.read(crate_name + ".o");
+            let ptr = vec::raw::to_ptr(contents);
+            crate_name.with_c_str(|name| {
+                llvm::LLVMCreateMemoryBufferWithMemoryRangeCopy(
+                    ptr as *i8, contents.len() as libc::size_t, name)
+            })
+        } else {
+            filename.with_c_str(|buf| {
+                llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
+            })
+        };
+        if mb as int == 0 { return None }
+        let of = match ObjectFile::new(mb) {
+            Some(of) => of,
+            _ => return None
         };
         let si = mk_section_iter(of.llof);
         while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
@@ -266,11 +351,17 @@ pub fn read_meta_section_name(os: Os) -> &'static str {
 }
 
 // A diagnostic function for dumping crate metadata to an output stream
-pub fn list_file_metadata(intr: @ident_interner,
+pub fn list_file_metadata(sess: Session,
+                          intr: @ident_interner,
                           os: Os,
                           path: &Path,
                           out: @mut io::Writer) {
-    match get_metadata_section(os, path) {
+    // guess the crate name from the pathname
+    let crate_name = path.filename_str().unwrap();
+    let crate_name = if crate_name.starts_with("lib") {
+        crate_name.slice_from(3) } else { crate_name };
+    let crate_name = crate_name.split('-').next().unwrap();
+    match get_metadata_section(sess, os, path, crate_name) {
       option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out),
       option::None => {
         write!(out, "could not find metadata in {}.\n", path.display())
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 4c6ca53694a..1b2ae78c627 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -808,7 +808,7 @@ fn check_heap_item(cx: &Context, it: &ast::item) {
 }
 
 static crate_attrs: &'static [&'static str] = &[
-    "crate_type", "link", "feature", "no_uv", "no_main", "no_std",
+    "crate_type", "feature", "no_uv", "no_main", "no_std",
     "desc", "comment", "license", "copyright", // not used in rustc now
 ];
 
@@ -830,7 +830,7 @@ static other_attrs: &'static [&'static str] = &[
     "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
     "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze",
     "no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag",
-    "packed", "simd", "repr", "deriving", "unsafe_destructor",
+    "packed", "simd", "repr", "deriving", "unsafe_destructor", "link",
 
     //mod-level
     "path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude",
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index ac7b8240176..deb4f00d7a0 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -77,14 +77,14 @@ use extra::time;
 use extra::sort;
 use syntax::ast::Name;
 use syntax::ast_map::{path, path_elt_to_str, path_name, path_pretty_name};
-use syntax::ast_util::{local_def};
+use syntax::ast_util::{local_def, is_local};
 use syntax::attr;
-use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::parse::token::{special_idents};
 use syntax::print::pprust::stmt_to_str;
 use syntax::{ast, ast_util, codemap, ast_map};
+use syntax::attr::AttrMetaMethods;
 use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic, OsWin32, OsAndroid};
 use syntax::visit;
 use syntax::visit::Visitor;
@@ -2996,7 +2996,7 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
     return map;
 }
 
-pub fn fill_crate_map(ccx: &mut CrateContext, map: ValueRef) {
+pub fn fill_crate_map(ccx: @mut CrateContext, map: ValueRef) {
     let mut subcrates: ~[ValueRef] = ~[];
     let mut i = 1;
     let cstore = ccx.sess.cstore;
@@ -3014,19 +3014,20 @@ pub fn fill_crate_map(ccx: &mut CrateContext, map: ValueRef) {
         subcrates.push(p2i(ccx, cr));
         i += 1;
     }
-    let event_loop_factory = if !*ccx.sess.building_library {
-        match ccx.tcx.lang_items.event_loop_factory() {
-            Some(did) => unsafe {
+    let event_loop_factory = match ccx.tcx.lang_items.event_loop_factory() {
+        Some(did) => unsafe {
+            if is_local(did) {
+                llvm::LLVMConstPointerCast(get_item_val(ccx, did.node),
+                                           ccx.int_type.ptr_to().to_ref())
+            } else {
                 let name = csearch::get_symbol(ccx.sess.cstore, did);
                 let global = name.with_c_str(|buf| {
                     llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type.to_ref(), buf)
                 });
                 global
-            },
-            None => C_null(ccx.int_type.ptr_to())
-        }
-    } else {
-        C_null(ccx.int_type.ptr_to())
+            }
+        },
+        None => C_null(ccx.int_type.ptr_to())
     };
     unsafe {
         let maptype = Type::array(&ccx.int_type, subcrates.len() as u64);
@@ -3106,18 +3107,6 @@ pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) {
     }
 }
 
-// Writes the current ABI version into the crate.
-pub fn write_abi_version(ccx: &mut CrateContext) {
-    unsafe {
-        let llval = C_uint(ccx, abi::abi_version);
-        let llglobal = "rust_abi_version".with_c_str(|buf| {
-            llvm::LLVMAddGlobal(ccx.llmod, val_ty(llval).to_ref(), buf)
-        });
-        llvm::LLVMSetInitializer(llglobal, llval);
-        llvm::LLVMSetGlobalConstant(llglobal, True);
-    }
-}
-
 pub fn trans_crate(sess: session::Session,
                    crate: ast::Crate,
                    analysis: &CrateAnalysis,
@@ -3177,7 +3166,6 @@ pub fn trans_crate(sess: session::Session,
     }
 
     glue::emit_tydescs(ccx);
-    write_abi_version(ccx);
     if ccx.sess.opts.debuginfo {
         debuginfo::finalize(ccx);
     }
@@ -3217,10 +3205,18 @@ pub fn trans_crate(sess: session::Session,
     let llcx = ccx.llcx;
     let link_meta = ccx.link_meta;
     let llmod = ccx.llmod;
+    let crate_types = crate.attrs.iter().filter_map(|a| {
+        if "crate_type" == a.name() {
+            a.value_str()
+        } else {
+            None
+        }
+    }).map(|a| a.to_owned()).collect();
 
     return CrateTranslation {
         context: llcx,
         module: llmod,
-        link: link_meta
+        link: link_meta,
+        crate_types: crate_types,
     };
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index ac7c50fdfd8..3dcc1dbebca 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -67,7 +67,7 @@ struct buf {
 }
 
 // sundown FFI
-#[link_args = "-lsundown"]
+#[link(name = "sundown", kind = "static")]
 extern {
     fn sdhtml_renderer(callbacks: *sd_callbacks,
                        options_ptr: *html_renderopt,
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index e699aa2edef..7cab7846f15 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -16,7 +16,8 @@
 
 #[desc = "rustdoc, the Rust documentation extractor"];
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "dylib"];
 
 #[feature(globs, struct_variant, managed_boxes)];
 
diff --git a/src/librustpkg/lib.rs b/src/librustpkg/lib.rs
index b87bd0c5824..157d8ba0105 100644
--- a/src/librustpkg/lib.rs
+++ b/src/librustpkg/lib.rs
@@ -17,7 +17,8 @@
        url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")];
 
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "dylib"];
 
 #[feature(globs, managed_boxes)];
 
@@ -114,7 +115,7 @@ impl<'self> PkgScript<'self> {
         let options = @session::options {
             binary: binary,
             maybe_sysroot: Some(@sysroot),
-            crate_type: session::bin_crate,
+            outputs: ~[session::OutputExecutable],
             .. (*session::basic_options()).clone()
         };
         let input = driver::file_input(script.clone());
diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs
index 5e867951b54..3b177b449d9 100644
--- a/src/librustpkg/tests.rs
+++ b/src/librustpkg/tests.rs
@@ -36,6 +36,7 @@ use path_util::{target_executable_in_workspace, target_test_in_workspace,
                chmod_read_only, platform_library_name};
 use rustc::back::link::get_cc_prog;
 use rustc::metadata::filesearch::rust_path;
+use rustc::driver::session;
 use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups};
 use syntax::diagnostic;
 use target::*;
@@ -1829,10 +1830,11 @@ fn test_linker_build() {
                                 @diagnostic::Emitter);
     let test_sys = test_sysroot();
     // FIXME (#9639): This needs to handle non-utf8 paths
+    let cc = get_cc_prog(sess);
     command_line_test([test_sys.as_str().unwrap().to_owned(),
                        ~"install",
                        ~"--linker",
-                       get_cc_prog(sess),
+                       cc,
                        ~"foo"],
                       workspace);
     assert_executable_exists(workspace, "foo");
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index f21357d0271..a8dbf145e53 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -25,7 +25,6 @@ use syntax::visit::Visitor;
 use syntax::util::small_vector::SmallVector;
 use rustc::back::link::output_type_exe;
 use rustc::back::link;
-use rustc::driver::session::{lib_crate, bin_crate};
 use context::{in_target, StopBefore, Link, Assemble, BuildContext};
 use package_id::PkgId;
 use package_source::PkgSrc;
@@ -195,8 +194,8 @@ pub fn compile_input(context: &BuildContext,
     debug!("compile_input's sysroot = {}", csysroot.display());
 
     let crate_type = match what {
-        Lib => lib_crate,
-        Test | Bench | Main => bin_crate
+        Lib => session::OutputDylib,
+        Test | Bench | Main => session::OutputExecutable,
     };
     let matches = getopts(debug_flags()
                           + match what {
@@ -239,7 +238,7 @@ pub fn compile_input(context: &BuildContext,
     debug!("Output type = {:?}", output_type);
 
     let options = @session::options {
-        crate_type: crate_type,
+        outputs: ~[crate_type],
         optimize: opt,
         test: what == Test || what == Bench,
         maybe_sysroot: Some(sysroot_to_use),
diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs
index 76882e885a8..a43759a6da7 100644
--- a/src/librustuv/lib.rs
+++ b/src/librustuv/lib.rs
@@ -41,9 +41,12 @@ via `close` and `delete` methods.
        url = "https://github.com/mozilla/rust/tree/master/src/librustuv")];
 
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "rlib"];
+#[crate_type = "dylib"];
 
 #[feature(macro_rules, globs)];
+#[allow(attribute_usage)]; // NOTE: remove after the next snapshot
 
 use std::cast::transmute;
 use std::cast;
diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs
index 3917efe9ca8..ca5c75122a6 100644
--- a/src/librustuv/uvll.rs
+++ b/src/librustuv/uvll.rs
@@ -524,8 +524,13 @@ pub unsafe fn guess_handle(handle: c_int) -> c_int {
 // second copies of everything. We obviously don't want this, so instead of
 // dying horribly during testing, we allow all of the test rustuv's references
 // to get resolved to the original rustuv crate.
-#[link_args = "-luv_support -luv"]
-#[cfg(not(test))]
+#[cfg(not(test), not(stage0))]
+#[link(name = "uv_support", kind = "static")]
+#[link(name = "uv", kind = "static")]
+extern {}
+
+#[cfg(not(test), stage0)]
+#[link_args = "-luv -luv_support"]
 extern {}
 
 extern {
@@ -717,12 +722,26 @@ extern {
     pub fn uv_signal_stop(handle: *uv_signal_t) -> c_int;
 }
 
-// libuv requires various system libraries to successfully link on some
-// platforms
-#[cfg(target_os = "linux")]
+// various platform libraries required by libuv
+#[cfg(not(stage0), not(target_os = "android"))]
+#[link(name = "pthread")]
+extern {}
+#[cfg(stage0)]
 #[link_args = "-lpthread"]
 extern {}
 
-#[cfg(target_os = "win32")]
+#[cfg(target_os = "win32", not(stage0))]
+#[link(name = "ws2_32")]
+#[link(name = "psapi")]
+#[link(name = "iphlpapi")]
+extern {}
+#[cfg(target_os = "win32", stage0)]
 #[link_args = "-lws2_32 -lpsapi -liphlpapi"]
 extern {}
+
+#[cfg(target_os = "freebsd", not(stage0))]
+#[link(name = "kvm")]
+extern {}
+#[cfg(target_os = "freebsd", stage0)]
+#[link_args = "-lkvm"]
+extern {}
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index a72bc6b8328..296091d26e6 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -51,7 +51,9 @@
 
 #[comment = "The Rust standard library"];
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "rlib"];
+#[crate_type = "dylib"];
 
 #[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
@@ -64,6 +66,7 @@
 
 #[deny(non_camel_case_types)];
 #[deny(missing_doc)];
+#[allow(attribute_usage)]; // NOTE: remove after the next snapshot
 
 // When testing libstd, bring in libuv as the I/O backend so tests can print
 // things and all of the std::io tests have an I/O interface to run on top
@@ -79,15 +82,7 @@
 #[cfg(test)] pub use ops = realstd::ops;
 #[cfg(test)] pub use cmp = realstd::cmp;
 
-// On Linux, link to the runtime with -lrt.
-#[cfg(target_os = "linux")]
-#[doc(hidden)]
-pub mod linkhack {
-    #[link_args="-lrustrt -lrt"]
-    #[link_args = "-lpthread"]
-    extern {
-    }
-}
+mod rtdeps;
 
 /* The Prelude. */
 
diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs
new file mode 100644
index 00000000000..ba4f120f626
--- /dev/null
+++ b/src/libstd/rtdeps.rs
@@ -0,0 +1,54 @@
+// Copyright 2013 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.
+
+//! This module contains the linkage attributes to all runtime dependencies of
+//! the stndard library This varies per-platform, but these libraries are
+//! necessary for running libstd.
+
+// All platforms need to link to rustrt
+#[link(name = "rustrt", kind = "static")]
+extern {}
+
+// LLVM implements the `frem` instruction as a call to `fmod`, which lives in
+// libm. Hence, we must explicitly link to it.
+//
+// On linux librt and libdl are indirect dependencies via rustrt,
+// and binutils 2.22+ won't add them automatically
+#[cfg(target_os = "linux")]
+#[link(name = "rt")]
+#[link(name = "dl")]
+#[link(name = "m")]
+#[link(name = "pthread")]
+#[link(name = "stdc++")]
+extern {}
+
+#[cfg(target_os = "android")]
+#[link(name = "dl")]
+#[link(name = "log")]
+#[link(name = "supc++")]
+#[link(name = "gnustl_shared")]
+#[link(name = "m")]
+extern {}
+
+#[cfg(target_os = "freebsd")]
+#[link(name = "execinfo")]
+#[link(name = "rt")]
+#[link(name = "stdc++")]
+#[link(name = "pthread")]
+extern {}
+
+#[cfg(target_os = "macos")]
+#[link(name = "pthread")]
+#[link(name = "stdc++")]
+extern {}
+
+#[cfg(stage0)]
+#[link_args = "-lstdc++"]
+extern {}
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 351eab35a77..7fd503a8d42 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -19,7 +19,8 @@
        uuid = "9311401b-d6ea-4cd9-a1d9-61f89499c645")];
 
 #[license = "MIT/ASL2"];
-#[crate_type = "lib"];
+#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot
+#[crate_type = "dylib"];
 
 #[feature(macro_rules, globs, managed_boxes)];
 
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
deleted file mode 100644
index 2a3e687ee55..00000000000
--- a/src/rt/rustrt.def.in
+++ /dev/null
@@ -1,51 +0,0 @@
-rust_dbg_abi_1
-rust_dbg_abi_2
-rust_dbg_static_mut
-rust_dbg_static_mut_check_four
-rust_get_time
-rust_tzset
-rust_gmtime
-rust_localtime
-rust_timegm
-rust_mktime
-rust_precise_time_ns
-rust_list_dir_val
-rust_list_dir_wfd_size
-rust_list_dir_wfd_fp_buf
-rust_unset_sigprocmask
-rust_env_pairs
-rust_win32_rand_acquire
-rust_win32_rand_gen
-rust_win32_rand_release
-upcall_rust_personality
-upcall_reset_stack_limit
-rust_dbg_call
-rust_dbg_do_nothing
-tdefl_compress_mem_to_heap
-tinfl_decompress_mem_to_heap
-rust_swap_registers
-rust_readdir
-rust_opendir
-rust_dbg_extern_identity_u32
-rust_dbg_extern_identity_u64
-rust_dbg_extern_identity_TwoU8s
-rust_dbg_extern_identity_TwoU16s
-rust_dbg_extern_identity_TwoU32s
-rust_dbg_extern_identity_TwoU64s
-rust_dbg_extern_identity_TwoDoubles
-rust_dbg_extern_return_TwoU8s
-rust_dbg_extern_return_TwoU16s
-rust_dbg_extern_return_TwoU32s
-rust_dbg_extern_return_TwoU64s
-rust_dbg_extern_identity_double
-rust_dbg_extern_identity_u8
-rust_try
-rust_begin_unwind
-rust_valgrind_stack_register
-rust_valgrind_stack_deregister
-rust_running_on_valgrind
-rust_get_num_cpus
-rust_get_test_int
-rust_pthread_mutex_t_size
-rust_pthread_cond_t_size
-rust_crit_section_size
diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in
index a6b887f058a..069f29e909d 100644
--- a/src/rustllvm/rustllvm.def.in
+++ b/src/rustllvm/rustllvm.def.in
@@ -630,3 +630,5 @@ LLVMAddReturnAttribute
 LLVMRemoveReturnAttribute
 LLVMTypeToString
 LLVMAddColdAttribute
+LLVMCreateMemoryBufferWithMemoryRange
+LLVMCreateMemoryBufferWithMemoryRangeCopy
diff --git a/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs b/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
index ca9c6eb79f9..01a90f1ad44 100644
--- a/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
+++ b/src/test/auxiliary/anon-extern-mod-cross-crate-1.rs
@@ -8,14 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[link_name = "rustrt"];
-#[link(name = "anonexternmod",
-       vers = "0.1")];
+#[link(name = "anonexternmod", vers = "0.1")];
 
 #[crate_type = "lib"];
 
 use std::libc;
 
 extern {
+    #[link(name = "rustrt")]
     pub fn rust_get_test_int() -> libc::intptr_t;
 }
diff --git a/src/test/auxiliary/extern-crosscrate-source.rs b/src/test/auxiliary/extern-crosscrate-source.rs
index e77efce7f58..89317a45171 100644
--- a/src/test/auxiliary/extern-crosscrate-source.rs
+++ b/src/test/auxiliary/extern-crosscrate-source.rs
@@ -18,6 +18,7 @@ use std::libc;
 pub mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/auxiliary/foreign_lib.rs b/src/test/auxiliary/foreign_lib.rs
index d67e329b04e..4705d13cd96 100644
--- a/src/test/auxiliary/foreign_lib.rs
+++ b/src/test/auxiliary/foreign_lib.rs
@@ -13,6 +13,7 @@
 pub mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         fn rust_get_test_int() -> libc::intptr_t;
     }
diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/Makefile b/src/test/run-make/bootstrap-from-c-with-uvio/Makefile
new file mode 100644
index 00000000000..7f466573da7
--- /dev/null
+++ b/src/test/run-make/bootstrap-from-c-with-uvio/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) lib.rs -Z gen-crate-map
+	ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot)
+	$(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR)
+	$(call RUN,main)
+	rm $(call DYLIB,boot)
+	$(call FAIL,main)
diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs
new file mode 100644
index 00000000000..85941ec74a8
--- /dev/null
+++ b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs
@@ -0,0 +1,25 @@
+// Copyright 2013 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.
+
+#[link(package_id = "boot", name = "boot", vers = "0.1")];
+#[crate_type = "lib"];
+
+extern mod rustuv; // pull in uvio
+
+use std::rt;
+
+#[no_mangle] // this needs to get called from C
+pub extern "C" fn foo(argc: int, argv: **u8) -> int {
+    do rt::start(argc, argv) {
+        do spawn {
+            println!("hello");
+        }
+    }
+}
diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/main.c b/src/test/run-make/bootstrap-from-c-with-uvio/main.c
new file mode 100644
index 00000000000..1872c1ea43b
--- /dev/null
+++ b/src/test/run-make/bootstrap-from-c-with-uvio/main.c
@@ -0,0 +1,16 @@
+// Copyright 2013 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.
+
+// this is the rust entry point that we're going to call.
+int foo(int argc, char *argv[]);
+
+int main(int argc, char *argv[]) {
+  return foo(argc, argv);
+}
diff --git a/src/test/run-make/c-dynamic-dylib/Makefile b/src/test/run-make/c-dynamic-dylib/Makefile
new file mode 100644
index 00000000000..2b2e5d56e92
--- /dev/null
+++ b/src/test/run-make/c-dynamic-dylib/Makefile
@@ -0,0 +1,14 @@
+-include ../tools.mk
+
+# This hits an assertion in the linker on older versions of osx apparently
+ifeq ($(shell uname),Darwin)
+all:
+	echo ignored
+else
+all: $(call DYLIB,cfoo)
+	$(RUSTC) foo.rs
+	$(RUSTC) bar.rs
+	$(call RUN,bar)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,cfoo)
+	$(call FAIL,bar)
+endif
diff --git a/src/test/run-make/c-dynamic-dylib/bar.rs b/src/test/run-make/c-dynamic-dylib/bar.rs
new file mode 100644
index 00000000000..7c4aca1e028
--- /dev/null
+++ b/src/test/run-make/c-dynamic-dylib/bar.rs
@@ -0,0 +1,5 @@
+extern mod foo;
+
+fn main() {
+    foo::rsfoo();
+}
diff --git a/src/test/run-make/c-dynamic-dylib/cfoo.c b/src/test/run-make/c-dynamic-dylib/cfoo.c
new file mode 100644
index 00000000000..9fe07f82f9e
--- /dev/null
+++ b/src/test/run-make/c-dynamic-dylib/cfoo.c
@@ -0,0 +1 @@
+int foo() { return 0; }
diff --git a/src/test/run-make/c-dynamic-dylib/foo.rs b/src/test/run-make/c-dynamic-dylib/foo.rs
new file mode 100644
index 00000000000..0c4a6f7df4b
--- /dev/null
+++ b/src/test/run-make/c-dynamic-dylib/foo.rs
@@ -0,0 +1,10 @@
+#[crate_type = "dylib"];
+
+#[link(name = "cfoo")]
+extern {
+    fn foo();
+}
+
+pub fn rsfoo() {
+    unsafe { foo() }
+}
diff --git a/src/test/run-make/c-dynamic-rlib/Makefile b/src/test/run-make/c-dynamic-rlib/Makefile
new file mode 100644
index 00000000000..18992703b2c
--- /dev/null
+++ b/src/test/run-make/c-dynamic-rlib/Makefile
@@ -0,0 +1,14 @@
+-include ../tools.mk
+
+# This hits an assertion in the linker on older versions of osx apparently
+ifeq ($(shell uname),Darwin)
+all:
+	echo ignored
+else
+all: $(call DYLIB,cfoo)
+	$(RUSTC) foo.rs
+	$(RUSTC) bar.rs
+	LD_LIBRARY_PATH=$(TMPDIR) $(call RUN,bar)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,cfoo)
+	$(call FAIL,bar)
+endif
diff --git a/src/test/run-make/c-dynamic-rlib/bar.rs b/src/test/run-make/c-dynamic-rlib/bar.rs
new file mode 100644
index 00000000000..7c4aca1e028
--- /dev/null
+++ b/src/test/run-make/c-dynamic-rlib/bar.rs
@@ -0,0 +1,5 @@
+extern mod foo;
+
+fn main() {
+    foo::rsfoo();
+}
diff --git a/src/test/run-make/c-dynamic-rlib/cfoo.c b/src/test/run-make/c-dynamic-rlib/cfoo.c
new file mode 100644
index 00000000000..9fe07f82f9e
--- /dev/null
+++ b/src/test/run-make/c-dynamic-rlib/cfoo.c
@@ -0,0 +1 @@
+int foo() { return 0; }
diff --git a/src/test/run-make/c-dynamic-rlib/foo.rs b/src/test/run-make/c-dynamic-rlib/foo.rs
new file mode 100644
index 00000000000..e64811af9c7
--- /dev/null
+++ b/src/test/run-make/c-dynamic-rlib/foo.rs
@@ -0,0 +1,10 @@
+#[crate_type = "rlib"];
+
+#[link(name = "cfoo")]
+extern {
+    fn foo();
+}
+
+pub fn rsfoo() {
+    unsafe { foo() }
+}
diff --git a/src/test/run-make/c-link-to-rust-dylib/Makefile b/src/test/run-make/c-link-to-rust-dylib/Makefile
new file mode 100644
index 00000000000..fb57a08a826
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-dylib/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) foo.rs
+	ln -s $(call DYLIB,foo-*) $(call DYLIB,foo)
+	$(CC) bar.c -lfoo -o $(call RUN,bar) -Wl,-rpath,$(TMPDIR)
+	$(call RUN,bar)
+	rm $(call DYLIB,foo)
+	$(call FAIL,bar)
diff --git a/src/test/run-make/c-link-to-rust-dylib/bar.c b/src/test/run-make/c-link-to-rust-dylib/bar.c
new file mode 100644
index 00000000000..bb4036b06e1
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-dylib/bar.c
@@ -0,0 +1,6 @@
+void foo();
+
+int main() {
+    foo();
+    return 0;
+}
diff --git a/src/test/run-make/c-link-to-rust-dylib/foo.rs b/src/test/run-make/c-link-to-rust-dylib/foo.rs
new file mode 100644
index 00000000000..6b6a786ef54
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-dylib/foo.rs
@@ -0,0 +1,4 @@
+#[crate_type = "dylib"];
+
+#[no_mangle]
+pub extern "C" fn foo() {}
diff --git a/src/test/run-make/c-link-to-rust-staticlib/Makefile b/src/test/run-make/c-link-to-rust-staticlib/Makefile
new file mode 100644
index 00000000000..a81f19d6eb3
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-staticlib/Makefile
@@ -0,0 +1,13 @@
+-include ../tools.mk
+
+ifneq ($(shell uname),Darwin)
+	EXTRAFLAGS := -lm -lrt -ldl -lpthread
+endif
+
+all:
+	$(RUSTC) foo.rs -Z gen-crate-map
+	ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo)
+	$(CC) bar.c -lfoo -o $(call RUN,bar) $(EXTRAFLAGS) -lstdc++ 
+	$(call RUN,bar)
+	rm $(call STATICLIB,foo*)
+	$(call RUN,bar)
diff --git a/src/test/run-make/c-link-to-rust-staticlib/bar.c b/src/test/run-make/c-link-to-rust-staticlib/bar.c
new file mode 100644
index 00000000000..bb4036b06e1
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-staticlib/bar.c
@@ -0,0 +1,6 @@
+void foo();
+
+int main() {
+    foo();
+    return 0;
+}
diff --git a/src/test/run-make/c-link-to-rust-staticlib/foo.rs b/src/test/run-make/c-link-to-rust-staticlib/foo.rs
new file mode 100644
index 00000000000..3da09eb6bb6
--- /dev/null
+++ b/src/test/run-make/c-link-to-rust-staticlib/foo.rs
@@ -0,0 +1,4 @@
+#[crate_type = "staticlib"];
+
+#[no_mangle]
+pub extern "C" fn foo() {}
diff --git a/src/test/run-make/c-static-dylib/Makefile b/src/test/run-make/c-static-dylib/Makefile
new file mode 100644
index 00000000000..62d9c8e90f2
--- /dev/null
+++ b/src/test/run-make/c-static-dylib/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+all: $(call STATICLIB,cfoo)
+	$(RUSTC) foo.rs
+	$(RUSTC) bar.rs
+	rm $(TMPDIR)/$(call STATICLIB_GLOB,cfoo)
+	$(call RUN,bar)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,foo)
+	$(call FAIL,bar)
diff --git a/src/test/run-make/c-static-dylib/bar.rs b/src/test/run-make/c-static-dylib/bar.rs
new file mode 100644
index 00000000000..7c4aca1e028
--- /dev/null
+++ b/src/test/run-make/c-static-dylib/bar.rs
@@ -0,0 +1,5 @@
+extern mod foo;
+
+fn main() {
+    foo::rsfoo();
+}
diff --git a/src/test/run-make/c-static-dylib/cfoo.c b/src/test/run-make/c-static-dylib/cfoo.c
new file mode 100644
index 00000000000..9fe07f82f9e
--- /dev/null
+++ b/src/test/run-make/c-static-dylib/cfoo.c
@@ -0,0 +1 @@
+int foo() { return 0; }
diff --git a/src/test/run-make/c-static-dylib/foo.rs b/src/test/run-make/c-static-dylib/foo.rs
new file mode 100644
index 00000000000..0c4a6f7df4b
--- /dev/null
+++ b/src/test/run-make/c-static-dylib/foo.rs
@@ -0,0 +1,10 @@
+#[crate_type = "dylib"];
+
+#[link(name = "cfoo")]
+extern {
+    fn foo();
+}
+
+pub fn rsfoo() {
+    unsafe { foo() }
+}
diff --git a/src/test/run-make/c-static-rlib/Makefile b/src/test/run-make/c-static-rlib/Makefile
new file mode 100644
index 00000000000..09eb4b1249e
--- /dev/null
+++ b/src/test/run-make/c-static-rlib/Makefile
@@ -0,0 +1,8 @@
+-include ../tools.mk
+
+all: $(call STATICLIB,cfoo)
+	$(RUSTC) foo.rs
+	$(RUSTC) bar.rs
+	rm $(TMPDIR)/$(call RLIB_GLOB,foo)
+	rm $(TMPDIR)/$(call STATICLIB_GLOB,cfoo)
+	$(call RUN,bar)
diff --git a/src/test/run-make/c-static-rlib/bar.rs b/src/test/run-make/c-static-rlib/bar.rs
new file mode 100644
index 00000000000..7c4aca1e028
--- /dev/null
+++ b/src/test/run-make/c-static-rlib/bar.rs
@@ -0,0 +1,5 @@
+extern mod foo;
+
+fn main() {
+    foo::rsfoo();
+}
diff --git a/src/test/run-make/c-static-rlib/cfoo.c b/src/test/run-make/c-static-rlib/cfoo.c
new file mode 100644
index 00000000000..9fe07f82f9e
--- /dev/null
+++ b/src/test/run-make/c-static-rlib/cfoo.c
@@ -0,0 +1 @@
+int foo() { return 0; }
diff --git a/src/test/run-make/c-static-rlib/foo.rs b/src/test/run-make/c-static-rlib/foo.rs
new file mode 100644
index 00000000000..e64811af9c7
--- /dev/null
+++ b/src/test/run-make/c-static-rlib/foo.rs
@@ -0,0 +1,10 @@
+#[crate_type = "rlib"];
+
+#[link(name = "cfoo")]
+extern {
+    fn foo();
+}
+
+pub fn rsfoo() {
+    unsafe { foo() }
+}
diff --git a/src/test/run-make/dylib-chain/Makefile b/src/test/run-make/dylib-chain/Makefile
new file mode 100644
index 00000000000..e60e904240d
--- /dev/null
+++ b/src/test/run-make/dylib-chain/Makefile
@@ -0,0 +1,12 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) m1.rs
+	$(RUSTC) m2.rs
+	$(RUSTC) m3.rs
+	$(RUSTC) m4.rs
+	$(call RUN,m4)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,m1)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,m2)
+	rm $(TMPDIR)/$(call DYLIB_GLOB,m3)
+	$(call FAIL,m4)
diff --git a/src/test/run-make/dylib-chain/m1.rs b/src/test/run-make/dylib-chain/m1.rs
new file mode 100644
index 00000000000..69ea8dcecfc
--- /dev/null
+++ b/src/test/run-make/dylib-chain/m1.rs
@@ -0,0 +1,2 @@
+#[crate_type = "dylib"];
+pub fn m1() {}
diff --git a/src/test/run-make/dylib-chain/m2.rs b/src/test/run-make/dylib-chain/m2.rs
new file mode 100644
index 00000000000..2f5d75a3872
--- /dev/null
+++ b/src/test/run-make/dylib-chain/m2.rs
@@ -0,0 +1,4 @@
+#[crate_type = "dylib"];
+extern mod m1;
+
+pub fn m2() { m1::m1() }
diff --git a/src/test/run-make/dylib-chain/m3.rs b/src/test/run-make/dylib-chain/m3.rs
new file mode 100644
index 00000000000..cc4ff2cff74
--- /dev/null
+++ b/src/test/run-make/dylib-chain/m3.rs
@@ -0,0 +1,4 @@
+#[crate_type = "dylib"];
+extern mod m2;
+
+pub fn m3() { m2::m2() }
diff --git a/src/test/run-make/dylib-chain/m4.rs b/src/test/run-make/dylib-chain/m4.rs
new file mode 100644
index 00000000000..c52d1f7fcbc
--- /dev/null
+++ b/src/test/run-make/dylib-chain/m4.rs
@@ -0,0 +1,3 @@
+extern mod m3;
+
+fn main() { m3::m3() }
diff --git a/src/test/run-make/mixing-deps/Makefile b/src/test/run-make/mixing-deps/Makefile
new file mode 100644
index 00000000000..9ab2abca729
--- /dev/null
+++ b/src/test/run-make/mixing-deps/Makefile
@@ -0,0 +1,7 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) both.rs
+	$(RUSTC) dylib.rs -Z prefer-dynamic
+	$(RUSTC) prog.rs
+	$(call RUN,prog)
diff --git a/src/test/run-make/mixing-deps/both.rs b/src/test/run-make/mixing-deps/both.rs
new file mode 100644
index 00000000000..89d7b6452c9
--- /dev/null
+++ b/src/test/run-make/mixing-deps/both.rs
@@ -0,0 +1,4 @@
+#[crate_type = "rlib"];
+#[crate_type = "dylib"];
+
+pub static foo: int = 4;
diff --git a/src/test/run-make/mixing-deps/dylib.rs b/src/test/run-make/mixing-deps/dylib.rs
new file mode 100644
index 00000000000..130bc2fe803
--- /dev/null
+++ b/src/test/run-make/mixing-deps/dylib.rs
@@ -0,0 +1,6 @@
+#[crate_type = "dylib"];
+extern mod both;
+
+use std::cast;
+
+pub fn addr() -> uint { unsafe { cast::transmute(&both::foo) } }
diff --git a/src/test/run-make/mixing-deps/prog.rs b/src/test/run-make/mixing-deps/prog.rs
new file mode 100644
index 00000000000..da90f8731f4
--- /dev/null
+++ b/src/test/run-make/mixing-deps/prog.rs
@@ -0,0 +1,9 @@
+extern mod dylib;
+extern mod both;
+
+use std::cast;
+
+fn main() {
+    assert_eq!(unsafe { cast::transmute::<&int, uint>(&both::foo) },
+               dylib::addr());
+}
diff --git a/src/test/run-make/mixing-libs/Makefile b/src/test/run-make/mixing-libs/Makefile
new file mode 100644
index 00000000000..eb00c801390
--- /dev/null
+++ b/src/test/run-make/mixing-libs/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) rlib.rs
+	$(RUSTC) dylib.rs && exit 1 || exit 0
+	$(RUSTC) rlib.rs --dylib
+	$(RUSTC) dylib.rs
+	rm $(call DYLIB,rlib-*)
+	$(RUSTC) prog.rs && exit 1 || exit 0
diff --git a/src/test/run-make/mixing-libs/dylib.rs b/src/test/run-make/mixing-libs/dylib.rs
new file mode 100644
index 00000000000..9652cb27641
--- /dev/null
+++ b/src/test/run-make/mixing-libs/dylib.rs
@@ -0,0 +1,4 @@
+#[crate_type = "dylib"];
+extern mod rlib;
+
+pub fn dylib() { rlib::rlib() }
diff --git a/src/test/run-make/mixing-libs/prog.rs b/src/test/run-make/mixing-libs/prog.rs
new file mode 100644
index 00000000000..ec67dea0ab0
--- /dev/null
+++ b/src/test/run-make/mixing-libs/prog.rs
@@ -0,0 +1,7 @@
+extern mod dylib;
+extern mod rlib;
+
+fn main() {
+    dylib::dylib();
+    rlib::rlib();
+}
diff --git a/src/test/run-make/mixing-libs/rlib.rs b/src/test/run-make/mixing-libs/rlib.rs
new file mode 100644
index 00000000000..32c322f3f59
--- /dev/null
+++ b/src/test/run-make/mixing-libs/rlib.rs
@@ -0,0 +1,2 @@
+#[crate_type = "rlib"];
+pub fn rlib() {}
diff --git a/src/test/run-make/prefer-dylib/Makefile b/src/test/run-make/prefer-dylib/Makefile
new file mode 100644
index 00000000000..8229547176a
--- /dev/null
+++ b/src/test/run-make/prefer-dylib/Makefile
@@ -0,0 +1,8 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) bar.rs --dylib --rlib
+	$(RUSTC) foo.rs -Z prefer-dynamic
+	$(call RUN,foo)
+	rm $(TMPDIR)/*bar*
+	$(call FAILS,foo)
diff --git a/src/test/run-make/prefer-dylib/bar.rs b/src/test/run-make/prefer-dylib/bar.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/src/test/run-make/prefer-dylib/bar.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/src/test/run-make/prefer-dylib/foo.rs b/src/test/run-make/prefer-dylib/foo.rs
new file mode 100644
index 00000000000..f86ef62a9fe
--- /dev/null
+++ b/src/test/run-make/prefer-dylib/foo.rs
@@ -0,0 +1,5 @@
+extern mod bar;
+
+fn main() {
+    bar::bar();
+}
diff --git a/src/test/run-make/prefer-rlib/Makefile b/src/test/run-make/prefer-rlib/Makefile
new file mode 100644
index 00000000000..eedb70c4efd
--- /dev/null
+++ b/src/test/run-make/prefer-rlib/Makefile
@@ -0,0 +1,8 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) bar.rs --dylib --rlib
+	ls $(TMPDIR)/$(call RLIB_GLOB,bar)
+	$(RUSTC) foo.rs
+	rm $(TMPDIR)/*bar*
+	$(call RUN,foo)
diff --git a/src/test/run-make/prefer-rlib/bar.rs b/src/test/run-make/prefer-rlib/bar.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/src/test/run-make/prefer-rlib/bar.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/src/test/run-make/prefer-rlib/foo.rs b/src/test/run-make/prefer-rlib/foo.rs
new file mode 100644
index 00000000000..f86ef62a9fe
--- /dev/null
+++ b/src/test/run-make/prefer-rlib/foo.rs
@@ -0,0 +1,5 @@
+extern mod bar;
+
+fn main() {
+    bar::bar();
+}
diff --git a/src/test/run-make/rlib-chain/Makefile b/src/test/run-make/rlib-chain/Makefile
new file mode 100644
index 00000000000..30b6811a388
--- /dev/null
+++ b/src/test/run-make/rlib-chain/Makefile
@@ -0,0 +1,10 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) m1.rs
+	$(RUSTC) m2.rs
+	$(RUSTC) m3.rs
+	$(RUSTC) m4.rs
+	$(call RUN,m4)
+	rm $(TMPDIR)/*lib
+	$(call RUN,m4)
diff --git a/src/test/run-make/rlib-chain/m1.rs b/src/test/run-make/rlib-chain/m1.rs
new file mode 100644
index 00000000000..1a244efd4dd
--- /dev/null
+++ b/src/test/run-make/rlib-chain/m1.rs
@@ -0,0 +1,2 @@
+#[crate_type = "rlib"];
+pub fn m1() {}
diff --git a/src/test/run-make/rlib-chain/m2.rs b/src/test/run-make/rlib-chain/m2.rs
new file mode 100644
index 00000000000..96f77122083
--- /dev/null
+++ b/src/test/run-make/rlib-chain/m2.rs
@@ -0,0 +1,4 @@
+#[crate_type = "rlib"];
+extern mod m1;
+
+pub fn m2() { m1::m1() }
diff --git a/src/test/run-make/rlib-chain/m3.rs b/src/test/run-make/rlib-chain/m3.rs
new file mode 100644
index 00000000000..cb8d7529160
--- /dev/null
+++ b/src/test/run-make/rlib-chain/m3.rs
@@ -0,0 +1,4 @@
+#[crate_type = "rlib"];
+extern mod m2;
+
+pub fn m3() { m2::m2() }
diff --git a/src/test/run-make/rlib-chain/m4.rs b/src/test/run-make/rlib-chain/m4.rs
new file mode 100644
index 00000000000..c52d1f7fcbc
--- /dev/null
+++ b/src/test/run-make/rlib-chain/m4.rs
@@ -0,0 +1,3 @@
+extern mod m3;
+
+fn main() { m3::m3() }
diff --git a/src/test/run-make/simple-dylib/Makefile b/src/test/run-make/simple-dylib/Makefile
new file mode 100644
index 00000000000..d4f215c69f0
--- /dev/null
+++ b/src/test/run-make/simple-dylib/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+all:
+	$(RUSTC) bar.rs --dylib
+	$(RUSTC) foo.rs
+	$(call RUN,foo)
diff --git a/src/test/run-make/simple-dylib/bar.rs b/src/test/run-make/simple-dylib/bar.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/src/test/run-make/simple-dylib/bar.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/src/test/run-make/simple-dylib/foo.rs b/src/test/run-make/simple-dylib/foo.rs
new file mode 100644
index 00000000000..f86ef62a9fe
--- /dev/null
+++ b/src/test/run-make/simple-dylib/foo.rs
@@ -0,0 +1,5 @@
+extern mod bar;
+
+fn main() {
+    bar::bar();
+}
diff --git a/src/test/run-make/simple-rlib/Makefile b/src/test/run-make/simple-rlib/Makefile
new file mode 100644
index 00000000000..e8909ef134b
--- /dev/null
+++ b/src/test/run-make/simple-rlib/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+all:
+	$(RUSTC) bar.rs --rlib
+	$(RUSTC) foo.rs
+	$(call RUN,foo)
diff --git a/src/test/run-make/simple-rlib/bar.rs b/src/test/run-make/simple-rlib/bar.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/src/test/run-make/simple-rlib/bar.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/src/test/run-make/simple-rlib/foo.rs b/src/test/run-make/simple-rlib/foo.rs
new file mode 100644
index 00000000000..f86ef62a9fe
--- /dev/null
+++ b/src/test/run-make/simple-rlib/foo.rs
@@ -0,0 +1,5 @@
+extern mod bar;
+
+fn main() {
+    bar::bar();
+}
diff --git a/src/test/run-make/static-unwinding/Makefile b/src/test/run-make/static-unwinding/Makefile
new file mode 100644
index 00000000000..cb039744265
--- /dev/null
+++ b/src/test/run-make/static-unwinding/Makefile
@@ -0,0 +1,6 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) lib.rs
+	$(RUSTC) main.rs
+	$(call RUN,main)
diff --git a/src/test/run-make/static-unwinding/lib.rs b/src/test/run-make/static-unwinding/lib.rs
new file mode 100644
index 00000000000..4e2cdb6c222
--- /dev/null
+++ b/src/test/run-make/static-unwinding/lib.rs
@@ -0,0 +1,15 @@
+#[crate_type = "rlib"];
+
+pub static mut statik: int = 0;
+
+struct A;
+impl Drop for A {
+    fn drop(&mut self) {
+        unsafe { statik = 1; }
+    }
+}
+
+pub fn callback(f: ||) {
+    let _a = A;
+    f();
+}
diff --git a/src/test/run-make/static-unwinding/main.rs b/src/test/run-make/static-unwinding/main.rs
new file mode 100644
index 00000000000..029933a819c
--- /dev/null
+++ b/src/test/run-make/static-unwinding/main.rs
@@ -0,0 +1,25 @@
+extern mod lib;
+
+use std::task;
+
+static mut statik: int = 0;
+
+struct A;
+impl Drop for A {
+    fn drop(&mut self) {
+        unsafe { statik = 1; }
+    }
+}
+
+fn main() {
+    do task::try {
+        let _a = A;
+        lib::callback(|| fail!());
+        1
+    };
+
+    unsafe {
+        assert!(lib::statik == 1);
+        assert!(statik == 1);
+    }
+}
diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk
new file mode 100644
index 00000000000..2d670cb873f
--- /dev/null
+++ b/src/test/run-make/tools.mk
@@ -0,0 +1,27 @@
+RUSTC := $(RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR)
+CC := $(CC) -L $(TMPDIR)
+
+RUN = $(TMPDIR)/$(1)
+FAILS = $(TMPDIR)/$(1) && exit 1 || exit 0
+
+RLIB_GLOB = lib$(1)*.rlib
+STATICLIB = $(TMPDIR)/lib$(1).a
+STATICLIB_GLOB = lib$(1)*.a
+
+ifeq ($(shell uname),Darwin)
+DYLIB_GLOB = lib$(1)*.dylib
+DYLIB = $(TMPDIR)/lib$(1).dylib
+else
+DYLIB_GLOB = lib$(1)*.so
+DYLIB = $(TMPDIR)/lib$(1).so
+endif
+
+%.a: %.o
+	ar crus $@ $<
+%.dylib: %.o
+	$(CC) -dynamiclib -Wl,-dylib -o $@ $<
+%.so: %.o
+	$(CC) -o $@ $< -shared
+$(TMPDIR)/lib%.o: %.c
+	$(CC) -c -o $@ $<
+
diff --git a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs
index a9c6141c18b..4c2e78db398 100644
--- a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs
+++ b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs
@@ -9,11 +9,15 @@
 // except according to those terms.
 
 // xfail-fast
+// xfail-pretty
 // aux-build:anon-extern-mod-cross-crate-1.rs
 extern mod anonexternmod;
 
 use anonexternmod::rust_get_test_int;
 
+#[link(name = "rustrt")] // we have explicitly chosen to require this
+extern {}
+
 pub fn main() {
     unsafe {
         rust_get_test_int();
diff --git a/src/test/run-pass/anon-extern-mod.rs b/src/test/run-pass/anon-extern-mod.rs
index 3323aa4e2cd..7e05bcc1fd2 100644
--- a/src/test/run-pass/anon-extern-mod.rs
+++ b/src/test/run-pass/anon-extern-mod.rs
@@ -10,7 +10,7 @@
 
 use std::libc;
 
-#[link_name = "rustrt"]
+#[link(name = "rustrt")]
 extern {
     fn rust_get_test_int() -> libc::intptr_t;
 }
diff --git a/src/test/run-pass/c-stack-as-value.rs b/src/test/run-pass/c-stack-as-value.rs
index f8d30a9a667..d590c35d9e2 100644
--- a/src/test/run-pass/c-stack-as-value.rs
+++ b/src/test/run-pass/c-stack-as-value.rs
@@ -11,6 +11,7 @@
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_get_test_int() -> libc::intptr_t;
     }
diff --git a/src/test/run-pass/extern-call-deep.rs b/src/test/run-pass/extern-call-deep.rs
index f55be369b57..e3b727fafd3 100644
--- a/src/test/run-pass/extern-call-deep.rs
+++ b/src/test/run-pass/extern-call-deep.rs
@@ -13,6 +13,7 @@ use std::libc;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/extern-call-deep2.rs b/src/test/run-pass/extern-call-deep2.rs
index 5bda762a68b..00ac0bfa118 100644
--- a/src/test/run-pass/extern-call-deep2.rs
+++ b/src/test/run-pass/extern-call-deep2.rs
@@ -14,6 +14,7 @@ use std::task;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/extern-call-indirect.rs b/src/test/run-pass/extern-call-indirect.rs
index 6222b681dc9..c49d589572c 100644
--- a/src/test/run-pass/extern-call-indirect.rs
+++ b/src/test/run-pass/extern-call-indirect.rs
@@ -13,6 +13,7 @@ use std::libc;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/extern-call-scrub.rs b/src/test/run-pass/extern-call-scrub.rs
index 548336962f5..0044f0f468f 100644
--- a/src/test/run-pass/extern-call-scrub.rs
+++ b/src/test/run-pass/extern-call-scrub.rs
@@ -18,6 +18,7 @@ use std::task;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/extern-pass-TwoU16s.rs b/src/test/run-pass/extern-pass-TwoU16s.rs
index bb6a68dfda4..c224a2f1f05 100644
--- a/src/test/run-pass/extern-pass-TwoU16s.rs
+++ b/src/test/run-pass/extern-pass-TwoU16s.rs
@@ -18,6 +18,7 @@ struct TwoU16s {
     one: u16, two: u16
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s;
 }
diff --git a/src/test/run-pass/extern-pass-TwoU32s.rs b/src/test/run-pass/extern-pass-TwoU32s.rs
index 3389c56c83b..592d42c65d1 100644
--- a/src/test/run-pass/extern-pass-TwoU32s.rs
+++ b/src/test/run-pass/extern-pass-TwoU32s.rs
@@ -16,6 +16,7 @@ struct TwoU32s {
     one: u32, two: u32
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s;
 }
diff --git a/src/test/run-pass/extern-pass-TwoU64s.rs b/src/test/run-pass/extern-pass-TwoU64s.rs
index 033359f85fa..d2520f32c7b 100644
--- a/src/test/run-pass/extern-pass-TwoU64s.rs
+++ b/src/test/run-pass/extern-pass-TwoU64s.rs
@@ -18,6 +18,7 @@ struct TwoU64s {
     one: u64, two: u64
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s;
 }
diff --git a/src/test/run-pass/extern-pass-TwoU8s.rs b/src/test/run-pass/extern-pass-TwoU8s.rs
index 8757f9593f7..5548b8a7d39 100644
--- a/src/test/run-pass/extern-pass-TwoU8s.rs
+++ b/src/test/run-pass/extern-pass-TwoU8s.rs
@@ -18,6 +18,7 @@ struct TwoU8s {
     one: u8, two: u8
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s;
 }
diff --git a/src/test/run-pass/extern-pass-char.rs b/src/test/run-pass/extern-pass-char.rs
index 0cdfaf29a05..85d0463fe72 100644
--- a/src/test/run-pass/extern-pass-char.rs
+++ b/src/test/run-pass/extern-pass-char.rs
@@ -10,6 +10,7 @@
 
 // Test a function that takes/returns a u8.
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_u8(v: u8) -> u8;
 }
diff --git a/src/test/run-pass/extern-pass-double.rs b/src/test/run-pass/extern-pass-double.rs
index cb0a061a7ab..2d35fe6043e 100644
--- a/src/test/run-pass/extern-pass-double.rs
+++ b/src/test/run-pass/extern-pass-double.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_double(v: f64) -> f64;
 }
diff --git a/src/test/run-pass/extern-pass-u32.rs b/src/test/run-pass/extern-pass-u32.rs
index aa54f014046..5ff3353a8c0 100644
--- a/src/test/run-pass/extern-pass-u32.rs
+++ b/src/test/run-pass/extern-pass-u32.rs
@@ -10,6 +10,7 @@
 
 // Test a function that takes/returns a u32.
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_u32(v: u32) -> u32;
 }
diff --git a/src/test/run-pass/extern-pass-u64.rs b/src/test/run-pass/extern-pass-u64.rs
index 7c39080a568..b94c57a7a66 100644
--- a/src/test/run-pass/extern-pass-u64.rs
+++ b/src/test/run-pass/extern-pass-u64.rs
@@ -10,6 +10,7 @@
 
 // Test a call to a function that takes/returns a u64.
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
 }
diff --git a/src/test/run-pass/extern-return-TwoU16s.rs b/src/test/run-pass/extern-return-TwoU16s.rs
index ba671a1c494..45efbbb2785 100644
--- a/src/test/run-pass/extern-return-TwoU16s.rs
+++ b/src/test/run-pass/extern-return-TwoU16s.rs
@@ -12,6 +12,7 @@ struct TwoU16s {
     one: u16, two: u16
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s;
 }
diff --git a/src/test/run-pass/extern-return-TwoU32s.rs b/src/test/run-pass/extern-return-TwoU32s.rs
index 90562f0f6b1..8258ee623ab 100644
--- a/src/test/run-pass/extern-return-TwoU32s.rs
+++ b/src/test/run-pass/extern-return-TwoU32s.rs
@@ -12,6 +12,7 @@ struct TwoU32s {
     one: u32, two: u32
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s;
 }
diff --git a/src/test/run-pass/extern-return-TwoU64s.rs b/src/test/run-pass/extern-return-TwoU64s.rs
index b52808ea32d..d8b051f57a9 100644
--- a/src/test/run-pass/extern-return-TwoU64s.rs
+++ b/src/test/run-pass/extern-return-TwoU64s.rs
@@ -14,6 +14,7 @@ struct TwoU64s {
     one: u64, two: u64
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s;
 }
diff --git a/src/test/run-pass/extern-return-TwoU8s.rs b/src/test/run-pass/extern-return-TwoU8s.rs
index 5d2fd140758..99dbd93fb7d 100644
--- a/src/test/run-pass/extern-return-TwoU8s.rs
+++ b/src/test/run-pass/extern-return-TwoU8s.rs
@@ -12,6 +12,7 @@ struct TwoU8s {
     one: u8, two: u8
 }
 
+#[link(name = "rustrt")]
 extern {
     pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s;
 }
diff --git a/src/test/run-pass/extern-stress.rs b/src/test/run-pass/extern-stress.rs
index 6ef196ccd41..7c16ae74c4a 100644
--- a/src/test/run-pass/extern-stress.rs
+++ b/src/test/run-pass/extern-stress.rs
@@ -17,6 +17,7 @@ use std::task;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/extern-yield.rs b/src/test/run-pass/extern-yield.rs
index 1d2f52d8c2e..a4ac197ac6a 100644
--- a/src/test/run-pass/extern-yield.rs
+++ b/src/test/run-pass/extern-yield.rs
@@ -14,6 +14,7 @@ use std::task;
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_call(cb: extern "C" fn (libc::uintptr_t) -> libc::uintptr_t,
                              data: libc::uintptr_t)
diff --git a/src/test/run-pass/foreign-call-no-runtime.rs b/src/test/run-pass/foreign-call-no-runtime.rs
index 283efa2ac7c..ee54423a97d 100644
--- a/src/test/run-pass/foreign-call-no-runtime.rs
+++ b/src/test/run-pass/foreign-call-no-runtime.rs
@@ -2,6 +2,7 @@ use std::cast;
 use std::libc;
 use std::unstable::run_in_bare_thread;
 
+#[link(name = "rustrt")]
 extern {
     fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t),
                      data: libc::uintptr_t) -> libc::uintptr_t;
diff --git a/src/test/run-pass/foreign-dupe.rs b/src/test/run-pass/foreign-dupe.rs
index 66d3bda30e6..260b1f272c2 100644
--- a/src/test/run-pass/foreign-dupe.rs
+++ b/src/test/run-pass/foreign-dupe.rs
@@ -14,7 +14,7 @@
 mod rustrt1 {
     use std::libc;
 
-    #[link_name = "rustrt"]
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_get_test_int() -> libc::intptr_t;
     }
@@ -23,7 +23,7 @@ mod rustrt1 {
 mod rustrt2 {
     use std::libc;
 
-    #[link_name = "rustrt"]
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_get_test_int() -> libc::intptr_t;
     }
diff --git a/src/test/run-pass/foreign-no-abi.rs b/src/test/run-pass/foreign-no-abi.rs
index 0702d49cfc1..eef45532265 100644
--- a/src/test/run-pass/foreign-no-abi.rs
+++ b/src/test/run-pass/foreign-no-abi.rs
@@ -13,6 +13,7 @@
 mod rustrt {
     use std::libc;
 
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_get_test_int() -> libc::intptr_t;
     }
diff --git a/src/test/run-pass/invoke-external-foreign.rs b/src/test/run-pass/invoke-external-foreign.rs
index 7e79311c5ad..ec531d391e4 100644
--- a/src/test/run-pass/invoke-external-foreign.rs
+++ b/src/test/run-pass/invoke-external-foreign.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // xfail-fast
+// xfail-pretty
 // aux-build:foreign_lib.rs
 
 // The purpose of this test is to check that we can
@@ -17,6 +18,9 @@
 
 extern mod foreign_lib;
 
+#[link(name = "rustrt")] // we have explicitly chosen to require this
+extern {}
+
 pub fn main() {
     unsafe {
         let _foo = foreign_lib::rustrt::rust_get_test_int();
diff --git a/src/test/run-pass/static-mut-foreign.rs b/src/test/run-pass/static-mut-foreign.rs
index bdfc747c8bc..0afe01bf7ed 100644
--- a/src/test/run-pass/static-mut-foreign.rs
+++ b/src/test/run-pass/static-mut-foreign.rs
@@ -14,7 +14,7 @@
 
 use std::libc;
 
-#[nolink]
+#[link(name = "rustrt")]
 extern {
     static mut rust_dbg_static_mut: libc::c_int;
     pub fn rust_dbg_static_mut_check_four();
diff --git a/src/test/run-pass/struct-return.rs b/src/test/run-pass/struct-return.rs
index 324186021b8..e2fe7a96016 100644
--- a/src/test/run-pass/struct-return.rs
+++ b/src/test/run-pass/struct-return.rs
@@ -16,7 +16,7 @@ pub struct Floats { a: f64, b: u8, c: f64 }
 mod rustrt {
     use super::{Floats, Quad};
 
-    #[nolink]
+    #[link(name = "rustrt")]
     extern {
         pub fn rust_dbg_abi_1(q: Quad) -> Quad;
         pub fn rust_dbg_abi_2(f: Floats) -> Floats;