rustc_metadata/
native_libs.rs

1use std::ops::ControlFlow;
2use std::path::{Path, PathBuf};
3
4use rustc_abi::ExternAbi;
5use rustc_ast::CRATE_NODE_ID;
6use rustc_attr_parsing as attr;
7use rustc_data_structures::fx::FxHashSet;
8use rustc_hir::attrs::AttributeKind;
9use rustc_hir::find_attr;
10use rustc_middle::query::LocalCrate;
11use rustc_middle::ty::{self, List, Ty, TyCtxt};
12use rustc_session::Session;
13use rustc_session::config::CrateType;
14use rustc_session::cstore::{
15    DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
16};
17use rustc_session::parse::feature_err;
18use rustc_session::search_paths::PathKind;
19use rustc_session::utils::NativeLibKind;
20use rustc_span::def_id::{DefId, LOCAL_CRATE};
21use rustc_span::{Symbol, sym};
22use rustc_target::spec::{BinaryFormat, LinkSelfContainedComponents};
23
24use crate::{errors, fluent_generated};
25
26/// The fallback directories are passed to linker, but not used when rustc does the search,
27/// because in the latter case the set of fallback directories cannot always be determined
28/// consistently at the moment.
29pub struct NativeLibSearchFallback<'a> {
30    pub self_contained_components: LinkSelfContainedComponents,
31    pub apple_sdk_root: Option<&'a Path>,
32}
33
34pub fn walk_native_lib_search_dirs<R>(
35    sess: &Session,
36    fallback: Option<NativeLibSearchFallback<'_>>,
37    mut f: impl FnMut(&Path, bool /*is_framework*/) -> ControlFlow<R>,
38) -> ControlFlow<R> {
39    // Library search paths explicitly supplied by user (`-L` on the command line).
40    for search_path in sess.target_filesearch().cli_search_paths(PathKind::Native) {
41        f(&search_path.dir, false)?;
42    }
43    for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) {
44        // Frameworks are looked up strictly in framework-specific paths.
45        if search_path.kind != PathKind::All {
46            f(&search_path.dir, true)?;
47        }
48    }
49
50    let Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }) = fallback
51    else {
52        return ControlFlow::Continue(());
53    };
54
55    // The toolchain ships some native library components and self-contained linking was enabled.
56    // Add the self-contained library directory to search paths.
57    if self_contained_components.intersects(
58        LinkSelfContainedComponents::LIBC
59            | LinkSelfContainedComponents::UNWIND
60            | LinkSelfContainedComponents::MINGW,
61    ) {
62        f(&sess.target_tlib_path.dir.join("self-contained"), false)?;
63    }
64
65    // Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot
66    // library directory instead of the self-contained directories.
67    // Sanitizer libraries have the same issue and are also linked by name on Apple targets.
68    // The targets here should be in sync with `copy_third_party_objects` in bootstrap.
69    // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind
70    // and sanitizers to self-contained directory, and stop adding this search path.
71    // FIXME: On AIX this also has the side-effect of making the list of library search paths
72    // non-empty, which is needed or the linker may decide to record the LIBPATH env, if
73    // defined, as the search path instead of appending the default search paths.
74    if sess.target.vendor == "fortanix"
75        || sess.target.os == "linux"
76        || sess.target.os == "fuchsia"
77        || sess.target.is_like_aix
78        || sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty()
79    {
80        f(&sess.target_tlib_path.dir, false)?;
81    }
82
83    // Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
84    // we must have the support library stubs in the library search path (#121430).
85    if let Some(sdk_root) = apple_sdk_root
86        && sess.target.env == "macabi"
87    {
88        f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
89        f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
90    }
91
92    ControlFlow::Continue(())
93}
94
95pub fn try_find_native_static_library(
96    sess: &Session,
97    name: &str,
98    verbatim: bool,
99) -> Option<PathBuf> {
100    let default = sess.staticlib_components(verbatim);
101    let formats = if verbatim {
102        vec![default]
103    } else {
104        // On Windows, static libraries sometimes show up as libfoo.a and other
105        // times show up as foo.lib
106        let unix = ("lib", ".a");
107        if default == unix { vec![default] } else { vec![default, unix] }
108    };
109
110    walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
111        if !is_framework {
112            for (prefix, suffix) in &formats {
113                let test = dir.join(format!("{prefix}{name}{suffix}"));
114                if test.exists() {
115                    return ControlFlow::Break(test);
116                }
117            }
118        }
119        ControlFlow::Continue(())
120    })
121    .break_value()
122}
123
124pub fn try_find_native_dynamic_library(
125    sess: &Session,
126    name: &str,
127    verbatim: bool,
128) -> Option<PathBuf> {
129    let default = sess.staticlib_components(verbatim);
130    let formats = if verbatim {
131        vec![default]
132    } else {
133        // While the official naming convention for MSVC import libraries
134        // is foo.lib, Meson follows the libfoo.dll.a convention to
135        // disambiguate .a for static libraries
136        let meson = ("lib", ".dll.a");
137        // and MinGW uses .a altogether
138        let mingw = ("lib", ".a");
139        vec![default, meson, mingw]
140    };
141
142    walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
143        if !is_framework {
144            for (prefix, suffix) in &formats {
145                let test = dir.join(format!("{prefix}{name}{suffix}"));
146                if test.exists() {
147                    return ControlFlow::Break(test);
148                }
149            }
150        }
151        ControlFlow::Continue(())
152    })
153    .break_value()
154}
155
156pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
157    try_find_native_static_library(sess, name, verbatim)
158        .unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
159}
160
161fn find_bundled_library(
162    name: Symbol,
163    verbatim: Option<bool>,
164    kind: NativeLibKind,
165    has_cfg: bool,
166    tcx: TyCtxt<'_>,
167) -> Option<Symbol> {
168    let sess = tcx.sess;
169    if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
170        && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
171        && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
172    {
173        let verbatim = verbatim.unwrap_or(false);
174        return find_native_static_library(name.as_str(), verbatim, sess)
175            .file_name()
176            .and_then(|s| s.to_str())
177            .map(Symbol::intern);
178    }
179    None
180}
181
182pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
183    let mut collector = Collector { tcx, libs: Vec::new() };
184    if tcx.sess.opts.unstable_opts.link_directives {
185        for module in tcx.foreign_modules(LOCAL_CRATE).values() {
186            collector.process_module(module);
187        }
188    }
189    collector.process_command_line();
190    collector.libs
191}
192
193pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
194    match lib.cfg {
195        Some(ref cfg) => attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None),
196        None => true,
197    }
198}
199
200struct Collector<'tcx> {
201    tcx: TyCtxt<'tcx>,
202    libs: Vec<NativeLib>,
203}
204
205impl<'tcx> Collector<'tcx> {
206    fn process_module(&mut self, module: &ForeignModule) {
207        let ForeignModule { def_id, abi, ref foreign_items } = *module;
208        let def_id = def_id.expect_local();
209
210        let sess = self.tcx.sess;
211
212        if matches!(abi, ExternAbi::Rust) {
213            return;
214        }
215
216        // Process all of the #[link(..)]-style arguments
217        let features = self.tcx.features();
218
219        for m in self.tcx.get_attrs(def_id, sym::link) {
220            let Some(items) = m.meta_item_list() else {
221                continue;
222            };
223
224            let mut name = None;
225            let mut kind = None;
226            let mut modifiers = None;
227            let mut cfg = None;
228            let mut wasm_import_module = None;
229            let mut import_name_type = None;
230            for item in items.iter() {
231                match item.name() {
232                    Some(sym::name) => {
233                        if name.is_some() {
234                            sess.dcx().emit_err(errors::MultipleNamesInLink { span: item.span() });
235                            continue;
236                        }
237                        let Some(link_name) = item.value_str() else {
238                            sess.dcx().emit_err(errors::LinkNameForm { span: item.span() });
239                            continue;
240                        };
241                        let span = item.name_value_literal_span().unwrap();
242                        if link_name.is_empty() {
243                            sess.dcx().emit_err(errors::EmptyLinkName { span });
244                        }
245                        name = Some((link_name, span));
246                    }
247                    Some(sym::kind) => {
248                        if kind.is_some() {
249                            sess.dcx().emit_err(errors::MultipleKindsInLink { span: item.span() });
250                            continue;
251                        }
252                        let Some(link_kind) = item.value_str() else {
253                            sess.dcx().emit_err(errors::LinkKindForm { span: item.span() });
254                            continue;
255                        };
256
257                        let span = item.name_value_literal_span().unwrap();
258                        let link_kind = match link_kind.as_str() {
259                            "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
260                            "dylib" => NativeLibKind::Dylib { as_needed: None },
261                            "framework" => {
262                                if !sess.target.is_like_darwin {
263                                    sess.dcx().emit_err(errors::LinkFrameworkApple { span });
264                                }
265                                NativeLibKind::Framework { as_needed: None }
266                            }
267                            "raw-dylib" => {
268                                if sess.target.is_like_windows {
269                                    // raw-dylib is stable and working on Windows
270                                } else if sess.target.binary_format == BinaryFormat::Elf
271                                    && features.raw_dylib_elf()
272                                {
273                                    // raw-dylib is unstable on ELF, but the user opted in
274                                } else if sess.target.binary_format == BinaryFormat::Elf
275                                    && sess.is_nightly_build()
276                                {
277                                    feature_err(
278                                        sess,
279                                        sym::raw_dylib_elf,
280                                        span,
281                                        fluent_generated::metadata_raw_dylib_elf_unstable,
282                                    )
283                                    .emit();
284                                } else {
285                                    sess.dcx().emit_err(errors::RawDylibOnlyWindows { span });
286                                }
287
288                                NativeLibKind::RawDylib
289                            }
290                            "link-arg" => {
291                                if !features.link_arg_attribute() {
292                                    feature_err(
293                                        sess,
294                                        sym::link_arg_attribute,
295                                        span,
296                                        fluent_generated::metadata_link_arg_unstable,
297                                    )
298                                    .emit();
299                                }
300                                NativeLibKind::LinkArg
301                            }
302                            kind => {
303                                sess.dcx().emit_err(errors::UnknownLinkKind { span, kind });
304                                continue;
305                            }
306                        };
307                        kind = Some(link_kind);
308                    }
309                    Some(sym::modifiers) => {
310                        if modifiers.is_some() {
311                            sess.dcx()
312                                .emit_err(errors::MultipleLinkModifiers { span: item.span() });
313                            continue;
314                        }
315                        let Some(link_modifiers) = item.value_str() else {
316                            sess.dcx().emit_err(errors::LinkModifiersForm { span: item.span() });
317                            continue;
318                        };
319                        modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
320                    }
321                    Some(sym::cfg) => {
322                        if cfg.is_some() {
323                            sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() });
324                            continue;
325                        }
326                        let Some(link_cfg) = item.meta_item_list() else {
327                            sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() });
328                            continue;
329                        };
330                        let [link_cfg] = link_cfg else {
331                            sess.dcx()
332                                .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
333                            continue;
334                        };
335                        let Some(link_cfg) = link_cfg.meta_item_or_bool() else {
336                            sess.dcx()
337                                .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
338                            continue;
339                        };
340                        if !features.link_cfg() {
341                            feature_err(
342                                sess,
343                                sym::link_cfg,
344                                item.span(),
345                                fluent_generated::metadata_link_cfg_unstable,
346                            )
347                            .emit();
348                        }
349                        cfg = Some(link_cfg.clone());
350                    }
351                    Some(sym::wasm_import_module) => {
352                        if wasm_import_module.is_some() {
353                            sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() });
354                            continue;
355                        }
356                        let Some(link_wasm_import_module) = item.value_str() else {
357                            sess.dcx().emit_err(errors::WasmImportForm { span: item.span() });
358                            continue;
359                        };
360                        wasm_import_module = Some((link_wasm_import_module, item.span()));
361                    }
362                    Some(sym::import_name_type) => {
363                        if import_name_type.is_some() {
364                            sess.dcx()
365                                .emit_err(errors::MultipleImportNameType { span: item.span() });
366                            continue;
367                        }
368                        let Some(link_import_name_type) = item.value_str() else {
369                            sess.dcx().emit_err(errors::ImportNameTypeForm { span: item.span() });
370                            continue;
371                        };
372                        if self.tcx.sess.target.arch != "x86" {
373                            sess.dcx().emit_err(errors::ImportNameTypeX86 { span: item.span() });
374                            continue;
375                        }
376
377                        let link_import_name_type = match link_import_name_type.as_str() {
378                            "decorated" => PeImportNameType::Decorated,
379                            "noprefix" => PeImportNameType::NoPrefix,
380                            "undecorated" => PeImportNameType::Undecorated,
381                            import_name_type => {
382                                sess.dcx().emit_err(errors::UnknownImportNameType {
383                                    span: item.span(),
384                                    import_name_type,
385                                });
386                                continue;
387                            }
388                        };
389                        import_name_type = Some((link_import_name_type, item.span()));
390                    }
391                    _ => {
392                        sess.dcx().emit_err(errors::UnexpectedLinkArg { span: item.span() });
393                    }
394                }
395            }
396
397            // Do this outside the above loop so we don't depend on modifiers coming after kinds
398            let mut verbatim = None;
399            if let Some((modifiers, span)) = modifiers {
400                for modifier in modifiers.as_str().split(',') {
401                    let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
402                        Some(m) => (m, modifier.starts_with('+')),
403                        None => {
404                            sess.dcx().emit_err(errors::InvalidLinkModifier { span });
405                            continue;
406                        }
407                    };
408
409                    macro report_unstable_modifier($feature: ident) {
410                        if !features.$feature() {
411                            // FIXME: make this translatable
412                            #[expect(rustc::untranslatable_diagnostic)]
413                            feature_err(
414                                sess,
415                                sym::$feature,
416                                span,
417                                format!("linking modifier `{modifier}` is unstable"),
418                            )
419                            .emit();
420                        }
421                    }
422                    let assign_modifier = |dst: &mut Option<bool>| {
423                        if dst.is_some() {
424                            sess.dcx().emit_err(errors::MultipleModifiers { span, modifier });
425                        } else {
426                            *dst = Some(value);
427                        }
428                    };
429                    match (modifier, &mut kind) {
430                        ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
431                            assign_modifier(bundle)
432                        }
433                        ("bundle", _) => {
434                            sess.dcx().emit_err(errors::BundleNeedsStatic { span });
435                        }
436
437                        ("verbatim", _) => assign_modifier(&mut verbatim),
438
439                        ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
440                            assign_modifier(whole_archive)
441                        }
442                        ("whole-archive", _) => {
443                            sess.dcx().emit_err(errors::WholeArchiveNeedsStatic { span });
444                        }
445
446                        ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
447                        | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
448                            report_unstable_modifier!(native_link_modifiers_as_needed);
449                            assign_modifier(as_needed)
450                        }
451                        ("as-needed", _) => {
452                            sess.dcx().emit_err(errors::AsNeededCompatibility { span });
453                        }
454
455                        _ => {
456                            sess.dcx().emit_err(errors::UnknownLinkModifier { span, modifier });
457                        }
458                    }
459                }
460            }
461
462            if let Some((_, span)) = wasm_import_module {
463                if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
464                    sess.dcx().emit_err(errors::IncompatibleWasmLink { span });
465                }
466            }
467
468            if wasm_import_module.is_some() {
469                (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
470            }
471            let Some((name, name_span)) = name else {
472                sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() });
473                continue;
474            };
475
476            // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
477            if let Some((_, span)) = import_name_type {
478                if kind != Some(NativeLibKind::RawDylib) {
479                    sess.dcx().emit_err(errors::ImportNameTypeRaw { span });
480                }
481            }
482
483            let dll_imports = match kind {
484                Some(NativeLibKind::RawDylib) => {
485                    if name.as_str().contains('\0') {
486                        sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
487                    }
488                    foreign_items
489                        .iter()
490                        .map(|&child_item| {
491                            self.build_dll_import(
492                                abi,
493                                import_name_type.map(|(import_name_type, _)| import_name_type),
494                                child_item,
495                            )
496                        })
497                        .collect()
498                }
499                _ => {
500                    for &child_item in foreign_items {
501                        if let Some(span) = find_attr!(self.tcx.get_all_attrs(child_item), AttributeKind::LinkOrdinal {span, ..} => *span)
502                        {
503                            sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span });
504                        }
505                    }
506
507                    Vec::new()
508                }
509            };
510
511            let kind = kind.unwrap_or(NativeLibKind::Unspecified);
512            let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
513            self.libs.push(NativeLib {
514                name,
515                filename,
516                kind,
517                cfg,
518                foreign_module: Some(def_id.to_def_id()),
519                verbatim,
520                dll_imports,
521            });
522        }
523    }
524
525    // Process libs passed on the command line
526    fn process_command_line(&mut self) {
527        // First, check for errors
528        let mut renames = FxHashSet::default();
529        for lib in &self.tcx.sess.opts.libs {
530            if let NativeLibKind::Framework { .. } = lib.kind
531                && !self.tcx.sess.target.is_like_darwin
532            {
533                // Cannot check this when parsing options because the target is not yet available.
534                self.tcx.dcx().emit_err(errors::LibFrameworkApple);
535            }
536            if let Some(ref new_name) = lib.new_name {
537                let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
538                if new_name.is_empty() {
539                    self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
540                } else if !any_duplicate {
541                    self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
542                } else if !renames.insert(&lib.name) {
543                    self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
544                }
545            }
546        }
547
548        // Update kind and, optionally, the name of all native libraries
549        // (there may be more than one) with the specified name. If any
550        // library is mentioned more than once, keep the latest mention
551        // of it, so that any possible dependent libraries appear before
552        // it. (This ensures that the linker is able to see symbols from
553        // all possible dependent libraries before linking in the library
554        // in question.)
555        for passed_lib in &self.tcx.sess.opts.libs {
556            // If we've already added any native libraries with the same
557            // name, they will be pulled out into `existing`, so that we
558            // can move them to the end of the list below.
559            let mut existing = self
560                .libs
561                .extract_if(.., |lib| {
562                    if lib.name.as_str() == passed_lib.name {
563                        // FIXME: This whole logic is questionable, whether modifiers are
564                        // involved or not, library reordering and kind overriding without
565                        // explicit `:rename` in particular.
566                        if lib.has_modifiers() || passed_lib.has_modifiers() {
567                            match lib.foreign_module {
568                                Some(def_id) => {
569                                    self.tcx.dcx().emit_err(errors::NoLinkModOverride {
570                                        span: Some(self.tcx.def_span(def_id)),
571                                    })
572                                }
573                                None => self
574                                    .tcx
575                                    .dcx()
576                                    .emit_err(errors::NoLinkModOverride { span: None }),
577                            };
578                        }
579                        if passed_lib.kind != NativeLibKind::Unspecified {
580                            lib.kind = passed_lib.kind;
581                        }
582                        if let Some(new_name) = &passed_lib.new_name {
583                            lib.name = Symbol::intern(new_name);
584                        }
585                        lib.verbatim = passed_lib.verbatim;
586                        return true;
587                    }
588                    false
589                })
590                .collect::<Vec<_>>();
591            if existing.is_empty() {
592                // Add if not found
593                let new_name: Option<&str> = passed_lib.new_name.as_deref();
594                let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
595                let filename = find_bundled_library(
596                    name,
597                    passed_lib.verbatim,
598                    passed_lib.kind,
599                    false,
600                    self.tcx,
601                );
602                self.libs.push(NativeLib {
603                    name,
604                    filename,
605                    kind: passed_lib.kind,
606                    cfg: None,
607                    foreign_module: None,
608                    verbatim: passed_lib.verbatim,
609                    dll_imports: Vec::new(),
610                });
611            } else {
612                // Move all existing libraries with the same name to the
613                // end of the command line.
614                self.libs.append(&mut existing);
615            }
616        }
617    }
618
619    fn i686_arg_list_size(&self, item: DefId) -> usize {
620        let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
621            self.tcx
622                .type_of(item)
623                .instantiate_identity()
624                .fn_sig(self.tcx)
625                .inputs()
626                .map_bound(|slice| self.tcx.mk_type_list(slice)),
627        );
628
629        argument_types
630            .iter()
631            .map(|ty| {
632                let layout = self
633                    .tcx
634                    .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
635                    .expect("layout")
636                    .layout;
637                // In both stdcall and fastcall, we always round up the argument size to the
638                // nearest multiple of 4 bytes.
639                (layout.size().bytes_usize() + 3) & !3
640            })
641            .sum()
642    }
643
644    fn build_dll_import(
645        &self,
646        abi: ExternAbi,
647        import_name_type: Option<PeImportNameType>,
648        item: DefId,
649    ) -> DllImport {
650        let span = self.tcx.def_span(item);
651
652        // This `extern` block should have been checked for general ABI support before, but let's
653        // double-check that.
654        assert!(self.tcx.sess.target.is_abi_supported(abi));
655
656        // This logic is similar to `AbiMap::canonize_abi` (in rustc_target/src/spec/abi_map.rs) but
657        // we need more detail than those adjustments, and we can't support all ABIs that are
658        // generally supported.
659        let calling_convention = if self.tcx.sess.target.arch == "x86" {
660            match abi {
661                ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
662                ExternAbi::Stdcall { .. } => {
663                    DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
664                }
665                // On Windows, `extern "system"` behaves like msvc's `__stdcall`.
666                // `__stdcall` only applies on x86 and on non-variadic functions:
667                // https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170
668                ExternAbi::System { .. } => {
669                    let c_variadic =
670                        self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
671
672                    if c_variadic {
673                        DllCallingConvention::C
674                    } else {
675                        DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
676                    }
677                }
678                ExternAbi::Fastcall { .. } => {
679                    DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
680                }
681                ExternAbi::Vectorcall { .. } => {
682                    DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
683                }
684                _ => {
685                    self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
686                }
687            }
688        } else {
689            match abi {
690                ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
691                    DllCallingConvention::C
692                }
693                _ => {
694                    self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
695                }
696            }
697        };
698
699        let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
700        let import_name_type = codegen_fn_attrs
701            .link_ordinal
702            .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
703
704        let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
705
706        if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
707            let name = name.as_str();
708            if name.contains('\0') {
709                self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
710            } else if let Some((left, right)) = name.split_once('@')
711                && (left.is_empty() || right.is_empty() || right.contains('@'))
712            {
713                self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
714            }
715        }
716
717        DllImport {
718            name,
719            import_name_type,
720            calling_convention,
721            span,
722            is_fn: self.tcx.def_kind(item).is_fn_like(),
723        }
724    }
725}