diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 8aeef3dca98..6287683c1a1 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -209,6 +209,9 @@ pub static tag_dylib_dependency_formats: uint = 0x67; pub static tag_method_argument_names: uint = 0x8e; pub static tag_method_argument_name: uint = 0x8f; +pub static tag_reachable_extern_fns: uint = 0x90; +pub static tag_reachable_extern_fn_id: uint = 0x91; + #[deriving(Clone, Show)] pub struct LinkMeta { pub crateid: CrateId, diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 74a804763e8..c7ad74dce57 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -314,3 +314,10 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId) let cdata = cstore.get_crate_data(did.krate); decoder::get_method_arg_names(&*cdata, did.node) } + +pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum) + -> Vec +{ + let cdata = cstore.get_crate_data(cnum); + decoder::get_reachable_extern_fns(&*cdata) +} diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 6469462734e..c67b5bf1a60 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -1325,3 +1325,17 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec { } return ret; } + +pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec { + let mut ret = Vec::new(); + let items = reader::get_doc(ebml::Doc::new(cdata.data()), + tag_reachable_extern_fns); + reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| { + ret.push(ast::DefId { + krate: cdata.cnum, + node: reader::doc_as_u32(doc), + }); + true + }); + return ret; +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index cd2f3360f83..37cb75e4697 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -75,6 +75,7 @@ pub struct EncodeParams<'a> { pub link_meta: &'a LinkMeta, pub cstore: &'a cstore::CStore, pub encode_inlined_item: EncodeInlinedItem<'a>, + pub reachable: &'a NodeSet, } pub struct EncodeContext<'a> { @@ -87,6 +88,7 @@ pub struct EncodeContext<'a> { pub cstore: &'a cstore::CStore, pub encode_inlined_item: RefCell>, pub type_abbrevs: tyencode::abbrev_map, + pub reachable: &'a NodeSet, } fn encode_name(ebml_w: &mut Encoder, name: Name) { @@ -1702,6 +1704,26 @@ fn encode_misc_info(ecx: &EncodeContext, ebml_w.end_tag(); } +fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) { + ebml_w.start_tag(tag_reachable_extern_fns); + + for id in ecx.reachable.iter() { + match ecx.tcx.map.find(*id) { + Some(ast_map::NodeItem(i)) => { + match i.node { + ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => { + ebml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id); + } + _ => {} + } + } + _ => {} + } + } + + ebml_w.end_tag(); +} + fn encode_crate_dep(ebml_w: &mut Encoder, dep: decoder::CrateDep) { ebml_w.start_tag(tag_crate_dep); @@ -1801,6 +1823,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) encode_inlined_item, link_meta, non_inlineable_statics, + reachable, .. } = parms; let ecx = EncodeContext { @@ -1813,6 +1836,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) cstore: cstore, encode_inlined_item: RefCell::new(encode_inlined_item), type_abbrevs: RefCell::new(HashMap::new()), + reachable: reachable, }; let mut ebml_w = writer::Encoder::new(wr); @@ -1864,6 +1888,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) // Encode miscellaneous info. i = ebml_w.writer.tell().unwrap(); encode_misc_info(&ecx, krate, &mut ebml_w); + encode_reachable_extern_fns(&ecx, &mut ebml_w); stats.misc_bytes = ebml_w.writer.tell().unwrap() - i; // Encode and index the items. diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 9be07eaaca9..09f5d2a3507 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2238,6 +2238,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI link_meta: &cx.link_meta, cstore: &cx.sess().cstore, encode_inlined_item: ie, + reachable: &cx.reachable, } } @@ -2374,6 +2375,16 @@ pub fn trans_crate(krate: ast::Crate, ccx.item_symbols.borrow().find(id).map(|s| s.to_string()) }).collect(); + // For the purposes of LTO, we add to the reachable set all of the upstream + // reachable extern fns. These functions are all part of the public ABI of + // the final product, so LTO needs to preserve them. + ccx.sess().cstore.iter_crate_data(|cnum, _| { + let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum); + reachable.extend(syms.move_iter().map(|did| { + csearch::get_symbol(&ccx.sess().cstore, did) + })); + }); + // Make sure that some other crucial symbols are not eliminated from the // module. This includes the main function, the crate map (used for debug // log settings and I/O), and finally the curious rust_stack_exhausted diff --git a/src/test/run-make/issue-14500/Makefile b/src/test/run-make/issue-14500/Makefile new file mode 100644 index 00000000000..c1087b0f55e --- /dev/null +++ b/src/test/run-make/issue-14500/Makefile @@ -0,0 +1,14 @@ +-include ../tools.mk + +# Test to make sure that reachable extern fns are always available in final +# productcs, including when LTO is used. In this test, the `foo` crate has a +# reahable symbol, and is a dependency of the `bar` crate. When the `bar` crate +# is compiled with LTO, it shouldn't strip the symbol from `foo`, and that's the +# only way that `foo.c` will successfully compile. + +all: + $(RUSTC) foo.rs --crate-type=rlib + $(RUSTC) bar.rs --crate-type=staticlib -Zlto -L. -o $(TMPDIR)/libbar.a + $(CC) foo.c -lbar -o $(call RUN_BINFILE,foo) $(EXTRACFLAGS) + $(call RUN,foo) + diff --git a/src/test/run-make/issue-14500/bar.rs b/src/test/run-make/issue-14500/bar.rs new file mode 100644 index 00000000000..4b4916fe96d --- /dev/null +++ b/src/test/run-make/issue-14500/bar.rs @@ -0,0 +1,11 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate foo; diff --git a/src/test/run-make/issue-14500/foo.c b/src/test/run-make/issue-14500/foo.c new file mode 100644 index 00000000000..25098ac479d --- /dev/null +++ b/src/test/run-make/issue-14500/foo.c @@ -0,0 +1,16 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern void foo(); + +int main() { + foo(); + return 0; +} diff --git a/src/test/run-make/issue-14500/foo.rs b/src/test/run-make/issue-14500/foo.rs new file mode 100644 index 00000000000..ceca907403f --- /dev/null +++ b/src/test/run-make/issue-14500/foo.rs @@ -0,0 +1,12 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_mangle] +pub extern fn foo() {} diff --git a/src/test/run-make/lto-smoke-c/Makefile b/src/test/run-make/lto-smoke-c/Makefile index 49a04ce42a0..9b44c3e582a 100644 --- a/src/test/run-make/lto-smoke-c/Makefile +++ b/src/test/run-make/lto-smoke-c/Makefile @@ -1,23 +1,10 @@ -include ../tools.mk -ifdef IS_WINDOWS - EXTRAFLAGS := -else -ifeq ($(shell uname),Darwin) -else -ifeq ($(shell uname),FreeBSD) - EXTRAFLAGS := -lm -lpthread -lgcc_s -else - EXTRAFLAGS := -lm -lrt -ldl -lpthread -endif -endif -endif - # Apparently older versions of GCC segfault if -g is passed... CC := $(CC:-g=) all: $(RUSTC) foo.rs -Z lto ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo) - $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRAFLAGS) -lstdc++ + $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRACFLAGS) -lstdc++ $(call RUN,bar) diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk index dedd739052c..c9c4c455e4f 100644 --- a/src/test/run-make/tools.mk +++ b/src/test/run-make/tools.mk @@ -53,6 +53,20 @@ RPATH_LINK_SEARCH = -Wl,-rpath-link=$(1) endif endif +# Extra flags needed to compile a working executable with the standard library +ifdef IS_WINDOWS + EXTRACFLAGS := +else +ifeq ($(shell uname),Darwin) +else +ifeq ($(shell uname),FreeBSD) + EXTRACFLAGS := -lm -lpthread -lgcc_s +else + EXTRACFLAGS := -lm -lrt -ldl -lpthread +endif +endif +endif + REMOVE_DYLIBS = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1)) REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))