Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c33318a

Browse files
committedJul 30, 2015
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

Lines changed: 7 additions & 2 deletions
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

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), \
@@ -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

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
@@ -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

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;
@@ -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

Lines changed: 13 additions & 16 deletions
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

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
@@ -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

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
}

‎src/librustc_trans/trans/base.rs

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ use trans::type_of::*;
8181
use trans::value::Value;
8282
use util::common::indenter;
8383
use util::sha2::Sha256;
84-
use util::nodemap::NodeMap;
84+
use util::nodemap::{NodeMap, NodeSet};
8585

8686
use arena::TypedArena;
8787
use libc::c_uint;
@@ -2014,17 +2014,11 @@ pub fn update_linkage(ccx: &CrateContext,
20142014
match id {
20152015
Some(id) if ccx.reachable().contains(&id) => {
20162016
llvm::SetLinkage(llval, llvm::ExternalLinkage);
2017-
if ccx.use_dll_storage_attrs() {
2018-
llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass);
2019-
}
20202017
},
20212018
_ => {
20222019
// `id` does not refer to an item in `ccx.reachable`.
20232020
if ccx.sess().opts.cg.codegen_units > 1 {
20242021
llvm::SetLinkage(llval, llvm::ExternalLinkage);
2025-
if ccx.use_dll_storage_attrs() {
2026-
llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass);
2027-
}
20282022
} else {
20292023
llvm::SetLinkage(llval, llvm::InternalLinkage);
20302024
}
@@ -2146,31 +2140,12 @@ pub fn register_fn_llvmty(ccx: &CrateContext,
21462140
ty::FnConverging(ccx.tcx().mk_nil())).unwrap_or_else(||{
21472141
ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
21482142
});
2149-
finish_register_fn(ccx, sym, node_id, llfn);
2143+
finish_register_fn(ccx, sym, node_id);
21502144
llfn
21512145
}
21522146

2153-
fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
2154-
llfn: ValueRef) {
2147+
fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId) {
21552148
ccx.item_symbols().borrow_mut().insert(node_id, sym);
2156-
2157-
// The stack exhaustion lang item shouldn't have a split stack because
2158-
// otherwise it would continue to be exhausted (bad), and both it and the
2159-
// eh_personality functions need to be externally linkable.
2160-
let def = ast_util::local_def(node_id);
2161-
if ccx.tcx().lang_items.stack_exhausted() == Some(def) {
2162-
attributes::split_stack(llfn, false);
2163-
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
2164-
if ccx.use_dll_storage_attrs() {
2165-
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
2166-
}
2167-
}
2168-
if ccx.tcx().lang_items.eh_personality() == Some(def) {
2169-
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
2170-
if ccx.use_dll_storage_attrs() {
2171-
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
2172-
}
2173-
}
21742149
}
21752150

21762151
fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -2192,7 +2167,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
21922167
let llfn = declare::define_rust_fn(ccx, &sym[..], node_type).unwrap_or_else(||{
21932168
ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
21942169
});
2195-
finish_register_fn(ccx, sym, node_id, llfn);
2170+
finish_register_fn(ccx, sym, node_id);
21962171
llfn
21972172
}
21982173

@@ -2206,8 +2181,8 @@ pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool {
22062181
/// Create the `main` function which will initialise the rust runtime and call users’ main
22072182
/// function.
22082183
pub fn create_entry_wrapper(ccx: &CrateContext,
2209-
sp: Span,
2210-
main_llfn: ValueRef) {
2184+
sp: Span,
2185+
main_llfn: ValueRef) {
22112186
let et = ccx.sess().entry_type.get().unwrap();
22122187
match et {
22132188
config::EntryMain => {
@@ -2233,12 +2208,6 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
22332208
panic!();
22342209
});
22352210

2236-
// FIXME: #16581: Marking a symbol in the executable with `dllexport`
2237-
// linkage forces MinGW's linker to output a `.reloc` section for ASLR
2238-
if ccx.sess().target.target.options.is_like_windows {
2239-
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
2240-
}
2241-
22422211
let llbb = unsafe {
22432212
llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn,
22442213
"top\0".as_ptr() as *const _)
@@ -2530,7 +2499,8 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
25302499
}
25312500

25322501
pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx>,
2533-
ie: encoder::EncodeInlinedItem<'a>)
2502+
ie: encoder::EncodeInlinedItem<'a>,
2503+
reachable: &'a NodeSet)
25342504
-> encoder::EncodeParams<'a, 'tcx> {
25352505
encoder::EncodeParams {
25362506
diag: cx.sess().diagnostic(),
@@ -2540,11 +2510,12 @@ pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx>
25402510
link_meta: cx.link_meta(),
25412511
cstore: &cx.sess().cstore,
25422512
encode_inlined_item: ie,
2543-
reachable: cx.reachable(),
2513+
reachable: reachable,
25442514
}
25452515
}
25462516

2547-
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
2517+
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate,
2518+
reachable: &NodeSet) -> Vec<u8> {
25482519
use flate;
25492520

25502521
let any_library = cx.sess().crate_types.borrow().iter().any(|ty| {
@@ -2557,7 +2528,8 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
25572528
let encode_inlined_item: encoder::EncodeInlinedItem =
25582529
Box::new(|ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii));
25592530

2560-
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item);
2531+
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item,
2532+
reachable);
25612533
let metadata = encoder::encode_metadata(encode_parms, krate);
25622534
let mut compressed = encoder::metadata_encoding_version.to_vec();
25632535
compressed.push_all(&flate::deflate_bytes(&metadata));
@@ -2582,7 +2554,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
25822554

25832555
/// Find any symbols that are defined in one compilation unit, but not declared
25842556
/// in any other compilation unit. Give these symbols internal linkage.
2585-
fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
2557+
fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
25862558
unsafe {
25872559
let mut declared = HashSet::new();
25882560

@@ -2665,6 +2637,41 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
26652637
}
26662638
}
26672639

2640+
/// The context provided lists a set of reachable ids as calculated by
2641+
/// middle::reachable, but this contains far more ids and symbols than we're
2642+
/// actually exposing from the object file. This function will filter the set in
2643+
/// the context to the set of ids which correspond to symbols that are exposed
2644+
/// from the object file being generated.
2645+
///
2646+
/// This list is later used by linkers to determine the set of symbols needed to
2647+
/// be exposed from a dynamic library and it's also encoded into the metadata.
2648+
pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet {
2649+
ccx.reachable().iter().map(|x| *x).filter(|id| {
2650+
// First, only worry about nodes which have a symbol name
2651+
ccx.item_symbols().borrow().contains_key(id)
2652+
}).filter(|&id| {
2653+
// Next, we want to ignore some FFI functions that are not exposed from
2654+
// this crate. Reachable FFI functions can be lumped into two
2655+
// categories:
2656+
//
2657+
// 1. Those that are included statically via a static library
2658+
// 2. Those included otherwise (e.g. dynamically or via a framework)
2659+
//
2660+
// Although our LLVM module is not literally emitting code for the
2661+
// statically included symbols, it's an export of our library which
2662+
// needs to be passed on to the linker and encoded in the metadata.
2663+
//
2664+
// As a result, if this id is an FFI item (foreign item) then we only
2665+
// let it through if it's included statically.
2666+
match ccx.tcx().map.get(id) {
2667+
ast_map::NodeForeignItem(..) => {
2668+
ccx.sess().cstore.is_statically_included_foreign_item(id)
2669+
}
2670+
_ => true,
2671+
}
2672+
}).collect()
2673+
}
2674+
26682675
pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation {
26692676
let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
26702677
let krate = tcx.map.krate();
@@ -2740,8 +2747,10 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
27402747
}
27412748
}
27422749

2750+
let reachable_symbol_ids = filter_reachable_ids(&shared_ccx);
2751+
27432752
// Translate the metadata.
2744-
let metadata = write_metadata(&shared_ccx, krate);
2753+
let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids);
27452754

27462755
if shared_ccx.sess().trans_stats() {
27472756
let stats = shared_ccx.stats();
@@ -2776,36 +2785,31 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
27762785
.map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
27772786
.collect();
27782787

2779-
let mut reachable: Vec<String> = shared_ccx.reachable().iter().filter_map(|id| {
2780-
shared_ccx.item_symbols().borrow().get(id).map(|s| s.to_string())
2781-
}).collect();
2788+
let sess = shared_ccx.sess();
2789+
let mut reachable_symbols = reachable_symbol_ids.iter().map(|id| {
2790+
shared_ccx.item_symbols().borrow()[id].to_string()
2791+
}).collect::<Vec<_>>();
2792+
if sess.entry_fn.borrow().is_some() {
2793+
reachable_symbols.push("main".to_string());
2794+
}
27822795

27832796
// For the purposes of LTO, we add to the reachable set all of the upstream
27842797
// reachable extern fns. These functions are all part of the public ABI of
27852798
// the final product, so LTO needs to preserve them.
2786-
shared_ccx.sess().cstore.iter_crate_data(|cnum, _| {
2787-
let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum);
2788-
reachable.extend(syms.into_iter().map(|did| {
2789-
csearch::get_symbol(&shared_ccx.sess().cstore, did)
2790-
}));
2791-
});
2792-
2793-
// Make sure that some other crucial symbols are not eliminated from the
2794-
// module. This includes the main function, the crate map (used for debug
2795-
// log settings and I/O), and finally the curious rust_stack_exhausted
2796-
// symbol. This symbol is required for use by the libmorestack library that
2797-
// we link in, so we must ensure that this symbol is not internalized (if
2798-
// defined in the crate).
2799-
reachable.push("main".to_string());
2800-
reachable.push("rust_stack_exhausted".to_string());
2801-
2802-
// referenced from .eh_frame section on some platforms
2803-
reachable.push("rust_eh_personality".to_string());
2804-
// referenced from rt/rust_try.ll
2805-
reachable.push("rust_eh_personality_catch".to_string());
2799+
if sess.lto() {
2800+
sess.cstore.iter_crate_data(|cnum, _| {
2801+
let syms = csearch::get_reachable_ids(&sess.cstore, cnum);
2802+
reachable_symbols.extend(syms.into_iter().filter(|did| {
2803+
csearch::is_extern_fn(&sess.cstore, *did, shared_ccx.tcx())
2804+
}).map(|did| {
2805+
csearch::get_symbol(&sess.cstore, did)
2806+
}));
2807+
});
2808+
}
28062809

28072810
if codegen_units > 1 {
2808-
internalize_symbols(&shared_ccx, &reachable.iter().cloned().collect());
2811+
internalize_symbols(&shared_ccx,
2812+
&reachable_symbols.iter().map(|x| &x[..]).collect());
28092813
}
28102814

28112815
let metadata_module = ModuleTranslation {
@@ -2820,7 +2824,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
28202824
metadata_module: metadata_module,
28212825
link: link_meta,
28222826
metadata: metadata,
2823-
reachable: reachable,
2827+
reachable: reachable_symbols,
28242828
crate_formats: formats,
28252829
no_builtins: no_builtins,
28262830
}

‎src/test/auxiliary/issue-25185-1.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111
// no-prefer-dynamic
1212

13+
#![feature(linked_from)]
14+
1315
#![crate_type = "rlib"]
1416

1517
#[link(name = "rust_test_helpers", kind = "static")]
18+
#[linked_from = "rust_test_helpers"]
1619
extern {
1720
pub fn rust_dbg_extern_identity_u32(u: u32) -> u32;
1821
}

‎src/test/run-pass/variadic-ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// ignore-msvc -- sprintf isn't a symbol in msvcrt? maybe a #define?
1112

1213
#![feature(libc, std_misc)]
1314

0 commit comments

Comments
 (0)
Please sign in to comment.