diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index e9498857fb9a3..f13a75648aea8 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -104,10 +104,17 @@ fn create_wrapper_function( false, ); - if tcx.sess.default_hidden_visibility() { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + #[cfg(feature = "master")] + match tcx.sess.default_visibility() { + rustc_target::spec::SymbolVisibility::Hidden => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)) + } + rustc_target::spec::SymbolVisibility::Protected => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Protected)) + } + rustc_target::spec::SymbolVisibility::Interposable => {} } + if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 2adac278c62f9..97eaf4fb54f68 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -77,18 +77,14 @@ pub(crate) unsafe fn codegen( // __rust_alloc_error_handler_should_panic let name = OomStrategy::SYMBOL; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + apply_visibility(tcx.sess.default_visibility(), ll_g); let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); let name = NO_ALLOC_SHIM_IS_UNSTABLE; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + apply_visibility(tcx.sess.default_visibility(), ll_g); let llval = llvm::LLVMConstInt(i8, 0, False); llvm::LLVMSetInitializer(ll_g, llval); } @@ -100,6 +96,18 @@ pub(crate) unsafe fn codegen( } } +unsafe fn apply_visibility(visibility: rustc_target::spec::SymbolVisibility, global: &llvm::Value) { + match visibility { + rustc_target::spec::SymbolVisibility::Hidden => { + unsafe { llvm::LLVMRustSetVisibility(global, llvm::Visibility::Hidden) }; + } + rustc_target::spec::SymbolVisibility::Protected => { + unsafe { llvm::LLVMRustSetVisibility(global, llvm::Visibility::Protected) }; + } + rustc_target::spec::SymbolVisibility::Interposable => {} + } +} + fn create_wrapper_function( tcx: TyCtxt<'_>, llcx: &Context, @@ -132,9 +140,8 @@ fn create_wrapper_function( None }; - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } + apply_visibility(tcx.sess.default_visibility(), llfn); + if tcx.sess.must_emit_unwind_tables() { let uwtable = attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index b0b29ca1280d5..cef6d7c117891 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -84,11 +84,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_type: &'ll Type, ) -> &'ll Value { // Declare C ABI functions with the visibility used by C by default. - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = self.tcx.sess.default_visibility().into(); declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type) } @@ -107,11 +103,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { unnamed: llvm::UnnamedAddr, fn_type: &'ll Type, ) -> &'ll Value { - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = self.tcx.sess.default_visibility().into(); declare_raw_fn(self, name, callconv, unnamed, visibility, fn_type) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9aabfd794bad7..d4a4225dfbe6c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -781,6 +781,7 @@ pub mod debuginfo { } use bitflags::bitflags; +use rustc_target::spec::SymbolVisibility; // These values **must** match with LLVMRustAllocKindFlags bitflags! { #[repr(transparent)] @@ -2417,3 +2418,13 @@ unsafe extern "C" { pub fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool; } + +impl From for Visibility { + fn from(value: SymbolVisibility) -> Self { + match value { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 57aa3deae806b..895897eca6b8b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -770,7 +770,7 @@ fn test_unstable_options_tracking_hash() { tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); - tracked!(default_hidden_visibility, Some(true)); + tracked!(default_visibility, Some(rustc_target::spec::SymbolVisibility::Hidden)); tracked!(dep_info_omit_d_target, true); tracked!(direct_access_external_data, Some(true)); tracked!(dual_proc_macros, true); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 954f746ce5b61..56ca9167d4daa 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -15,6 +15,7 @@ use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_span::symbol::Symbol; +use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; @@ -305,6 +306,16 @@ pub enum Visibility { Protected, } +impl From for Visibility { + fn from(value: SymbolVisibility) -> Self { + match value { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} + impl<'tcx> CodegenUnit<'tcx> { #[inline] pub fn new(name: Symbol) -> CodegenUnit<'tcx> { diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index ad05cca66cab3..5bd484d7bb002 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -904,26 +904,22 @@ fn mono_item_visibility<'tcx>( } fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { - if !tcx.sess.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; - } + let export_level = if is_generic { + // Generic functions never have export-level C. + SymbolExportLevel::Rust + } else { + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, + _ => SymbolExportLevel::Rust, + } + }; + match export_level { + // C-export level items remain at `Default` to allow C code to + // access and interpose them. + SymbolExportLevel::C => Visibility::Default, - // C-export level items remain at `Default`, all other internal - // items become `Hidden`. - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default, - _ => Visibility::Hidden, + // For all other symbols, `default_visibility` determines which visibility to use. + SymbolExportLevel::Rust => tcx.sess.default_visibility().into(), } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b052d8d72c7d8..0d293415aa9c7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3008,7 +3008,8 @@ pub(crate) mod dep_tracking { use rustc_span::edition::Edition; use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, - RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, WasmCAbi, + RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTriple, + TlsModel, WasmCAbi, }; use super::{ @@ -3101,6 +3102,7 @@ pub(crate) mod dep_tracking { StackProtector, SwitchWithOptPath, SymbolManglingVersion, + SymbolVisibility, RemapPathScopeComponents, SourceFileHashAlgorithm, OutFileName, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aac776d2919aa..1de09b8be4d63 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -13,8 +13,8 @@ use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, - RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, - WasmCAbi, + RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, + TargetTriple, TlsModel, WasmCAbi, }; use crate::config::*; @@ -416,6 +416,8 @@ mod desc { "one of: `disabled`, `trampolines`, or `aliases`"; pub(crate) const parse_symbol_mangling_version: &str = "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; + pub(crate) const parse_opt_symbol_visibility: &str = + "one of: `hidden`, `protected`, or `interposable`"; pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; @@ -922,6 +924,20 @@ mod parse { true } + pub(crate) fn parse_opt_symbol_visibility( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + if let Some(v) = v { + if let Ok(vis) = SymbolVisibility::from_str(v) { + *slot = Some(vis); + } else { + return false; + } + } + true + } + pub(crate) fn parse_optimization_fuel( slot: &mut Option<(String, u64)>, v: Option<&str>, @@ -1688,8 +1704,8 @@ options! { "compress debug info sections (none, zlib, zstd, default: none)"), deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], "deduplicate identical diagnostics (default: yes)"), - default_hidden_visibility: Option = (None, parse_opt_bool, [TRACKED], - "overrides the `default_hidden_visibility` setting of the target"), + default_visibility: Option = (None, parse_opt_symbol_visibility, [TRACKED], + "overrides the `default_visibility` setting of the target"), dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], "in dep-info output, omit targets for tracking dependencies of the dep-info files \ themselves (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index adc28e3846254..d67e69fe0fb72 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -31,7 +31,8 @@ use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel, + SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, + TargetTriple, TlsModel, }; use crate::code_stats::CodeStats; @@ -617,12 +618,13 @@ impl Session { } } - /// Whether the default visibility of symbols should be "hidden" rather than "default". - pub fn default_hidden_visibility(&self) -> bool { + /// Returns the default symbol visibility. + pub fn default_visibility(&self) -> SymbolVisibility { self.opts .unstable_opts - .default_hidden_visibility - .unwrap_or(self.target.options.default_hidden_visibility) + .default_visibility + .or(self.target.options.default_visibility) + .unwrap_or(SymbolVisibility::Interposable) } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f327c1fd17902..d1fcfe4adef18 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -830,6 +830,46 @@ impl RelroLevel { } } +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SymbolVisibility { + Hidden, + Protected, + Interposable, +} + +impl SymbolVisibility { + pub fn desc(&self) -> &str { + match *self { + SymbolVisibility::Hidden => "hidden", + SymbolVisibility::Protected => "protected", + SymbolVisibility::Interposable => "interposable", + } + } +} + +impl FromStr for SymbolVisibility { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "hidden" => Ok(SymbolVisibility::Hidden), + "protected" => Ok(SymbolVisibility::Protected), + "interposable" => Ok(SymbolVisibility::Interposable), + _ => Err(()), + } + } +} + +impl ToJson for SymbolVisibility { + fn to_json(&self) -> Json { + match *self { + SymbolVisibility::Hidden => "hidden".to_json(), + SymbolVisibility::Protected => "protected".to_json(), + SymbolVisibility::Interposable => "interposable".to_json(), + } + } +} + impl FromStr for RelroLevel { type Err = (); @@ -2326,13 +2366,12 @@ pub struct TargetOptions { /// for this target unconditionally. pub no_builtins: bool, - /// The default visibility for symbols in this target should be "hidden" - /// rather than "default". + /// The default visibility for symbols in this target. /// - /// This value typically shouldn't be accessed directly, but through - /// the `rustc_session::Session::default_hidden_visibility` method, which - /// allows `rustc` users to override this setting using cmdline flags. - pub default_hidden_visibility: bool, + /// This value typically shouldn't be accessed directly, but through the + /// `rustc_session::Session::default_visibility` method, which allows `rustc` users to override + /// this setting using cmdline flags. + pub default_visibility: Option, /// Whether a .debug_gdb_scripts section will be added to the output object file pub emit_debug_gdb_scripts: bool, @@ -2623,7 +2662,7 @@ impl Default for TargetOptions { requires_lto: false, singlethread: false, no_builtins: false, - default_hidden_visibility: false, + default_visibility: None, emit_debug_gdb_scripts: true, requires_uwtable: false, default_uwtable: false, @@ -2963,6 +3002,18 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(level) => base.$key_name = Some(level), + _ => return Some(Err(format!("'{}' is not a valid value for \ + symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, DebuginfoKind) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -3353,7 +3404,7 @@ impl Target { key!(requires_lto, bool); key!(singlethread, bool); key!(no_builtins, bool); - key!(default_hidden_visibility, bool); + key!(default_visibility, Option)?; key!(emit_debug_gdb_scripts, bool); key!(requires_uwtable, bool); key!(default_uwtable, bool); @@ -3633,7 +3684,7 @@ impl ToJson for Target { target_option_val!(requires_lto); target_option_val!(singlethread); target_option_val!(no_builtins); - target_option_val!(default_hidden_visibility); + target_option_val!(default_visibility); target_option_val!(emit_debug_gdb_scripts); target_option_val!(requires_uwtable); target_option_val!(default_uwtable); diff --git a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md deleted file mode 100644 index 579add4a9d985..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# `default-hidden-visibility` - -The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/656 - ------------------------- - -This flag can be used to override the target's -[`default_hidden_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_hidden_visibility) -setting. -Using `-Zdefault_hidden_visibility=yes` is roughly equivalent to Clang's -[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) -cmdline flag. diff --git a/src/doc/unstable-book/src/compiler-flags/default-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-visibility.md new file mode 100644 index 0000000000000..623e961d6eeca --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/default-visibility.md @@ -0,0 +1,44 @@ +# `default-visibility` + +The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/782 + +------------------------ + +This flag can be used to override the target's +[`default_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_visibility) +setting. + +This option only affects building of shared objects and should have no effect on executables. + +Visibility an be set to one of three options: + +* protected +* hidden +* interposable + +## Hidden visibility + +Using `-Zdefault-visibility=hidden` is roughly equivalent to Clang's +[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) +cmdline flag. Hidden symbols will not be exported from the created shared object, so cannot be +referenced from other shared objects or from executables. + +## Protected visibility + +Using `-Zdefault-visibility=protected` will cause rust-mangled symbols to be emitted with +"protected" visibility. This signals the compiler, the linker and the runtime linker that these +symbols cannot be overridden by the executable or by other shared objects earlier in the load order. + +This will allow the compiler to emit direct references to symbols, which may improve performance. It +also removes the need for these symbols to be resolved when a shared object built with this option +is loaded. + +Using protected visibility when linking with GNU ld prior to 2.40 will result in linker errors when +building for Linux. Other linkers such as LLD are not affected. + +## Interposable + +Using `-Zdefault-visibility=interposable` will cause symbols to be emitted with "default" +visibility. On platforms that support it, this makes it so that symbols can be interposed, which +means that they can be overridden by symbols with the same name from the executable or by other +shared objects earier in the load order. diff --git a/tests/codegen/default-hidden-visibility.rs b/tests/codegen/default-visibility.rs similarity index 63% rename from tests/codegen/default-hidden-visibility.rs rename to tests/codegen/default-visibility.rs index 2bea8f62a40db..055fd1385cf2a 100644 --- a/tests/codegen/default-hidden-visibility.rs +++ b/tests/codegen/default-visibility.rs @@ -1,11 +1,12 @@ -// Verifies that `Session::default_hidden_visibility` is affected when using the related cmdline -// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/656. See +// Verifies that `Session::default_visibility` is affected when using the related cmdline +// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/782. See // also https://github.com/rust-lang/rust/issues/73295 and // https://github.com/rust-lang/rust/issues/37530. -//@ revisions:DEFAULT YES NO -//@[YES] compile-flags: -Zdefault-hidden-visibility=yes -//@[NO] compile-flags: -Zdefault-hidden-visibility=no +//@ revisions:HIDDEN PROTECTED INTERPOSABLE +//@[HIDDEN] compile-flags: -Zdefault-visibility=hidden +//@[PROTECTED] compile-flags: -Zdefault-visibility=protected +//@[INTERPOSABLE] compile-flags: -Zdefault-visibility=interposable // The test scenario is specifically about visibility of symbols exported out of dynamically linked // libraries. @@ -26,6 +27,6 @@ pub static tested_symbol: [u8; 6] = *b"foobar"; // //@ only-x86_64-unknown-linux-gnu -// DEFAULT: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant -// YES: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = hidden constant -// NO: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant +// HIDDEN: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = hidden constant +// PROTECTED: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = protected constant +// INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant