Skip to content

Commit 3541ffb

Browse files
committed
rustc: Add _imp_ symbols later in compilation
On MSVC targets rustc will add symbols prefixed with `_imp_` to LLVM modules to "emulate" dllexported statics as that workaround is still in place after rust-lang#27438 hasn't been solved otherwise. These statics, however, were getting gc'd by ThinLTO accidentally which later would cause linking failures. This commit updates the location we add such symbols to happen just before codegen to ensure that (a) they're not eliminated by the optimizer and (b) the optimizer doesn't even worry about them. Closes rust-lang#45347
1 parent 0be6863 commit 3541ffb

File tree

4 files changed

+115
-50
lines changed

4 files changed

+115
-50
lines changed

src/librustc_trans/back/write.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use back::lto;
1212
use back::link::{self, get_linker, remove};
1313
use back::linker::LinkerInfo;
1414
use back::symbol_export::ExportedSymbols;
15+
use base;
16+
use consts;
1517
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
1618
use rustc::dep_graph::DepGraph;
1719
use rustc::middle::cstore::{LinkMeta, EncodedMetadata};
@@ -35,12 +37,13 @@ use syntax::attr;
3537
use syntax::ext::hygiene::Mark;
3638
use syntax_pos::MultiSpan;
3739
use syntax_pos::symbol::Symbol;
40+
use type_::Type;
3841
use context::{is_pie_binary, get_reloc_model};
3942
use jobserver::{Client, Acquired};
4043
use rustc_demangle;
4144

4245
use std::any::Any;
43-
use std::ffi::CString;
46+
use std::ffi::{CString, CStr};
4447
use std::fs;
4548
use std::io;
4649
use std::io::Write;
@@ -315,6 +318,8 @@ pub struct CodegenContext {
315318
metadata_module_config: Arc<ModuleConfig>,
316319
allocator_module_config: Arc<ModuleConfig>,
317320
pub tm_factory: Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>,
321+
pub msvc_imps_needed: bool,
322+
pub target_pointer_width: String,
318323

319324
// Number of cgus excluding the allocator/metadata modules
320325
pub total_cgus: usize,
@@ -586,6 +591,10 @@ unsafe fn codegen(cgcx: &CodegenContext,
586591
let module_name = Some(&module_name[..]);
587592
let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
588593

594+
if cgcx.msvc_imps_needed {
595+
create_msvc_imps(cgcx, llcx, llmod);
596+
}
597+
589598
// A codegen-specific pass manager is used to generate object
590599
// files for an LLVM module.
591600
//
@@ -1300,6 +1309,8 @@ fn start_executing_work(tcx: TyCtxt,
13001309
allocator_module_config: allocator_config,
13011310
tm_factory: target_machine_factory(tcx.sess),
13021311
total_cgus,
1312+
msvc_imps_needed: msvc_imps_needed(tcx),
1313+
target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
13031314
};
13041315

13051316
// This is the "main loop" of parallel work happening for parallel codegen.
@@ -2133,3 +2144,51 @@ pub fn submit_translated_module_to_llvm(tcx: TyCtxt,
21332144
cost,
21342145
})));
21352146
}
2147+
2148+
fn msvc_imps_needed(tcx: TyCtxt) -> bool {
2149+
tcx.sess.target.target.options.is_like_msvc &&
2150+
tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib)
2151+
}
2152+
2153+
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
2154+
// This is required to satisfy `dllimport` references to static data in .rlibs
2155+
// when using MSVC linker. We do this only for data, as linker can fix up
2156+
// code references on its own.
2157+
// See #26591, #27438
2158+
fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) {
2159+
if !cgcx.msvc_imps_needed {
2160+
return
2161+
}
2162+
// The x86 ABI seems to require that leading underscores are added to symbol
2163+
// names, so we need an extra underscore on 32-bit. There's also a leading
2164+
// '\x01' here which disables LLVM's symbol mangling (e.g. no extra
2165+
// underscores added in front).
2166+
let prefix = if cgcx.target_pointer_width == "32" {
2167+
"\x01__imp__"
2168+
} else {
2169+
"\x01__imp_"
2170+
};
2171+
unsafe {
2172+
let i8p_ty = Type::i8p_llcx(llcx);
2173+
let globals = base::iter_globals(llmod)
2174+
.filter(|&val| {
2175+
llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage &&
2176+
llvm::LLVMIsDeclaration(val) == 0
2177+
})
2178+
.map(move |val| {
2179+
let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
2180+
let mut imp_name = prefix.as_bytes().to_vec();
2181+
imp_name.extend(name.to_bytes());
2182+
let imp_name = CString::new(imp_name).unwrap();
2183+
(imp_name, val)
2184+
})
2185+
.collect::<Vec<_>>();
2186+
for (imp_name, val) in globals {
2187+
let imp = llvm::LLVMAddGlobal(llmod,
2188+
i8p_ty.to_ref(),
2189+
imp_name.as_ptr() as *const _);
2190+
llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
2191+
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
2192+
}
2193+
}
2194+
}

src/librustc_trans/base.rs

+3-49
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ use rustc::util::nodemap::{NodeSet, FxHashMap, FxHashSet, DefIdSet};
7878
use CrateInfo;
7979

8080
use std::any::Any;
81-
use std::ffi::{CStr, CString};
81+
use std::ffi::CString;
8282
use std::str;
8383
use std::sync::Arc;
8484
use std::time::{Instant, Duration};
@@ -812,47 +812,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
812812
return (metadata_llcx, metadata_llmod, metadata, hashes);
813813
}
814814

815-
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
816-
// This is required to satisfy `dllimport` references to static data in .rlibs
817-
// when using MSVC linker. We do this only for data, as linker can fix up
818-
// code references on its own.
819-
// See #26591, #27438
820-
fn create_imps(sess: &Session,
821-
llvm_module: &ModuleLlvm) {
822-
// The x86 ABI seems to require that leading underscores are added to symbol
823-
// names, so we need an extra underscore on 32-bit. There's also a leading
824-
// '\x01' here which disables LLVM's symbol mangling (e.g. no extra
825-
// underscores added in front).
826-
let prefix = if sess.target.target.target_pointer_width == "32" {
827-
"\x01__imp__"
828-
} else {
829-
"\x01__imp_"
830-
};
831-
unsafe {
832-
let exported: Vec<_> = iter_globals(llvm_module.llmod)
833-
.filter(|&val| {
834-
llvm::LLVMRustGetLinkage(val) ==
835-
llvm::Linkage::ExternalLinkage &&
836-
llvm::LLVMIsDeclaration(val) == 0
837-
})
838-
.collect();
839-
840-
let i8p_ty = Type::i8p_llcx(llvm_module.llcx);
841-
for val in exported {
842-
let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
843-
let mut imp_name = prefix.as_bytes().to_vec();
844-
imp_name.extend(name.to_bytes());
845-
let imp_name = CString::new(imp_name).unwrap();
846-
let imp = llvm::LLVMAddGlobal(llvm_module.llmod,
847-
i8p_ty.to_ref(),
848-
imp_name.as_ptr() as *const _);
849-
llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
850-
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
851-
}
852-
}
853-
}
854-
855-
struct ValueIter {
815+
pub struct ValueIter {
856816
cur: ValueRef,
857817
step: unsafe extern "C" fn(ValueRef) -> ValueRef,
858818
}
@@ -871,7 +831,7 @@ impl Iterator for ValueIter {
871831
}
872832
}
873833

874-
fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter {
834+
pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter {
875835
unsafe {
876836
ValueIter {
877837
cur: llvm::LLVMGetFirstGlobal(llmod),
@@ -1437,12 +1397,6 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
14371397
tm: create_target_machine(ccx.sess()),
14381398
};
14391399

1440-
// Adjust exported symbols for MSVC dllimport
1441-
if ccx.sess().target.target.options.is_like_msvc &&
1442-
ccx.sess().crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) {
1443-
create_imps(ccx.sess(), &llvm_module);
1444-
}
1445-
14461400
ModuleTranslation {
14471401
name: cgu_name,
14481402
source: ModuleSource::Translated(llvm_module),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 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+
// compile-flags: -Z thinlto -C codegen-units=8 -C prefer-dynamic
13+
14+
#![crate_type = "rlib"]
15+
#![crate_type = "dylib"]
16+
17+
pub static A: u32 = 43;
18+
19+
pub mod a {
20+
pub static A: u32 = 43;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2017 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:msvc-imp-present.rs
12+
// compile-flags: -Z thinlto -C codegen-units=8
13+
// min-llvm-version: 4.0
14+
// no-prefer-dynamic
15+
16+
// On MSVC we have a "hack" where we emit symbols that look like `_imp_$name`
17+
// for all exported statics. This is done because we apply `dllimport` to all
18+
// imported constants and this allows everything to actually link correctly.
19+
//
20+
// The ThinLTO passes aggressively remove symbols if they can, and this test
21+
// asserts that the ThinLTO passes don't remove these compiler-generated
22+
// `_imp_*` symbols. The external library that we link in here is compiled with
23+
// ThinLTO and multiple codegen units and has a few exported constants. Note
24+
// that we also namely compile the library as both a dylib and an rlib, but we
25+
// link the rlib to ensure that we assert those generated symbols exist.
26+
27+
extern crate msvc_imp_present as bar;
28+
29+
fn main() {
30+
println!("{}", bar::A);
31+
}

0 commit comments

Comments
 (0)