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.
This commit is contained in:
Alex Crichton 2015-07-28 17:19:08 -07:00
parent 18607149fb
commit e648c96c5f
14 changed files with 227 additions and 91 deletions

View File

@ -277,10 +277,15 @@ $(foreach target,$(CFG_TARGET), \
# Fun times!
#
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
#
# FIXME(stage0): remove this macro and the usage below (and the commments above)
# when a new snapshot is available. Also remove the
# RUSTFLAGS$(1)_.._T_ variable in mk/target.mk along with
# CUSTOM_DEPS (as they were only added for this)
define ADD_RUSTC_LLVM_DEF_TO_MSVC
ifeq ($$(findstring msvc,$(1)),msvc)
RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
RUSTFLAGS0_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
CUSTOM_DEPS0_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)

View File

@ -40,7 +40,7 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \
$$(RT_OUTPUT_DIR_$(2))/$$(dep)) \
$$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \
$$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) \
$$(CUSTOM_DEPS_$(4)_T_$(2))
$$(CUSTOM_DEPS$(1)_$(4)_T_$(2))
endef
$(foreach host,$(CFG_HOST), \
@ -92,7 +92,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
$$(RUSTFLAGS_$(4)) \
$$(RUSTFLAGS_$(4)_T_$(2)) \
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
--out-dir $$(@D) \
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \
$$<

View File

@ -26,8 +26,7 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) {
// Add the new dylib search path var
let var = DynamicLibrary::envvar();
let newpath = DynamicLibrary::create_path(&path);
let newpath = newpath.to_str().unwrap().to_string();
cmd.env(var, &newpath);
cmd.env(var, newpath);
}
pub struct Result {pub status: ExitStatus, pub out: String, pub err: String}

View File

@ -205,8 +205,8 @@ pub const tag_plugin_registrar_fn: usize = 0x10b; // top-level only
pub const tag_method_argument_names: usize = 0x85;
pub const tag_method_argument_name: usize = 0x86;
pub const tag_reachable_extern_fns: usize = 0x10c; // top-level only
pub const tag_reachable_extern_fn_id: usize = 0x87;
pub const tag_reachable_ids: usize = 0x10c; // top-level only
pub const tag_reachable_id: usize = 0x87;
pub const tag_items_data_item_stability: usize = 0x88;

View File

@ -352,11 +352,11 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
decoder::get_method_arg_names(&*cdata, did.node)
}
pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
pub fn get_reachable_ids(cstore: &cstore::CStore, cnum: ast::CrateNum)
-> Vec<ast::DefId>
{
let cdata = cstore.get_crate_data(cnum);
decoder::get_reachable_extern_fns(&*cdata)
decoder::get_reachable_ids(&*cdata)
}
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 {
let cdata = cstore.get_crate_data(impl_did.krate);
decoder::is_default_impl(&*cdata, impl_did.node)
}
pub fn is_extern_fn(cstore: &cstore::CStore, did: ast::DefId,
tcx: &ty::ctxt) -> bool {
let cdata = cstore.get_crate_data(did.krate);
decoder::is_extern_fn(&*cdata, did.node, tcx)
}

View File

@ -45,6 +45,7 @@ use std::str;
use rbml::reader;
use rbml;
use serialize::Decodable;
use syntax::abi;
use syntax::attr;
use syntax::parse::token::{IdentInterner, special_idents};
use syntax::parse::token;
@ -1418,10 +1419,10 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
}
}
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
pub fn get_reachable_ids(cdata: Cmd) -> Vec<ast::DefId> {
let items = reader::get_doc(rbml::Doc::new(cdata.data()),
tag_reachable_extern_fns);
reader::tagged_docs(items, tag_reachable_extern_fn_id).map(|doc| {
tag_reachable_ids);
reader::tagged_docs(items, tag_reachable_id).map(|doc| {
ast::DefId {
krate: cdata.cnum,
node: reader::doc_as_u32(doc),
@ -1543,3 +1544,21 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
Decodable::decode(&mut decoder).unwrap()
}).collect()
}
pub fn is_extern_fn(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) -> bool {
let root_doc = rbml::Doc::new(cdata.data());
let items = reader::get_doc(root_doc, tag_items);
let item_doc = match maybe_find_item(id, items) {
Some(doc) => doc,
None => return false,
};
if let Fn = item_family(item_doc) {
let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx);
generics.types.is_empty() && match ty.sty {
ty::TyBareFn(_, fn_ty) => fn_ty.abi != abi::Rust,
_ => false,
}
} else {
false
}
}

View File

@ -1781,9 +1781,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
// FIXME (#2166): This is not nearly enough to support correct versioning
// but is enough to get transitive crate dependencies working.
rbml_w.start_tag(tag_crate_deps);
let r = get_ordered_deps(cstore);
for dep in &r {
encode_crate_dep(rbml_w, (*dep).clone());
for dep in &get_ordered_deps(cstore) {
encode_crate_dep(rbml_w, dep);
}
rbml_w.end_tag();
}
@ -1971,24 +1970,22 @@ fn encode_misc_info(ecx: &EncodeContext,
rbml_w.end_tag();
}
fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
rbml_w.start_tag(tag_reachable_extern_fns);
// Encodes all reachable symbols in this crate into the metadata.
//
// This pass is seeded off the reachability list calculated in the
// middle::reachable module but filters out items that either don't have a
// symbol associated with them (they weren't translated) or if they're an FFI
// definition (as that's not defined in this crate).
fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) {
rbml_w.start_tag(tag_reachable_ids);
for id in ecx.reachable {
if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) {
if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node {
if abi != abi::Rust && !generics.is_type_parameterized() {
rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
}
}
}
rbml_w.wr_tagged_u32(tag_reachable_id, *id);
}
rbml_w.end_tag();
}
fn encode_crate_dep(rbml_w: &mut Encoder,
dep: decoder::CrateDep) {
dep: &decoder::CrateDep) {
rbml_w.start_tag(tag_crate_dep);
rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name);
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>>,
// Encode miscellaneous info.
i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
encode_misc_info(&ecx, krate, &mut rbml_w);
encode_reachable_extern_fns(&ecx, &mut rbml_w);
encode_reachable(&ecx, &mut rbml_w);
stats.misc_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
// Encode and index the items.

View File

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

View File

@ -902,6 +902,12 @@ fn link_args(cmd: &mut Linker,
}
cmd.output_filename(out_filename);
// If we're building a dynamic library then some platforms need to make sure
// that all symbols are exported correctly from the dynamic library.
if dylib {
cmd.export_symbols(sess, trans, tmpdir);
}
// When linking a dynamic library, we put the metadata into a section of the
// executable. This metadata is in a separate object file from the main
// object file, so we link that in here.

View File

@ -9,14 +9,21 @@
// except according to those terms.
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, BufWriter};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::fs;
use back::archive;
use metadata::csearch;
use metadata::cstore;
use session::Session;
use session::config;
use session::config::DebugInfoLevel::{NoDebugInfo, LimitedDebugInfo, FullDebugInfo};
use session::config::CrateTypeDylib;
use session::config;
use syntax::ast;
use trans::CrateTranslation;
/// Linker abstraction used by back::link to build up the command to invoke a
/// linker.
@ -48,6 +55,8 @@ pub trait Linker {
fn hint_dynamic(&mut self);
fn whole_archives(&mut self);
fn no_whole_archives(&mut self);
fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
tmpdir: &Path);
}
pub struct GnuLinker<'a> {
@ -192,6 +201,10 @@ impl<'a> Linker for GnuLinker<'a> {
if !self.takes_hints() { return }
self.cmd.arg("-Wl,-Bdynamic");
}
fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) {
// noop, visibility in object files takes care of this
}
}
pub struct MsvcLinker<'a> {
@ -301,4 +314,61 @@ impl<'a> Linker for MsvcLinker<'a> {
// we do on Unix platforms.
fn hint_static(&mut self) {}
fn hint_dynamic(&mut self) {}
// Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
// export symbols from a dynamic library. When building a dynamic library,
// however, we're going to want some symbols exported, so this function
// generates a DEF file which lists all the symbols.
//
// The linker will read this `*.def` file and export all the symbols from
// the dynamic library. Note that this is not as simple as just exporting
// all the symbols in the current crate (as specified by `trans.reachable`)
// but rather we also need to possibly export the symbols of upstream
// crates. Upstream rlibs may be linked statically to this dynamic library,
// in which case they may continue to transitively be used and hence need
// their symbols exported.
fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
tmpdir: &Path) {
let path = tmpdir.join("lib.def");
let res = (|| -> io::Result<()> {
let mut f = BufWriter::new(try!(File::create(&path)));
// Start off with the standard module name header and then go
// straight to exports.
try!(writeln!(f, "LIBRARY"));
try!(writeln!(f, "EXPORTS"));
// Write out all our local symbols
for sym in trans.reachable.iter() {
try!(writeln!(f, " {}", sym));
}
// Take a look at how all upstream crates are linked into this
// dynamic library. For all statically linked libraries we take all
// their reachable symbols and emit them as well.
let cstore = &sess.cstore;
let symbols = trans.crate_formats[&CrateTypeDylib].iter();
let symbols = symbols.enumerate().filter_map(|(i, f)| {
if let Some(cstore::RequireStatic) = *f {
Some((i + 1) as ast::CrateNum)
} else {
None
}
}).flat_map(|cnum| {
csearch::get_reachable_ids(cstore, cnum)
}).map(|did| {
csearch::get_symbol(cstore, did)
});
for symbol in symbols {
try!(writeln!(f, " {}", symbol));
}
Ok(())
})();
if let Err(e) = res {
sess.fatal(&format!("failed to write lib.def file: {}", e));
}
let mut arg = OsString::from("/DEF:");
arg.push(path);
self.cmd.arg(&arg);
}
}

View File

@ -81,7 +81,7 @@ use trans::type_of::*;
use trans::value::Value;
use util::common::indenter;
use util::sha2::Sha256;
use util::nodemap::NodeMap;
use util::nodemap::{NodeMap, NodeSet};
use arena::TypedArena;
use libc::c_uint;
@ -2007,17 +2007,11 @@ 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);
}
@ -2158,28 +2152,12 @@ pub fn register_fn_llvmty(ccx: &CrateContext,
ty::FnConverging(ccx.tcx().mk_nil())).unwrap_or_else(||{
ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
});
finish_register_fn(ccx, sym, node_id, llfn);
finish_register_fn(ccx, sym, node_id);
llfn
}
fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
llfn: ValueRef) {
fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId) {
ccx.item_symbols().borrow_mut().insert(node_id, sym);
// The eh_personality function need to be externally linkable.
let def = ast_util::local_def(node_id);
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);
}
}
if ccx.tcx().lang_items.eh_unwind_resume() == Some(def) {
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
if ccx.use_dll_storage_attrs() {
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
}
}
}
fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@ -2201,7 +2179,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let llfn = declare::define_rust_fn(ccx, &sym[..], node_type).unwrap_or_else(||{
ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym));
});
finish_register_fn(ccx, sym, node_id, llfn);
finish_register_fn(ccx, sym, node_id);
llfn
}
@ -2215,8 +2193,8 @@ pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool {
/// Create the `main` function which will initialise the rust runtime and call users main
/// function.
pub fn create_entry_wrapper(ccx: &CrateContext,
sp: Span,
main_llfn: ValueRef) {
sp: Span,
main_llfn: ValueRef) {
let et = ccx.sess().entry_type.get().unwrap();
match et {
config::EntryMain => {
@ -2242,12 +2220,6 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
panic!();
});
// 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 {
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
}
let llbb = unsafe {
llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn,
"top\0".as_ptr() as *const _)
@ -2524,7 +2496,8 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
}
pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx>,
ie: encoder::EncodeInlinedItem<'a>)
ie: encoder::EncodeInlinedItem<'a>,
reachable: &'a NodeSet)
-> encoder::EncodeParams<'a, 'tcx> {
encoder::EncodeParams {
diag: cx.sess().diagnostic(),
@ -2534,11 +2507,12 @@ pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx>
link_meta: cx.link_meta(),
cstore: &cx.sess().cstore,
encode_inlined_item: ie,
reachable: cx.reachable(),
reachable: reachable,
}
}
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate,
reachable: &NodeSet) -> Vec<u8> {
use flate;
let any_library = cx.sess().crate_types.borrow().iter().any(|ty| {
@ -2551,7 +2525,8 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
let encode_inlined_item: encoder::EncodeInlinedItem =
Box::new(|ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii));
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item);
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item,
reachable);
let metadata = encoder::encode_metadata(encode_parms, krate);
let mut compressed = encoder::metadata_encoding_version.to_vec();
compressed.push_all(&flate::deflate_bytes(&metadata));
@ -2576,7 +2551,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
/// Find any symbols that are defined in one compilation unit, but not declared
/// in any other compilation unit. Give these symbols internal linkage.
fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) {
unsafe {
let mut declared = HashSet::new();
@ -2659,6 +2634,41 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
}
}
/// The context provided lists a set of reachable ids as calculated by
/// middle::reachable, but this contains far more ids and symbols than we're
/// actually exposing from the object file. This function will filter the set in
/// the context to the set of ids which correspond to symbols that are exposed
/// from the object file being generated.
///
/// This list is later used by linkers to determine the set of symbols needed to
/// be exposed from a dynamic library and it's also encoded into the metadata.
pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet {
ccx.reachable().iter().map(|x| *x).filter(|id| {
// First, only worry about nodes which have a symbol name
ccx.item_symbols().borrow().contains_key(id)
}).filter(|&id| {
// Next, we want to ignore some FFI functions that are not exposed from
// this crate. Reachable FFI functions can be lumped into two
// categories:
//
// 1. Those that are included statically via a static library
// 2. Those included otherwise (e.g. dynamically or via a framework)
//
// Although our LLVM module is not literally emitting code for the
// statically included symbols, it's an export of our library which
// needs to be passed on to the linker and encoded in the metadata.
//
// As a result, if this id is an FFI item (foreign item) then we only
// let it through if it's included statically.
match ccx.tcx().map.get(id) {
ast_map::NodeForeignItem(..) => {
ccx.sess().cstore.is_statically_included_foreign_item(id)
}
_ => true,
}
}).collect()
}
pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation {
let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
let krate = tcx.map.krate();
@ -2734,8 +2744,10 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
}
}
let reachable_symbol_ids = filter_reachable_ids(&shared_ccx);
// Translate the metadata.
let metadata = write_metadata(&shared_ccx, krate);
let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids);
if shared_ccx.sess().trans_stats() {
let stats = shared_ccx.stats();
@ -2770,31 +2782,31 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
.map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
.collect();
let mut reachable: Vec<String> = shared_ccx.reachable().iter().filter_map(|id| {
shared_ccx.item_symbols().borrow().get(id).map(|s| s.to_string())
}).collect();
let sess = shared_ccx.sess();
let mut reachable_symbols = reachable_symbol_ids.iter().map(|id| {
shared_ccx.item_symbols().borrow()[id].to_string()
}).collect::<Vec<_>>();
if sess.entry_fn.borrow().is_some() {
reachable_symbols.push("main".to_string());
}
// For the purposes of LTO, we add to the reachable set all of the upstream
// reachable extern fns. These functions are all part of the public ABI of
// the final product, so LTO needs to preserve them.
shared_ccx.sess().cstore.iter_crate_data(|cnum, _| {
let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum);
reachable.extend(syms.into_iter().map(|did| {
csearch::get_symbol(&shared_ccx.sess().cstore, did)
}));
});
// Make sure that some other crucial symbols are not eliminated from the
// module, including the main function.
reachable.push("main".to_string());
// referenced from .eh_frame section on some platforms
reachable.push("rust_eh_personality".to_string());
// referenced from rt/rust_try.ll
reachable.push("rust_eh_personality_catch".to_string());
if sess.lto() {
sess.cstore.iter_crate_data(|cnum, _| {
let syms = csearch::get_reachable_ids(&sess.cstore, cnum);
reachable_symbols.extend(syms.into_iter().filter(|did| {
csearch::is_extern_fn(&sess.cstore, *did, shared_ccx.tcx())
}).map(|did| {
csearch::get_symbol(&sess.cstore, did)
}));
});
}
if codegen_units > 1 {
internalize_symbols(&shared_ccx, &reachable.iter().cloned().collect());
internalize_symbols(&shared_ccx,
&reachable_symbols.iter().map(|x| &x[..]).collect());
}
let metadata_module = ModuleTranslation {
@ -2809,7 +2821,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat
metadata_module: metadata_module,
link: link_meta,
metadata: metadata,
reachable: reachable,
reachable: reachable_symbols,
crate_formats: formats,
no_builtins: no_builtins,
}

View File

@ -10,9 +10,12 @@
// no-prefer-dynamic
#![feature(linked_from)]
#![crate_type = "rlib"]
#[link(name = "rust_test_helpers", kind = "static")]
#[linked_from = "rust_test_helpers"]
extern {
pub fn rust_dbg_extern_identity_u32(u: u32) -> u32;
}

View File

@ -0,0 +1,16 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[linked_from = "foo"] //~ ERROR experimental feature
extern {
fn foo();
}
fn main() {}

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-msvc -- sprintf isn't a symbol in msvcrt? maybe a #define?
#![feature(libc, std_misc)]