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