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_middle::query::LocalCrate;
9use rustc_middle::ty::{self, List, Ty, TyCtxt};
10use rustc_session::Session;
11use rustc_session::config::CrateType;
12use rustc_session::cstore::{
13 DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
14};
15use rustc_session::parse::feature_err;
16use rustc_session::search_paths::PathKind;
17use rustc_session::utils::NativeLibKind;
18use rustc_span::def_id::{DefId, LOCAL_CRATE};
19use rustc_span::{Symbol, sym};
20use rustc_target::spec::{BinaryFormat, LinkSelfContainedComponents};
21
22use crate::{errors, fluent_generated};
23
24pub struct NativeLibSearchFallback<'a> {
28 pub self_contained_components: LinkSelfContainedComponents,
29 pub apple_sdk_root: Option<&'a Path>,
30}
31
32pub fn walk_native_lib_search_dirs<R>(
33 sess: &Session,
34 fallback: Option<NativeLibSearchFallback<'_>>,
35 mut f: impl FnMut(&Path, bool ) -> ControlFlow<R>,
36) -> ControlFlow<R> {
37 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Native) {
39 f(&search_path.dir, false)?;
40 }
41 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) {
42 if search_path.kind != PathKind::All {
44 f(&search_path.dir, true)?;
45 }
46 }
47
48 let Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }) = fallback
49 else {
50 return ControlFlow::Continue(());
51 };
52
53 if self_contained_components.intersects(
56 LinkSelfContainedComponents::LIBC
57 | LinkSelfContainedComponents::UNWIND
58 | LinkSelfContainedComponents::MINGW,
59 ) {
60 f(&sess.target_tlib_path.dir.join("self-contained"), false)?;
61 }
62
63 if sess.target.vendor == "fortanix"
73 || sess.target.os == "linux"
74 || sess.target.os == "fuchsia"
75 || sess.target.is_like_aix
76 || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
77 {
78 f(&sess.target_tlib_path.dir, false)?;
79 }
80
81 if let Some(sdk_root) = apple_sdk_root
84 && sess.target.llvm_target.contains("macabi")
85 {
86 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
87 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
88 }
89
90 ControlFlow::Continue(())
91}
92
93pub fn try_find_native_static_library(
94 sess: &Session,
95 name: &str,
96 verbatim: bool,
97) -> Option<PathBuf> {
98 let default = sess.staticlib_components(verbatim);
99 let formats = if verbatim {
100 vec![default]
101 } else {
102 let unix = ("lib", ".a");
105 if default == unix { vec![default] } else { vec![default, unix] }
106 };
107
108 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
109 if !is_framework {
110 for (prefix, suffix) in &formats {
111 let test = dir.join(format!("{prefix}{name}{suffix}"));
112 if test.exists() {
113 return ControlFlow::Break(test);
114 }
115 }
116 }
117 ControlFlow::Continue(())
118 })
119 .break_value()
120}
121
122pub fn try_find_native_dynamic_library(
123 sess: &Session,
124 name: &str,
125 verbatim: bool,
126) -> Option<PathBuf> {
127 let default = sess.staticlib_components(verbatim);
128 let formats = if verbatim {
129 vec![default]
130 } else {
131 let meson = ("lib", ".dll.a");
135 let mingw = ("lib", ".a");
137 vec![default, meson, mingw]
138 };
139
140 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
141 if !is_framework {
142 for (prefix, suffix) in &formats {
143 let test = dir.join(format!("{prefix}{name}{suffix}"));
144 if test.exists() {
145 return ControlFlow::Break(test);
146 }
147 }
148 }
149 ControlFlow::Continue(())
150 })
151 .break_value()
152}
153
154pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
155 try_find_native_static_library(sess, name, verbatim)
156 .unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
157}
158
159fn find_bundled_library(
160 name: Symbol,
161 verbatim: Option<bool>,
162 kind: NativeLibKind,
163 has_cfg: bool,
164 tcx: TyCtxt<'_>,
165) -> Option<Symbol> {
166 let sess = tcx.sess;
167 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
168 && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
169 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
170 {
171 let verbatim = verbatim.unwrap_or(false);
172 return find_native_static_library(name.as_str(), verbatim, sess)
173 .file_name()
174 .and_then(|s| s.to_str())
175 .map(Symbol::intern);
176 }
177 None
178}
179
180pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
181 let mut collector = Collector { tcx, libs: Vec::new() };
182 if tcx.sess.opts.unstable_opts.link_directives {
183 for module in tcx.foreign_modules(LOCAL_CRATE).values() {
184 collector.process_module(module);
185 }
186 }
187 collector.process_command_line();
188 collector.libs
189}
190
191pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
192 match lib.cfg {
193 Some(ref cfg) => attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None),
194 None => true,
195 }
196}
197
198struct Collector<'tcx> {
199 tcx: TyCtxt<'tcx>,
200 libs: Vec<NativeLib>,
201}
202
203impl<'tcx> Collector<'tcx> {
204 fn process_module(&mut self, module: &ForeignModule) {
205 let ForeignModule { def_id, abi, ref foreign_items } = *module;
206 let def_id = def_id.expect_local();
207
208 let sess = self.tcx.sess;
209
210 if matches!(abi, ExternAbi::Rust | ExternAbi::RustIntrinsic) {
211 return;
212 }
213
214 let features = self.tcx.features();
216
217 for m in self.tcx.get_attrs(def_id, sym::link) {
218 let Some(items) = m.meta_item_list() else {
219 continue;
220 };
221
222 let mut name = None;
223 let mut kind = None;
224 let mut modifiers = None;
225 let mut cfg = None;
226 let mut wasm_import_module = None;
227 let mut import_name_type = None;
228 for item in items.iter() {
229 match item.name_or_empty() {
230 sym::name => {
231 if name.is_some() {
232 sess.dcx().emit_err(errors::MultipleNamesInLink { span: item.span() });
233 continue;
234 }
235 let Some(link_name) = item.value_str() else {
236 sess.dcx().emit_err(errors::LinkNameForm { span: item.span() });
237 continue;
238 };
239 let span = item.name_value_literal_span().unwrap();
240 if link_name.is_empty() {
241 sess.dcx().emit_err(errors::EmptyLinkName { span });
242 }
243 name = Some((link_name, span));
244 }
245 sym::kind => {
246 if kind.is_some() {
247 sess.dcx().emit_err(errors::MultipleKindsInLink { span: item.span() });
248 continue;
249 }
250 let Some(link_kind) = item.value_str() else {
251 sess.dcx().emit_err(errors::LinkKindForm { span: item.span() });
252 continue;
253 };
254
255 let span = item.name_value_literal_span().unwrap();
256 let link_kind = match link_kind.as_str() {
257 "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
258 "dylib" => NativeLibKind::Dylib { as_needed: None },
259 "framework" => {
260 if !sess.target.is_like_osx {
261 sess.dcx().emit_err(errors::LinkFrameworkApple { span });
262 }
263 NativeLibKind::Framework { as_needed: None }
264 }
265 "raw-dylib" => {
266 if sess.target.is_like_windows {
267 } else if sess.target.binary_format == BinaryFormat::Elf
269 && features.raw_dylib_elf()
270 {
271 } else if sess.target.binary_format == BinaryFormat::Elf
273 && sess.is_nightly_build()
274 {
275 feature_err(
276 sess,
277 sym::raw_dylib_elf,
278 span,
279 fluent_generated::metadata_raw_dylib_elf_unstable,
280 )
281 .emit();
282 } else {
283 sess.dcx().emit_err(errors::RawDylibOnlyWindows { span });
284 }
285
286 NativeLibKind::RawDylib
287 }
288 "link-arg" => {
289 if !features.link_arg_attribute() {
290 feature_err(
291 sess,
292 sym::link_arg_attribute,
293 span,
294 fluent_generated::metadata_link_arg_unstable,
295 )
296 .emit();
297 }
298 NativeLibKind::LinkArg
299 }
300 kind => {
301 sess.dcx().emit_err(errors::UnknownLinkKind { span, kind });
302 continue;
303 }
304 };
305 kind = Some(link_kind);
306 }
307 sym::modifiers => {
308 if modifiers.is_some() {
309 sess.dcx()
310 .emit_err(errors::MultipleLinkModifiers { span: item.span() });
311 continue;
312 }
313 let Some(link_modifiers) = item.value_str() else {
314 sess.dcx().emit_err(errors::LinkModifiersForm { span: item.span() });
315 continue;
316 };
317 modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
318 }
319 sym::cfg => {
320 if cfg.is_some() {
321 sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() });
322 continue;
323 }
324 let Some(link_cfg) = item.meta_item_list() else {
325 sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() });
326 continue;
327 };
328 let [link_cfg] = link_cfg else {
329 sess.dcx()
330 .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
331 continue;
332 };
333 let Some(link_cfg) = link_cfg.meta_item_or_bool() else {
334 sess.dcx()
335 .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
336 continue;
337 };
338 if !features.link_cfg() {
339 feature_err(
340 sess,
341 sym::link_cfg,
342 item.span(),
343 fluent_generated::metadata_link_cfg_unstable,
344 )
345 .emit();
346 }
347 cfg = Some(link_cfg.clone());
348 }
349 sym::wasm_import_module => {
350 if wasm_import_module.is_some() {
351 sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() });
352 continue;
353 }
354 let Some(link_wasm_import_module) = item.value_str() else {
355 sess.dcx().emit_err(errors::WasmImportForm { span: item.span() });
356 continue;
357 };
358 wasm_import_module = Some((link_wasm_import_module, item.span()));
359 }
360 sym::import_name_type => {
361 if import_name_type.is_some() {
362 sess.dcx()
363 .emit_err(errors::MultipleImportNameType { span: item.span() });
364 continue;
365 }
366 let Some(link_import_name_type) = item.value_str() else {
367 sess.dcx().emit_err(errors::ImportNameTypeForm { span: item.span() });
368 continue;
369 };
370 if self.tcx.sess.target.arch != "x86" {
371 sess.dcx().emit_err(errors::ImportNameTypeX86 { span: item.span() });
372 continue;
373 }
374
375 let link_import_name_type = match link_import_name_type.as_str() {
376 "decorated" => PeImportNameType::Decorated,
377 "noprefix" => PeImportNameType::NoPrefix,
378 "undecorated" => PeImportNameType::Undecorated,
379 import_name_type => {
380 sess.dcx().emit_err(errors::UnknownImportNameType {
381 span: item.span(),
382 import_name_type,
383 });
384 continue;
385 }
386 };
387 import_name_type = Some((link_import_name_type, item.span()));
388 }
389 _ => {
390 sess.dcx().emit_err(errors::UnexpectedLinkArg { span: item.span() });
391 }
392 }
393 }
394
395 let mut verbatim = None;
397 if let Some((modifiers, span)) = modifiers {
398 for modifier in modifiers.as_str().split(',') {
399 let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
400 Some(m) => (m, modifier.starts_with('+')),
401 None => {
402 sess.dcx().emit_err(errors::InvalidLinkModifier { span });
403 continue;
404 }
405 };
406
407 macro report_unstable_modifier($feature: ident) {
408 if !features.$feature() {
409 #[expect(rustc::untranslatable_diagnostic)]
411 feature_err(
412 sess,
413 sym::$feature,
414 span,
415 format!("linking modifier `{modifier}` is unstable"),
416 )
417 .emit();
418 }
419 }
420 let assign_modifier = |dst: &mut Option<bool>| {
421 if dst.is_some() {
422 sess.dcx().emit_err(errors::MultipleModifiers { span, modifier });
423 } else {
424 *dst = Some(value);
425 }
426 };
427 match (modifier, &mut kind) {
428 ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
429 assign_modifier(bundle)
430 }
431 ("bundle", _) => {
432 sess.dcx().emit_err(errors::BundleNeedsStatic { span });
433 }
434
435 ("verbatim", _) => assign_modifier(&mut verbatim),
436
437 ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
438 assign_modifier(whole_archive)
439 }
440 ("whole-archive", _) => {
441 sess.dcx().emit_err(errors::WholeArchiveNeedsStatic { span });
442 }
443
444 ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
445 | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
446 report_unstable_modifier!(native_link_modifiers_as_needed);
447 assign_modifier(as_needed)
448 }
449 ("as-needed", _) => {
450 sess.dcx().emit_err(errors::AsNeededCompatibility { span });
451 }
452
453 _ => {
454 sess.dcx().emit_err(errors::UnknownLinkModifier { span, modifier });
455 }
456 }
457 }
458 }
459
460 if let Some((_, span)) = wasm_import_module {
461 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
462 sess.dcx().emit_err(errors::IncompatibleWasmLink { span });
463 }
464 }
465
466 if wasm_import_module.is_some() {
467 (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
468 }
469 let Some((name, name_span)) = name else {
470 sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() });
471 continue;
472 };
473
474 if let Some((_, span)) = import_name_type {
476 if kind != Some(NativeLibKind::RawDylib) {
477 sess.dcx().emit_err(errors::ImportNameTypeRaw { span });
478 }
479 }
480
481 let dll_imports = match kind {
482 Some(NativeLibKind::RawDylib) => {
483 if name.as_str().contains('\0') {
484 sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
485 }
486 foreign_items
487 .iter()
488 .map(|&child_item| {
489 self.build_dll_import(
490 abi,
491 import_name_type.map(|(import_name_type, _)| import_name_type),
492 child_item,
493 )
494 })
495 .collect()
496 }
497 _ => {
498 for &child_item in foreign_items {
499 if self.tcx.def_kind(child_item).has_codegen_attrs()
500 && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
501 {
502 let link_ordinal_attr =
503 self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
504 sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
505 span: link_ordinal_attr.span(),
506 });
507 }
508 }
509
510 Vec::new()
511 }
512 };
513
514 let kind = kind.unwrap_or(NativeLibKind::Unspecified);
515 let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
516 self.libs.push(NativeLib {
517 name,
518 filename,
519 kind,
520 cfg,
521 foreign_module: Some(def_id.to_def_id()),
522 verbatim,
523 dll_imports,
524 });
525 }
526 }
527
528 fn process_command_line(&mut self) {
530 let mut renames = FxHashSet::default();
532 for lib in &self.tcx.sess.opts.libs {
533 if let NativeLibKind::Framework { .. } = lib.kind
534 && !self.tcx.sess.target.is_like_osx
535 {
536 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
538 }
539 if let Some(ref new_name) = lib.new_name {
540 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
541 if new_name.is_empty() {
542 self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
543 } else if !any_duplicate {
544 self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
545 } else if !renames.insert(&lib.name) {
546 self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
547 }
548 }
549 }
550
551 for passed_lib in &self.tcx.sess.opts.libs {
559 let mut existing = self
563 .libs
564 .extract_if(.., |lib| {
565 if lib.name.as_str() == passed_lib.name {
566 if lib.has_modifiers() || passed_lib.has_modifiers() {
570 match lib.foreign_module {
571 Some(def_id) => {
572 self.tcx.dcx().emit_err(errors::NoLinkModOverride {
573 span: Some(self.tcx.def_span(def_id)),
574 })
575 }
576 None => self
577 .tcx
578 .dcx()
579 .emit_err(errors::NoLinkModOverride { span: None }),
580 };
581 }
582 if passed_lib.kind != NativeLibKind::Unspecified {
583 lib.kind = passed_lib.kind;
584 }
585 if let Some(new_name) = &passed_lib.new_name {
586 lib.name = Symbol::intern(new_name);
587 }
588 lib.verbatim = passed_lib.verbatim;
589 return true;
590 }
591 false
592 })
593 .collect::<Vec<_>>();
594 if existing.is_empty() {
595 let new_name: Option<&str> = passed_lib.new_name.as_deref();
597 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
598 let filename = find_bundled_library(
599 name,
600 passed_lib.verbatim,
601 passed_lib.kind,
602 false,
603 self.tcx,
604 );
605 self.libs.push(NativeLib {
606 name,
607 filename,
608 kind: passed_lib.kind,
609 cfg: None,
610 foreign_module: None,
611 verbatim: passed_lib.verbatim,
612 dll_imports: Vec::new(),
613 });
614 } else {
615 self.libs.append(&mut existing);
618 }
619 }
620 }
621
622 fn i686_arg_list_size(&self, item: DefId) -> usize {
623 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
624 self.tcx
625 .type_of(item)
626 .instantiate_identity()
627 .fn_sig(self.tcx)
628 .inputs()
629 .map_bound(|slice| self.tcx.mk_type_list(slice)),
630 );
631
632 argument_types
633 .iter()
634 .map(|ty| {
635 let layout = self
636 .tcx
637 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
638 .expect("layout")
639 .layout;
640 (layout.size().bytes_usize() + 3) & !3
643 })
644 .sum()
645 }
646
647 fn build_dll_import(
648 &self,
649 abi: ExternAbi,
650 import_name_type: Option<PeImportNameType>,
651 item: DefId,
652 ) -> DllImport {
653 let span = self.tcx.def_span(item);
654
655 let calling_convention = if self.tcx.sess.target.arch == "x86" {
657 match abi {
658 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
659 ExternAbi::Stdcall { .. } => {
660 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
661 }
662 ExternAbi::System { .. } => {
666 let c_variadic =
667 self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
668
669 if c_variadic {
670 DllCallingConvention::C
671 } else {
672 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
673 }
674 }
675 ExternAbi::Fastcall { .. } => {
676 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
677 }
678 ExternAbi::Vectorcall { .. } => {
679 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
680 }
681 _ => {
682 self.tcx.dcx().emit_fatal(errors::UnsupportedAbiI686 { span });
683 }
684 }
685 } else {
686 match abi {
687 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
688 DllCallingConvention::C
689 }
690 _ => {
691 self.tcx.dcx().emit_fatal(errors::UnsupportedAbi { span });
692 }
693 }
694 };
695
696 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
697 let import_name_type = codegen_fn_attrs
698 .link_ordinal
699 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
700
701 DllImport {
702 name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)),
703 import_name_type,
704 calling_convention,
705 span,
706 is_fn: self.tcx.def_kind(item).is_fn_like(),
707 }
708 }
709}