Skip to content

Commit a0efd3a

Browse files
committed
trans: Be a little more picky about dllimport
Currently you can hit a link error on MSVC by only referencing static items from a crate (no functions for example) and then link to the crate statically (as all Rust crates do 99% of the time). A detailed investigation can be found [on github][details], but the tl;dr is that we need to stop applying dllimport so aggressively. This commit alters the application of dllimport on constants to only cases where the crate the constant originated from will be linked as a dylib in some output crate type. That way if we're just linking rlibs (like the motivation for this issue) we won't use dllimport. For the compiler, however, (which has lots of dylibs) we'll use dllimport. [details]: #26591 (comment) cc #26591
1 parent ee2d3bc commit a0efd3a

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

src/librustc_trans/trans/base.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId,
228228
// FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
229229
// FIXME(nagisa): investigate whether it can be changed into define_global
230230
let c = declare::declare_global(ccx, &name[..], ty);
231+
231232
// Thread-local statics in some other crate need to *always* be linked
232233
// against in a thread-local fashion, so we need to be sure to apply the
233234
// thread-local attribute locally if it was present remotely. If we
@@ -239,7 +240,42 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId,
239240
llvm::set_thread_local(c, true);
240241
}
241242
}
242-
if ccx.use_dll_storage_attrs() {
243+
244+
// MSVC is a little ornery about how items are imported across dlls, and for
245+
// lots more info on dllimport/dllexport see the large comment in
246+
// SharedCrateContext::new. Unfortunately, unlike functions, statics
247+
// imported from dlls *must* be tagged with dllimport (if you forget
248+
// dllimport on a function then the linker fixes it up with an injected
249+
// shim). This means that to link correctly to an upstream Rust dynamic
250+
// library we need to make sure its statics are tagged with dllimport.
251+
//
252+
// Hence, if this translation is using dll storage attributes and the crate
253+
// that this const originated from is being imported as a dylib at some
254+
// point we tag this with dllimport.
255+
//
256+
// Note that this is not 100% correct for a variety of reasons:
257+
//
258+
// 1. If we are producing an rlib and linking to an upstream rlib, we'll
259+
// omit the dllimport. It's a possibility, though, that some later
260+
// downstream compilation will link the same upstream dependency as a
261+
// dylib and use our rlib, causing linker errors because we didn't use
262+
// dllimport.
263+
// 2. We may have multiple crate output types. For example if we are
264+
// emitting a statically linked binary as well as a dynamic library we'll
265+
// want to omit dllimport for the binary but we need to have it for the
266+
// dylib.
267+
//
268+
// For most every day uses, however, this should suffice. During the
269+
// bootstrap we're almost always linking upstream to a dylib for some crate
270+
// type output, so most imports will be tagged with dllimport (somewhat
271+
// appropriately). Otherwise rust dylibs linking against rust dylibs is
272+
// pretty rare in Rust so this will likely always be `false` and we'll never
273+
// tag with dllimport.
274+
//
275+
// Note that we can't just blindly tag all constants with dllimport as can
276+
// cause linkage errors when we're not actually linking against a dll. For
277+
// more info on this see rust-lang/rust#26591.
278+
if ccx.use_dll_storage_attrs() && ccx.upstream_dylib_used(did.krate) {
243279
llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass);
244280
}
245281
ccx.externs().borrow_mut().insert(name.to_string(), c);

src/librustc_trans/trans/context.rs

+24
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use llvm;
1212
use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef};
1313
use metadata::common::LinkMeta;
14+
use metadata::cstore;
1415
use middle::def::ExportMap;
1516
use middle::traits;
1617
use trans::adt;
@@ -778,6 +779,29 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
778779
pub fn use_dll_storage_attrs(&self) -> bool {
779780
self.shared.use_dll_storage_attrs()
780781
}
782+
783+
/// Tests whether the given `krate` (an upstream crate) is ever used as a
784+
/// dynamic library for the final linkage of this crate.
785+
pub fn upstream_dylib_used(&self, krate: ast::CrateNum) -> bool {
786+
let tcx = self.tcx();
787+
let formats = tcx.dependency_formats.borrow();
788+
tcx.sess.crate_types.borrow().iter().any(|ct| {
789+
match formats[ct].get(krate as usize - 1) {
790+
// If a crate is explicitly linked dynamically then we're
791+
// definitely using it dynamically. If it's not being linked
792+
// then currently that means it's being included through another
793+
// dynamic library, so we're including it dynamically.
794+
Some(&Some(cstore::RequireDynamic)) |
795+
Some(&None) => true,
796+
797+
// Static linkage isn't included dynamically and if there's not
798+
// an entry in the array then this crate type isn't actually
799+
// doing much linkage so there's nothing dynamic going on.
800+
Some(&Some(cstore::RequireStatic)) |
801+
None => false,
802+
}
803+
})
804+
}
781805
}
782806

783807
/// Declare any llvm intrinsics that you might need

src/test/auxiliary/xcrate-static.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2015 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+
// no-prefer-dynamic
12+
13+
#![crate_type = "rlib"]
14+
15+
pub static FOO: u8 = 8;

src/test/run-pass/xcrate-static.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2015 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+
// aux-build:xcrate-static.rs
12+
13+
extern crate xcrate_static;
14+
15+
fn main() {
16+
println!("{}", xcrate_static::FOO);
17+
}

0 commit comments

Comments
 (0)