diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 718a9fd58dea4..34af4826c3ea4 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -204,7 +204,11 @@ pub trait CrateStore<'tcx> : Any { fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool; fn is_allocator(&self, cnum: ast::CrateNum) -> bool; fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec; + /// The name of the crate as it is referred to in source code of the current + /// crate. fn crate_name(&self, cnum: ast::CrateNum) -> InternedString; + /// The name of the crate as it is stored in the crate's metadata. + fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString; fn crate_hash(&self, cnum: ast::CrateNum) -> Svh; fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString; fn crate_struct_field_attrs(&self, cnum: ast::CrateNum) @@ -385,6 +389,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec { unimplemented!() } fn crate_name(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() } + fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString { + unimplemented!() + } fn crate_hash(&self, cnum: ast::CrateNum) -> Svh { unimplemented!() } fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() } fn crate_struct_field_attrs(&self, cnum: ast::CrateNum) diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 11cb2b8a818f4..493994fd74a5d 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -277,10 +277,10 @@ impl<'a> CrateReader<'a> { } fn verify_no_symbol_conflicts(&self, - crate_name: &str, span: Span, metadata: &MetadataBlob) { let disambiguator = decoder::get_crate_disambiguator(metadata.as_slice()); + let crate_name = decoder::get_crate_name(metadata.as_slice()); // Check for (potential) conflicts with the local crate if self.local_crate_name == crate_name && @@ -318,7 +318,7 @@ impl<'a> CrateReader<'a> { -> (ast::CrateNum, Rc, cstore::CrateSource) { self.verify_rustc_version(name, span, &lib.metadata); - self.verify_no_symbol_conflicts(name, span, &lib.metadata); + self.verify_no_symbol_conflicts(span, &lib.metadata); // Claim this crate number and cache it let cnum = self.next_crate_num; diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index f9446d7667c69..fb4dbbba8da49 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -339,6 +339,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { token::intern_and_get_ident(&self.get_crate_data(cnum).name[..]) } + fn original_crate_name(&self, cnum: ast::CrateNum) -> token::InternedString + { + token::intern_and_get_ident(&self.get_crate_data(cnum).name()) + } + fn crate_hash(&self, cnum: ast::CrateNum) -> Svh { let cdata = self.get_crate_data(cnum); diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 1e265c546c5c4..17c485c73497f 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -248,7 +248,7 @@ impl CStore { impl crate_metadata { pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() } - pub fn name(&self) -> String { decoder::get_crate_name(self.data()) } + pub fn name(&self) -> &str { decoder::get_crate_name(self.data()) } pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) } pub fn disambiguator(&self) -> &str { decoder::get_crate_disambiguator(self.data()) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 79209a7d26da2..00810ec71abbf 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -1288,10 +1288,10 @@ pub fn get_crate_hash(data: &[u8]) -> Svh { Svh::new(hashdoc.as_str_slice()) } -pub fn maybe_get_crate_name(data: &[u8]) -> Option { +pub fn maybe_get_crate_name(data: &[u8]) -> Option<&str> { let cratedoc = rbml::Doc::new(data); reader::maybe_get_doc(cratedoc, tag_crate_crate_name).map(|doc| { - doc.as_str_slice().to_string() + doc.as_str_slice() }) } @@ -1308,7 +1308,7 @@ pub fn get_crate_triple(data: &[u8]) -> Option { triple_doc.map(|s| s.as_str().to_string()) } -pub fn get_crate_name(data: &[u8]) -> String { +pub fn get_crate_name(data: &[u8]) -> &str { maybe_get_crate_name(data).expect("no crate name in crate") } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index b6b330c3734b0..934e0e16a9688 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -23,6 +23,7 @@ use session::config::CrateTypeDylib; use session::config; use syntax::ast; use trans::CrateTranslation; +use trans::link_guard; /// Linker abstraction used by back::link to build up the command to invoke a /// linker. @@ -359,6 +360,26 @@ impl<'a> Linker for MsvcLinker<'a> { for symbol in symbols { writeln!(f, " {}", symbol)?; } + + // Add link-guard symbols + { + // local crate + let symbol = link_guard::link_guard_name(&trans.link.crate_name[..], + &trans.link.crate_hash); + try!(writeln!(f, " {}", symbol)); + } + // statically linked dependencies + for (i, format) in formats[&CrateTypeDylib].iter().enumerate() { + if *format == Linkage::Static { + let cnum = (i + 1) as ast::CrateNum; + let crate_name = cstore.original_crate_name(cnum); + let svh = cstore.crate_hash(cnum); + + let symbol = link_guard::link_guard_name(&crate_name[..], &svh); + try!(writeln!(f, " {}", symbol)); + } + } + Ok(()) })(); if let Err(e) = res { diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index e903fc2811d02..6bbf323ef5e8b 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -79,6 +79,7 @@ use trans::expr; use trans::glue; use trans::inline; use trans::intrinsic; +use trans::link_guard; use trans::machine; use trans::machine::{llalign_of_min, llsize_of, llsize_of_real}; use trans::meth; @@ -2382,6 +2383,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) { unsafe { llvm::LLVMPositionBuilderAtEnd(bld, llbb); + link_guard::insert_reference_to_link_guard(ccx, llbb); debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx); let (start_fn, args) = if use_start_lang_item { @@ -2758,6 +2760,8 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>, collector::print_collection_results(&ccx); } + emit_link_guard_if_necessary(&shared_ccx); + for ccx in shared_ccx.iter() { if ccx.sess().opts.debuginfo != NoDebugInfo { debuginfo::finalize(&ccx); @@ -2818,6 +2822,8 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>, if sess.entry_fn.borrow().is_some() { reachable_symbols.push("main".to_string()); } + reachable_symbols.push(link_guard::link_guard_name(&link_meta.crate_name, + &link_meta.crate_hash)); // 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 @@ -2861,6 +2867,24 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>, } } +fn emit_link_guard_if_necessary(shared_ccx: &SharedCrateContext) { + let link_meta = shared_ccx.link_meta(); + let link_guard_name = link_guard::link_guard_name(&link_meta.crate_name, + &link_meta.crate_hash); + let link_guard_name = CString::new(link_guard_name).unwrap(); + + // Check if the link-guard has already been emitted in a codegen unit + let link_guard_already_emitted = shared_ccx.iter().any(|ccx| { + let link_guard = unsafe { llvm::LLVMGetNamedValue(ccx.llmod(), + link_guard_name.as_ptr()) }; + !link_guard.is_null() + }); + + if !link_guard_already_emitted { + link_guard::get_or_insert_link_guard(&shared_ccx.get_ccx(0)); + } +} + /// We visit all the items in the krate and translate them. We do /// this in two walks. The first walk just finds module items. It then /// walks the full contents of those module items and translates all diff --git a/src/librustc_trans/trans/link_guard.rs b/src/librustc_trans/trans/link_guard.rs new file mode 100644 index 0000000000000..94606cafce5de --- /dev/null +++ b/src/librustc_trans/trans/link_guard.rs @@ -0,0 +1,116 @@ +// Copyright 2012-2016 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. + +use back::svh::Svh; +use libc::c_uint; +use llvm; +use std::ffi::CString; +use std::ptr; +use trans::attributes; +use trans::builder; +use trans::CrateContext; +use trans::declare; +use trans::type_::Type; + +const GUARD_PREFIX: &'static str = "__rustc_link_guard_"; + +pub fn link_guard_name(crate_name: &str, crate_svh: &Svh) -> String { + + let mut guard_name = String::new(); + + guard_name.push_str(GUARD_PREFIX); + guard_name.push_str(crate_name); + guard_name.push_str("_"); + guard_name.push_str(crate_svh.as_str()); + + guard_name +} + +pub fn get_or_insert_link_guard<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) + -> llvm::ValueRef { + + let guard_name = link_guard_name(&ccx.tcx().crate_name[..], + &ccx.link_meta().crate_hash); + + let guard_function = unsafe { + let guard_name_c_string = CString::new(&guard_name[..]).unwrap(); + llvm::LLVMGetNamedValue(ccx.llmod(), guard_name_c_string.as_ptr()) + }; + + if guard_function != ptr::null_mut() { + return guard_function; + } + + let llfty = Type::func(&[], &Type::void(ccx)); + let guard_function = declare::define_cfn(ccx, + &guard_name[..], + llfty, + ccx.tcx().mk_nil()).unwrap_or_else(|| { + ccx.sess().bug("Link guard already defined."); + }); + + attributes::emit_uwtable(guard_function, true); + attributes::unwind(guard_function, false); + + let bld = ccx.raw_builder(); + unsafe { + let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), + guard_function, + "link_guard_top\0".as_ptr() as *const _); + llvm::LLVMPositionBuilderAtEnd(bld, llbb); + + for crate_num in ccx.sess().cstore.crates() { + if !ccx.sess().cstore.is_explicitly_linked(crate_num) { + continue; + } + + let crate_name = ccx.sess().cstore.original_crate_name(crate_num); + let svh = ccx.sess().cstore.crate_hash(crate_num); + + let dependency_guard_name = link_guard_name(&crate_name[..], &svh); + + let decl = declare::declare_cfn(ccx, + &dependency_guard_name[..], + llfty, + ccx.tcx().mk_nil()); + attributes::unwind(decl, false); + + llvm::LLVMPositionBuilderAtEnd(bld, llbb); + + let args: &[llvm::ValueRef] = &[]; + llvm::LLVMRustBuildCall(bld, + decl, + args.as_ptr(), + args.len() as c_uint, + 0 as *mut _, + builder::noname()); + } + + llvm::LLVMBuildRetVoid(bld); + } + + guard_function +} + +pub fn insert_reference_to_link_guard<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + llbb: llvm::BasicBlockRef) { + let guard_function = get_or_insert_link_guard(ccx); + + unsafe { + llvm::LLVMPositionBuilderAtEnd(ccx.raw_builder(), llbb); + let args: &[llvm::ValueRef] = &[]; + llvm::LLVMRustBuildCall(ccx.raw_builder(), + guard_function, + args.as_ptr(), + args.len() as c_uint, + 0 as *mut _, + builder::noname()); + } +} diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index f77f7c30987c8..2e71128a1e78b 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -53,6 +53,7 @@ mod expr; mod glue; mod inline; mod intrinsic; +pub mod link_guard; mod machine; mod _match; mod meth; diff --git a/src/test/run-make/link-guard/Makefile b/src/test/run-make/link-guard/Makefile new file mode 100644 index 0000000000000..38970652cb580 --- /dev/null +++ b/src/test/run-make/link-guard/Makefile @@ -0,0 +1,13 @@ +-include ../tools.mk + +all: + -mkdir -p $(TMPDIR)/good + -mkdir -p $(TMPDIR)/bad + $(BARE_RUSTC) ./good/lib.rs -C prefer-dynamic --out-dir="$(TMPDIR)/good" + $(BARE_RUSTC) ./bad/lib.rs -C prefer-dynamic --out-dir="$(TMPDIR)/bad" + $(BARE_RUSTC) -L "$(TMPDIR)/good" -C prefer-dynamic -Crpath ./main.rs --out-dir="$(TMPDIR)" + # This should succeed because the correct library is in LD_LIBRARY_PATH + $(LD_LIB_PATH_ENVVAR)="$(TMPDIR)/good:$($(LD_LIB_PATH_ENVVAR))" $(TMPDIR)/main + # This should fail because the wrong library is in LD_LIBRARY_PATH + OUTPUT=`$(LD_LIB_PATH_ENVVAR)="$(TMPDIR)/bad:$($(LD_LIB_PATH_ENVVAR))" $(TMPDIR)/main || exit 0` + if ["$(OUTPUT)" == "bad"]; then exit 1; fi diff --git a/src/test/run-make/link-guard/bad/lib.rs b/src/test/run-make/link-guard/bad/lib.rs new file mode 100644 index 0000000000000..c13c0d5e92f91 --- /dev/null +++ b/src/test/run-make/link-guard/bad/lib.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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. + +#![crate_name="thelibrary"] +#![crate_type="dylib"] + +pub fn some_library_function() { + println!("bad"); +} diff --git a/src/test/run-make/link-guard/good/lib.rs b/src/test/run-make/link-guard/good/lib.rs new file mode 100644 index 0000000000000..c13c0d5e92f91 --- /dev/null +++ b/src/test/run-make/link-guard/good/lib.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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. + +#![crate_name="thelibrary"] +#![crate_type="dylib"] + +pub fn some_library_function() { + println!("bad"); +} diff --git a/src/test/run-make/link-guard/main.rs b/src/test/run-make/link-guard/main.rs new file mode 100644 index 0000000000000..c422316d9183d --- /dev/null +++ b/src/test/run-make/link-guard/main.rs @@ -0,0 +1,15 @@ +// Copyright 2016 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 thelibrary; + +fn main() { + thelibrary::some_library_function(); +} diff --git a/src/test/run-make/relocation-model/Makefile b/src/test/run-make/relocation-model/Makefile index b22f34fa35b54..485ecbb4b5a59 100644 --- a/src/test/run-make/relocation-model/Makefile +++ b/src/test/run-make/relocation-model/Makefile @@ -7,8 +7,7 @@ all: others $(RUSTC) -C relocation-model=default foo.rs $(call RUN,foo) - $(RUSTC) -C relocation-model=default --crate-type=dylib foo.rs - $(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs + $(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs --emit=link,obj ifdef IS_MSVC # FIXME(#28026) @@ -17,5 +16,4 @@ else others: $(RUSTC) -C relocation-model=static foo.rs $(call RUN,foo) - $(RUSTC) -C relocation-model=static --crate-type=dylib foo.rs endif