From 38eeebdfed97af43354ed924e498764e8f9cdae5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 2 Aug 2018 12:06:57 -0700 Subject: [PATCH 1/2] rustc: Refactor MonoItem linkage/visibility calculation The previous iteration was a large `match` which was quite heavily indented, making it slightly difficult to read and see what was going on. This iteration is a refactoring (no functional change intended) to make it a bit easier on the eyes and a bit easier to modify over time. --- src/librustc_mir/monomorphize/partitioning.rs | 291 +++++++++--------- 1 file changed, 151 insertions(+), 140 deletions(-) diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 8d6d01633a12e..31aa918b56f99 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -300,13 +300,6 @@ fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let is_incremental_build = tcx.sess.opts.incremental.is_some(); let mut internalization_candidates = FxHashSet(); - // Determine if monomorphizations instantiated in this crate will be made - // available to downstream crates. This depends on whether we are in - // share-generics mode and whether the current crate can even have - // downstream crates. - let export_generics = tcx.sess.opts.share_generics() && - tcx.local_crate_exports_generics(); - for mono_item in mono_items { match mono_item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => {} @@ -322,146 +315,38 @@ fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, None => fallback_cgu_name(tcx), }; - let make_codegen_unit = || { - CodegenUnit::new(codegen_unit_name.clone()) - }; - let codegen_unit = codegen_units.entry(codegen_unit_name.clone()) - .or_insert_with(make_codegen_unit); + .or_insert_with(|| CodegenUnit::new(codegen_unit_name.clone())); let mut can_be_internalized = true; - let default_visibility = |id: DefId, is_generic: bool| { - if !tcx.sess.target.target.options.default_hidden_visibility { - return Visibility::Default - } + let (linkage, visibility) = mono_item_linkage_and_visibility( + tcx, + &mono_item, + &mut can_be_internalized, + &|id, is_generic| { + if !tcx.sess.target.target.options.default_hidden_visibility { + return Visibility::Default + } - // Generic functions never have export level C - if is_generic { - return Visibility::Hidden - } + // Generic functions never have export level C + if is_generic { + return Visibility::Hidden + } - // Things with export level C don't get instantiated in downstream - // crates - if !id.is_local() { - return Visibility::Hidden - } + // Things with export level C don't get instantiated in + // downstream crates + if !id.is_local() { + return Visibility::Hidden + } - if let Some(&SymbolExportLevel::C) = tcx.reachable_non_generics(id.krate) - .get(&id) { - Visibility::Default - } else { - Visibility::Hidden - } - }; - let (linkage, visibility) = match mono_item.explicit_linkage(tcx) { - Some(explicit_linkage) => (explicit_linkage, Visibility::Default), - None => { - match mono_item { - MonoItem::Fn(ref instance) => { - let visibility = match instance.def { - InstanceDef::Item(def_id) => { - let is_generic = instance.substs - .types() - .next() - .is_some(); - - // The `start_fn` lang item is actually a - // monomorphized instance of a function in the - // standard library, used for the `main` - // function. We don't want to export it so we - // tag it with `Hidden` visibility but this - // symbol is only referenced from the actual - // `main` symbol which we unfortunately don't - // know anything about during - // partitioning/collection. As a result we - // forcibly keep this symbol out of the - // `internalization_candidates` set. - // - // FIXME: eventually we don't want to always - // force this symbol to have hidden - // visibility, it should indeed be a candidate - // for internalization, but we have to - // understand that it's referenced from the - // `main` symbol we'll generate later. - if tcx.lang_items().start_fn() == Some(def_id) { - can_be_internalized = false; - Visibility::Hidden - } else if def_id.is_local() { - if is_generic { - if export_generics { - if tcx.is_unreachable_local_definition(def_id) { - // This instance cannot be used - // from another crate. - Visibility::Hidden - } else { - // This instance might be useful in - // a downstream crate. - can_be_internalized = false; - default_visibility(def_id, true) - } - } else { - // We are not exporting generics or - // the definition is not reachable - // for downstream crates, we can - // internalize its instantiations. - Visibility::Hidden - } - } else { - // This isn't a generic function. - if tcx.is_reachable_non_generic(def_id) { - can_be_internalized = false; - debug_assert!(!is_generic); - default_visibility(def_id, false) - } else { - Visibility::Hidden - } - } - } else { - // This is an upstream DefId. - if export_generics && is_generic { - // If it is a upstream monomorphization - // and we export generics, we must make - // it available to downstream crates. - can_be_internalized = false; - default_visibility(def_id, true) - } else { - Visibility::Hidden - } - } - } - InstanceDef::FnPtrShim(..) | - InstanceDef::Virtual(..) | - InstanceDef::Intrinsic(..) | - InstanceDef::ClosureOnceShim { .. } | - InstanceDef::DropGlue(..) | - InstanceDef::CloneShim(..) => { - Visibility::Hidden - } - }; - (Linkage::External, visibility) - } - MonoItem::Static(def_id) => { - let visibility = if tcx.is_reachable_non_generic(def_id) { - can_be_internalized = false; - default_visibility(def_id, false) - } else { - Visibility::Hidden - }; - (Linkage::External, visibility) - } - MonoItem::GlobalAsm(node_id) => { - let def_id = tcx.hir.local_def_id(node_id); - let visibility = if tcx.is_reachable_non_generic(def_id) { - can_be_internalized = false; - default_visibility(def_id, false) - } else { - Visibility::Hidden - }; - (Linkage::External, visibility) - } + // C-export level items remain at `Default`, all other internal + // items become `Hidden` + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportLevel::C) => Visibility::Default, + _ => Visibility::Hidden, } - } - }; + }, + ); if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } @@ -487,6 +372,132 @@ fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +fn mono_item_linkage_and_visibility( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_item: &MonoItem<'tcx>, + can_be_internalized: &mut bool, + default: &dyn Fn(DefId, bool) -> Visibility, +) -> (Linkage, Visibility) { + if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { + return (explicit_linkage, Visibility::Default) + } + let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, default); + (Linkage::External, vis) +} + +fn mono_item_visibility( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_item: &MonoItem<'tcx>, + can_be_internalized: &mut bool, + default_visibility: &dyn Fn(DefId, bool) -> Visibility, +) -> Visibility { + let instance = match mono_item { + // This is pretty complicated, go below + MonoItem::Fn(instance) => instance, + + // Misc handling for generics and such, but otherwise + MonoItem::Static(def_id) => { + return if tcx.is_reachable_non_generic(*def_id) { + *can_be_internalized = false; + default_visibility(*def_id, false) + } else { + Visibility::Hidden + }; + } + MonoItem::GlobalAsm(node_id) => { + let def_id = tcx.hir.local_def_id(*node_id); + return if tcx.is_reachable_non_generic(def_id) { + *can_be_internalized = false; + default_visibility(def_id, false) + } else { + Visibility::Hidden + }; + } + }; + + let def_id = match instance.def { + InstanceDef::Item(def_id) => def_id, + + // These are all compiler glue and such, never exported, always hidden. + InstanceDef::FnPtrShim(..) | + InstanceDef::Virtual(..) | + InstanceDef::Intrinsic(..) | + InstanceDef::ClosureOnceShim { .. } | + InstanceDef::DropGlue(..) | + InstanceDef::CloneShim(..) => { + return Visibility::Hidden + } + }; + + // The `start_fn` lang item is actually a monomorphized instance of a + // function in the standard library, used for the `main` function. We don't + // want to export it so we tag it with `Hidden` visibility but this symbol + // is only referenced from the actual `main` symbol which we unfortunately + // don't know anything about during partitioning/collection. As a result we + // forcibly keep this symbol out of the `internalization_candidates` set. + // + // FIXME: eventually we don't want to always force this symbol to have + // hidden visibility, it should indeed be a candidate for + // internalization, but we have to understand that it's referenced + // from the `main` symbol we'll generate later. + if tcx.lang_items().start_fn() == Some(def_id) { + *can_be_internalized = false; + return Visibility::Hidden + } + + // Determine if monomorphizations instantiated in this crate will be made + // available to downstream crates. This depends on whether we are in + // share-generics mode and whether the current crate can even have + // downstream crates. + let export_generics = tcx.sess.opts.share_generics() && + tcx.local_crate_exports_generics(); + + let is_generic = instance.substs.types().next().is_some(); + + // Upstream `DefId` instances get different handling than local ones + if !def_id.is_local() { + return if export_generics && is_generic { + // If it is a upstream monomorphization + // and we export generics, we must make + // it available to downstream crates. + *can_be_internalized = false; + default_visibility(def_id, true) + } else { + Visibility::Hidden + } + } + + if is_generic { + if export_generics { + if tcx.is_unreachable_local_definition(def_id) { + // This instance cannot be used + // from another crate. + Visibility::Hidden + } else { + // This instance might be useful in + // a downstream crate. + *can_be_internalized = false; + default_visibility(def_id, true) + } + } else { + // We are not exporting generics or + // the definition is not reachable + // for downstream crates, we can + // internalize its instantiations. + Visibility::Hidden + } + } else { + // This isn't a generic function. + if tcx.is_reachable_non_generic(def_id) { + *can_be_internalized = false; + debug_assert!(!is_generic); + default_visibility(def_id, false) + } else { + Visibility::Hidden + } + } +} + fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<'tcx>, target_cgu_count: usize, crate_name: &str) { From 7c58ab671fb49200c0cbd1ad9218713a5c3afe0d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 2 Aug 2018 12:30:43 -0700 Subject: [PATCH 2/2] rustc: Tweak visibility of some lang items This commit tweaks the linker-level visibility of some lang items that rustc uses and defines. Notably this means that `#[panic_implementation]` and `#[alloc_error_handler]` functions are never marked as `internal`. It's up to the linker to eliminate these, not rustc. Additionally `#[global_allocator]` generated symbols are no longer forced to `Default` visibility (fully exported), but rather they're relaxed to `Hidden` visibility). This symbols are *not* needed across DLL boundaries, only as a local implementation detail of the compiler-injected allocator symbols, so `Hidden` should suffice. Closes #51342 Closes #52795 --- src/librustc/middle/reachable.rs | 11 +- src/librustc_allocator/expand.rs | 7 +- src/librustc_codegen_llvm/allocator.rs | 7 +- src/librustc_codegen_llvm/base.rs | 24 +++- src/librustc_mir/monomorphize/partitioning.rs | 136 ++++++++++++------ .../wasm-symbols-not-exported/Makefile | 16 +++ .../run-make/wasm-symbols-not-exported/bar.rs | 45 ++++++ .../run-make/wasm-symbols-not-exported/foo.rs | 17 +++ .../verify-exported-symbols.js | 31 ++++ 9 files changed, 238 insertions(+), 56 deletions(-) create mode 100644 src/test/run-make/wasm-symbols-not-exported/Makefile create mode 100644 src/test/run-make/wasm-symbols-not-exported/bar.rs create mode 100644 src/test/run-make/wasm-symbols-not-exported/foo.rs create mode 100644 src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 94246db37069a..a09942258e22d 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,7 +15,7 @@ // makes all other generics or inline functions that it references // reachable as well. -use hir::CodegenFnAttrs; +use hir::{CodegenFnAttrs, CodegenFnAttrFlags}; use hir::map as hir_map; use hir::def::Def; use hir::def_id::{DefId, CrateNum}; @@ -28,7 +28,6 @@ use util::nodemap::{NodeSet, FxHashSet}; use rustc_target::spec::abi::Abi; use syntax::ast; -use syntax::attr; use hir; use hir::def_id::LOCAL_CRATE; use hir::intravisit::{Visitor, NestedVisitorMap}; @@ -359,8 +358,12 @@ struct CollectPrivateImplItemsVisitor<'a, 'tcx: 'a> { impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> { fn visit_item(&mut self, item: &hir::Item) { // Anything which has custom linkage gets thrown on the worklist no - // matter where it is in the crate. - if attr::contains_name(&item.attrs, "linkage") { + // matter where it is in the crate, along with "special std symbols" + // which are currently akin to allocator symbols. + let def_id = self.tcx.hir.local_def_id(item.id); + let codegen_attrs = self.tcx.codegen_fn_attrs(def_id); + if codegen_attrs.linkage.is_some() || + codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { self.worklist.push(item.id); } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ffbbd8a33a1d0..05843852ee0de 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -13,7 +13,7 @@ use rustc_errors; use syntax::{ ast::{ self, Arg, Attribute, Crate, Expr, FnHeader, Generics, Ident, Item, ItemKind, - LitKind, Mac, Mod, Mutability, StrStyle, Ty, TyKind, Unsafety, VisibilityKind, + Mac, Mod, Mutability, Ty, TyKind, Unsafety, VisibilityKind, }, attr, codemap::{ @@ -236,17 +236,12 @@ impl<'a> AllocFnFactory<'a> { } fn attrs(&self) -> Vec { - let key = Symbol::intern("linkage"); - let value = LitKind::Str(Symbol::intern("external"), StrStyle::Cooked); - let linkage = self.cx.meta_name_value(self.span, key, value); - let no_mangle = Symbol::intern("no_mangle"); let no_mangle = self.cx.meta_word(self.span, no_mangle); let special = Symbol::intern("rustc_std_internal_symbol"); let special = self.cx.meta_word(self.span, special); vec![ - self.cx.attribute(self.span, linkage), self.cx.attribute(self.span, no_mangle), self.cx.attribute(self.span, special), ] diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index 2d401da958b13..0beb8a8844c95 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs @@ -67,14 +67,15 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind if tcx.sess.target.target.options.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } - if tcx.sess.target.target.options.requires_uwtable { - attributes::emit_uwtable(llfn, true); - } + if tcx.sess.target.target.options.requires_uwtable { + attributes::emit_uwtable(llfn, true); + } let callee = CString::new(kind.fn_name(method.name)).unwrap(); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr(), ty); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 41336165684f6..0a8bc03d8d41c 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -809,8 +809,28 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, rx, codegen_units.len()); - // Codegen an allocator shim, if any - let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() { + // Codegen an allocator shim, if necessary. + // + // If the crate doesn't have an `allocator_kind` set then there's definitely + // no shim to generate. Otherwise we also check our dependency graph for all + // our output crate types. If anything there looks like its a `Dynamic` + // linkage, then it's already got an allocator shim and we'll be using that + // one instead. If nothing exists then it's our job to generate the + // allocator! + let any_dynamic_crate = tcx.sess.dependency_formats.borrow() + .iter() + .any(|(_, list)| { + use rustc::middle::dependency_format::Linkage; + list.iter().any(|linkage| { + match linkage { + Linkage::Dynamic => true, + _ => false, + } + }) + }); + let allocator_module = if any_dynamic_crate { + None + } else if let Some(kind) = *tcx.sess.allocator_kind.get() { unsafe { let llmod_id = "allocator"; let modules = ModuleLlvm::new(tcx.sess, llmod_id); diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 31aa918b56f99..4e32ca483a125 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -104,6 +104,7 @@ use monomorphize::collector::InliningMap; use rustc::dep_graph::WorkProductId; +use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::mir::mono::{Linkage, Visibility}; @@ -300,6 +301,13 @@ fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let is_incremental_build = tcx.sess.opts.incremental.is_some(); let mut internalization_candidates = FxHashSet(); + // Determine if monomorphizations instantiated in this crate will be made + // available to downstream crates. This depends on whether we are in + // share-generics mode and whether the current crate can even have + // downstream crates. + let export_generics = tcx.sess.opts.share_generics() && + tcx.local_crate_exports_generics(); + for mono_item in mono_items { match mono_item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => {} @@ -323,29 +331,7 @@ fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx, &mono_item, &mut can_be_internalized, - &|id, is_generic| { - if !tcx.sess.target.target.options.default_hidden_visibility { - return Visibility::Default - } - - // Generic functions never have export level C - if is_generic { - return Visibility::Hidden - } - - // Things with export level C don't get instantiated in - // downstream crates - if !id.is_local() { - return Visibility::Hidden - } - - // C-export level items remain at `Default`, all other internal - // items become `Hidden` - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportLevel::C) => Visibility::Default, - _ => Visibility::Hidden, - } - }, + export_generics, ); if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); @@ -376,12 +362,17 @@ fn mono_item_linkage_and_visibility( tcx: TyCtxt<'a, 'tcx, 'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, - default: &dyn Fn(DefId, bool) -> Visibility, + export_generics: bool, ) -> (Linkage, Visibility) { if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { return (explicit_linkage, Visibility::Default) } - let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, default); + let vis = mono_item_visibility( + tcx, + mono_item, + can_be_internalized, + export_generics, + ); (Linkage::External, vis) } @@ -389,7 +380,7 @@ fn mono_item_visibility( tcx: TyCtxt<'a, 'tcx, 'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, - default_visibility: &dyn Fn(DefId, bool) -> Visibility, + export_generics: bool, ) -> Visibility { let instance = match mono_item { // This is pretty complicated, go below @@ -399,7 +390,7 @@ fn mono_item_visibility( MonoItem::Static(def_id) => { return if tcx.is_reachable_non_generic(*def_id) { *can_be_internalized = false; - default_visibility(*def_id, false) + default_visibility(tcx, *def_id, false) } else { Visibility::Hidden }; @@ -408,7 +399,7 @@ fn mono_item_visibility( let def_id = tcx.hir.local_def_id(*node_id); return if tcx.is_reachable_non_generic(def_id) { *can_be_internalized = false; - default_visibility(def_id, false) + default_visibility(tcx, def_id, false) } else { Visibility::Hidden }; @@ -440,18 +431,13 @@ fn mono_item_visibility( // hidden visibility, it should indeed be a candidate for // internalization, but we have to understand that it's referenced // from the `main` symbol we'll generate later. + // + // This may be fixable with a new `InstanceDef` perhaps? Unsure! if tcx.lang_items().start_fn() == Some(def_id) { *can_be_internalized = false; return Visibility::Hidden } - // Determine if monomorphizations instantiated in this crate will be made - // available to downstream crates. This depends on whether we are in - // share-generics mode and whether the current crate can even have - // downstream crates. - let export_generics = tcx.sess.opts.share_generics() && - tcx.local_crate_exports_generics(); - let is_generic = instance.substs.types().next().is_some(); // Upstream `DefId` instances get different handling than local ones @@ -461,7 +447,7 @@ fn mono_item_visibility( // and we export generics, we must make // it available to downstream crates. *can_be_internalized = false; - default_visibility(def_id, true) + default_visibility(tcx, def_id, true) } else { Visibility::Hidden } @@ -477,7 +463,7 @@ fn mono_item_visibility( // This instance might be useful in // a downstream crate. *can_be_internalized = false; - default_visibility(def_id, true) + default_visibility(tcx, def_id, true) } } else { // We are not exporting generics or @@ -487,14 +473,82 @@ fn mono_item_visibility( Visibility::Hidden } } else { - // This isn't a generic function. + + // If this isn't a generic function then we mark this a `Default` if + // this is a reachable item, meaning that it's a symbol other crates may + // access when they link to us. if tcx.is_reachable_non_generic(def_id) { *can_be_internalized = false; debug_assert!(!is_generic); - default_visibility(def_id, false) - } else { - Visibility::Hidden + return default_visibility(tcx, def_id, false) + } + + // If this isn't reachable then we're gonna tag this with `Hidden` + // visibility. In some situations though we'll want to prevent this + // symbol from being internalized. + // + // There's two categories of items here: + // + // * First is weak lang items. These are basically mechanisms for + // libcore to forward-reference symbols defined later in crates like + // the standard library or `#[panic_implementation]` definitions. The + // definition of these weak lang items needs to be referenceable by + // libcore, so we're no longer a candidate for internalization. + // Removal of these functions can't be done by LLVM but rather must be + // done by the linker as it's a non-local decision. + // + // * Second is "std internal symbols". Currently this is primarily used + // for allocator symbols. Allocators are a little weird in their + // implementation, but the idea is that the compiler, at the last + // minute, defines an allocator with an injected object file. The + // `alloc` crate references these symbols (`__rust_alloc`) and the + // definition doesn't get hooked up until a linked crate artifact is + // generated. + // + // The symbols synthesized by the compiler (`__rust_alloc`) are thin + // veneers around the actual implementation, some other symbol which + // implements the same ABI. These symbols (things like `__rg_alloc`, + // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std + // internal symbols". + // + // The std-internal symbols here **should not show up in a dll as an + // exported interface**, so they return `false` from + // `is_reachable_non_generic` above and we'll give them `Hidden` + // visibility below. Like the weak lang items, though, we can't let + // LLVM internalize them as this decision is left up to the linker to + // omit them, so prevent them from being internalized. + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); + let std_internal_symbol = codegen_fn_attrs.flags + .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + if tcx.is_weak_lang_item(def_id) || std_internal_symbol { + *can_be_internalized = false; } + + Visibility::Hidden + } +} + +fn default_visibility(tcx: TyCtxt, id: DefId, is_generic: bool) -> Visibility { + if !tcx.sess.target.target.options.default_hidden_visibility { + return Visibility::Default + } + + // Generic functions never have export level C + if is_generic { + return Visibility::Hidden + } + + // Things with export level C don't get instantiated in + // downstream crates + if !id.is_local() { + return Visibility::Hidden + } + + // C-export level items remain at `Default`, all other internal + // items become `Hidden` + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportLevel::C) => Visibility::Default, + _ => Visibility::Hidden, } } diff --git a/src/test/run-make/wasm-symbols-not-exported/Makefile b/src/test/run-make/wasm-symbols-not-exported/Makefile new file mode 100644 index 0000000000000..872825113a845 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/Makefile @@ -0,0 +1,16 @@ +-include ../../run-make-fulldeps/tools.mk + +ifeq ($(TARGET),wasm32-unknown-unknown) +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) verify-exported-symbols.js $(TMPDIR)/foo.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O + $(NODE) verify-exported-symbols.js $(TMPDIR)/foo.wasm + $(RUSTC) bar.rs --target wasm32-unknown-unknown + $(NODE) verify-exported-symbols.js $(TMPDIR)/bar.wasm + $(RUSTC) bar.rs --target wasm32-unknown-unknown -O + $(NODE) verify-exported-symbols.js $(TMPDIR)/bar.wasm +else +all: +endif + diff --git a/src/test/run-make/wasm-symbols-not-exported/bar.rs b/src/test/run-make/wasm-symbols-not-exported/bar.rs new file mode 100644 index 0000000000000..979ec44b04077 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/bar.rs @@ -0,0 +1,45 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(panic_implementation, alloc_error_handler)] +#![crate_type = "cdylib"] +#![no_std] + +use core::alloc::*; + +struct B; + +unsafe impl GlobalAlloc for B { + unsafe fn alloc(&self, x: Layout) -> *mut u8 { + 1 as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, x: Layout) { + } +} + +#[global_allocator] +static A: B = B; + +#[no_mangle] +pub extern fn foo(a: u32) -> u32 { + assert_eq!(a, 3); + a * 2 +} + +#[alloc_error_handler] +fn a(_: core::alloc::Layout) -> ! { + loop {} +} + +#[panic_implementation] +fn b(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make/wasm-symbols-not-exported/foo.rs b/src/test/run-make/wasm-symbols-not-exported/foo.rs new file mode 100644 index 0000000000000..cd8c7cb205d96 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/foo.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "cdylib"] + +#[no_mangle] +pub extern fn foo() { + println!("foo"); + panic!("test"); +} diff --git a/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js b/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js new file mode 100644 index 0000000000000..060830eb01a71 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js @@ -0,0 +1,31 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let list = WebAssembly.Module.exports(m); +console.log('exports', list); + +let bad = false; +for (let i = 0; i < list.length; i++) { + const e = list[i]; + if (e.name == "foo" || e.kind != "function") + continue; + + console.log('unexpected exported symbol:', e.name); + bad = true; +} + +if (bad) + process.exit(1);