diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2018-08-02 12:30:43 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2018-08-07 08:42:38 -0700 |
| commit | 7c58ab671fb49200c0cbd1ad9218713a5c3afe0d (patch) | |
| tree | 98ffced29d28fd7353586a2663fcc8cbfae47b44 /src | |
| parent | 38eeebdfed97af43354ed924e498764e8f9cdae5 (diff) | |
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc/middle/reachable.rs | 11 | ||||
| -rw-r--r-- | src/librustc_allocator/expand.rs | 7 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/allocator.rs | 7 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/base.rs | 24 | ||||
| -rw-r--r-- | src/librustc_mir/monomorphize/partitioning.rs | 136 | ||||
| -rw-r--r-- | src/test/run-make/wasm-symbols-not-exported/Makefile | 16 | ||||
| -rw-r--r-- | src/test/run-make/wasm-symbols-not-exported/bar.rs | 45 | ||||
| -rw-r--r-- | src/test/run-make/wasm-symbols-not-exported/foo.rs | 17 | ||||
| -rw-r--r-- | src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js | 31 |
9 files changed, 238 insertions, 56 deletions
diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 94246db3706..a09942258e2 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 ffbbd8a33a1..05843852ee0 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<Attribute> { - 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 2d401da958b..0beb8a8844c 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 41336165684..0a8bc03d8d4 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 31aa918b56f..4e32ca483a1 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 00000000000..872825113a8 --- /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 00000000000..979ec44b040 --- /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 <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. + +#![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 00000000000..cd8c7cb205d --- /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 <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. + +#![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 00000000000..060830eb01a --- /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 <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. + +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); |
