Skip to main content

rustc_codegen_ssa/back/
symbol_export.rs

1use std::collections::hash_map::Entry::*;
2
3use rustc_abi::{CanonAbi, X86Call};
4use rustc_ast::expand::allocator::{AllocatorKind, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
5use rustc_data_structures::unord::UnordMap;
6use rustc_hir as hir;
7use rustc_hir::def::DefKind;
8use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
9use rustc_middle::bug;
10use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
11use rustc_middle::middle::exported_symbols::{
12    ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
13};
14use rustc_middle::query::LocalCrate;
15use rustc_middle::ty::{
16    self, GenericArgKind, GenericArgsRef, Instance, ShimKind, SymbolName, Ty, TyCtxt,
17};
18use rustc_middle::util::Providers;
19use rustc_session::config::CrateType;
20use rustc_span::Span;
21use rustc_symbol_mangling::mangle_internal_symbol;
22use rustc_target::spec::{Arch, Os, TlsModel};
23use tracing::debug;
24
25use crate::SymbolExport;
26use crate::back::symbol_export;
27use crate::base::allocator_shim_contents;
28
29fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
30    crates_export_threshold(tcx.crate_types())
31}
32
33fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
34    match crate_type {
35        CrateType::Executable | CrateType::StaticLib | CrateType::ProcMacro | CrateType::Cdylib => {
36            SymbolExportLevel::C
37        }
38        CrateType::Rlib | CrateType::Dylib | CrateType::Sdylib => SymbolExportLevel::Rust,
39    }
40}
41
42pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel {
43    if crate_types
44        .iter()
45        .any(|&crate_type| crate_export_threshold(crate_type) == SymbolExportLevel::Rust)
46    {
47        SymbolExportLevel::Rust
48    } else {
49        SymbolExportLevel::C
50    }
51}
52
53fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<SymbolExportInfo> {
54    if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
55        return Default::default();
56    }
57
58    reachable_non_generics_helper(tcx)
59}
60
61/// Exposed separately *without* the "should codegen" check so Miri can access it.
62pub fn reachable_non_generics_helper(tcx: TyCtxt<'_>) -> DefIdMap<SymbolExportInfo> {
63    let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE);
64
65    let mut reachable_non_generics: DefIdMap<_> = tcx
66        .reachable_set(())
67        .items()
68        .filter_map(|&def_id| {
69            // We want to ignore some FFI functions that are not exposed from
70            // this crate. Reachable FFI functions can be lumped into two
71            // categories:
72            //
73            // 1. Those that are included statically via a static library
74            // 2. Those included otherwise (e.g., dynamically or via a framework)
75            //
76            // Although our LLVM module is not literally emitting code for the
77            // statically included symbols, it's an export of our library which
78            // needs to be passed on to the linker and encoded in the metadata.
79            //
80            // As a result, if this id is an FFI item (foreign item) then we only
81            // let it through if it's included statically.
82            if let Some(parent_id) = tcx.opt_local_parent(def_id)
83                && let DefKind::ForeignMod = tcx.def_kind(parent_id)
84            {
85                let library = tcx.native_library(def_id)?;
86                return library.kind.is_statically_included().then_some(def_id);
87            }
88
89            // Only consider nodes that actually have exported symbols.
90            match tcx.def_kind(def_id) {
91                DefKind::Fn | DefKind::AssocFn
92                    if tcx.constness(def_id) == hir::Constness::Const { always: true } =>
93                {
94                    return None;
95                }
96                DefKind::Fn | DefKind::Static { .. } => {}
97                DefKind::AssocFn if tcx.impl_of_assoc(def_id.to_def_id()).is_some() => {}
98                _ => return None,
99            };
100
101            let generics = tcx.generics_of(def_id);
102            if generics.requires_monomorphization(tcx) {
103                return None;
104            }
105
106            if Instance::mono(tcx, def_id.into()).def.requires_inline(tcx) {
107                return None;
108            }
109
110            if tcx.cross_crate_inlinable(def_id) { None } else { Some(def_id) }
111        })
112        .map(|def_id| {
113            let export_level = if is_compiler_builtins {
114                // We don't want to export compiler-builtins symbols from any
115                // dylibs, even rust dylibs. Unlike all other crates it gets
116                // duplicated in every linker invocation and it may otherwise
117                // unintentionally override definitions of these symbols by
118                // libgcc or compiler-rt for C code.
119                SymbolExportLevel::Rust
120            } else {
121                symbol_export_level(tcx, def_id.to_def_id())
122            };
123            let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
124            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/symbol_export.rs:124",
                        "rustc_codegen_ssa::back::symbol_export",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/symbol_export.rs"),
                        ::tracing_core::__macro_support::Option::Some(124u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::symbol_export"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("EXPORTED SYMBOL (local): {0} ({1:?})",
                                                    tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
                                                    export_level) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
125                "EXPORTED SYMBOL (local): {} ({:?})",
126                tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
127                export_level
128            );
129            let info = SymbolExportInfo {
130                level: export_level,
131                kind: if tcx.is_static(def_id.to_def_id()) {
132                    if codegen_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
133                        SymbolExportKind::Tls
134                    } else {
135                        SymbolExportKind::Data
136                    }
137                } else {
138                    SymbolExportKind::Text
139                },
140                used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
141                    || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER),
142                rustc_std_internal_symbol: codegen_attrs
143                    .flags
144                    .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
145                    || codegen_attrs
146                        .flags
147                        .contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM),
148            };
149            (def_id.to_def_id(), info)
150        })
151        .into();
152
153    if let Some(id) = tcx.proc_macro_decls_static(()) {
154        reachable_non_generics.insert(
155            id.to_def_id(),
156            SymbolExportInfo {
157                level: SymbolExportLevel::C,
158                kind: SymbolExportKind::Data,
159                used: false,
160                rustc_std_internal_symbol: false,
161            },
162        );
163    }
164
165    reachable_non_generics
166}
167
168fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
169    let export_threshold = threshold(tcx);
170
171    if let Some(&info) = tcx.reachable_non_generics(LOCAL_CRATE).get(&def_id.to_def_id()) {
172        info.level.is_below_threshold(export_threshold)
173    } else {
174        false
175    }
176}
177
178fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
179    tcx.reachable_non_generics(def_id.krate).contains_key(&def_id)
180}
181
182fn exported_non_generic_symbols_provider_local<'tcx>(
183    tcx: TyCtxt<'tcx>,
184    _: LocalCrate,
185) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
186    if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
187        return &[];
188    }
189
190    exported_non_generic_symbols_helper(tcx)
191}
192
193/// Exposed separately *without* the "should codegen" check so Miri can access it.
194pub fn exported_non_generic_symbols_helper<'tcx>(
195    tcx: TyCtxt<'tcx>,
196) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
197    // FIXME: Sorting this is unnecessary since we are sorting later anyway.
198    //        Can we skip the later sorting?
199    let sorted = tcx.with_stable_hashing_context(|mut hcx| {
200        tcx.reachable_non_generics(LOCAL_CRATE).to_sorted(&mut hcx, true)
201    });
202
203    let mut symbols: Vec<_> =
204        sorted.iter().map(|&(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)).collect();
205
206    // Export TLS shims
207    if !tcx.sess.target.dll_tls_export {
208        symbols.extend(sorted.iter().filter_map(|&(&def_id, &info)| {
209            tcx.needs_thread_local_shim(def_id).then(|| {
210                (
211                    ExportedSymbol::ThreadLocalShim(def_id),
212                    SymbolExportInfo {
213                        level: info.level,
214                        kind: SymbolExportKind::Text,
215                        used: info.used,
216                        rustc_std_internal_symbol: info.rustc_std_internal_symbol,
217                    },
218                )
219            })
220        }))
221    }
222
223    symbols.extend(sorted.iter().flat_map(|&(&def_id, &info)| {
224        tcx.codegen_fn_attrs(def_id).foreign_item_symbol_aliases.iter().map(
225            move |&(foreign_item, _linkage, _visibility)| {
226                (ExportedSymbol::NonGeneric(foreign_item), info)
227            },
228        )
229    }));
230
231    if tcx.entry_fn(()).is_some() {
232        let exported_symbol =
233            ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
234
235        symbols.push((
236            exported_symbol,
237            SymbolExportInfo {
238                level: SymbolExportLevel::C,
239                kind: SymbolExportKind::Text,
240                used: false,
241                rustc_std_internal_symbol: false,
242            },
243        ));
244    }
245
246    // Sort so we get a stable incr. comp. hash.
247    symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx));
248
249    tcx.arena.alloc_from_iter(symbols)
250}
251
252fn exported_generic_symbols_provider_local<'tcx>(
253    tcx: TyCtxt<'tcx>,
254    _: LocalCrate,
255) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
256    if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() {
257        return &[];
258    }
259
260    let mut symbols: Vec<_> = ::alloc::vec::Vec::new()vec![];
261
262    if tcx.local_crate_exports_generics() {
263        use rustc_hir::attrs::Linkage;
264        use rustc_middle::mono::{MonoItem, Visibility};
265        use rustc_middle::ty::InstanceKind;
266
267        // Normally, we require that shared monomorphizations are not hidden,
268        // because if we want to re-use a monomorphization from a Rust dylib, it
269        // needs to be exported.
270        // However, on platforms that don't allow for Rust dylibs, having
271        // external linkage is enough for monomorphization to be linked to.
272        let need_visibility = tcx.sess.target.dynamic_linking && !tcx.sess.target.only_cdylib;
273
274        let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
275
276        // Do not export symbols that cannot be instantiated by downstream crates.
277        let reachable_set = tcx.reachable_set(());
278        let is_local_to_current_crate = |ty: Ty<'_>| {
279            let no_refs = ty.peel_refs();
280            let root_def_id = match no_refs.kind() {
281                ty::Closure(closure, _) => *closure,
282                ty::FnDef(def_id, _) => *def_id,
283                ty::Coroutine(def_id, _) => *def_id,
284                ty::CoroutineClosure(def_id, _) => *def_id,
285                ty::CoroutineWitness(def_id, _) => *def_id,
286                _ => return false,
287            };
288            let Some(root_def_id) = root_def_id.as_local() else {
289                return false;
290            };
291
292            let is_local = !reachable_set.contains(&root_def_id);
293            is_local
294        };
295
296        let is_instantiable_downstream =
297            |did: Option<DefId>, generic_args: GenericArgsRef<'tcx>| {
298                generic_args
299                    .types()
300                    .chain(did.into_iter().map(move |did| tcx.type_of(did).skip_binder()))
301                    .all(move |arg| {
302                        arg.walk().all(|ty| {
303                            ty.as_type().map_or(true, |ty| !is_local_to_current_crate(ty))
304                        })
305                    })
306            };
307
308        // The symbols created in this loop are sorted below it
309        #[allow(rustc::potential_query_instability)]
310        for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) {
311            if data.linkage != Linkage::External {
312                // We can only re-use things with external linkage, otherwise
313                // we'll get a linker error
314                continue;
315            }
316
317            if need_visibility && data.visibility == Visibility::Hidden {
318                // If we potentially share things from Rust dylibs, they must
319                // not be hidden
320                continue;
321            }
322
323            if !tcx.sess.opts.share_generics() {
324                if tcx.codegen_fn_attrs(mono_item.def_id()).inline
325                    == rustc_hir::attrs::InlineAttr::Never
326                {
327                    // this is OK, we explicitly allow sharing inline(never) across crates even
328                    // without share-generics.
329                } else {
330                    continue;
331                }
332            }
333
334            // Note: These all set rustc_std_internal_symbol to false as generic functions must not
335            // be marked with this attribute and we are only handling generic functions here.
336            match *mono_item {
337                MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => {
338                    let has_generics = args.non_erasable_generics().next().is_some();
339
340                    let should_export =
341                        has_generics && is_instantiable_downstream(Some(def), &args);
342
343                    if should_export {
344                        let symbol = ExportedSymbol::Generic(def, args);
345                        symbols.push((
346                            symbol,
347                            SymbolExportInfo {
348                                level: SymbolExportLevel::Rust,
349                                kind: SymbolExportKind::Text,
350                                used: false,
351                                rustc_std_internal_symbol: false,
352                            },
353                        ));
354                    }
355                }
356                MonoItem::Fn(Instance {
357                    def: InstanceKind::Shim(ShimKind::DropGlue(_, Some(ty))),
358                    args,
359                }) => {
360                    // A little sanity-check
361                    {
    match (&args.non_erasable_generics().next(),
            &Some(GenericArgKind::Type(ty))) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty)));
362
363                    // Drop glue did is always going to be non-local outside of libcore, thus we don't need to check it's locality (which includes invoking `type_of` query).
364                    let should_export = match ty.kind() {
365                        ty::Adt(_, args) => is_instantiable_downstream(None, args),
366                        ty::Closure(_, args) => is_instantiable_downstream(None, args),
367                        _ => true,
368                    };
369
370                    if should_export {
371                        symbols.push((
372                            ExportedSymbol::DropGlue(ty),
373                            SymbolExportInfo {
374                                level: SymbolExportLevel::Rust,
375                                kind: SymbolExportKind::Text,
376                                used: false,
377                                rustc_std_internal_symbol: false,
378                            },
379                        ));
380                    }
381                }
382                MonoItem::Fn(Instance {
383                    def: InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(_, ty)),
384                    args,
385                }) => {
386                    // A little sanity-check
387                    {
    match (&args.non_erasable_generics().next(),
            &Some(GenericArgKind::Type(ty))) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty)));
388                    symbols.push((
389                        ExportedSymbol::AsyncDropGlueCtorShim(ty),
390                        SymbolExportInfo {
391                            level: SymbolExportLevel::Rust,
392                            kind: SymbolExportKind::Text,
393                            used: false,
394                            rustc_std_internal_symbol: false,
395                        },
396                    ));
397                }
398                MonoItem::Fn(Instance {
399                    def: InstanceKind::Shim(ShimKind::AsyncDropGlue(def, ty)),
400                    args: _,
401                }) => {
402                    symbols.push((
403                        ExportedSymbol::AsyncDropGlue(def, ty),
404                        SymbolExportInfo {
405                            level: SymbolExportLevel::Rust,
406                            kind: SymbolExportKind::Text,
407                            used: false,
408                            rustc_std_internal_symbol: false,
409                        },
410                    ));
411                }
412                _ => {
413                    // Any other symbols don't qualify for sharing
414                }
415            }
416        }
417    }
418
419    // Sort so we get a stable incr. comp. hash.
420    symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx));
421
422    tcx.arena.alloc_from_iter(symbols)
423}
424
425fn upstream_monomorphizations_provider(
426    tcx: TyCtxt<'_>,
427    (): (),
428) -> DefIdMap<UnordMap<GenericArgsRef<'_>, CrateNum>> {
429    let cnums = tcx.crates(());
430
431    let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
432
433    let drop_glue_fn_def_id = tcx.lang_items().drop_glue_fn();
434    let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
435
436    for &cnum in cnums.iter() {
437        for (exported_symbol, _) in tcx.exported_generic_symbols(cnum).iter() {
438            let (def_id, args) = match *exported_symbol {
439                ExportedSymbol::Generic(def_id, args) => (def_id, args),
440                ExportedSymbol::DropGlue(ty) => {
441                    if let Some(drop_in_place_fn_def_id) = drop_glue_fn_def_id {
442                        (drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
443                    } else {
444                        // `drop_glue` does not exist, don't try to use it.
445                        continue;
446                    }
447                }
448                ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
449                    if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
450                        (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
451                    } else {
452                        continue;
453                    }
454                }
455                ExportedSymbol::AsyncDropGlue(def_id, ty) => (def_id, tcx.mk_args(&[ty.into()])),
456                ExportedSymbol::NonGeneric(..)
457                | ExportedSymbol::ThreadLocalShim(..)
458                | ExportedSymbol::NoDefId(..) => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("{0:?}", exported_symbol)));
}unreachable!("{exported_symbol:?}"),
459            };
460
461            let args_map = instances.entry(def_id).or_default();
462
463            match args_map.entry(args) {
464                Occupied(mut e) => {
465                    // If there are multiple monomorphizations available,
466                    // we select one deterministically.
467                    let other_cnum = *e.get();
468                    if tcx.stable_crate_id(other_cnum) > tcx.stable_crate_id(cnum) {
469                        e.insert(cnum);
470                    }
471                }
472                Vacant(e) => {
473                    e.insert(cnum);
474                }
475            }
476        }
477    }
478
479    instances
480}
481
482fn upstream_monomorphizations_for_provider(
483    tcx: TyCtxt<'_>,
484    def_id: DefId,
485) -> Option<&UnordMap<GenericArgsRef<'_>, CrateNum>> {
486    if !!def_id.is_local() {
    ::core::panicking::panic("assertion failed: !def_id.is_local()")
};assert!(!def_id.is_local());
487    tcx.upstream_monomorphizations(()).get(&def_id)
488}
489
490fn upstream_drop_glue_for_provider<'tcx>(
491    tcx: TyCtxt<'tcx>,
492    args: GenericArgsRef<'tcx>,
493) -> Option<CrateNum> {
494    let def_id = tcx.lang_items().drop_glue_fn()?;
495    tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
496}
497
498fn upstream_async_drop_glue_for_provider<'tcx>(
499    tcx: TyCtxt<'tcx>,
500    args: GenericArgsRef<'tcx>,
501) -> Option<CrateNum> {
502    let def_id = tcx.lang_items().async_drop_in_place_fn()?;
503    tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
504}
505
506fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
507    !tcx.reachable_set(()).contains(&def_id)
508}
509
510pub(crate) fn provide(providers: &mut Providers) {
511    providers.queries.reachable_non_generics = reachable_non_generics_provider;
512    providers.queries.is_reachable_non_generic = is_reachable_non_generic_provider_local;
513    providers.queries.exported_non_generic_symbols = exported_non_generic_symbols_provider_local;
514    providers.queries.exported_generic_symbols = exported_generic_symbols_provider_local;
515    providers.queries.upstream_monomorphizations = upstream_monomorphizations_provider;
516    providers.queries.is_unreachable_local_definition = is_unreachable_local_definition_provider;
517    providers.queries.upstream_drop_glue_for = upstream_drop_glue_for_provider;
518    providers.queries.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider;
519    providers.queries.wasm_import_module_map = wasm_import_module_map;
520    providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
521    providers.extern_queries.upstream_monomorphizations_for =
522        upstream_monomorphizations_for_provider;
523}
524
525pub(crate) fn allocator_shim_symbols(
526    tcx: TyCtxt<'_>,
527    kind: AllocatorKind,
528) -> impl Iterator<Item = (String, SymbolExportKind)> {
529    allocator_shim_contents(tcx, kind)
530        .into_iter()
531        .map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
532        .chain([mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE)])
533        .map(move |symbol_name| {
534            let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
535
536            (
537                symbol_export::exporting_symbol_name_for_instance_in_crate(
538                    tcx,
539                    exported_symbol,
540                    LOCAL_CRATE,
541                ),
542                SymbolExportKind::Text,
543            )
544        })
545}
546
547fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel {
548    // We export anything that's not mangled at the "C" layer as it probably has
549    // to do with ABI concerns. We do not, however, apply such treatment to
550    // special symbols in the standard library for various plumbing between
551    // core/std/allocators/etc. For example symbols used to hook up allocation
552    // are not considered for export
553    let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id);
554    let is_extern = codegen_fn_attrs.contains_extern_indicator();
555    let std_internal =
556        codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
557    let eii = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
558
559    if is_extern && !std_internal && !eii {
560        let target = &tcx.sess.target.llvm_target;
561        // WebAssembly cannot export data symbols, so reduce their export level
562        // FIXME(jdonszelmann) don't do a substring match here.
563        if target.contains("emscripten") {
564            if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
565                return SymbolExportLevel::Rust;
566            }
567        }
568
569        SymbolExportLevel::C
570    } else {
571        SymbolExportLevel::Rust
572    }
573}
574
575/// This is the symbol name of the given instance instantiated in a specific crate.
576pub(crate) fn symbol_name_for_instance_in_crate<'tcx>(
577    tcx: TyCtxt<'tcx>,
578    symbol: ExportedSymbol<'tcx>,
579    instantiating_crate: CrateNum,
580) -> String {
581    // If this is something instantiated in the local crate then we might
582    // already have cached the name as a query result.
583    if instantiating_crate == LOCAL_CRATE {
584        return symbol.symbol_name_for_local_instance(tcx).to_string();
585    }
586
587    // This is something instantiated in an upstream crate, so we have to use
588    // the slower (because uncached) version of computing the symbol name.
589    match symbol {
590        ExportedSymbol::NonGeneric(def_id) => {
591            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
592                tcx,
593                Instance::mono(tcx, def_id),
594                instantiating_crate,
595            )
596        }
597        ExportedSymbol::Generic(def_id, args) => {
598            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
599                tcx,
600                Instance::new_raw(def_id, args),
601                instantiating_crate,
602            )
603        }
604        ExportedSymbol::ThreadLocalShim(def_id) => {
605            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
606                tcx,
607                ty::Instance {
608                    def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)),
609                    args: ty::GenericArgs::empty(),
610                },
611                instantiating_crate,
612            )
613        }
614        ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate(
615            tcx,
616            Instance::resolve_drop_glue(tcx, ty),
617            instantiating_crate,
618        ),
619        ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
620            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
621                tcx,
622                Instance::resolve_async_drop_in_place(tcx, ty),
623                instantiating_crate,
624            )
625        }
626        ExportedSymbol::AsyncDropGlue(def_id, ty) => {
627            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
628                tcx,
629                Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty),
630                instantiating_crate,
631            )
632        }
633        ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
634    }
635}
636
637fn calling_convention_for_symbol<'tcx>(
638    tcx: TyCtxt<'tcx>,
639    symbol: ExportedSymbol<'tcx>,
640) -> (CanonAbi, &'tcx [rustc_target::callconv::ArgAbi<'tcx, Ty<'tcx>>]) {
641    let instance = match symbol {
642        ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
643            if tcx.is_static(def_id) =>
644        {
645            None
646        }
647        ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)),
648        ExportedSymbol::Generic(def_id, args) => Some(Instance::new_raw(def_id, args)),
649        // DropGlue always use the Rust calling convention and thus follow the target's default
650        // symbol decoration scheme.
651        ExportedSymbol::DropGlue(..) => None,
652        // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the
653        // target's default symbol decoration scheme.
654        ExportedSymbol::AsyncDropGlueCtorShim(..) => None,
655        ExportedSymbol::AsyncDropGlue(..) => None,
656        // NoDefId always follow the target's default symbol decoration scheme.
657        ExportedSymbol::NoDefId(..) => None,
658        // ThreadLocalShim always follow the target's default symbol decoration scheme.
659        ExportedSymbol::ThreadLocalShim(..) => None,
660    };
661
662    instance
663        .map(|i| {
664            tcx.fn_abi_of_instance(
665                ty::TypingEnv::fully_monomorphized().as_query_input((i, ty::List::empty())),
666            )
667            .unwrap_or_else(|_| ::rustc_middle::util::bug::bug_fmt(format_args!("fn_abi_of_instance({0:?}) failed",
        i))bug!("fn_abi_of_instance({i:?}) failed"))
668        })
669        .map(|fnabi| (fnabi.conv, &fnabi.args[..]))
670        // FIXME(workingjubilee): why don't we know the convention here?
671        .unwrap_or((CanonAbi::Rust, &[]))
672}
673
674/// This is the symbol name of the given instance as seen by the linker.
675///
676/// On 32-bit Windows symbols are decorated according to their calling conventions.
677pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
678    tcx: TyCtxt<'tcx>,
679    symbol: ExportedSymbol<'tcx>,
680    export_kind: SymbolExportKind,
681    instantiating_crate: CrateNum,
682) -> String {
683    let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
684
685    // thread local will not be a function call,
686    // so it is safe to return before windows symbol decoration check.
687    if let Some(name) = maybe_emutls_symbol_name(tcx, symbol, &undecorated) {
688        return name;
689    }
690
691    let target = &tcx.sess.target;
692    if !target.is_like_windows {
693        // Mach-O has a global "_" suffix and `object` crate will handle it.
694        // ELF does not have any symbol decorations.
695        return undecorated;
696    }
697
698    let prefix = match target.arch {
699        Arch::X86 => Some('_'),
700        Arch::X86_64 => None,
701        // Only functions are decorated for arm64ec.
702        Arch::Arm64EC if export_kind == SymbolExportKind::Text => Some('#'),
703        // Only x86/64 and arm64ec use symbol decorations.
704        _ => return undecorated,
705    };
706
707    let (callconv, args) = calling_convention_for_symbol(tcx, symbol);
708
709    // Decorate symbols with prefixes, suffixes and total number of bytes of arguments.
710    // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
711    let (prefix, suffix) = match callconv {
712        CanonAbi::X86(X86Call::Fastcall) => ("@", "@"),
713        CanonAbi::X86(X86Call::Stdcall) => ("_", "@"),
714        CanonAbi::X86(X86Call::Vectorcall) => ("", "@@"),
715        _ => {
716            if let Some(prefix) = prefix {
717                undecorated.insert(0, prefix);
718            }
719            return undecorated;
720        }
721    };
722
723    let args_in_bytes: u64 = args
724        .iter()
725        .map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8))
726        .sum();
727    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}{3}", prefix, undecorated,
                suffix, args_in_bytes))
    })format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
728}
729
730pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>(
731    tcx: TyCtxt<'tcx>,
732    symbol: ExportedSymbol<'tcx>,
733    cnum: CrateNum,
734) -> String {
735    let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, cnum);
736    maybe_emutls_symbol_name(tcx, symbol, &undecorated).unwrap_or(undecorated)
737}
738
739/// On amdhsa, `gpu-kernel` functions have an associated metadata object with a `.kd` suffix.
740/// Add it to the symbols list for all kernel functions, so that it is exported in the linked
741/// object.
742pub(crate) fn extend_exported_symbols<'tcx>(
743    symbols: &mut Vec<SymbolExport>,
744    tcx: TyCtxt<'tcx>,
745    symbol: ExportedSymbol<'tcx>,
746    instantiating_crate: CrateNum,
747) {
748    let (callconv, _) = calling_convention_for_symbol(tcx, symbol);
749
750    if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != Os::AmdHsa {
751        return;
752    }
753
754    let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
755
756    // Add the symbol for the kernel descriptor (with .kd suffix)
757    // Per https://llvm.org/docs/AMDGPUUsage.html#symbols these will always be `STT_OBJECT` so
758    // export as data.
759    symbols.push(SymbolExport::new(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}.kd", undecorated))
    })format!("{undecorated}.kd"), SymbolExportKind::Data));
760}
761
762fn maybe_emutls_symbol_name<'tcx>(
763    tcx: TyCtxt<'tcx>,
764    symbol: ExportedSymbol<'tcx>,
765    undecorated: &str,
766) -> Option<String> {
767    if #[allow(non_exhaustive_omitted_patterns)] match tcx.sess.tls_model() {
    TlsModel::Emulated => true,
    _ => false,
}matches!(tcx.sess.tls_model(), TlsModel::Emulated)
768        && let ExportedSymbol::NonGeneric(def_id) = symbol
769        && tcx.is_thread_local_static(def_id)
770    {
771        // When using emutls, LLVM will add the `__emutls_v.` prefix to thread local symbols,
772        // and exported symbol name need to match this.
773        Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("__emutls_v.{0}", undecorated))
    })format!("__emutls_v.{undecorated}"))
774    } else {
775        None
776    }
777}
778
779fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<String> {
780    // Build up a map from DefId to a `NativeLib` structure, where
781    // `NativeLib` internally contains information about
782    // `#[link(wasm_import_module = "...")]` for example.
783    let native_libs = tcx.native_libraries(cnum);
784
785    let def_id_to_native_lib = native_libs
786        .iter()
787        .filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
788        .collect::<DefIdMap<_>>();
789
790    let mut ret = DefIdMap::default();
791    for (def_id, lib) in tcx.foreign_modules(cnum).iter() {
792        let module = def_id_to_native_lib.get(def_id).and_then(|s| s.wasm_import_module());
793        let Some(module) = module else { continue };
794        ret.extend(lib.foreign_items.iter().map(|id| {
795            {
    match (&id.krate, &cnum) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(id.krate, cnum);
796            (*id, module.to_string())
797        }));
798    }
799
800    ret
801}
802
803pub fn escape_symbol_name(tcx: TyCtxt<'_>, symbol: &str, span: Span) -> String {
804    // https://github.com/llvm/llvm-project/blob/a55fbab0cffc9b4af497b9e4f187b61143743e06/llvm/lib/MC/MCSymbol.cpp
805    use rustc_target::spec::{Arch, BinaryFormat};
806    if !symbol.is_empty()
807        && symbol.chars().all(|c| #[allow(non_exhaustive_omitted_patterns)] match c {
    '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '$' | '.' => true,
    _ => false,
}matches!(c, '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '$' | '.'))
808    {
809        return symbol.to_string();
810    }
811    if tcx.sess.target.binary_format == BinaryFormat::Xcoff {
812        tcx.sess.dcx().span_fatal(
813            span,
814            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("symbol escaping is not supported for the binary format {0}",
                tcx.sess.target.binary_format))
    })format!(
815                "symbol escaping is not supported for the binary format {}",
816                tcx.sess.target.binary_format
817            ),
818        );
819    }
820    if tcx.sess.target.arch == Arch::Nvptx64 {
821        tcx.sess.dcx().span_fatal(
822            span,
823            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("symbol escaping is not supported for the architecture {0}",
                tcx.sess.target.arch))
    })format!(
824                "symbol escaping is not supported for the architecture {}",
825                tcx.sess.target.arch
826            ),
827        );
828    }
829    let mut escaped_symbol = String::new();
830    escaped_symbol.push('\"');
831    for c in symbol.chars() {
832        match c {
833            '\n' => escaped_symbol.push_str("\\\n"),
834            '"' => escaped_symbol.push_str("\\\""),
835            '\\' => escaped_symbol.push_str("\\\\"),
836            c => escaped_symbol.push(c),
837        }
838    }
839    escaped_symbol.push('\"');
840    escaped_symbol
841}