1 use crate::base
::allocator_kind_for_codegen
;
3 use std
::collections
::hash_map
::Entry
::*;
5 use rustc_ast
::expand
::allocator
::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE}
;
6 use rustc_data_structures
::fx
::FxHashMap
;
7 use rustc_hir
::def
::DefKind
;
8 use rustc_hir
::def_id
::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}
;
9 use rustc_middle
::middle
::codegen_fn_attrs
::CodegenFnAttrFlags
;
10 use rustc_middle
::middle
::exported_symbols
::{
11 metadata_symbol_name
, ExportedSymbol
, SymbolExportInfo
, SymbolExportKind
, SymbolExportLevel
,
13 use rustc_middle
::query
::LocalCrate
;
14 use rustc_middle
::ty
::Instance
;
15 use rustc_middle
::ty
::{self, SymbolName, TyCtxt}
;
16 use rustc_middle
::ty
::{GenericArgKind, GenericArgsRef}
;
17 use rustc_middle
::util
::Providers
;
18 use rustc_session
::config
::{CrateType, OomStrategy}
;
19 use rustc_target
::spec
::SanitizerSet
;
21 pub fn threshold(tcx
: TyCtxt
<'_
>) -> SymbolExportLevel
{
22 crates_export_threshold(tcx
.crate_types())
25 fn crate_export_threshold(crate_type
: CrateType
) -> SymbolExportLevel
{
27 CrateType
::Executable
| CrateType
::Staticlib
| CrateType
::ProcMacro
| CrateType
::Cdylib
=> {
30 CrateType
::Rlib
| CrateType
::Dylib
=> SymbolExportLevel
::Rust
,
34 pub fn crates_export_threshold(crate_types
: &[CrateType
]) -> SymbolExportLevel
{
37 .any(|&crate_type
| crate_export_threshold(crate_type
) == SymbolExportLevel
::Rust
)
39 SymbolExportLevel
::Rust
45 fn reachable_non_generics_provider(tcx
: TyCtxt
<'_
>, _
: LocalCrate
) -> DefIdMap
<SymbolExportInfo
> {
46 if !tcx
.sess
.opts
.output_types
.should_codegen() {
47 return Default
::default();
50 // Check to see if this crate is a "special runtime crate". These
51 // crates, implementation details of the standard library, typically
52 // have a bunch of `pub extern` and `#[no_mangle]` functions as the
53 // ABI between them. We don't want their symbols to have a `C`
54 // export level, however, as they're just implementation details.
55 // Down below we'll hardwire all of the symbols to the `Rust` export
57 let special_runtime_crate
=
58 tcx
.is_panic_runtime(LOCAL_CRATE
) || tcx
.is_compiler_builtins(LOCAL_CRATE
);
60 let mut reachable_non_generics
: DefIdMap
<_
> = tcx
63 .filter_map(|&def_id
| {
64 // We want to ignore some FFI functions that are not exposed from
65 // this crate. Reachable FFI functions can be lumped into two
68 // 1. Those that are included statically via a static library
69 // 2. Those included otherwise (e.g., dynamically or via a framework)
71 // Although our LLVM module is not literally emitting code for the
72 // statically included symbols, it's an export of our library which
73 // needs to be passed on to the linker and encoded in the metadata.
75 // As a result, if this id is an FFI item (foreign item) then we only
76 // let it through if it's included statically.
77 if let Some(parent_id
) = tcx
.opt_local_parent(def_id
)
78 && let DefKind
::ForeignMod
= tcx
.def_kind(parent_id
)
80 let library
= tcx
.native_library(def_id
)?
;
81 return library
.kind
.is_statically_included().then_some(def_id
);
84 // Only consider nodes that actually have exported symbols.
85 match tcx
.def_kind(def_id
) {
86 DefKind
::Fn
| DefKind
::Static(_
) => {}
87 DefKind
::AssocFn
if tcx
.impl_of_method(def_id
.to_def_id()).is_some() => {}
91 let generics
= tcx
.generics_of(def_id
);
92 if generics
.requires_monomorphization(tcx
) {
96 // Functions marked with #[inline] are codegened with "internal"
97 // linkage and are not exported unless marked with an extern
99 if !Instance
::mono(tcx
, def_id
.to_def_id()).def
.generates_cgu_internal_copy(tcx
)
100 || tcx
.codegen_fn_attrs(def_id
.to_def_id()).contains_extern_indicator()
108 // We won't link right if this symbol is stripped during LTO.
109 let name
= tcx
.symbol_name(Instance
::mono(tcx
, def_id
.to_def_id())).name
;
110 let used
= name
== "rust_eh_personality";
112 let export_level
= if special_runtime_crate
{
113 SymbolExportLevel
::Rust
115 symbol_export_level(tcx
, def_id
.to_def_id())
117 let codegen_attrs
= tcx
.codegen_fn_attrs(def_id
.to_def_id());
119 "EXPORTED SYMBOL (local): {} ({:?})",
120 tcx
.symbol_name(Instance
::mono(tcx
, def_id
.to_def_id())),
123 let info
= SymbolExportInfo
{
125 kind
: if tcx
.is_static(def_id
.to_def_id()) {
126 if codegen_attrs
.flags
.contains(CodegenFnAttrFlags
::THREAD_LOCAL
) {
127 SymbolExportKind
::Tls
129 SymbolExportKind
::Data
132 SymbolExportKind
::Text
134 used
: codegen_attrs
.flags
.contains(CodegenFnAttrFlags
::USED
)
135 || codegen_attrs
.flags
.contains(CodegenFnAttrFlags
::USED_LINKER
)
138 (def_id
.to_def_id(), info
)
142 if let Some(id
) = tcx
.proc_macro_decls_static(()) {
143 reachable_non_generics
.insert(
146 level
: SymbolExportLevel
::C
,
147 kind
: SymbolExportKind
::Data
,
153 reachable_non_generics
156 fn is_reachable_non_generic_provider_local(tcx
: TyCtxt
<'_
>, def_id
: LocalDefId
) -> bool
{
157 let export_threshold
= threshold(tcx
);
159 if let Some(&info
) = tcx
.reachable_non_generics(LOCAL_CRATE
).get(&def_id
.to_def_id()) {
160 info
.level
.is_below_threshold(export_threshold
)
166 fn is_reachable_non_generic_provider_extern(tcx
: TyCtxt
<'_
>, def_id
: DefId
) -> bool
{
167 tcx
.reachable_non_generics(def_id
.krate
).contains_key(&def_id
)
170 fn exported_symbols_provider_local(
173 ) -> &[(ExportedSymbol
<'_
>, SymbolExportInfo
)] {
174 if !tcx
.sess
.opts
.output_types
.should_codegen() {
178 // FIXME: Sorting this is unnecessary since we are sorting later anyway.
179 // Can we skip the later sorting?
180 let sorted
= tcx
.with_stable_hashing_context(|hcx
| {
181 tcx
.reachable_non_generics(LOCAL_CRATE
).to_sorted(&hcx
, true)
184 let mut symbols
: Vec
<_
> =
185 sorted
.iter().map(|(&def_id
, &info
)| (ExportedSymbol
::NonGeneric(def_id
), info
)).collect();
188 if !tcx
.sess
.target
.dll_tls_export
{
189 symbols
.extend(sorted
.iter().filter_map(|(&def_id
, &info
)| {
190 tcx
.needs_thread_local_shim(def_id
).then(|| {
192 ExportedSymbol
::ThreadLocalShim(def_id
),
195 kind
: SymbolExportKind
::Text
,
203 if tcx
.entry_fn(()).is_some() {
204 let exported_symbol
=
205 ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, tcx
.sess
.target
.entry_name
.as_ref()));
210 level
: SymbolExportLevel
::C
,
211 kind
: SymbolExportKind
::Text
,
217 // Mark allocator shim symbols as exported only if they were generated.
218 if allocator_kind_for_codegen(tcx
).is_some() {
219 for symbol_name
in ALLOCATOR_METHODS
221 .map(|method
| format
!("__rust_{}", method
.name
))
222 .chain(["__rust_alloc_error_handler".to_string(), OomStrategy
::SYMBOL
.to_string()])
224 let exported_symbol
= ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, &symbol_name
));
229 level
: SymbolExportLevel
::Rust
,
230 kind
: SymbolExportKind
::Text
,
236 let exported_symbol
=
237 ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, NO_ALLOC_SHIM_IS_UNSTABLE
));
241 level
: SymbolExportLevel
::Rust
,
242 kind
: SymbolExportKind
::Data
,
248 if tcx
.sess
.instrument_coverage() || tcx
.sess
.opts
.cg
.profile_generate
.enabled() {
249 // These are weak symbols that point to the profile version and the
250 // profile name, which need to be treated as exported so LTO doesn't nix
252 const PROFILER_WEAK_SYMBOLS
: [&str; 2] =
253 ["__llvm_profile_raw_version", "__llvm_profile_filename"];
255 symbols
.extend(PROFILER_WEAK_SYMBOLS
.iter().map(|sym
| {
256 let exported_symbol
= ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, sym
));
260 level
: SymbolExportLevel
::C
,
261 kind
: SymbolExportKind
::Data
,
268 if tcx
.sess
.opts
.unstable_opts
.sanitizer
.contains(SanitizerSet
::MEMORY
) {
269 let mut msan_weak_symbols
= Vec
::new();
271 // Similar to profiling, preserve weak msan symbol during LTO.
272 if tcx
.sess
.opts
.unstable_opts
.sanitizer_recover
.contains(SanitizerSet
::MEMORY
) {
273 msan_weak_symbols
.push("__msan_keep_going");
276 if tcx
.sess
.opts
.unstable_opts
.sanitizer_memory_track_origins
!= 0 {
277 msan_weak_symbols
.push("__msan_track_origins");
280 symbols
.extend(msan_weak_symbols
.into_iter().map(|sym
| {
281 let exported_symbol
= ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, sym
));
285 level
: SymbolExportLevel
::C
,
286 kind
: SymbolExportKind
::Data
,
293 if tcx
.crate_types().contains(&CrateType
::Dylib
)
294 || tcx
.crate_types().contains(&CrateType
::ProcMacro
)
296 let symbol_name
= metadata_symbol_name(tcx
);
297 let exported_symbol
= ExportedSymbol
::NoDefId(SymbolName
::new(tcx
, &symbol_name
));
302 level
: SymbolExportLevel
::C
,
303 kind
: SymbolExportKind
::Data
,
309 if tcx
.sess
.opts
.share_generics() && tcx
.local_crate_exports_generics() {
310 use rustc_middle
::mir
::mono
::{Linkage, MonoItem, Visibility}
;
311 use rustc_middle
::ty
::InstanceDef
;
313 // Normally, we require that shared monomorphizations are not hidden,
314 // because if we want to re-use a monomorphization from a Rust dylib, it
315 // needs to be exported.
316 // However, on platforms that don't allow for Rust dylibs, having
317 // external linkage is enough for monomorphization to be linked to.
318 let need_visibility
= tcx
.sess
.target
.dynamic_linking
&& !tcx
.sess
.target
.only_cdylib
;
320 let (_
, cgus
) = tcx
.collect_and_partition_mono_items(());
322 for (mono_item
, data
) in cgus
.iter().flat_map(|cgu
| cgu
.items().iter()) {
323 if data
.linkage
!= Linkage
::External
{
324 // We can only re-use things with external linkage, otherwise
325 // we'll get a linker error
329 if need_visibility
&& data
.visibility
== Visibility
::Hidden
{
330 // If we potentially share things from Rust dylibs, they must
336 MonoItem
::Fn(Instance { def: InstanceDef::Item(def), args }
) => {
337 if args
.non_erasable_generics(tcx
, def
).next().is_some() {
338 let symbol
= ExportedSymbol
::Generic(def
, args
);
342 level
: SymbolExportLevel
::Rust
,
343 kind
: SymbolExportKind
::Text
,
349 MonoItem
::Fn(Instance { def: InstanceDef::DropGlue(def_id, Some(ty)), args }
) => {
350 // A little sanity-check
352 args
.non_erasable_generics(tcx
, def_id
).next(),
353 Some(GenericArgKind
::Type(ty
))
356 ExportedSymbol
::DropGlue(ty
),
358 level
: SymbolExportLevel
::Rust
,
359 kind
: SymbolExportKind
::Text
,
365 // Any other symbols don't qualify for sharing
371 // Sort so we get a stable incr. comp. hash.
372 symbols
.sort_by_cached_key(|s
| s
.0.symbol_name_for_local_instance(tcx
));
374 tcx
.arena
.alloc_from_iter(symbols
)
377 fn upstream_monomorphizations_provider(
380 ) -> DefIdMap
<FxHashMap
<GenericArgsRef
<'_
>, CrateNum
>> {
381 let cnums
= tcx
.crates(());
383 let mut instances
: DefIdMap
<FxHashMap
<_
, _
>> = Default
::default();
385 let drop_in_place_fn_def_id
= tcx
.lang_items().drop_in_place_fn();
387 for &cnum
in cnums
.iter() {
388 for (exported_symbol
, _
) in tcx
.exported_symbols(cnum
).iter() {
389 let (def_id
, args
) = match *exported_symbol
{
390 ExportedSymbol
::Generic(def_id
, args
) => (def_id
, args
),
391 ExportedSymbol
::DropGlue(ty
) => {
392 if let Some(drop_in_place_fn_def_id
) = drop_in_place_fn_def_id
{
393 (drop_in_place_fn_def_id
, tcx
.mk_args(&[ty
.into()]))
395 // `drop_in_place` in place does not exist, don't try
400 ExportedSymbol
::NonGeneric(..)
401 | ExportedSymbol
::ThreadLocalShim(..)
402 | ExportedSymbol
::NoDefId(..) => {
403 // These are no monomorphizations
408 let args_map
= instances
.entry(def_id
).or_default();
410 match args_map
.entry(args
) {
412 // If there are multiple monomorphizations available,
413 // we select one deterministically.
414 let other_cnum
= *e
.get();
415 if tcx
.stable_crate_id(other_cnum
) > tcx
.stable_crate_id(cnum
) {
429 fn upstream_monomorphizations_for_provider(
432 ) -> Option
<&FxHashMap
<GenericArgsRef
<'_
>, CrateNum
>> {
433 debug_assert
!(!def_id
.is_local());
434 tcx
.upstream_monomorphizations(()).get(&def_id
)
437 fn upstream_drop_glue_for_provider
<'tcx
>(
439 args
: GenericArgsRef
<'tcx
>,
440 ) -> Option
<CrateNum
> {
441 if let Some(def_id
) = tcx
.lang_items().drop_in_place_fn() {
442 tcx
.upstream_monomorphizations_for(def_id
).and_then(|monos
| monos
.get(&args
).cloned())
448 fn is_unreachable_local_definition_provider(tcx
: TyCtxt
<'_
>, def_id
: LocalDefId
) -> bool
{
449 !tcx
.reachable_set(()).contains(&def_id
)
452 pub fn provide(providers
: &mut Providers
) {
453 providers
.reachable_non_generics
= reachable_non_generics_provider
;
454 providers
.is_reachable_non_generic
= is_reachable_non_generic_provider_local
;
455 providers
.exported_symbols
= exported_symbols_provider_local
;
456 providers
.upstream_monomorphizations
= upstream_monomorphizations_provider
;
457 providers
.is_unreachable_local_definition
= is_unreachable_local_definition_provider
;
458 providers
.upstream_drop_glue_for
= upstream_drop_glue_for_provider
;
459 providers
.wasm_import_module_map
= wasm_import_module_map
;
460 providers
.extern_queries
.is_reachable_non_generic
= is_reachable_non_generic_provider_extern
;
461 providers
.extern_queries
.upstream_monomorphizations_for
=
462 upstream_monomorphizations_for_provider
;
465 fn symbol_export_level(tcx
: TyCtxt
<'_
>, sym_def_id
: DefId
) -> SymbolExportLevel
{
466 // We export anything that's not mangled at the "C" layer as it probably has
467 // to do with ABI concerns. We do not, however, apply such treatment to
468 // special symbols in the standard library for various plumbing between
469 // core/std/allocators/etc. For example symbols used to hook up allocation
470 // are not considered for export
471 let codegen_fn_attrs
= tcx
.codegen_fn_attrs(sym_def_id
);
472 let is_extern
= codegen_fn_attrs
.contains_extern_indicator();
474 codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::RUSTC_STD_INTERNAL_SYMBOL
);
476 if is_extern
&& !std_internal
{
477 let target
= &tcx
.sess
.target
.llvm_target
;
478 // WebAssembly cannot export data symbols, so reduce their export level
479 if target
.contains("emscripten") {
480 if let DefKind
::Static(_
) = tcx
.def_kind(sym_def_id
) {
481 return SymbolExportLevel
::Rust
;
487 SymbolExportLevel
::Rust
491 /// This is the symbol name of the given instance instantiated in a specific crate.
492 pub fn symbol_name_for_instance_in_crate
<'tcx
>(
494 symbol
: ExportedSymbol
<'tcx
>,
495 instantiating_crate
: CrateNum
,
497 // If this is something instantiated in the local crate then we might
498 // already have cached the name as a query result.
499 if instantiating_crate
== LOCAL_CRATE
{
500 return symbol
.symbol_name_for_local_instance(tcx
).to_string();
503 // This is something instantiated in an upstream crate, so we have to use
504 // the slower (because uncached) version of computing the symbol name.
506 ExportedSymbol
::NonGeneric(def_id
) => {
507 rustc_symbol_mangling
::symbol_name_for_instance_in_crate(
509 Instance
::mono(tcx
, def_id
),
513 ExportedSymbol
::Generic(def_id
, args
) => {
514 rustc_symbol_mangling
::symbol_name_for_instance_in_crate(
516 Instance
::new(def_id
, args
),
520 ExportedSymbol
::ThreadLocalShim(def_id
) => {
521 rustc_symbol_mangling
::symbol_name_for_instance_in_crate(
524 def
: ty
::InstanceDef
::ThreadLocalShim(def_id
),
525 args
: ty
::GenericArgs
::empty(),
530 ExportedSymbol
::DropGlue(ty
) => rustc_symbol_mangling
::symbol_name_for_instance_in_crate(
532 Instance
::resolve_drop_in_place(tcx
, ty
),
535 ExportedSymbol
::NoDefId(symbol_name
) => symbol_name
.to_string(),
539 /// This is the symbol name of the given instance as seen by the linker.
541 /// On 32-bit Windows symbols are decorated according to their calling conventions.
542 pub fn linking_symbol_name_for_instance_in_crate
<'tcx
>(
544 symbol
: ExportedSymbol
<'tcx
>,
545 instantiating_crate
: CrateNum
,
547 use rustc_target
::abi
::call
::Conv
;
549 let mut undecorated
= symbol_name_for_instance_in_crate(tcx
, symbol
, instantiating_crate
);
551 let target
= &tcx
.sess
.target
;
552 if !target
.is_like_windows
{
553 // Mach-O has a global "_" suffix and `object` crate will handle it.
554 // ELF does not have any symbol decorations.
558 let x86
= match &target
.arch
[..] {
561 // Only x86/64 use symbol decorations.
562 _
=> return undecorated
,
565 let instance
= match symbol
{
566 ExportedSymbol
::NonGeneric(def_id
) | ExportedSymbol
::Generic(def_id
, _
)
567 if tcx
.is_static(def_id
) =>
571 ExportedSymbol
::NonGeneric(def_id
) => Some(Instance
::mono(tcx
, def_id
)),
572 ExportedSymbol
::Generic(def_id
, args
) => Some(Instance
::new(def_id
, args
)),
573 // DropGlue always use the Rust calling convention and thus follow the target's default
574 // symbol decoration scheme.
575 ExportedSymbol
::DropGlue(..) => None
,
576 // NoDefId always follow the target's default symbol decoration scheme.
577 ExportedSymbol
::NoDefId(..) => None
,
578 // ThreadLocalShim always follow the target's default symbol decoration scheme.
579 ExportedSymbol
::ThreadLocalShim(..) => None
,
582 let (conv
, args
) = instance
584 tcx
.fn_abi_of_instance(ty
::ParamEnv
::reveal_all().and((i
, ty
::List
::empty())))
585 .unwrap_or_else(|_
| bug
!("fn_abi_of_instance({i:?}) failed"))
587 .map(|fnabi
| (fnabi
.conv
, &fnabi
.args
[..]))
588 .unwrap_or((Conv
::Rust
, &[]));
590 // Decorate symbols with prefixes, suffixes and total number of bytes of arguments.
591 // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
592 let (prefix
, suffix
) = match conv
{
593 Conv
::X86Fastcall
=> ("@", "@"),
594 Conv
::X86Stdcall
=> ("_", "@"),
595 Conv
::X86VectorCall
=> ("", "@@"),
598 undecorated
.insert(0, '_'
);
604 let args_in_bytes
: u64 = args
606 .map(|abi
| abi
.layout
.size
.bytes().next_multiple_of(target
.pointer_width
as u64 / 8))
608 format
!("{prefix}{undecorated}{suffix}{args_in_bytes}")
611 fn wasm_import_module_map(tcx
: TyCtxt
<'_
>, cnum
: CrateNum
) -> FxHashMap
<DefId
, String
> {
612 // Build up a map from DefId to a `NativeLib` structure, where
613 // `NativeLib` internally contains information about
614 // `#[link(wasm_import_module = "...")]` for example.
615 let native_libs
= tcx
.native_libraries(cnum
);
617 let def_id_to_native_lib
= native_libs
619 .filter_map(|lib
| lib
.foreign_module
.map(|id
| (id
, lib
)))
620 .collect
::<FxHashMap
<_
, _
>>();
622 let mut ret
= FxHashMap
::default();
623 for (def_id
, lib
) in tcx
.foreign_modules(cnum
).iter() {
624 let module
= def_id_to_native_lib
.get(&def_id
).and_then(|s
| s
.wasm_import_module());
625 let Some(module
) = module
else { continue }
;
626 ret
.extend(lib
.foreign_items
.iter().map(|id
| {
627 assert_eq
!(id
.krate
, cnum
);
628 (*id
, module
.to_string())