Skip to content

Commit c33318a

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 1d67c7f commit c33318a

File tree

13 files changed

+211
-99
lines changed

13 files changed

+211
-99
lines changed

mk/platform.mk

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

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

mk/target.mk

+2-2
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), \
@@ -93,7 +93,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
9393
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
9494
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
9595
$$(RUSTFLAGS_$(4)) \
96-
$$(RUSTFLAGS_$(4)_T_$(2)) \
96+
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
9797
--out-dir $$(@D) \
9898
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \
9999
$$<

src/compiletest/procsrv.rs

+1-2
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

+2-2
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

+8-2
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,11 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
356356
decoder::get_method_arg_names(&*cdata, did.node)
357357
}
358358

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

366366
pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool {
@@ -412,3 +412,9 @@ pub fn is_default_impl(cstore: &cstore::CStore, impl_did: ast::DefId) -> bool {
412412
let cdata = cstore.get_crate_data(impl_did.krate);
413413
decoder::is_default_impl(&*cdata, impl_did.node)
414414
}
415+
416+
pub fn is_extern_fn(cstore: &cstore::CStore, did: ast::DefId,
417+
tcx: &ty::ctxt) -> bool {
418+
let cdata = cstore.get_crate_data(did.krate);
419+
decoder::is_extern_fn(&*cdata, did.node, tcx)
420+
}

src/librustc/metadata/decoder.rs

+22-3
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;
@@ -1396,10 +1397,10 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
13961397
}
13971398
}
13981399

1399-
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
1400+
pub fn get_reachable_ids(cdata: Cmd) -> Vec<ast::DefId> {
14001401
let items = reader::get_doc(rbml::Doc::new(cdata.data()),
1401-
tag_reachable_extern_fns);
1402-
reader::tagged_docs(items, tag_reachable_extern_fn_id).map(|doc| {
1402+
tag_reachable_ids);
1403+
reader::tagged_docs(items, tag_reachable_id).map(|doc| {
14031404
ast::DefId {
14041405
krate: cdata.cnum,
14051406
node: reader::doc_as_u32(doc),
@@ -1521,3 +1522,21 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
15211522
Decodable::decode(&mut decoder).unwrap()
15221523
}).collect()
15231524
}
1525+
1526+
pub fn is_extern_fn(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) -> bool {
1527+
let root_doc = rbml::Doc::new(cdata.data());
1528+
let items = reader::get_doc(root_doc, tag_items);
1529+
let item_doc = match maybe_find_item(id, items) {
1530+
Some(doc) => doc,
1531+
None => return false,
1532+
};
1533+
if let Fn = item_family(item_doc) {
1534+
let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx);
1535+
generics.types.is_empty() && match ty.sty {
1536+
ty::TyBareFn(_, fn_ty) => fn_ty.abi != abi::Rust,
1537+
_ => false,
1538+
}
1539+
} else {
1540+
false
1541+
}
1542+
}

src/librustc/metadata/encoder.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -1792,9 +1792,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
17921792
// FIXME (#2166): This is not nearly enough to support correct versioning
17931793
// but is enough to get transitive crate dependencies working.
17941794
rbml_w.start_tag(tag_crate_deps);
1795-
let r = get_ordered_deps(cstore);
1796-
for dep in &r {
1797-
encode_crate_dep(rbml_w, (*dep).clone());
1795+
for dep in &get_ordered_deps(cstore) {
1796+
encode_crate_dep(rbml_w, dep);
17981797
}
17991798
rbml_w.end_tag();
18001799
}
@@ -1982,24 +1981,22 @@ fn encode_misc_info(ecx: &EncodeContext,
19821981
rbml_w.end_tag();
19831982
}
19841983

1985-
fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
1986-
rbml_w.start_tag(tag_reachable_extern_fns);
1987-
1984+
// Encodes all reachable symbols in this crate into the metadata.
1985+
//
1986+
// This pass is seeded off the reachability list calculated in the
1987+
// middle::reachable module but filters out items that either don't have a
1988+
// symbol associated with them (they weren't translated) or if they're an FFI
1989+
// definition (as that's not defined in this crate).
1990+
fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) {
1991+
rbml_w.start_tag(tag_reachable_ids);
19881992
for id in ecx.reachable {
1989-
if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) {
1990-
if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node {
1991-
if abi != abi::Rust && !generics.is_type_parameterized() {
1992-
rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
1993-
}
1994-
}
1995-
}
1993+
rbml_w.wr_tagged_u32(tag_reachable_id, *id);
19961994
}
1997-
19981995
rbml_w.end_tag();
19991996
}
20001997

20011998
fn encode_crate_dep(rbml_w: &mut Encoder,
2002-
dep: decoder::CrateDep) {
1999+
dep: &decoder::CrateDep) {
20032000
rbml_w.start_tag(tag_crate_dep);
20042001
rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name);
20052002
rbml_w.wr_tagged_str(tag_crate_dep_hash, dep.hash.as_str());
@@ -2181,7 +2178,7 @@ fn encode_metadata_inner(wr: &mut Cursor<Vec<u8>>,
21812178
// Encode miscellaneous info.
21822179
i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
21832180
encode_misc_info(&ecx, krate, &mut rbml_w);
2184-
encode_reachable_extern_fns(&ecx, &mut rbml_w);
2181+
encode_reachable(&ecx, &mut rbml_w);
21852182
stats.misc_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
21862183

21872184
// Encode and index the items.

src/librustc_llvm/lib.rs

+2
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

+6
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,12 @@ fn link_args(cmd: &mut Linker,
905905
}
906906
cmd.output_filename(out_filename);
907907

908+
// If we're building a dynamic library then some platforms need to make sure
909+
// that all symbols are exported correctly from the dynamic library.
910+
if dylib {
911+
cmd.export_symbols(sess, trans, tmpdir);
912+
}
913+
908914
// Stack growth requires statically linking a __morestack function. Note
909915
// that this is listed *before* all other libraries. Due to the usage of the
910916
// --as-needed flag below, the standard library may only be useful for its

src/librustc_trans/back/linker.rs

+72-2
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)