about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/failure.rs19
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/metadata/common.rs3
-rw-r--r--src/librustc/metadata/csearch.rs8
-rw-r--r--src/librustc/metadata/decoder.rs15
-rw-r--r--src/librustc/metadata/encoder.rs4
-rw-r--r--src/librustc/middle/lang_items.rs28
-rw-r--r--src/librustc/middle/trans/base.rs23
-rw-r--r--src/librustc/middle/trans/cleanup.rs28
-rw-r--r--src/librustc/middle/trans/context.rs3
-rw-r--r--src/librustc/middle/trans/foreign.rs9
-rw-r--r--src/librustc/middle/weak_lang_items.rs124
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/libstd/rt/stack.rs38
-rw-r--r--src/libstd/rt/unwind.rs42
-rw-r--r--src/test/auxiliary/lang-item-public.rs7
-rw-r--r--src/test/auxiliary/weak-lang-items.rs37
-rw-r--r--src/test/compile-fail/weak-lang-item.rs19
-rw-r--r--src/test/run-pass/smallest-hello-world.rs4
-rw-r--r--src/test/run-pass/weak-lang-item.rs21
20 files changed, 403 insertions, 32 deletions
diff --git a/src/libcore/failure.rs b/src/libcore/failure.rs
index c4a2c9a6099..003ebed6364 100644
--- a/src/libcore/failure.rs
+++ b/src/libcore/failure.rs
@@ -60,10 +60,19 @@ fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
 
 #[cold]
 pub fn begin_unwind(fmt: &fmt::Arguments, file: &'static str, line: uint) -> ! {
-    // FIXME: this should be a proper lang item, it should not just be some
-    //        undefined symbol sitting in the middle of nowhere.
     #[allow(ctypes)]
-    extern { fn rust_begin_unwind(fmt: &fmt::Arguments, file: &'static str,
-                                  line: uint) -> !; }
-    unsafe { rust_begin_unwind(fmt, file, line) }
+    #[cfg(stage0)]
+    extern {
+        #[link_name = "rust_begin_unwind"]
+        fn begin_unwind(fmt: &fmt::Arguments, file: &'static str,
+                        line: uint) -> !;
+    }
+    #[allow(ctypes)]
+    #[cfg(not(stage0))]
+    extern {
+        #[lang = "begin_unwind"]
+        fn begin_unwind(fmt: &fmt::Arguments, file: &'static str,
+                        line: uint) -> !;
+    }
+    unsafe { begin_unwind(fmt, file, line) }
 }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 678d525ddcb..5f13ed4942e 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -78,6 +78,7 @@ pub mod middle {
     pub mod dead;
     pub mod expr_use_visitor;
     pub mod dependency_format;
+    pub mod weak_lang_items;
 }
 
 pub mod front {
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 518dc4b05ca..f30d6119339 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -167,8 +167,9 @@ pub static tag_lang_items: uint = 0x70;
 pub static tag_lang_items_item: uint = 0x71;
 pub static tag_lang_items_item_id: uint = 0x72;
 pub static tag_lang_items_item_node_id: uint = 0x73;
+pub static tag_lang_items_missing: uint = 0x74;
 
-pub static tag_item_unnamed_field: uint = 0x74;
+pub static tag_item_unnamed_field: uint = 0x75;
 pub static tag_items_data_item_visibility: uint = 0x76;
 pub static tag_items_data_item_sized: uint = 0x77;
 
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index fc5c01d4d8d..f30e24a3151 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -15,6 +15,7 @@
 use metadata::common::*;
 use metadata::cstore;
 use metadata::decoder;
+use middle::lang_items;
 use middle::ty;
 use middle::typeck;
 
@@ -298,3 +299,10 @@ pub fn get_dylib_dependency_formats(cstore: &cstore::CStore,
     let cdata = cstore.get_crate_data(cnum);
     decoder::get_dylib_dependency_formats(&*cdata)
 }
+
+pub fn get_missing_lang_items(cstore: &cstore::CStore, cnum: ast::CrateNum)
+    -> Vec<lang_items::LangItem>
+{
+    let cdata = cstore.get_crate_data(cnum);
+    decoder::get_missing_lang_items(&*cdata)
+}
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index da21cde3d6c..b3ef888c0b4 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -21,6 +21,7 @@ use metadata::cstore;
 use metadata::tydecode::{parse_ty_data, parse_def_id,
                          parse_type_param_def_data,
                          parse_bare_fn_ty_data, parse_trait_ref_data};
+use middle::lang_items;
 use middle::ty::{ImplContainer, TraitContainer};
 use middle::ty;
 use middle::typeck;
@@ -1299,3 +1300,17 @@ pub fn get_dylib_dependency_formats(cdata: Cmd)
     }
     return result;
 }
+
+pub fn get_missing_lang_items(cdata: Cmd)
+    -> Vec<lang_items::LangItem>
+{
+    let items = reader::get_doc(reader::Doc(cdata.data()), tag_lang_items);
+    let mut result = Vec::new();
+    reader::tagged_docs(items, tag_lang_items_missing, |missing_doc| {
+        let item: lang_items::LangItem =
+            FromPrimitive::from_u32(reader::doc_as_u32(missing_doc)).unwrap();
+        result.push(item);
+        true
+    });
+    return result;
+}
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index a7ba8300aed..34baed3acc8 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1518,6 +1518,10 @@ fn encode_lang_items(ecx: &EncodeContext, ebml_w: &mut Encoder) {
         }
     }
 
+    for i in ecx.tcx.lang_items.missing.iter() {
+        ebml_w.wr_tagged_u32(tag_lang_items_missing, *i as u32);
+    }
+
     ebml_w.end_tag();   // tag_lang_items
 }
 
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index ed8b27a8660..81e9c3f5ec6 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -23,6 +23,7 @@
 use driver::session::Session;
 use metadata::csearch::each_lang_item;
 use middle::ty;
+use middle::weak_lang_items;
 use syntax::ast;
 use syntax::ast_util::local_def;
 use syntax::attr::AttrMetaMethods;
@@ -41,13 +42,14 @@ macro_rules! lets_do_this {
         $( $variant:ident, $name:expr, $method:ident; )*
     ) => {
 
-#[deriving(FromPrimitive)]
+#[deriving(FromPrimitive, Eq, TotalEq, Hash)]
 pub enum LangItem {
     $($variant),*
 }
 
 pub struct LanguageItems {
-    pub items: Vec<Option<ast::DefId>> ,
+    pub items: Vec<Option<ast::DefId>>,
+    pub missing: Vec<LangItem>,
 }
 
 impl LanguageItems {
@@ -55,7 +57,8 @@ impl LanguageItems {
         fn foo(_: LangItem) -> Option<ast::DefId> { None }
 
         LanguageItems {
-            items: vec!($(foo($variant)),*)
+            items: vec!($(foo($variant)),*),
+            missing: Vec::new(),
         }
     }
 
@@ -198,7 +201,8 @@ pub fn collect_language_items(krate: &ast::Crate,
                               session: &Session) -> LanguageItems {
     let mut collector = LanguageItemCollector::new(session);
     collector.collect(krate);
-    let LanguageItemCollector { items, .. } = collector;
+    let LanguageItemCollector { mut items, .. } = collector;
+    weak_lang_items::check_crate(krate, session, &mut items);
     session.abort_if_errors();
     items
 }
@@ -240,8 +244,20 @@ lets_do_this! {
 
     StrEqFnLangItem,                 "str_eq",                  str_eq_fn;
     UniqStrEqFnLangItem,             "uniq_str_eq",             uniq_str_eq_fn;
+
+    // A number of failure-related lang items. The `fail_` item corresponds to
+    // divide-by-zero and various failure cases with `match`. The
+    // `fail_bounds_check` item is for indexing arrays.
+    //
+    // The `begin_unwind` lang item has a predefined symbol name and is sort of
+    // a "weak lang item" in the sense that a crate is not required to have it
+    // defined to use it, but a final product is required to define it
+    // somewhere. Additionally, there are restrictions on crates that use a weak
+    // lang item, but do not have it defined.
     FailFnLangItem,                  "fail_",                   fail_fn;
     FailBoundsCheckFnLangItem,       "fail_bounds_check",       fail_bounds_check_fn;
+    BeginUnwindLangItem,             "begin_unwind",            begin_unwind;
+
     ExchangeMallocFnLangItem,        "exchange_malloc",         exchange_malloc_fn;
     ClosureExchangeMallocFnLangItem, "closure_exchange_malloc", closure_exchange_malloc_fn;
     ExchangeFreeFnLangItem,          "exchange_free",           exchange_free_fn;
@@ -257,7 +273,7 @@ lets_do_this! {
 
     TypeIdLangItem,                  "type_id",                 type_id;
 
-    EhPersonalityLangItem,           "eh_personality",          eh_personality_fn;
+    EhPersonalityLangItem,           "eh_personality",          eh_personality;
 
     ManagedHeapLangItem,             "managed_heap",            managed_heap;
     ExchangeHeapLangItem,            "exchange_heap",           exchange_heap;
@@ -276,4 +292,6 @@ lets_do_this! {
     NoCopyItem,                      "no_copy_bound",           no_copy_bound;
     NoShareItem,                     "no_share_bound",          no_share_bound;
     ManagedItem,                     "managed_bound",           managed_bound;
+
+    StackExhaustedLangItem,          "stack_exhausted",         stack_exhausted;
 }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 92e3b95abad..a88bc243277 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -38,6 +38,7 @@ use lib;
 use metadata::{csearch, encoder};
 use middle::astencode;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
+use middle::weak_lang_items;
 use middle::trans::_match;
 use middle::trans::adt;
 use middle::trans::build::*;
@@ -1679,6 +1680,19 @@ fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: StrBuf, node_id: ast::N
         lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
     }
 
+    // The stack exhaustion lang item shouldn't have a split stack because
+    // otherwise it would continue to be exhausted (bad), and both it and the
+    // eh_personality functions need to be externally linkable.
+    let def = ast_util::local_def(node_id);
+    if ccx.tcx.lang_items.stack_exhausted() == Some(def) {
+        unset_split_stack(llfn);
+        lib::llvm::SetLinkage(llfn, lib::llvm::ExternalLinkage);
+    }
+    if ccx.tcx.lang_items.eh_personality() == Some(def) {
+        lib::llvm::SetLinkage(llfn, lib::llvm::ExternalLinkage);
+    }
+
+
     if is_entry_fn(ccx.sess(), node_id) {
         create_entry_wrapper(ccx, sp, llfn);
     }
@@ -1816,8 +1830,13 @@ fn exported_name(ccx: &CrateContext, id: ast::NodeId,
                 // Don't mangle
                 path.last().unwrap().to_str().to_strbuf()
             } else {
-                // Usual name mangling
-                mangle_exported_name(ccx, path, ty, id)
+                match weak_lang_items::link_name(attrs) {
+                    Some(name) => name.get().to_strbuf(),
+                    None => {
+                        // Usual name mangling
+                        mangle_exported_name(ccx, path, ty, id)
+                    }
+                }
             }
         })
     }
diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs
index 776de01614d..87cde42e398 100644
--- a/src/librustc/middle/trans/cleanup.rs
+++ b/src/librustc/middle/trans/cleanup.rs
@@ -14,7 +14,6 @@
  */
 
 use lib::llvm::{BasicBlockRef, ValueRef};
-use middle::lang_items::{EhPersonalityLangItem};
 use middle::trans::base;
 use middle::trans::build;
 use middle::trans::callee;
@@ -665,8 +664,31 @@ impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> {
                                     false);
 
         // The exception handling personality function.
-        let def_id = common::langcall(pad_bcx, None, "", EhPersonalityLangItem);
-        let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, ExprId(0));
+        //
+        // If our compilation unit has the `eh_personality` lang item somewhere
+        // within it, then we just need to translate that. Otherwise, we're
+        // building an rlib which will depend on some upstream implementation of
+        // this function, so we just codegen a generic reference to it. We don't
+        // specify any of the types for the function, we just make it a symbol
+        // that LLVM can later use.
+        let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() {
+            Some(def_id) => callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)),
+            None => {
+                let mut personality = self.ccx.eh_personality.borrow_mut();
+                match *personality {
+                    Some(llpersonality) => llpersonality,
+                    None => {
+                        let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
+                        let f = base::decl_cdecl_fn(self.ccx.llmod,
+                                                    "rust_eh_personality",
+                                                    fty,
+                                                    ty::mk_i32());
+                        *personality = Some(f);
+                        f
+                    }
+                }
+            }
+        };
 
         // The only landing pad clause will be 'cleanup'
         let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u);
diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs
index 71fcb0b4de5..a737361b55a 100644
--- a/src/librustc/middle/trans/context.rs
+++ b/src/librustc/middle/trans/context.rs
@@ -122,6 +122,8 @@ pub struct CrateContext {
     pub uses_gc: bool,
     pub dbg_cx: Option<debuginfo::CrateDebugContext>,
 
+    pub eh_personality: RefCell<Option<ValueRef>>,
+
     intrinsics: RefCell<HashMap<&'static str, ValueRef>>,
 }
 
@@ -224,6 +226,7 @@ impl CrateContext {
                 builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
                 uses_gc: false,
                 dbg_cx: dbg_cx,
+                eh_personality: RefCell::new(None),
                 intrinsics: RefCell::new(HashMap::new()),
             };
 
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 2c4043a62f5..fb6aff0e26e 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -13,6 +13,7 @@ use back::{link};
 use lib::llvm::llvm;
 use lib::llvm::{ValueRef, CallConv, StructRetAttribute, Linkage};
 use lib;
+use middle::weak_lang_items;
 use middle::trans::base::push_ctxt;
 use middle::trans::base;
 use middle::trans::build::*;
@@ -815,10 +816,12 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
 // the massive simplifications that have occurred.
 
 pub fn link_name(i: &ast::ForeignItem) -> InternedString {
-     match attr::first_attr_value_str_by_name(i.attrs.as_slice(),
-                                              "link_name") {
-        None => token::get_ident(i.ident),
+    match attr::first_attr_value_str_by_name(i.attrs.as_slice(), "link_name") {
         Some(ln) => ln.clone(),
+        None => match weak_lang_items::link_name(i.attrs.as_slice()) {
+            Some(name) => name,
+            None => token::get_ident(i.ident),
+        }
     }
 }
 
diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs
new file mode 100644
index 00000000000..cbe04e4fcda
--- /dev/null
+++ b/src/librustc/middle/weak_lang_items.rs
@@ -0,0 +1,124 @@
+// Copyright 2014 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.
+
+//! Validity checking for weak lang items
+
+use driver::config;
+use driver::session::Session;
+use metadata::csearch;
+use middle::lang_items;
+
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::parse::token::InternedString;
+use syntax::visit::Visitor;
+use syntax::visit;
+
+use collections::HashSet;
+
+macro_rules! weak_lang_items( ($($name:ident, $item:ident, $sym:ident;)*) => (
+
+struct Context<'a> {
+    sess: &'a Session,
+    items: &'a mut lang_items::LanguageItems,
+}
+
+/// Checks the crate for usage of weak lang items, returning a vector of all the
+/// language items required by this crate, but not defined yet.
+pub fn check_crate(krate: &ast::Crate,
+                   sess: &Session,
+                   items: &mut lang_items::LanguageItems) {
+    // These are never called by user code, they're generated by the compiler.
+    // They will never implicitly be added to the `missing` array unless we do
+    // so here.
+    if items.stack_exhausted().is_none() {
+        items.missing.push(lang_items::StackExhaustedLangItem);
+    }
+    if items.eh_personality().is_none() {
+        items.missing.push(lang_items::EhPersonalityLangItem);
+    }
+
+    {
+        let mut cx = Context { sess: sess, items: items };
+        visit::walk_crate(&mut cx, krate, ());
+    }
+    verify(sess, items);
+}
+
+pub fn link_name(attrs: &[ast::Attribute]) -> Option<InternedString> {
+    lang_items::extract(attrs).and_then(|name| {
+        $(if name.get() == stringify!($name) {
+            Some(InternedString::new(stringify!($sym)))
+        } else)* {
+            None
+        }
+    })
+}
+
+fn verify(sess: &Session, items: &lang_items::LanguageItems) {
+    // We only need to check for the presence of weak lang items if we're
+    // emitting something that's not an rlib.
+    let needs_check = sess.crate_types.borrow().iter().any(|kind| {
+        match *kind {
+            config::CrateTypeDylib |
+            config::CrateTypeExecutable |
+            config::CrateTypeStaticlib => true,
+            config::CrateTypeRlib => false,
+        }
+    });
+    if !needs_check { return }
+
+    let mut missing = HashSet::new();
+    sess.cstore.iter_crate_data(|cnum, _| {
+        for item in csearch::get_missing_lang_items(&sess.cstore, cnum).iter() {
+            missing.insert(*item);
+        }
+    });
+
+    $(
+        if missing.contains(&lang_items::$item) && items.$name().is_none() {
+            sess.err(format!("language item required, but not found: `{}`",
+                             stringify!($name)));
+
+        }
+    )*
+}
+
+impl<'a> Context<'a> {
+    fn register(&mut self, name: &str, span: Span) {
+        $(if name == stringify!($name) {
+            if self.items.$name().is_none() {
+                self.items.missing.push(lang_items::$item);
+            }
+        } else)* {
+            self.sess.span_err(span,
+                               format!("unknown external lang item: `{}`",
+                                       name));
+        }
+    }
+}
+
+impl<'a> Visitor<()> for Context<'a> {
+    fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) {
+        match lang_items::extract(i.attrs.as_slice()) {
+            None => {}
+            Some(lang_item) => self.register(lang_item.get(), i.span),
+        }
+        visit::walk_foreign_item(self, i, ())
+    }
+}
+
+) )
+
+weak_lang_items!(
+    begin_unwind,       BeginUnwindLangItem,        rust_begin_unwind;
+    stack_exhausted,    StackExhaustedLangItem,     rust_stack_exhausted;
+    eh_personality,     EhPersonalityLangItem,      rust_eh_personality;
+)
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 791ee96d672..4e2b23310ab 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -71,7 +71,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<StrBuf>)
     let sessopts = driver::config::Options {
         maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
         addl_lib_search_paths: RefCell::new(libs),
-        crate_types: vec!(driver::config::CrateTypeDylib),
+        crate_types: vec!(driver::config::CrateTypeRlib),
         lint_opts: vec!((lint::Warnings, lint::allow)),
         ..rustc::driver::config::basic_options().clone()
     };
diff --git a/src/libstd/rt/stack.rs b/src/libstd/rt/stack.rs
index b9bcd1de8fc..b3be742e1ed 100644
--- a/src/libstd/rt/stack.rs
+++ b/src/libstd/rt/stack.rs
@@ -30,12 +30,9 @@ pub static RED_ZONE: uint = 20 * 1024;
 /// stacks are currently not enabled as segmented stacks, but rather one giant
 /// stack segment. This means that whenever we run out of stack, we want to
 /// truly consider it to be stack overflow rather than allocating a new stack.
-#[no_mangle]      // - this is called from C code
-#[no_split_stack] // - it would be sad for this function to trigger __morestack
-#[doc(hidden)]    // - Function must be `pub` to get exported, but it's
-                  //   irrelevant for documentation purposes.
-#[cfg(not(test))] // in testing, use the original libstd's version
-pub extern "C" fn rust_stack_exhausted() {
+#[cfg(not(test), not(stage0))] // in testing, use the original libstd's version
+#[lang = "stack_exhausted"]
+extern fn stack_exhausted() {
     use option::{Option, None, Some};
     use owned::Box;
     use rt::local::Local;
@@ -106,6 +103,35 @@ pub extern "C" fn rust_stack_exhausted() {
     }
 }
 
+#[no_mangle]      // - this is called from C code
+#[no_split_stack] // - it would be sad for this function to trigger __morestack
+#[doc(hidden)]    // - Function must be `pub` to get exported, but it's
+                  //   irrelevant for documentation purposes.
+#[cfg(stage0, not(test))] // in testing, use the original libstd's version
+pub extern "C" fn rust_stack_exhausted() {
+    use option::{Option, None, Some};
+    use owned::Box;
+    use rt::local::Local;
+    use rt::task::Task;
+    use str::Str;
+    use intrinsics;
+
+    unsafe {
+        let limit = get_sp_limit();
+        record_sp_limit(limit - RED_ZONE / 2);
+        let task: Option<Box<Task>> = Local::try_take();
+        let name = match task {
+            Some(ref task) => {
+                task.name.as_ref().map(|n| n.as_slice())
+            }
+            None => None
+        };
+        let name = name.unwrap_or("<unknown>");
+        rterrln!("task '{}' has overflowed its stack", name);
+        intrinsics::abort();
+    }
+}
+
 #[inline(always)]
 pub unsafe fn record_stack_bounds(stack_lo: uint, stack_hi: uint) {
     // When the old runtime had segmented stacks, it used a calculation that was
diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs
index 1cc513825a7..af87a31b7bd 100644
--- a/src/libstd/rt/unwind.rs
+++ b/src/libstd/rt/unwind.rs
@@ -212,7 +212,23 @@ pub mod eabi {
     }
 
     #[lang="eh_personality"]
+    #[cfg(not(stage0))]
+    extern fn eh_personality(
+        version: c_int,
+        actions: uw::_Unwind_Action,
+        exception_class: uw::_Unwind_Exception_Class,
+        ue_header: *uw::_Unwind_Exception,
+        context: *uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        unsafe {
+            __gcc_personality_v0(version, actions, exception_class, ue_header,
+                                 context)
+        }
+    }
+    #[lang="eh_personality"]
     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
+    #[cfg(stage0)]
     pub extern "C" fn rust_eh_personality(
         version: c_int,
         actions: uw::_Unwind_Action,
@@ -264,7 +280,21 @@ pub mod eabi {
     }
 
     #[lang="eh_personality"]
+    #[cfg(not(stage0))]
+    extern "C" fn eh_personality(
+        state: uw::_Unwind_State,
+        ue_header: *uw::_Unwind_Exception,
+        context: *uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        unsafe {
+            __gcc_personality_v0(state, ue_header, context)
+        }
+    }
+
+    #[lang="eh_personality"]
     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
+    #[cfg(stage0)]
     pub extern "C" fn rust_eh_personality(
         state: uw::_Unwind_State,
         ue_header: *uw::_Unwind_Exception,
@@ -296,8 +326,15 @@ pub mod eabi {
 }
 
 // Entry point of failure from the libcore crate
+#[cfg(not(test), not(stage0))]
+#[lang = "begin_unwind"]
+pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
+                                file: &'static str, line: uint) -> ! {
+    begin_unwind_fmt(msg, file, line)
+}
+
 #[no_mangle]
-#[cfg(not(test))]
+#[cfg(not(test), stage0)]
 pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
                                 file: &'static str, line: uint) -> ! {
     begin_unwind_fmt(msg, file, line)
@@ -310,7 +347,8 @@ pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
 /// on (e.g.) the inlining of other functions as possible), by moving
 /// the actual formatting into this shared place.
 #[inline(never)] #[cold]
-pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
+pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str,
+                        line: uint) -> ! {
     // We do two allocations here, unfortunately. But (a) they're
     // required with the current scheme, and (b) we don't handle
     // failure + OOM properly anyway (see comment in begin_unwind
diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs
index 1435a101115..2c9a5bc433f 100644
--- a/src/test/auxiliary/lang-item-public.rs
+++ b/src/test/auxiliary/lang-item-public.rs
@@ -13,5 +13,8 @@
 #[lang="fail_"]
 fn fail(_: *i8, _: *i8, _: uint) -> ! { loop {} }
 
-#[no_mangle]
-pub extern "C" fn rust_stack_exhausted() {}
+#[lang = "stack_exhausted"]
+extern fn stack_exhausted() {}
+
+#[lang = "eh_personality"]
+extern fn eh_personality() {}
diff --git a/src/test/auxiliary/weak-lang-items.rs b/src/test/auxiliary/weak-lang-items.rs
new file mode 100644
index 00000000000..68a2ae24b85
--- /dev/null
+++ b/src/test/auxiliary/weak-lang-items.rs
@@ -0,0 +1,37 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+// This aux-file will require the eh_personality function to be codegen'd, but
+// it hasn't been defined just yet. Make sure we don't explode.
+
+#![no_std]
+#![feature(phase)]
+#![crate_type = "rlib"]
+
+#[phase(syntax, link)]
+extern crate core;
+
+struct A;
+
+impl core::ops::Drop for A {
+    fn drop(&mut self) {}
+}
+
+pub fn foo() {
+    let _a = A;
+    fail!("wut");
+}
+
+mod std {
+    pub use core::{option, fmt};
+}
+
diff --git a/src/test/compile-fail/weak-lang-item.rs b/src/test/compile-fail/weak-lang-item.rs
new file mode 100644
index 00000000000..a1b64b77ac1
--- /dev/null
+++ b/src/test/compile-fail/weak-lang-item.rs
@@ -0,0 +1,19 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:weak-lang-items.rs
+// error-pattern: language item required, but not found: `begin_unwind`
+// error-pattern: language item required, but not found: `stack_exhausted`
+// error-pattern: language item required, but not found: `eh_personality`
+
+#![no_std]
+
+extern crate core;
+extern crate other = "weak-lang-items";
diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs
index b95f0769d53..0dbd4c67530 100644
--- a/src/test/run-pass/smallest-hello-world.rs
+++ b/src/test/run-pass/smallest-hello-world.rs
@@ -19,8 +19,8 @@ extern crate libc;
 extern { fn puts(s: *u8); }
 extern "rust-intrinsic" { fn transmute<T, U>(t: T) -> U; }
 
-#[no_mangle]
-pub extern fn rust_stack_exhausted() {}
+#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
+#[lang = "eh_personality"] extern fn eh_personality() {}
 
 #[start]
 #[no_split_stack]
diff --git a/src/test/run-pass/weak-lang-item.rs b/src/test/run-pass/weak-lang-item.rs
new file mode 100644
index 00000000000..889259b6acd
--- /dev/null
+++ b/src/test/run-pass/weak-lang-item.rs
@@ -0,0 +1,21 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:weak-lang-items.rs
+
+extern crate other = "weak-lang-items";
+
+use std::task;
+
+fn main() {
+    let _ = task::try(proc() {
+        other::foo()
+    });
+}