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::LinkSelfContainedComponents;
21
22use crate::{errors, fluent_generated};
23
24pub fn walk_native_lib_search_dirs<R>(
25 sess: &Session,
26 self_contained_components: LinkSelfContainedComponents,
27 apple_sdk_root: Option<&Path>,
28 mut f: impl FnMut(&Path, bool ) -> ControlFlow<R>,
29) -> ControlFlow<R> {
30 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Native) {
32 f(&search_path.dir, false)?;
33 }
34 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) {
35 if search_path.kind != PathKind::All {
37 f(&search_path.dir, true)?;
38 }
39 }
40
41 if self_contained_components.intersects(
44 LinkSelfContainedComponents::LIBC
45 | LinkSelfContainedComponents::UNWIND
46 | LinkSelfContainedComponents::MINGW,
47 ) {
48 f(&sess.target_tlib_path.dir.join("self-contained"), false)?;
49 }
50
51 if sess.target.vendor == "fortanix"
61 || sess.target.os == "linux"
62 || sess.target.os == "fuchsia"
63 || sess.target.is_like_aix
64 || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
65 {
66 f(&sess.target_tlib_path.dir, false)?;
67 }
68
69 if let Some(sdk_root) = apple_sdk_root
72 && sess.target.llvm_target.contains("macabi")
73 {
74 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
75 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
76 }
77
78 ControlFlow::Continue(())
79}
80
81pub fn try_find_native_static_library(
82 sess: &Session,
83 name: &str,
84 verbatim: bool,
85) -> Option<PathBuf> {
86 let formats = if verbatim {
87 vec![("".into(), "".into())]
88 } else {
89 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
90 let unix = ("lib".into(), ".a".into());
93 if os == unix { vec![os] } else { vec![os, unix] }
94 };
95
96 walk_native_lib_search_dirs(
98 sess,
99 LinkSelfContainedComponents::empty(),
100 None,
101 |dir, is_framework| {
102 if !is_framework {
103 for (prefix, suffix) in &formats {
104 let test = dir.join(format!("{prefix}{name}{suffix}"));
105 if test.exists() {
106 return ControlFlow::Break(test);
107 }
108 }
109 }
110 ControlFlow::Continue(())
111 },
112 )
113 .break_value()
114}
115
116pub fn try_find_native_dynamic_library(
117 sess: &Session,
118 name: &str,
119 verbatim: bool,
120) -> Option<PathBuf> {
121 let formats = if verbatim {
122 vec![("".into(), "".into())]
123 } else {
124 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
127 let meson = ("lib".into(), ".dll.a".into());
130 let mingw = ("lib".into(), ".a".into());
132 vec![os, meson, mingw]
133 };
134
135 walk_native_lib_search_dirs(
136 sess,
137 LinkSelfContainedComponents::empty(),
138 None,
139 |dir, is_framework| {
140 if !is_framework {
141 for (prefix, suffix) in &formats {
142 let test = dir.join(format!("{prefix}{name}{suffix}"));
143 if test.exists() {
144 return ControlFlow::Break(test);
145 }
146 }
147 }
148 ControlFlow::Continue(())
149 },
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 sess.dcx().emit_err(errors::RawDylibOnlyWindows { span });
268 }
269 NativeLibKind::RawDylib
270 }
271 "link-arg" => {
272 if !features.link_arg_attribute() {
273 feature_err(
274 sess,
275 sym::link_arg_attribute,
276 span,
277 fluent_generated::metadata_link_arg_unstable,
278 )
279 .emit();
280 }
281 NativeLibKind::LinkArg
282 }
283 kind => {
284 sess.dcx().emit_err(errors::UnknownLinkKind { span, kind });
285 continue;
286 }
287 };
288 kind = Some(link_kind);
289 }
290 sym::modifiers => {
291 if modifiers.is_some() {
292 sess.dcx()
293 .emit_err(errors::MultipleLinkModifiers { span: item.span() });
294 continue;
295 }
296 let Some(link_modifiers) = item.value_str() else {
297 sess.dcx().emit_err(errors::LinkModifiersForm { span: item.span() });
298 continue;
299 };
300 modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
301 }
302 sym::cfg => {
303 if cfg.is_some() {
304 sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() });
305 continue;
306 }
307 let Some(link_cfg) = item.meta_item_list() else {
308 sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() });
309 continue;
310 };
311 let [link_cfg] = link_cfg else {
312 sess.dcx()
313 .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
314 continue;
315 };
316 let Some(link_cfg) = link_cfg.meta_item_or_bool() else {
317 sess.dcx()
318 .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
319 continue;
320 };
321 if !features.link_cfg() {
322 feature_err(
323 sess,
324 sym::link_cfg,
325 item.span(),
326 fluent_generated::metadata_link_cfg_unstable,
327 )
328 .emit();
329 }
330 cfg = Some(link_cfg.clone());
331 }
332 sym::wasm_import_module => {
333 if wasm_import_module.is_some() {
334 sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() });
335 continue;
336 }
337 let Some(link_wasm_import_module) = item.value_str() else {
338 sess.dcx().emit_err(errors::WasmImportForm { span: item.span() });
339 continue;
340 };
341 wasm_import_module = Some((link_wasm_import_module, item.span()));
342 }
343 sym::import_name_type => {
344 if import_name_type.is_some() {
345 sess.dcx()
346 .emit_err(errors::MultipleImportNameType { span: item.span() });
347 continue;
348 }
349 let Some(link_import_name_type) = item.value_str() else {
350 sess.dcx().emit_err(errors::ImportNameTypeForm { span: item.span() });
351 continue;
352 };
353 if self.tcx.sess.target.arch != "x86" {
354 sess.dcx().emit_err(errors::ImportNameTypeX86 { span: item.span() });
355 continue;
356 }
357
358 let link_import_name_type = match link_import_name_type.as_str() {
359 "decorated" => PeImportNameType::Decorated,
360 "noprefix" => PeImportNameType::NoPrefix,
361 "undecorated" => PeImportNameType::Undecorated,
362 import_name_type => {
363 sess.dcx().emit_err(errors::UnknownImportNameType {
364 span: item.span(),
365 import_name_type,
366 });
367 continue;
368 }
369 };
370 import_name_type = Some((link_import_name_type, item.span()));
371 }
372 _ => {
373 sess.dcx().emit_err(errors::UnexpectedLinkArg { span: item.span() });
374 }
375 }
376 }
377
378 let mut verbatim = None;
380 if let Some((modifiers, span)) = modifiers {
381 for modifier in modifiers.as_str().split(',') {
382 let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
383 Some(m) => (m, modifier.starts_with('+')),
384 None => {
385 sess.dcx().emit_err(errors::InvalidLinkModifier { span });
386 continue;
387 }
388 };
389
390 macro report_unstable_modifier($feature: ident) {
391 if !features.$feature() {
392 #[expect(rustc::untranslatable_diagnostic)]
394 feature_err(
395 sess,
396 sym::$feature,
397 span,
398 format!("linking modifier `{modifier}` is unstable"),
399 )
400 .emit();
401 }
402 }
403 let assign_modifier = |dst: &mut Option<bool>| {
404 if dst.is_some() {
405 sess.dcx().emit_err(errors::MultipleModifiers { span, modifier });
406 } else {
407 *dst = Some(value);
408 }
409 };
410 match (modifier, &mut kind) {
411 ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
412 assign_modifier(bundle)
413 }
414 ("bundle", _) => {
415 sess.dcx().emit_err(errors::BundleNeedsStatic { span });
416 }
417
418 ("verbatim", _) => assign_modifier(&mut verbatim),
419
420 ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
421 assign_modifier(whole_archive)
422 }
423 ("whole-archive", _) => {
424 sess.dcx().emit_err(errors::WholeArchiveNeedsStatic { span });
425 }
426
427 ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
428 | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
429 report_unstable_modifier!(native_link_modifiers_as_needed);
430 assign_modifier(as_needed)
431 }
432 ("as-needed", _) => {
433 sess.dcx().emit_err(errors::AsNeededCompatibility { span });
434 }
435
436 _ => {
437 sess.dcx().emit_err(errors::UnknownLinkModifier { span, modifier });
438 }
439 }
440 }
441 }
442
443 if let Some((_, span)) = wasm_import_module {
444 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
445 sess.dcx().emit_err(errors::IncompatibleWasmLink { span });
446 }
447 }
448
449 if wasm_import_module.is_some() {
450 (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
451 }
452 let Some((name, name_span)) = name else {
453 sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
454 continue;
455 };
456
457 if let Some((_, span)) = import_name_type {
459 if kind != Some(NativeLibKind::RawDylib) {
460 sess.dcx().emit_err(errors::ImportNameTypeRaw { span });
461 }
462 }
463
464 let dll_imports = match kind {
465 Some(NativeLibKind::RawDylib) => {
466 if name.as_str().contains('\0') {
467 sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
468 }
469 foreign_items
470 .iter()
471 .map(|&child_item| {
472 self.build_dll_import(
473 abi,
474 import_name_type.map(|(import_name_type, _)| import_name_type),
475 child_item,
476 )
477 })
478 .collect()
479 }
480 _ => {
481 for &child_item in foreign_items {
482 if self.tcx.def_kind(child_item).has_codegen_attrs()
483 && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
484 {
485 let link_ordinal_attr =
486 self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
487 sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
488 span: link_ordinal_attr.span,
489 });
490 }
491 }
492
493 Vec::new()
494 }
495 };
496
497 let kind = kind.unwrap_or(NativeLibKind::Unspecified);
498 let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
499 self.libs.push(NativeLib {
500 name,
501 filename,
502 kind,
503 cfg,
504 foreign_module: Some(def_id.to_def_id()),
505 verbatim,
506 dll_imports,
507 });
508 }
509 }
510
511 fn process_command_line(&mut self) {
513 let mut renames = FxHashSet::default();
515 for lib in &self.tcx.sess.opts.libs {
516 if let NativeLibKind::Framework { .. } = lib.kind
517 && !self.tcx.sess.target.is_like_osx
518 {
519 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
521 }
522 if let Some(ref new_name) = lib.new_name {
523 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
524 if new_name.is_empty() {
525 self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
526 } else if !any_duplicate {
527 self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
528 } else if !renames.insert(&lib.name) {
529 self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
530 }
531 }
532 }
533
534 for passed_lib in &self.tcx.sess.opts.libs {
542 let mut existing = self
546 .libs
547 .extract_if(.., |lib| {
548 if lib.name.as_str() == passed_lib.name {
549 if lib.has_modifiers() || passed_lib.has_modifiers() {
553 match lib.foreign_module {
554 Some(def_id) => {
555 self.tcx.dcx().emit_err(errors::NoLinkModOverride {
556 span: Some(self.tcx.def_span(def_id)),
557 })
558 }
559 None => self
560 .tcx
561 .dcx()
562 .emit_err(errors::NoLinkModOverride { span: None }),
563 };
564 }
565 if passed_lib.kind != NativeLibKind::Unspecified {
566 lib.kind = passed_lib.kind;
567 }
568 if let Some(new_name) = &passed_lib.new_name {
569 lib.name = Symbol::intern(new_name);
570 }
571 lib.verbatim = passed_lib.verbatim;
572 return true;
573 }
574 false
575 })
576 .collect::<Vec<_>>();
577 if existing.is_empty() {
578 let new_name: Option<&str> = passed_lib.new_name.as_deref();
580 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
581 let filename = find_bundled_library(
582 name,
583 passed_lib.verbatim,
584 passed_lib.kind,
585 false,
586 self.tcx,
587 );
588 self.libs.push(NativeLib {
589 name,
590 filename,
591 kind: passed_lib.kind,
592 cfg: None,
593 foreign_module: None,
594 verbatim: passed_lib.verbatim,
595 dll_imports: Vec::new(),
596 });
597 } else {
598 self.libs.append(&mut existing);
601 }
602 }
603 }
604
605 fn i686_arg_list_size(&self, item: DefId) -> usize {
606 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
607 self.tcx
608 .type_of(item)
609 .instantiate_identity()
610 .fn_sig(self.tcx)
611 .inputs()
612 .map_bound(|slice| self.tcx.mk_type_list(slice)),
613 );
614
615 argument_types
616 .iter()
617 .map(|ty| {
618 let layout = self
619 .tcx
620 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
621 .expect("layout")
622 .layout;
623 (layout.size().bytes_usize() + 3) & !3
626 })
627 .sum()
628 }
629
630 fn build_dll_import(
631 &self,
632 abi: ExternAbi,
633 import_name_type: Option<PeImportNameType>,
634 item: DefId,
635 ) -> DllImport {
636 let span = self.tcx.def_span(item);
637
638 let calling_convention = if self.tcx.sess.target.arch == "x86" {
640 match abi {
641 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
642 ExternAbi::Stdcall { .. } => {
643 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
644 }
645 ExternAbi::System { .. } => {
649 let c_variadic =
650 self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
651
652 if c_variadic {
653 DllCallingConvention::C
654 } else {
655 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
656 }
657 }
658 ExternAbi::Fastcall { .. } => {
659 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
660 }
661 ExternAbi::Vectorcall { .. } => {
662 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
663 }
664 _ => {
665 self.tcx.dcx().emit_fatal(errors::UnsupportedAbiI686 { span });
666 }
667 }
668 } else {
669 match abi {
670 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
671 DllCallingConvention::C
672 }
673 _ => {
674 self.tcx.dcx().emit_fatal(errors::UnsupportedAbi { span });
675 }
676 }
677 };
678
679 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
680 let import_name_type = codegen_fn_attrs
681 .link_ordinal
682 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
683
684 DllImport {
685 name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)),
686 import_name_type,
687 calling_convention,
688 span,
689 is_fn: self.tcx.def_kind(item).is_fn_like(),
690 }
691 }
692}