Skip to content

Commit e648c96

Browse files
committed
trans: Stop informing LLVM about dllexport
Rust's current compilation model makes it impossible on Windows to generate one object file with a complete and final set of dllexport annotations. This is because when an object is generated the compiler doesn't actually know if it will later be included in a dynamic library or not. The compiler works around this today by flagging *everything* as dllexport, but this has the drawback of exposing too much. Thankfully there are alternate methods of specifying the exported surface area of a dll on Windows, one of which is passing a `*.def` file to the linker which lists all public symbols of the dynamic library. This commit removes all locations that add `dllexport` to LLVM variables and instead dynamically generates a `*.def` file which is passed to the linker. This file will include all the public symbols of the current object file as well as all upstream libraries, and the crucial aspect is that it's only used when generating a dynamic library. When generating an executable this file isn't generated, so all the symbols aren't exported from an executable. To ensure that statically included native libraries are reexported correctly, the previously added support for the `#[linked_from]` attribute is used to determine the set of FFI symbols that are exported from a dynamic library, and this is required to get the compiler to link correctly.
1 parent 1860714 commit e648c96

File tree

14 files changed

+227
-91
lines changed

14 files changed

+227
-91
lines changed

mk/platform.mk

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,15 @@ $(foreach target,$(CFG_TARGET), \
277277
# Fun times!
278278
#
279279
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
280+
#
281+
# FIXME(stage0): remove this macro and the usage below (and the commments above)
282+
# when a new snapshot is available. Also remove the
283+
# RUSTFLAGS$(1)_.._T_ variable in mk/target.mk along with
284+
# CUSTOM_DEPS (as they were only added for this)
280285
define ADD_RUSTC_LLVM_DEF_TO_MSVC
281286
ifeq ($$(findstring msvc,$(1)),msvc)
282-
RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
283-
CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
287+
RUSTFLAGS0_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
288+
CUSTOM_DEPS0_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
284289

285290
$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
286291
$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)

mk/target.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \
4040
$$(RT_OUTPUT_DIR_$(2))/$$(dep)) \
4141
$$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \
4242
$$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) \
43-
$$(CUSTOM_DEPS_$(4)_T_$(2))
43+
$$(CUSTOM_DEPS$(1)_$(4)_T_$(2))
4444
endef
4545

4646
$(foreach host,$(CFG_HOST), \
@@ -92,7 +92,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
9292
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
9393
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
9494
$$(RUSTFLAGS_$(4)) \
95-
$$(RUSTFLAGS_$(4)_T_$(2)) \
95+
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
9696
--out-dir $$(@D) \
9797
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \
9898
$$<

src/compiletest/procsrv.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) {
2626
// Add the new dylib search path var
2727
let var = DynamicLibrary::envvar();
2828
let newpath = DynamicLibrary::create_path(&path);
29-
let newpath = newpath.to_str().unwrap().to_string();
30-
cmd.env(var, &newpath);
29+
cmd.env(var, newpath);
3130
}
3231

3332
pub struct Result {pub status: ExitStatus, pub out: String, pub err: String}

src/librustc/metadata/common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ pub const tag_plugin_registrar_fn: usize = 0x10b; // top-level only
205205
pub const tag_method_argument_names: usize = 0x85;
206206
pub const tag_method_argument_name: usize = 0x86;
207207

208-
pub const tag_reachable_extern_fns: usize = 0x10c; // top-level only
209-
pub const tag_reachable_extern_fn_id: usize = 0x87;
208+
pub const tag_reachable_ids: usize = 0x10c; // top-level only
209+
pub const tag_reachable_id: usize = 0x87;
210210

211211
pub const tag_items_data_item_stability: usize = 0x88;
212212

src/librustc/metadata/csearch.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,11 +352,11 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
352352
decoder::get_method_arg_names(&*cdata, did.node)
353353
}
354354

355-
pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
355+
pub fn get_reachable_ids(cstore: &cstore::CStore, cnum: ast::CrateNum)
356356
-> Vec<ast::DefId>
357357
{
358358
let cdata = cstore.get_crate_data(cnum);
359-
decoder::get_reachable_extern_fns(&*cdata)
359+
decoder::get_reachable_ids(&*cdata)
360360
}
361361

362362
pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool {
@@ -400,3 +400,9 @@ pub fn is_default_impl(cstore: &cstore::CStore, impl_did: ast::DefId) -> bool {
400400
let cdata = cstore.get_crate_data(impl_did.krate);
401401
decoder::is_default_impl(&*cdata, impl_did.node)
402402
}
403+
404+
pub fn is_extern_fn(cstore: &cstore::CStore, did: ast::DefId,
405+
tcx: &ty::ctxt) -> bool {
406+
let cdata = cstore.get_crate_data(did.krate);
407+
decoder::is_extern_fn(&*cdata, did.node, tcx)
408+
}

src/librustc/metadata/decoder.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use std::str;
4545
use rbml::reader;
4646
use rbml;
4747
use serialize::Decodable;
48+
use syntax::abi;
4849
use syntax::attr;
4950
use syntax::parse::token::{IdentInterner, special_idents};
5051
use syntax::parse::token;
@@ -1418,10 +1419,10 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
14181419
}
14191420
}
14201421

1421-
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
1422+
pub fn get_reachable_ids(cdata: Cmd) -> Vec<ast::DefId> {
14221423
let items = reader::get_doc(rbml::Doc::new(cdata.data()),
1423-
tag_reachable_extern_fns);
1424-
reader::tagged_docs(items, tag_reachable_extern_fn_id).map(|doc| {
1424+
tag_reachable_ids);
1425+
reader::tagged_docs(items, tag_reachable_id).map(|doc| {
14251426
ast::DefId {
14261427
krate: cdata.cnum,
14271428
node: reader::doc_as_u32(doc),
@@ -1543,3 +1544,21 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
15431544
Decodable::decode(&mut decoder).unwrap()
15441545
}).collect()
15451546
}
1547+
1548+
pub fn is_extern_fn(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) -> bool {
1549+
let root_doc = rbml::Doc::new(cdata.data());
1550+
let items = reader::get_doc(root_doc, tag_items);
1551+
let item_doc = match maybe_find_item(id, items) {
1552+
Some(doc) => doc,
1553+
None => return false,
1554+
};
1555+
if let Fn = item_family(item_doc) {
1556+
let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx);
1557+
generics.types.is_empty() && match ty.sty {
1558+
ty::TyBareFn(_, fn_ty) => fn_ty.abi != abi::Rust,
1559+
_ => false,
1560+
}
1561+
} else {
1562+
false
1563+
}
1564+
}

src/librustc/metadata/encoder.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,9 +1781,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
17811781
// FIXME (#2166): This is not nearly enough to support correct versioning
17821782
// but is enough to get transitive crate dependencies working.
17831783
rbml_w.start_tag(tag_crate_deps);
1784-
let r = get_ordered_deps(cstore);
1785-
for dep in &r {
1786-
encode_crate_dep(rbml_w, (*dep).clone());
1784+
for dep in &get_ordered_deps(cstore) {
1785+
encode_crate_dep(rbml_w, dep);
17871786
}
17881787
rbml_w.end_tag();
17891788
}
@@ -1971,24 +1970,22 @@ fn encode_misc_info(ecx: &EncodeContext,
19711970
rbml_w.end_tag();
19721971
}
19731972

1974-
fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
1975-
rbml_w.start_tag(tag_reachable_extern_fns);
1976-
1973+
// Encodes all reachable symbols in this crate into the metadata.
1974+
//
1975+
// This pass is seeded off the reachability list calculated in the
1976+
// middle::reachable module but filters out items that either don't have a
1977+
// symbol associated with them (they weren't translated) or if they're an FFI
1978+
// definition (as that's not defined in this crate).
1979+
fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) {
1980+
rbml_w.start_tag(tag_reachable_ids);
19771981
for id in ecx.reachable {
1978-
if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) {
1979-
if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node {
1980-
if abi != abi::Rust && !generics.is_type_parameterized() {
1981-
rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
1982-
}
1983-
}
1984-
}
1982+
rbml_w.wr_tagged_u32(tag_reachable_id, *id);
19851983
}
1986-
19871984
rbml_w.end_tag();
19881985
}
19891986

19901987
fn encode_crate_dep(rbml_w: &mut Encoder,
1991-
dep: decoder::CrateDep) {
1988+
dep: &decoder::CrateDep) {
19921989
rbml_w.start_tag(tag_crate_dep);
19931990
rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name);
19941991
rbml_w.wr_tagged_str(tag_crate_dep_hash, dep.hash.as_str());
@@ -2170,7 +2167,7 @@ fn encode_metadata_inner(wr: &mut Cursor<Vec<u8>>,
21702167
// Encode miscellaneous info.
21712168
i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
21722169
encode_misc_info(&ecx, krate, &mut rbml_w);
2173-
encode_reachable_extern_fns(&ecx, &mut rbml_w);
2170+
encode_reachable(&ecx, &mut rbml_w);
21742171
stats.misc_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
21752172

21762173
// Encode and index the items.

src/librustc_llvm/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#![feature(link_args)]
3232
#![feature(staged_api)]
3333
#![feature(vec_push_all)]
34+
#![cfg_attr(not(stage0), feature(linked_from))]
3435

3536
extern crate libc;
3637
#[macro_use] #[no_link] extern crate rustc_bitflags;
@@ -598,6 +599,7 @@ pub mod debuginfo {
598599
// automatically updated whenever LLVM is updated to include an up-to-date
599600
// set of the libraries we need to link to LLVM for.
600601
#[link(name = "rustllvm", kind = "static")]
602+
#[cfg_attr(not(stage0), linked_from = "rustllvm")] // not quite true but good enough
601603
extern {
602604
/* Create and destroy contexts. */
603605
pub fn LLVMContextCreate() -> ContextRef;

src/librustc_trans/back/link.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,12 @@ fn link_args(cmd: &mut Linker,
902902
}
903903
cmd.output_filename(out_filename);
904904

905+
// If we're building a dynamic library then some platforms need to make sure
906+
// that all symbols are exported correctly from the dynamic library.
907+
if dylib {
908+
cmd.export_symbols(sess, trans, tmpdir);
909+
}
910+
905911
// When linking a dynamic library, we put the metadata into a section of the
906912
// executable. This metadata is in a separate object file from the main
907913
// object file, so we link that in here.

src/librustc_trans/back/linker.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,21 @@
99
// except according to those terms.
1010

1111
use std::ffi::OsString;
12+
use std::fs::{self, File};
13+
use std::io::{self, BufWriter};
14+
use std::io::prelude::*;
1215
use std::path::{Path, PathBuf};
1316
use std::process::Command;
14-
use std::fs;
1517

1618
use back::archive;
19+
use metadata::csearch;
20+
use metadata::cstore;
1721
use session::Session;
18-
use session::config;
1922
use session::config::DebugInfoLevel::{NoDebugInfo, LimitedDebugInfo, FullDebugInfo};
23+
use session::config::CrateTypeDylib;
24+
use session::config;
25+
use syntax::ast;
26+
use trans::CrateTranslation;
2027

2128
/// Linker abstraction used by back::link to build up the command to invoke a
2229
/// linker.
@@ -48,6 +55,8 @@ pub trait Linker {
4855
fn hint_dynamic(&mut self);
4956
fn whole_archives(&mut self);
5057
fn no_whole_archives(&mut self);
58+
fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
59+
tmpdir: &Path);
5160
}
5261

5362
pub struct GnuLinker<'a> {
@@ -192,6 +201,10 @@ impl<'a> Linker for GnuLinker<'a> {
192201
if !self.takes_hints() { return }
193202
self.cmd.arg("-Wl,-Bdynamic");
194203
}
204+
205+
fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) {
206+
// noop, visibility in object files takes care of this
207+
}
195208
}
196209

197210
pub struct MsvcLinker<'a> {
@@ -301,4 +314,61 @@ impl<'a> Linker for MsvcLinker<'a> {
301314
// we do on Unix platforms.
302315
fn hint_static(&mut self) {}
303316
fn hint_dynamic(&mut self) {}
317+
318+
// Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
319+
// export symbols from a dynamic library. When building a dynamic library,
320+
// however, we're going to want some symbols exported, so this function
321+
// generates a DEF file which lists all the symbols.
322+
//
323+
// The linker will read this `*.def` file and export all the symbols from
324+
// the dynamic library. Note that this is not as simple as just exporting
325+
// all the symbols in the current crate (as specified by `trans.reachable`)
326+
// but rather we also need to possibly export the symbols of upstream
327+
// crates. Upstream rlibs may be linked statically to this dynamic library,
328+
// in which case they may continue to transitively be used and hence need
329+
// their symbols exported.
330+
fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
331+
tmpdir: &Path) {
332+
let path = tmpdir.join("lib.def");
333+
let res = (|| -> io::Result<()> {
334+
let mut f = BufWriter::new(try!(File::create(&path)));
335+
336+
// Start off with the standard module name header and then go
337+
// straight to exports.
338+
try!(writeln!(f, "LIBRARY"));
339+
try!(writeln!(f, "EXPORTS"));
340+
341+
// Write out all our local symbols
342+
for sym in trans.reachable.iter() {
343+
try!(writeln!(f, " {}", sym));
344+
}
345+
346+
// Take a look at how all upstream crates are linked into this
347+
// dynamic library. For all statically linked libraries we take all
348+
// their reachable symbols and emit them as well.
349+
let cstore = &sess.cstore;
350+
let symbols = trans.crate_formats[&CrateTypeDylib].iter();
351+
let symbols = symbols.enumerate().filter_map(|(i, f)| {
352+
if let Some(cstore::RequireStatic) = *f {
353+
Some((i + 1) as ast::CrateNum)
354+
} else {
355+
None
356+
}
357+
}).flat_map(|cnum| {
358+
csearch::get_reachable_ids(cstore, cnum)
359+
}).map(|did| {
360+
csearch::get_symbol(cstore, did)
361+
});
362+
for symbol in symbols {
363+
try!(writeln!(f, " {}", symbol));
364+
}
365+
Ok(())
366+
})();
367+
if let Err(e) = res {
368+
sess.fatal(&format!("failed to write lib.def file: {}", e));
369+
}
370+
let mut arg = OsString::from("/DEF:");
371+
arg.push(path);
372+
self.cmd.arg(&arg);
373+
}
304374
}

0 commit comments

Comments
 (0)