1use std::ops::ControlFlow;
2use std::path::{Path, PathBuf};
3
4use rustc_abi::ExternAbi;
5use rustc_attr_parsing::eval_config_entry;
6use rustc_data_structures::fx::FxHashSet;
7use rustc_hir::attrs::{NativeLibKind, PeImportNameType};
8use rustc_hir::def::DefKind;
9use rustc_hir::find_attr;
10use rustc_middle::bug;
11use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
12use rustc_middle::query::LocalCrate;
13use rustc_middle::ty::{self, List, Ty, TyCtxt};
14use rustc_session::Session;
15use rustc_session::config::CrateType;
16use rustc_session::cstore::{
17 DllCallingConvention, DllImport, DllImportSymbolType, ForeignModule, NativeLib,
18};
19use rustc_session::search_paths::PathKind;
20use rustc_span::Symbol;
21use rustc_span::def_id::{DefId, LOCAL_CRATE};
22use rustc_target::spec::{Arch, BinaryFormat, CfgAbi, Env, LinkSelfContainedComponents, Os};
23
24use crate::diagnostics;
25
26pub 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 ) -> ControlFlow<R>,
38) -> ControlFlow<R> {
39 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 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 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 let has_shared_llvm_apple_darwin =
66 sess.target.is_like_darwin && sess.target_tlib_path.dir.join("libLLVM.dylib").exists();
67
68 if sess.target.cfg_abi == CfgAbi::Fortanix
80 || sess.target.os == Os::Linux
81 || sess.target.os == Os::Fuchsia
82 || sess.target.is_like_aix
83 || sess.target.is_like_darwin
84 && (!sess.sanitizers().is_empty() || has_shared_llvm_apple_darwin)
85 || sess.target.os == Os::Windows
86 && sess.target.env == Env::Gnu
87 && sess.target.cfg_abi == CfgAbi::Llvm
88 {
89 f(&sess.target_tlib_path.dir, false)?;
90 }
91
92 if let Some(sdk_root) = apple_sdk_root
95 && sess.target.env == Env::MacAbi
96 {
97 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
98 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
99 }
100
101 ControlFlow::Continue(())
102}
103
104pub fn try_find_native_static_library(
105 sess: &Session,
106 name: &str,
107 verbatim: bool,
108) -> Option<PathBuf> {
109 let default = sess.staticlib_components(verbatim);
110 let formats = if verbatim {
111 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
112 } else {
113 let unix = ("lib", ".a");
116 if default == unix { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default] } else { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default, unix]))vec![default, unix] }
117 };
118
119 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
120 if !is_framework {
121 for (prefix, suffix) in &formats {
122 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
123 if test.exists() {
124 return ControlFlow::Break(test);
125 }
126 }
127 }
128 ControlFlow::Continue(())
129 })
130 .break_value()
131}
132
133pub fn try_find_native_dynamic_library(
134 sess: &Session,
135 name: &str,
136 verbatim: bool,
137) -> Option<PathBuf> {
138 let default = sess.staticlib_components(verbatim);
139 let formats = if verbatim {
140 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
141 } else {
142 let meson = ("lib", ".dll.a");
146 let mingw = ("lib", ".a");
148 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default, meson, mingw]))vec![default, meson, mingw]
149 };
150
151 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
152 if !is_framework {
153 for (prefix, suffix) in &formats {
154 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
155 if test.exists() {
156 return ControlFlow::Break(test);
157 }
158 }
159 }
160 ControlFlow::Continue(())
161 })
162 .break_value()
163}
164
165pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
166 try_find_native_static_library(sess, name, verbatim).unwrap_or_else(|| {
167 sess.dcx().emit_fatal(diagnostics::MissingNativeLibrary::new(name, verbatim))
168 })
169}
170
171fn find_bundled_library(
172 name: Symbol,
173 verbatim: Option<bool>,
174 kind: NativeLibKind,
175 has_cfg: bool,
176 tcx: TyCtxt<'_>,
177) -> Option<Symbol> {
178 let sess = tcx.sess;
179 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind
180 && tcx.crate_types().iter().any(|t| #[allow(non_exhaustive_omitted_patterns)] match t {
&CrateType::Rlib | CrateType::StaticLib => true,
_ => false,
}matches!(t, &CrateType::Rlib | CrateType::StaticLib))
181 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
182 {
183 let verbatim = verbatim.unwrap_or(false);
184 return find_native_static_library(name.as_str(), verbatim, sess)
185 .file_name()
186 .and_then(|s| s.to_str())
187 .map(Symbol::intern);
188 }
189 None
190}
191
192pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
193 let mut collector = Collector { tcx, libs: Vec::new() };
194 if tcx.sess.opts.unstable_opts.link_directives {
195 for module in tcx.foreign_modules(LOCAL_CRATE).values() {
196 collector.process_module(module);
197 }
198 }
199 collector.process_command_line();
200 for lib in &mut collector.libs {
201 if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest {
205 if let NativeLibKind::Static { .. } = lib.kind {
206 if !tcx.sess.opts.unstable_opts.ui_testing {
207 let diag = if lib.foreign_module.is_none() {
208 diagnostics::StaticLinkingNotSupported::UserRequested {
209 lib_name: lib.name,
210 target: tcx.sess.target.llvm_target.as_ref(),
211 }
212 } else {
213 diagnostics::StaticLinkingNotSupported::FromDependency {
214 lib_name: lib.name,
215 target: tcx.sess.target.llvm_target.as_ref(),
216 }
217 };
218 tcx.dcx().emit_warn(diag);
219 }
220
221 lib.kind = NativeLibKind::Dylib { as_needed: None };
222 }
223 }
224 }
225 collector.libs
226}
227
228pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
229 match lib.cfg {
230 Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(),
231 None => true,
232 }
233}
234
235struct Collector<'tcx> {
236 tcx: TyCtxt<'tcx>,
237 libs: Vec<NativeLib>,
238}
239
240impl<'tcx> Collector<'tcx> {
241 fn process_module(&mut self, module: &ForeignModule) {
242 let ForeignModule { def_id, abi, ref foreign_items } = *module;
243 let def_id = def_id.expect_local();
244
245 let sess = self.tcx.sess;
246
247 if #[allow(non_exhaustive_omitted_patterns)] match abi {
ExternAbi::Rust => true,
_ => false,
}matches!(abi, ExternAbi::Rust) {
248 return;
249 }
250
251 for attr in {
{
'done:
{
for i in
::rustc_hir::attrs::HasAttrs::get_attrs(def_id, &self.tcx) {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(Link(links, _)) => {
break 'done Some(links);
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(self.tcx, def_id, Link(links, _) => links).into_flat_iter() {
252 let dll_imports = match attr.kind {
253 NativeLibKind::RawDylib { .. } => foreign_items
254 .iter()
255 .filter_map(|&child_item| {
256 self.build_dll_import(
257 abi,
258 attr.import_name_type.map(|(import_name_type, _)| import_name_type),
259 child_item,
260 )
261 })
262 .collect(),
263 _ => {
264 for &child_item in foreign_items {
265 if let Some(span) =
266 {
{
'done:
{
for i in
::rustc_hir::attrs::HasAttrs::get_attrs(child_item, &self.tcx)
{
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(LinkOrdinal { span, .. }) => {
break 'done Some(*span);
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(self.tcx, child_item, LinkOrdinal {span, ..} => *span)
267 {
268 sess.dcx().emit_err(diagnostics::LinkOrdinalRawDylib { span });
269 }
270 }
271
272 Vec::new()
273 }
274 };
275
276 let filename = find_bundled_library(
277 attr.name,
278 attr.verbatim,
279 attr.kind,
280 attr.cfg.is_some(),
281 self.tcx,
282 );
283 self.libs.push(NativeLib {
284 name: attr.name,
285 filename,
286 kind: attr.kind,
287 cfg: attr.cfg.clone(),
288 foreign_module: Some(def_id.to_def_id()),
289 verbatim: attr.verbatim,
290 dll_imports,
291 });
292 }
293 }
294
295 fn process_command_line(&mut self) {
297 let mut renames = FxHashSet::default();
299 for lib in &self.tcx.sess.opts.libs {
300 if let NativeLibKind::Framework { .. } = lib.kind
301 && !self.tcx.sess.target.is_like_darwin
302 {
303 self.tcx.dcx().emit_err(diagnostics::LibFrameworkApple);
305 }
306 if let Some(ref new_name) = lib.new_name {
307 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
308 if new_name.is_empty() {
309 self.tcx
310 .dcx()
311 .emit_err(diagnostics::EmptyRenamingTarget { lib_name: &lib.name });
312 } else if !any_duplicate {
313 self.tcx.dcx().emit_err(diagnostics::RenamingNoLink { lib_name: &lib.name });
314 } else if !renames.insert(&lib.name) {
315 self.tcx.dcx().emit_err(diagnostics::MultipleRenamings { lib_name: &lib.name });
316 }
317 }
318 }
319
320 for passed_lib in &self.tcx.sess.opts.libs {
328 let mut existing = self
332 .libs
333 .extract_if(.., |lib| {
334 if lib.name.as_str() == passed_lib.name {
335 if lib.has_modifiers() || passed_lib.has_modifiers() {
339 match lib.foreign_module {
340 Some(def_id) => {
341 self.tcx.dcx().emit_err(diagnostics::NoLinkModOverride {
342 span: Some(self.tcx.def_span(def_id)),
343 })
344 }
345 None => self
346 .tcx
347 .dcx()
348 .emit_err(diagnostics::NoLinkModOverride { span: None }),
349 };
350 }
351 if passed_lib.kind != NativeLibKind::Unspecified {
352 lib.kind = passed_lib.kind;
353 }
354 if let Some(new_name) = &passed_lib.new_name {
355 lib.name = Symbol::intern(new_name);
356 }
357 lib.verbatim = passed_lib.verbatim;
358 return true;
359 }
360 false
361 })
362 .collect::<Vec<_>>();
363 if existing.is_empty() {
364 let new_name: Option<&str> = passed_lib.new_name.as_deref();
366 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
367 let filename = find_bundled_library(
368 name,
369 passed_lib.verbatim,
370 passed_lib.kind,
371 false,
372 self.tcx,
373 );
374 self.libs.push(NativeLib {
375 name,
376 filename,
377 kind: passed_lib.kind,
378 cfg: None,
379 foreign_module: None,
380 verbatim: passed_lib.verbatim,
381 dll_imports: Vec::new(),
382 });
383 } else {
384 self.libs.append(&mut existing);
387 }
388 }
389 }
390
391 fn i686_arg_list_size(&self, item: DefId) -> usize {
392 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
393 self.tcx
394 .type_of(item)
395 .instantiate_identity()
396 .skip_norm_wip()
397 .fn_sig(self.tcx)
398 .inputs()
399 .map_bound(|slice| self.tcx.mk_type_list(slice)),
400 );
401
402 argument_types
403 .iter()
404 .map(|ty| {
405 let layout = self
406 .tcx
407 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
408 .expect("layout")
409 .layout;
410 (layout.size().bytes_usize() + 3) & !3
413 })
414 .sum()
415 }
416
417 fn build_dll_import(
418 &self,
419 abi: ExternAbi,
420 import_name_type: Option<PeImportNameType>,
421 item: DefId,
422 ) -> Option<DllImport> {
423 let span = self.tcx.def_span(item);
424
425 if !self.tcx.sess.target.is_abi_supported(abi) {
::core::panicking::panic("assertion failed: self.tcx.sess.target.is_abi_supported(abi)")
};assert!(self.tcx.sess.target.is_abi_supported(abi));
428
429 let calling_convention = if self.tcx.sess.target.arch == Arch::X86 {
433 match abi {
434 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
435 ExternAbi::Stdcall { .. } => {
436 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
437 }
438 ExternAbi::System { .. } => {
442 let c_variadic = self
443 .tcx
444 .type_of(item)
445 .instantiate_identity()
446 .skip_norm_wip()
447 .fn_sig(self.tcx)
448 .c_variadic();
449
450 if c_variadic {
451 DllCallingConvention::C
452 } else {
453 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
454 }
455 }
456 ExternAbi::Fastcall { .. } => {
457 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
458 }
459 ExternAbi::Vectorcall { .. } => {
460 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
461 }
462 _ => {
463 self.tcx.dcx().emit_fatal(diagnostics::RawDylibUnsupportedAbi { span });
464 }
465 }
466 } else {
467 match abi {
468 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
469 DllCallingConvention::C
470 }
471 _ => {
472 self.tcx.dcx().emit_fatal(diagnostics::RawDylibUnsupportedAbi { span });
473 }
474 }
475 };
476
477 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
478 let import_name_type = codegen_fn_attrs
479 .link_ordinal
480 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
481
482 let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
483
484 if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
485 let name = name.as_str();
486 if name.contains('\0') {
487 self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
488 } else if let Some((left, right)) = name.split_once('@')
489 && (left.is_empty() || right.is_empty() || right.contains('@'))
490 {
491 self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
492 }
493 }
494
495 let def_kind = self.tcx.def_kind(item);
496 let symbol_type = if def_kind.is_fn_like() {
497 DllImportSymbolType::Function
498 } else if #[allow(non_exhaustive_omitted_patterns)] match def_kind {
DefKind::Static { .. } => true,
_ => false,
}matches!(def_kind, DefKind::Static { .. }) {
499 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
500 DllImportSymbolType::ThreadLocal
501 } else {
502 DllImportSymbolType::Static
503 }
504 } else if def_kind == DefKind::ForeignTy {
505 return None;
506 } else {
507 ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected type for raw-dylib: {0}",
def_kind.descr(item)));bug!("Unexpected type for raw-dylib: {}", def_kind.descr(item));
508 };
509
510 let size = match symbol_type {
511 DllImportSymbolType::Function => rustc_abi::Size::ZERO,
513 DllImportSymbolType::Static | DllImportSymbolType::ThreadLocal => {
514 let ty = self.tcx.type_of(item).instantiate_identity().skip_norm_wip();
515 self.tcx
516 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
517 .ok()
518 .map(|layout| layout.size)
519 .unwrap_or_else(|| ::rustc_middle::util::bug::bug_fmt(format_args!("Non-function symbols must have a size"))bug!("Non-function symbols must have a size"))
520 }
521 };
522
523 Some(DllImport { name, import_name_type, calling_convention, span, symbol_type, size })
524 }
525}