]> git.lizzy.rs Git - rust.git/commitdiff
Add a "link-guard" to avoid accidentally linking to a wrong dylib at runtime.
authorMichael Woerister <michaelwoerister@posteo.net>
Tue, 1 Mar 2016 13:19:00 +0000 (08:19 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Fri, 25 Mar 2016 18:07:19 +0000 (14:07 -0400)
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.

14 files changed:
src/librustc/middle/cstore.rs
src/librustc_metadata/creader.rs
src/librustc_metadata/csearch.rs
src/librustc_metadata/cstore.rs
src/librustc_metadata/decoder.rs
src/librustc_trans/back/linker.rs
src/librustc_trans/trans/base.rs
src/librustc_trans/trans/link_guard.rs [new file with mode: 0644]
src/librustc_trans/trans/mod.rs
src/test/run-make/link-guard/Makefile [new file with mode: 0644]
src/test/run-make/link-guard/bad/lib.rs [new file with mode: 0644]
src/test/run-make/link-guard/good/lib.rs [new file with mode: 0644]
src/test/run-make/link-guard/main.rs [new file with mode: 0644]
src/test/run-make/relocation-model/Makefile

index 718a9fd58dea4a1b84c32680bdb9f78bfa6c5ecd..34af4826c3ea46318d7ed3839e0953a39a0380e7 100644 (file)
@@ -204,7 +204,11 @@ fn dylib_dependency_formats(&self, cnum: ast::CrateNum)
     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 @@ fn is_allocator(&self, cnum: ast::CrateNum) -> bool { unimplemented!() }
     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)
index 11cb2b8a818f4b66eff2cd7b36d11cce116f4e23..493994fd74a5d610c34a956bd683a28a76276fdd 100644 (file)
@@ -277,10 +277,10 @@ fn verify_rustc_version(&self,
     }
 
     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 @@ fn register_crate(&mut self,
                       -> (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;
index f9446d7667c6903fd498cbb6f81d9a52e8177e98..fb4dbbba8da49d2e6ba9effc643ee7c930eaf58c 100644 (file)
@@ -339,6 +339,11 @@ fn crate_name(&self, cnum: ast::CrateNum) -> token::InternedString
         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);
index 1e265c546c5c4ca3f3b38860d3ca4cb374c8d04b..17c485c73497f8c31310e28977f5fb488f1797ca 100644 (file)
@@ -248,7 +248,7 @@ pub fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<ast::Crate
 
 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())
index 79209a7d26da26717d39ded110ccb941f55b6874..00810ec71abbfa963a1e9113bf0ee9930629bb3b 100644 (file)
@@ -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")
 }
 
index b6b330c3734b0310f67d1202634aa743164baf10..934e0e16a9688bb01dcd3eb4fe3265b2252f069d 100644 (file)
@@ -23,6 +23,7 @@
 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 @@ fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
             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 {
index e903fc2811d02423b3074b5a619e949eb4dd48e8..6bbf323ef5e8be19ea83b9ba6bb7b173db571cfd 100644 (file)
@@ -79,6 +79,7 @@
 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 @@ fn create_entry_fn(ccx: &CrateContext,
         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 (file)
index 0000000..94606ca
--- /dev/null
@@ -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());
+    }
+}
index f77f7c30987c88a84779c4bfa2abd125d64ebac9..2e71128a1e78bd8f9e646b4d5fdfab7f837f6128 100644 (file)
@@ -53,6 +53,7 @@
 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 (file)
index 0000000..3897065
--- /dev/null
@@ -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 (file)
index 0000000..c13c0d5
--- /dev/null
@@ -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");
+}
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 (file)
index 0000000..c13c0d5
--- /dev/null
@@ -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");
+}
diff --git a/src/test/run-make/link-guard/main.rs b/src/test/run-make/link-guard/main.rs
new file mode 100644 (file)
index 0000000..c422316
--- /dev/null
@@ -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();
+}
index b22f34fa35b54af8b262159d26d461e6dc301db6..485ecbb4b5a59a60b0cad78692d977374face91d 100644 (file)
@@ -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