Skip to content

Commit f6b0f17

Browse files
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.
1 parent 00c206f commit f6b0f17

File tree

14 files changed

+241
-9
lines changed

14 files changed

+241
-9
lines changed

src/librustc/middle/cstore.rs

+7
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,11 @@ pub trait CrateStore<'tcx> : Any {
204204
fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool;
205205
fn is_allocator(&self, cnum: ast::CrateNum) -> bool;
206206
fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>;
207+
/// The name of the crate as it is referred to in source code of the current
208+
/// crate.
207209
fn crate_name(&self, cnum: ast::CrateNum) -> InternedString;
210+
/// The name of the crate as it is stored in the crate's metadata.
211+
fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString;
208212
fn crate_hash(&self, cnum: ast::CrateNum) -> Svh;
209213
fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString;
210214
fn crate_struct_field_attrs(&self, cnum: ast::CrateNum)
@@ -385,6 +389,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
385389
fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>
386390
{ unimplemented!() }
387391
fn crate_name(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() }
392+
fn original_crate_name(&self, cnum: ast::CrateNum) -> InternedString {
393+
unimplemented!()
394+
}
388395
fn crate_hash(&self, cnum: ast::CrateNum) -> Svh { unimplemented!() }
389396
fn crate_disambiguator(&self, cnum: ast::CrateNum) -> InternedString { unimplemented!() }
390397
fn crate_struct_field_attrs(&self, cnum: ast::CrateNum)

src/librustc_metadata/creader.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,10 @@ impl<'a> CrateReader<'a> {
277277
}
278278

279279
fn verify_no_symbol_conflicts(&self,
280-
crate_name: &str,
281280
span: Span,
282281
metadata: &MetadataBlob) {
283282
let disambiguator = decoder::get_crate_disambiguator(metadata.as_slice());
283+
let crate_name = decoder::get_crate_name(metadata.as_slice());
284284

285285
// Check for (potential) conflicts with the local crate
286286
if self.local_crate_name == crate_name &&
@@ -318,7 +318,7 @@ impl<'a> CrateReader<'a> {
318318
-> (ast::CrateNum, Rc<cstore::crate_metadata>,
319319
cstore::CrateSource) {
320320
self.verify_rustc_version(name, span, &lib.metadata);
321-
self.verify_no_symbol_conflicts(name, span, &lib.metadata);
321+
self.verify_no_symbol_conflicts(span, &lib.metadata);
322322

323323
// Claim this crate number and cache it
324324
let cnum = self.next_crate_num;

src/librustc_metadata/csearch.rs

+5
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
340340
token::intern_and_get_ident(&self.get_crate_data(cnum).name[..])
341341
}
342342

343+
fn original_crate_name(&self, cnum: ast::CrateNum) -> token::InternedString
344+
{
345+
token::intern_and_get_ident(&self.get_crate_data(cnum).name())
346+
}
347+
343348
fn crate_hash(&self, cnum: ast::CrateNum) -> Svh
344349
{
345350
let cdata = self.get_crate_data(cnum);

src/librustc_metadata/cstore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl CStore {
248248

249249
impl crate_metadata {
250250
pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() }
251-
pub fn name(&self) -> String { decoder::get_crate_name(self.data()) }
251+
pub fn name(&self) -> &str { decoder::get_crate_name(self.data()) }
252252
pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) }
253253
pub fn disambiguator(&self) -> &str {
254254
decoder::get_crate_disambiguator(self.data())

src/librustc_metadata/decoder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1311,10 +1311,10 @@ pub fn get_crate_hash(data: &[u8]) -> Svh {
13111311
Svh::new(hashdoc.as_str_slice())
13121312
}
13131313

1314-
pub fn maybe_get_crate_name(data: &[u8]) -> Option<String> {
1314+
pub fn maybe_get_crate_name(data: &[u8]) -> Option<&str> {
13151315
let cratedoc = rbml::Doc::new(data);
13161316
reader::maybe_get_doc(cratedoc, tag_crate_crate_name).map(|doc| {
1317-
doc.as_str_slice().to_string()
1317+
doc.as_str_slice()
13181318
})
13191319
}
13201320

@@ -1331,7 +1331,7 @@ pub fn get_crate_triple(data: &[u8]) -> Option<String> {
13311331
triple_doc.map(|s| s.as_str().to_string())
13321332
}
13331333

1334-
pub fn get_crate_name(data: &[u8]) -> String {
1334+
pub fn get_crate_name(data: &[u8]) -> &str {
13351335
maybe_get_crate_name(data).expect("no crate name in crate")
13361336
}
13371337

src/librustc_trans/back/linker.rs

+21
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use session::config::CrateTypeDylib;
2323
use session::config;
2424
use syntax::ast;
2525
use trans::CrateTranslation;
26+
use trans::link_guard;
2627

2728
/// Linker abstraction used by back::link to build up the command to invoke a
2829
/// linker.
@@ -359,6 +360,26 @@ impl<'a> Linker for MsvcLinker<'a> {
359360
for symbol in symbols {
360361
try!(writeln!(f, " {}", symbol));
361362
}
363+
364+
// Add link-guard symbols
365+
{
366+
// local crate
367+
let symbol = link_guard::link_guard_name(&trans.link.crate_name[..],
368+
&trans.link.crate_hash);
369+
try!(writeln!(f, " {}", symbol));
370+
}
371+
// statically linked dependencies
372+
for (i, format) in formats[&CrateTypeDylib].iter().enumerate() {
373+
if *format == Linkage::Static {
374+
let cnum = (i + 1) as ast::CrateNum;
375+
let crate_name = cstore.original_crate_name(cnum);
376+
let svh = cstore.crate_hash(cnum);
377+
378+
let symbol = link_guard::link_guard_name(&crate_name[..], &svh);
379+
try!(writeln!(f, " {}", symbol));
380+
}
381+
}
382+
362383
Ok(())
363384
})();
364385
if let Err(e) = res {

src/librustc_trans/trans/base.rs

+24
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ use trans::expr;
7777
use trans::foreign;
7878
use trans::glue;
7979
use trans::intrinsic;
80+
use trans::link_guard;
8081
use trans::machine;
8182
use trans::machine::{llsize_of, llsize_of_real};
8283
use trans::meth;
@@ -2668,6 +2669,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
26682669
unsafe {
26692670
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
26702671

2672+
link_guard::insert_reference_to_link_guard(ccx, llbb);
26712673
debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx);
26722674

26732675
let (start_fn, args) = if use_start_lang_item {
@@ -3213,6 +3215,8 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
32133215
collector::print_collection_results(&ccx);
32143216
}
32153217

3218+
emit_link_guard_if_necessary(&shared_ccx);
3219+
32163220
for ccx in shared_ccx.iter() {
32173221
if ccx.sess().opts.debuginfo != NoDebugInfo {
32183222
debuginfo::finalize(&ccx);
@@ -3273,6 +3277,8 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
32733277
if sess.entry_fn.borrow().is_some() {
32743278
reachable_symbols.push("main".to_string());
32753279
}
3280+
reachable_symbols.push(link_guard::link_guard_name(&link_meta.crate_name,
3281+
&link_meta.crate_hash));
32763282

32773283
// For the purposes of LTO, we add to the reachable set all of the upstream
32783284
// reachable extern fns. These functions are all part of the public ABI of
@@ -3316,6 +3322,24 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
33163322
}
33173323
}
33183324

3325+
fn emit_link_guard_if_necessary(shared_ccx: &SharedCrateContext) {
3326+
let link_meta = shared_ccx.link_meta();
3327+
let link_guard_name = link_guard::link_guard_name(&link_meta.crate_name,
3328+
&link_meta.crate_hash);
3329+
let link_guard_name = CString::new(link_guard_name).unwrap();
3330+
3331+
// Check if the link-guard has already been emitted in a codegen unit
3332+
let link_guard_already_emitted = shared_ccx.iter().any(|ccx| {
3333+
let link_guard = unsafe { llvm::LLVMGetNamedValue(ccx.llmod(),
3334+
link_guard_name.as_ptr()) };
3335+
!link_guard.is_null()
3336+
});
3337+
3338+
if !link_guard_already_emitted {
3339+
link_guard::get_or_insert_link_guard(&shared_ccx.get_ccx(0));
3340+
}
3341+
}
3342+
33193343
/// We visit all the items in the krate and translate them. We do
33203344
/// this in two walks. The first walk just finds module items. It then
33213345
/// walks the full contents of those module items and translates all
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use back::svh::Svh;
12+
use libc::c_uint;
13+
use llvm;
14+
use std::ffi::CString;
15+
use std::ptr;
16+
use trans::attributes;
17+
use trans::builder;
18+
use trans::CrateContext;
19+
use trans::declare;
20+
use trans::type_::Type;
21+
22+
const GUARD_PREFIX: &'static str = "__rustc_link_guard_";
23+
24+
pub fn link_guard_name(crate_name: &str, crate_svh: &Svh) -> String {
25+
26+
let mut guard_name = String::new();
27+
28+
guard_name.push_str(GUARD_PREFIX);
29+
guard_name.push_str(crate_name);
30+
guard_name.push_str("_");
31+
guard_name.push_str(crate_svh.as_str());
32+
33+
guard_name
34+
}
35+
36+
pub fn get_or_insert_link_guard<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>)
37+
-> llvm::ValueRef {
38+
39+
let guard_name = link_guard_name(&ccx.tcx().crate_name[..],
40+
&ccx.link_meta().crate_hash);
41+
42+
let guard_function = unsafe {
43+
let guard_name_c_string = CString::new(&guard_name[..]).unwrap();
44+
llvm::LLVMGetNamedValue(ccx.llmod(), guard_name_c_string.as_ptr())
45+
};
46+
47+
if guard_function != ptr::null_mut() {
48+
return guard_function;
49+
}
50+
51+
let llfty = Type::func(&[], &Type::void(ccx));
52+
let guard_function = declare::define_cfn(ccx,
53+
&guard_name[..],
54+
llfty,
55+
ccx.tcx().mk_nil()).unwrap_or_else(|| {
56+
ccx.sess().bug("Link guard already defined.");
57+
});
58+
59+
attributes::emit_uwtable(guard_function, true);
60+
attributes::unwind(guard_function, false);
61+
62+
let bld = ccx.raw_builder();
63+
unsafe {
64+
let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(),
65+
guard_function,
66+
"link_guard_top\0".as_ptr() as *const _);
67+
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
68+
69+
for crate_num in ccx.sess().cstore.crates() {
70+
if !ccx.sess().cstore.is_explicitly_linked(crate_num) {
71+
continue;
72+
}
73+
74+
let crate_name = ccx.sess().cstore.original_crate_name(crate_num);
75+
let svh = ccx.sess().cstore.crate_hash(crate_num);
76+
77+
let dependency_guard_name = link_guard_name(&crate_name[..], &svh);
78+
79+
let decl = declare::declare_cfn(ccx,
80+
&dependency_guard_name[..],
81+
llfty,
82+
ccx.tcx().mk_nil());
83+
attributes::unwind(decl, false);
84+
85+
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
86+
87+
let args: &[llvm::ValueRef] = &[];
88+
llvm::LLVMRustBuildCall(bld,
89+
decl,
90+
args.as_ptr(),
91+
args.len() as c_uint,
92+
0 as *mut _,
93+
builder::noname());
94+
}
95+
96+
llvm::LLVMBuildRetVoid(bld);
97+
}
98+
99+
guard_function
100+
}
101+
102+
pub fn insert_reference_to_link_guard<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
103+
llbb: llvm::BasicBlockRef) {
104+
let guard_function = get_or_insert_link_guard(ccx);
105+
106+
unsafe {
107+
llvm::LLVMPositionBuilderAtEnd(ccx.raw_builder(), llbb);
108+
let args: &[llvm::ValueRef] = &[];
109+
llvm::LLVMRustBuildCall(ccx.raw_builder(),
110+
guard_function,
111+
args.as_ptr(),
112+
args.len() as c_uint,
113+
0 as *mut _,
114+
builder::noname());
115+
}
116+
}

src/librustc_trans/trans/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ mod foreign;
5353
mod glue;
5454
mod inline;
5555
mod intrinsic;
56+
pub mod link_guard;
5657
mod llrepr;
5758
mod machine;
5859
mod _match;

src/test/run-make/link-guard/Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-include ../tools.mk
2+
3+
all:
4+
-mkdir -p $(TMPDIR)/good
5+
-mkdir -p $(TMPDIR)/bad
6+
$(BARE_RUSTC) ./good/lib.rs -C prefer-dynamic --out-dir="$(TMPDIR)/good"
7+
$(BARE_RUSTC) ./bad/lib.rs -C prefer-dynamic --out-dir="$(TMPDIR)/bad"
8+
$(BARE_RUSTC) -L "$(TMPDIR)/good" -C prefer-dynamic -Crpath ./main.rs --out-dir="$(TMPDIR)"
9+
# This should succeed because the correct library is in LD_LIBRARY_PATH
10+
$(LD_LIB_PATH_ENVVAR)="$(TMPDIR)/good:$($(LD_LIB_PATH_ENVVAR))" $(TMPDIR)/main
11+
# This should fail because the wrong library is in LD_LIBRARY_PATH
12+
OUTPUT=`$(LD_LIB_PATH_ENVVAR)="$(TMPDIR)/bad:$($(LD_LIB_PATH_ENVVAR))" $(TMPDIR)/main || exit 0`
13+
if ["$(OUTPUT)" == "bad"]; then exit 1; fi
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_name="thelibrary"]
12+
#![crate_type="dylib"]
13+
14+
pub fn some_library_function() {
15+
println!("bad");
16+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_name="thelibrary"]
12+
#![crate_type="dylib"]
13+
14+
pub fn some_library_function() {
15+
println!("bad");
16+
}

src/test/run-make/link-guard/main.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern crate thelibrary;
12+
13+
fn main() {
14+
thelibrary::some_library_function();
15+
}

src/test/run-make/relocation-model/Makefile

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ all: others
77
$(RUSTC) -C relocation-model=default foo.rs
88
$(call RUN,foo)
99

10-
$(RUSTC) -C relocation-model=default --crate-type=dylib foo.rs
11-
$(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs
10+
$(RUSTC) -C relocation-model=dynamic-no-pic --crate-type=dylib foo.rs --emit=link,obj
1211

1312
ifdef IS_MSVC
1413
# FIXME(#28026)
@@ -17,5 +16,4 @@ else
1716
others:
1817
$(RUSTC) -C relocation-model=static foo.rs
1918
$(call RUN,foo)
20-
$(RUSTC) -C relocation-model=static --crate-type=dylib foo.rs
2119
endif

0 commit comments

Comments
 (0)