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
This commit is contained in:
parent
38eeebdfed
commit
7c58ab671f
@ -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 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);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
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 @@ fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
|
||||
}
|
||||
|
||||
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),
|
||||
]
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
16
src/test/run-make/wasm-symbols-not-exported/Makefile
Normal file
16
src/test/run-make/wasm-symbols-not-exported/Makefile
Normal file
@ -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
|
||||
|
45
src/test/run-make/wasm-symbols-not-exported/bar.rs
Normal file
45
src/test/run-make/wasm-symbols-not-exported/bar.rs
Normal file
@ -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 {}
|
||||
}
|
17
src/test/run-make/wasm-symbols-not-exported/foo.rs
Normal file
17
src/test/run-make/wasm-symbols-not-exported/foo.rs
Normal file
@ -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");
|
||||
}
|
@ -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);
|
Loading…
Reference in New Issue
Block a user