From 3d32cf5d9afdd8c375935ac875b2a20f24159e52 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 12 May 2015 14:46:19 -0700 Subject: [PATCH] rustc_trans: Apply dllexport attributes for MSVC This commit modifies the compiler to emit `dllexport` for all reachable functions and data on MSVC targets, regardless of whether a dynamic library is being created or not. More details can be found in the commit itself. --- src/librustc_trans/trans/base.rs | 18 +++++++++- src/librustc_trans/trans/context.rs | 55 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4c936ddbcef..4a6c73009f1 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -237,6 +237,9 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId, llvm::set_thread_local(c, true); } } + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass); + } ccx.externs().borrow_mut().insert(name.to_string(), c); return c; } @@ -1940,11 +1943,17 @@ pub fn update_linkage(ccx: &CrateContext, match id { Some(id) if ccx.reachable().contains(&id) => { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } }, _ => { // `id` does not refer to an item in `ccx.reachable`. if ccx.sess().opts.cg.codegen_units > 1 { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } } else { llvm::SetLinkage(llval, llvm::InternalLinkage); } @@ -2103,9 +2112,15 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId, if ccx.tcx().lang_items.stack_exhausted() == Some(def) { attributes::split_stack(llfn, false); llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } if ccx.tcx().lang_items.eh_personality() == Some(def) { llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } } @@ -2172,7 +2187,7 @@ fn create_entry_fn(ccx: &CrateContext, // FIXME: #16581: Marking a symbol in the executable with `dllexport` // linkage forces MinGW's linker to output a `.reloc` section for ASLR if ccx.sess().target.target.options.is_like_windows { - unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) } + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); } let llbb = unsafe { @@ -2589,6 +2604,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { if !declared.contains(&name) && !reachable.contains(str::from_utf8(&name).unwrap()) { llvm::SetLinkage(val, llvm::InternalLinkage); + llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass); } } } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 41ef566f2fd..51db0adf5b7 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -75,6 +75,7 @@ pub struct SharedCrateContext<'tcx> { available_monomorphizations: RefCell>, available_drop_glues: RefCell, String>>, + use_dll_storage_attrs: bool, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -251,6 +252,51 @@ pub fn new(crate_name: &str, create_context_and_module(&tcx.sess, "metadata") }; + // An interesting part of Windows which MSVC forces our hand on (and + // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` + // attributes in LLVM IR as well as native dependencies (in C these + // correspond to `__declspec(dllimport)`). + // + // Whenever a dynamic library is built by MSVC it must have its public + // interface specified by functions tagged with `dllexport` or otherwise + // they're not available to be linked against. This poses a few problems + // for the compiler, some of which are somewhat fundamental, but we use + // the `use_dll_storage_attrs` variable below to attach the `dllexport` + // attribute to all LLVM functions that are reachable (e.g. they're + // already tagged with external linkage). This is suboptimal for a few + // reasons: + // + // * If an object file will never be included in a dynamic library, + // there's no need to attach the dllexport attribute. Most object + // files in Rust are not destined to become part of a dll as binaries + // are statically linked by default. + // * If the compiler is emitting both an rlib and a dylib, the same + // source object file is currently used but with MSVC this may be less + // feasible. The compiler may be able to get around this, but it may + // involve some invasive changes to deal with this. + // + // The flipside of this situation is that whenever you link to a dll and + // you import a function from it, the import should be tagged with + // `dllimport`. At this time, however, the compiler does not emit + // `dllimport` for any declarations other than constants (where it is + // required), which is again suboptimal for even more reasons! + // + // * Calling a function imported from another dll without using + // `dllimport` causes the linker/compiler to have extra overhead (one + // `jmp` instruction on x86) when calling the function. + // * The same object file may be used in different circumstances, so a + // function may be imported from a dll if the object is linked into a + // dll, but it may be just linked against if linked into an rlib. + // * The compiler has no knowledge about whether native functions should + // be tagged dllimport or not. + // + // For now the compiler takes the perf hit (I do not have any numbers to + // this effect) by marking very little as `dllimport` and praying the + // linker will take care of everything. Fixing this problem will likely + // require adding a few attributes to Rust itself (feature gated at the + // start) and then strongly recommending static linkage on MSVC! + let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + let mut shared_ccx = SharedCrateContext { local_ccxs: Vec::with_capacity(local_count), metadata_llmod: metadata_llmod, @@ -277,6 +323,7 @@ pub fn new(crate_name: &str, check_drop_flag_for_sanity: check_drop_flag_for_sanity, available_monomorphizations: RefCell::new(FnvHashSet()), available_drop_glues: RefCell::new(FnvHashMap()), + use_dll_storage_attrs: use_dll_storage_attrs, }; for i in 0..local_count { @@ -365,6 +412,10 @@ pub fn sess<'a>(&'a self) -> &'a Session { pub fn stats<'a>(&'a self) -> &'a Stats { &self.stats } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.use_dll_storage_attrs + } } impl<'tcx> LocalCrateContext<'tcx> { @@ -733,6 +784,10 @@ pub fn check_drop_flag_for_sanity(&self) -> bool { // values. self.shared.check_drop_flag_for_sanity } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.shared.use_dll_storage_attrs() + } } /// Declare any llvm intrinsics that you might need -- 2.44.0