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 formats = if verbatim {
99 vec![("".into(), "".into())]
100 } else {
101 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
102 let unix = ("lib".into(), ".a".into());
105 if os == unix { vec![os] } else { vec![os, 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 formats = if verbatim {
128 vec![("".into(), "".into())]
129 } else {
130 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
133 let meson = ("lib".into(), ".dll.a".into());
136 let mingw = ("lib".into(), ".a".into());
138 vec![os, 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 | ExternAbi::RustIntrinsic) {
212 return;
213 }
214
215 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_or_empty() {
231 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 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_osx {
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 } else if sess.target.binary_format == BinaryFormat::Elf
270 && features.raw_dylib_elf()
271 {
272 } 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 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 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 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 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 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 #[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 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 self.tcx.def_kind(child_item).has_codegen_attrs()
501 && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
502 {
503 let link_ordinal_attr =
504 self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
505 sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
506 span: link_ordinal_attr.span(),
507 });
508 }
509 }
510
511 Vec::new()
512 }
513 };
514
515 let kind = kind.unwrap_or(NativeLibKind::Unspecified);
516 let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
517 self.libs.push(NativeLib {
518 name,
519 filename,
520 kind,
521 cfg,
522 foreign_module: Some(def_id.to_def_id()),
523 verbatim,
524 dll_imports,
525 });
526 }
527 }
528
529 fn process_command_line(&mut self) {
531 let mut renames = FxHashSet::default();
533 for lib in &self.tcx.sess.opts.libs {
534 if let NativeLibKind::Framework { .. } = lib.kind
535 && !self.tcx.sess.target.is_like_osx
536 {
537 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
539 }
540 if let Some(ref new_name) = lib.new_name {
541 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
542 if new_name.is_empty() {
543 self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
544 } else if !any_duplicate {
545 self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
546 } else if !renames.insert(&lib.name) {
547 self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
548 }
549 }
550 }
551
552 for passed_lib in &self.tcx.sess.opts.libs {
560 let mut existing = self
564 .libs
565 .extract_if(.., |lib| {
566 if lib.name.as_str() == passed_lib.name {
567 if lib.has_modifiers() || passed_lib.has_modifiers() {
571 match lib.foreign_module {
572 Some(def_id) => {
573 self.tcx.dcx().emit_err(errors::NoLinkModOverride {
574 span: Some(self.tcx.def_span(def_id)),
575 })
576 }
577 None => self
578 .tcx
579 .dcx()
580 .emit_err(errors::NoLinkModOverride { span: None }),
581 };
582 }
583 if passed_lib.kind != NativeLibKind::Unspecified {
584 lib.kind = passed_lib.kind;
585 }
586 if let Some(new_name) = &passed_lib.new_name {
587 lib.name = Symbol::intern(new_name);
588 }
589 lib.verbatim = passed_lib.verbatim;
590 return true;
591 }
592 false
593 })
594 .collect::<Vec<_>>();
595 if existing.is_empty() {
596 let new_name: Option<&str> = passed_lib.new_name.as_deref();
598 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
599 let filename = find_bundled_library(
600 name,
601 passed_lib.verbatim,
602 passed_lib.kind,
603 false,
604 self.tcx,
605 );
606 self.libs.push(NativeLib {
607 name,
608 filename,
609 kind: passed_lib.kind,
610 cfg: None,
611 foreign_module: None,
612 verbatim: passed_lib.verbatim,
613 dll_imports: Vec::new(),
614 });
615 } else {
616 self.libs.append(&mut existing);
619 }
620 }
621 }
622
623 fn i686_arg_list_size(&self, item: DefId) -> usize {
624 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
625 self.tcx
626 .type_of(item)
627 .instantiate_identity()
628 .fn_sig(self.tcx)
629 .inputs()
630 .map_bound(|slice| self.tcx.mk_type_list(slice)),
631 );
632
633 argument_types
634 .iter()
635 .map(|ty| {
636 let layout = self
637 .tcx
638 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
639 .expect("layout")
640 .layout;
641 (layout.size().bytes_usize() + 3) & !3
644 })
645 .sum()
646 }
647
648 fn build_dll_import(
649 &self,
650 abi: ExternAbi,
651 import_name_type: Option<PeImportNameType>,
652 item: DefId,
653 ) -> DllImport {
654 let span = self.tcx.def_span(item);
655
656 let calling_convention = if self.tcx.sess.target.arch == "x86" {
658 match abi {
659 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
660 ExternAbi::Stdcall { .. } => {
661 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
662 }
663 ExternAbi::System { .. } => {
667 let c_variadic =
668 self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
669
670 if c_variadic {
671 DllCallingConvention::C
672 } else {
673 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
674 }
675 }
676 ExternAbi::Fastcall { .. } => {
677 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
678 }
679 ExternAbi::Vectorcall { .. } => {
680 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
681 }
682 _ => {
683 self.tcx.dcx().emit_fatal(errors::UnsupportedAbiI686 { span });
684 }
685 }
686 } else {
687 match abi {
688 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
689 DllCallingConvention::C
690 }
691 _ => {
692 self.tcx.dcx().emit_fatal(errors::UnsupportedAbi { span });
693 }
694 }
695 };
696
697 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
698 let import_name_type = codegen_fn_attrs
699 .link_ordinal
700 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
701
702 DllImport {
703 name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)),
704 import_name_type,
705 calling_convention,
706 span,
707 is_fn: self.tcx.def_kind(item).is_fn_like(),
708 }
709 }
710}