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 8d6d01633a12e..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}; @@ -322,146 +323,16 @@ 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 - } - - // 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 - } - - 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) - } - } - } - }; + let (linkage, visibility) = mono_item_linkage_and_visibility( + tcx, + &mono_item, + &mut can_be_internalized, + export_generics, + ); if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } @@ -487,6 +358,200 @@ 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, + 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, + export_generics, + ); + (Linkage::External, vis) +} + +fn mono_item_visibility( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_item: &MonoItem<'tcx>, + can_be_internalized: &mut bool, + export_generics: bool, +) -> 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(tcx, *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(tcx, 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. + // + // 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 + } + + 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(tcx, 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(tcx, 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 { + + // 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); + 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, + } +} + fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<'tcx>, target_cgu_count: usize, crate_name: &str) { 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);