Add a "link-guard" to avoid accidentally linking to a wrong dylib at runtime.
We want to prevent compiling something against one version of a dynamic library and then, at runtime accidentally using a different version of the dynamic library. With the old symbol-naming scheme this could not happen because every symbol had the SVH in it and you'd get an error by the dynamic linker when using the wrong version of a dylib. With the new naming scheme this isn't the case any more, so this patch adds the "link-guard" to prevent this error case. This is implemented as follows: - In every crate that we compile, we emit a function called "__rustc_link_guard_<crate-name>_<crate-svh>" - The body of this function contains calls to the "__rustc_link_guard" functions of all dependencies. - An executable contains a call to it's own "__rustc_link_guard" function. As a consequence the "__rustc_link_guard" function call graph mirrors the crate graph and the dynamic linker will fail if a wrong dylib is loaded somewhere because its "__rustc_link_guard" function will contain a different SVH in its name.
This commit is contained in:
parent
82b5f1d869
commit
2475707322
@ -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<ast::Attribute>;
|
||||
/// 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<ast::Attribute>
|
||||
{ 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)
|
||||
|
@ -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::crate_metadata>,
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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<String> {
|
||||
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<String> {
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
116
src/librustc_trans/trans/link_guard.rs
Normal file
116
src/librustc_trans/trans/link_guard.rs
Normal file
@ -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 <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.
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@ mod expr;
|
||||
mod glue;
|
||||
mod inline;
|
||||
mod intrinsic;
|
||||
pub mod link_guard;
|
||||
mod machine;
|
||||
mod _match;
|
||||
mod meth;
|
||||
|
13
src/test/run-make/link-guard/Makefile
Normal file
13
src/test/run-make/link-guard/Makefile
Normal file
@ -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
|
16
src/test/run-make/link-guard/bad/lib.rs
Normal file
16
src/test/run-make/link-guard/bad/lib.rs
Normal file
@ -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 <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.
|
||||
|
||||
#![crate_name="thelibrary"]
|
||||
#![crate_type="dylib"]
|
||||
|
||||
pub fn some_library_function() {
|
||||
println!("bad");
|
||||
}
|
16
src/test/run-make/link-guard/good/lib.rs
Normal file
16
src/test/run-make/link-guard/good/lib.rs
Normal file
@ -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 <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.
|
||||
|
||||
#![crate_name="thelibrary"]
|
||||
#![crate_type="dylib"]
|
||||
|
||||
pub fn some_library_function() {
|
||||
println!("bad");
|
||||
}
|
15
src/test/run-make/link-guard/main.rs
Normal file
15
src/test/run-make/link-guard/main.rs
Normal file
@ -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 <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.
|
||||
|
||||
extern crate thelibrary;
|
||||
|
||||
fn main() {
|
||||
thelibrary::some_library_function();
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user