about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-08-02 12:30:43 -0700
committerAlex Crichton <alex@alexcrichton.com>2018-08-07 08:42:38 -0700
commit7c58ab671fb49200c0cbd1ad9218713a5c3afe0d (patch)
tree98ffced29d28fd7353586a2663fcc8cbfae47b44 /src
parent38eeebdfed97af43354ed924e498764e8f9cdae5 (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.rs11
-rw-r--r--src/librustc_allocator/expand.rs7
-rw-r--r--src/librustc_codegen_llvm/allocator.rs7
-rw-r--r--src/librustc_codegen_llvm/base.rs24
-rw-r--r--src/librustc_mir/monomorphize/partitioning.rs136
-rw-r--r--src/test/run-make/wasm-symbols-not-exported/Makefile16
-rw-r--r--src/test/run-make/wasm-symbols-not-exported/bar.rs45
-rw-r--r--src/test/run-make/wasm-symbols-not-exported/foo.rs17
-rw-r--r--src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js31
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);