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::cstore::{
16 DllCallingConvention, DllImport, DllImportSymbolType, ForeignModule, NativeLib,
17};
18use rustc_session::search_paths::PathKind;
19use rustc_span::Symbol;
20use rustc_span::def_id::{DefId, LOCAL_CRATE};
21use rustc_target::spec::{Arch, BinaryFormat, CfgAbi, Env, LinkSelfContainedComponents, Os};
22
23use crate::diagnostics;
24
25pub 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 ) -> ControlFlow<R>,
37) -> ControlFlow<R> {
38 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 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 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 let has_shared_llvm_apple_darwin =
65 sess.target.is_like_darwin && sess.target_tlib_path.dir.join("libLLVM.dylib").exists();
66
67 if sess.target.cfg_abi == CfgAbi::Fortanix
79 || sess.target.os == Os::Linux
80 || sess.target.os == Os::Fuchsia
81 || sess.target.is_like_aix
82 || sess.target.is_like_darwin
83 && (!sess.sanitizers().is_empty() || has_shared_llvm_apple_darwin)
84 || sess.target.os == Os::Windows
85 && sess.target.env == Env::Gnu
86 && sess.target.cfg_abi == CfgAbi::Llvm
87 {
88 f(&sess.target_tlib_path.dir, false)?;
89 }
90
91 if let Some(sdk_root) = apple_sdk_root
94 && sess.target.env == Env::MacAbi
95 {
96 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
97 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
98 }
99
100 ControlFlow::Continue(())
101}
102
103pub fn try_find_native_static_library(
104 sess: &Session,
105 name: &str,
106 verbatim: bool,
107) -> Option<PathBuf> {
108 let default = sess.staticlib_components(verbatim);
109 let formats = if verbatim {
110 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
111 } else {
112 let unix = ("lib", ".a");
115 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] }
116 };
117
118 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
119 if !is_framework {
120 for (prefix, suffix) in &formats {
121 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
122 if test.exists() {
123 return ControlFlow::Break(test);
124 }
125 }
126 }
127 ControlFlow::Continue(())
128 })
129 .break_value()
130}
131
132pub fn try_find_native_dynamic_library(
133 sess: &Session,
134 name: &str,
135 verbatim: bool,
136) -> Option<PathBuf> {
137 let default = sess.staticlib_components(verbatim);
138 let formats = if verbatim {
139 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
140 } else {
141 let meson = ("lib", ".dll.a");
145 let mingw = ("lib", ".a");
147 ::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]
148 };
149
150 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
151 if !is_framework {
152 for (prefix, suffix) in &formats {
153 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
154 if test.exists() {
155 return ControlFlow::Break(test);
156 }
157 }
158 }
159 ControlFlow::Continue(())
160 })
161 .break_value()
162}
163
164pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
165 try_find_native_static_library(sess, name, verbatim).unwrap_or_else(|| {
166 sess.dcx().emit_fatal(diagnostics::MissingNativeLibrary::new(name, verbatim))
167 })
168}
169
170pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
171 let mut collector = Collector { tcx, libs: Vec::new() };
172 if tcx.sess.opts.unstable_opts.link_directives {
173 for module in tcx.foreign_modules(LOCAL_CRATE).values() {
174 collector.process_module(module);
175 }
176 }
177 collector.process_command_line();
178 for lib in &mut collector.libs {
179 if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest {
183 if let NativeLibKind::Static { .. } = lib.kind {
184 if !tcx.sess.opts.unstable_opts.ui_testing {
185 let diag = if lib.foreign_module.is_none() {
186 diagnostics::StaticLinkingNotSupported::UserRequested {
187 lib_name: lib.name,
188 target: tcx.sess.target.llvm_target.as_ref(),
189 }
190 } else {
191 diagnostics::StaticLinkingNotSupported::FromDependency {
192 lib_name: lib.name,
193 target: tcx.sess.target.llvm_target.as_ref(),
194 }
195 };
196 tcx.dcx().emit_warn(diag);
197 }
198
199 lib.kind = NativeLibKind::Dylib { as_needed: None };
200 }
201 }
202 }
203 collector.libs
204}
205
206pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
207 match lib.cfg {
208 Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(),
209 None => true,
210 }
211}
212
213struct Collector<'tcx> {
214 tcx: TyCtxt<'tcx>,
215 libs: Vec<NativeLib>,
216}
217
218impl<'tcx> Collector<'tcx> {
219 fn process_module(&mut self, module: &ForeignModule) {
220 let ForeignModule { def_id, abi, ref foreign_items } = *module;
221 let def_id = def_id.expect_local();
222
223 let sess = self.tcx.sess;
224
225 if #[allow(non_exhaustive_omitted_patterns)] match abi {
ExternAbi::Rust => true,
_ => false,
}matches!(abi, ExternAbi::Rust) {
226 return;
227 }
228
229 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() {
230 let dll_imports = match attr.kind {
231 NativeLibKind::RawDylib { .. } => foreign_items
232 .iter()
233 .filter_map(|&child_item| {
234 self.build_dll_import(
235 abi,
236 attr.import_name_type.map(|(import_name_type, _)| import_name_type),
237 child_item,
238 )
239 })
240 .collect(),
241 _ => {
242 for &child_item in foreign_items {
243 if let Some(span) =
244 {
{
'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)
245 {
246 sess.dcx().emit_err(diagnostics::LinkOrdinalRawDylib { span });
247 }
248 }
249
250 Vec::new()
251 }
252 };
253
254 self.libs.push(NativeLib {
255 name: attr.name,
256 kind: attr.kind,
257 cfg: attr.cfg.clone(),
258 foreign_module: Some(def_id.to_def_id()),
259 verbatim: attr.verbatim,
260 dll_imports,
261 });
262 }
263 }
264
265 fn process_command_line(&mut self) {
267 let mut renames = FxHashSet::default();
269 for lib in &self.tcx.sess.opts.libs {
270 if let NativeLibKind::Framework { .. } = lib.kind
271 && !self.tcx.sess.target.is_like_darwin
272 {
273 self.tcx.dcx().emit_err(diagnostics::LibFrameworkApple);
275 }
276 if let Some(ref new_name) = lib.new_name {
277 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
278 if new_name.is_empty() {
279 self.tcx
280 .dcx()
281 .emit_err(diagnostics::EmptyRenamingTarget { lib_name: &lib.name });
282 } else if !any_duplicate {
283 self.tcx.dcx().emit_err(diagnostics::RenamingNoLink { lib_name: &lib.name });
284 } else if !renames.insert(&lib.name) {
285 self.tcx.dcx().emit_err(diagnostics::MultipleRenamings { lib_name: &lib.name });
286 }
287 }
288 }
289
290 for passed_lib in &self.tcx.sess.opts.libs {
298 let mut existing = self
302 .libs
303 .extract_if(.., |lib| {
304 if lib.name.as_str() == passed_lib.name {
305 if lib.has_modifiers() || passed_lib.has_modifiers() {
309 match lib.foreign_module {
310 Some(def_id) => {
311 self.tcx.dcx().emit_err(diagnostics::NoLinkModOverride {
312 span: Some(self.tcx.def_span(def_id)),
313 })
314 }
315 None => self
316 .tcx
317 .dcx()
318 .emit_err(diagnostics::NoLinkModOverride { span: None }),
319 };
320 }
321 if passed_lib.kind != NativeLibKind::Unspecified {
322 lib.kind = passed_lib.kind;
323 }
324 if let Some(new_name) = &passed_lib.new_name {
325 lib.name = Symbol::intern(new_name);
326 }
327 lib.verbatim = passed_lib.verbatim;
328 return true;
329 }
330 false
331 })
332 .collect::<Vec<_>>();
333 if existing.is_empty() {
334 let new_name: Option<&str> = passed_lib.new_name.as_deref();
336 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
337 self.libs.push(NativeLib {
338 name,
339 kind: passed_lib.kind,
340 cfg: None,
341 foreign_module: None,
342 verbatim: passed_lib.verbatim,
343 dll_imports: Vec::new(),
344 });
345 } else {
346 self.libs.append(&mut existing);
349 }
350 }
351 }
352
353 fn i686_arg_list_size(&self, item: DefId) -> usize {
354 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
355 self.tcx
356 .type_of(item)
357 .instantiate_identity()
358 .skip_norm_wip()
359 .fn_sig(self.tcx)
360 .inputs()
361 .map_bound(|slice| self.tcx.mk_type_list(slice)),
362 );
363
364 argument_types
365 .iter()
366 .map(|ty| {
367 let layout = self
368 .tcx
369 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
370 .expect("layout")
371 .layout;
372 (layout.size().bytes_usize() + 3) & !3
375 })
376 .sum()
377 }
378
379 fn build_dll_import(
380 &self,
381 abi: ExternAbi,
382 import_name_type: Option<PeImportNameType>,
383 item: DefId,
384 ) -> Option<DllImport> {
385 let span = self.tcx.def_span(item);
386
387 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));
390
391 let calling_convention = if self.tcx.sess.target.arch == Arch::X86 {
395 match abi {
396 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
397 ExternAbi::Stdcall { .. } => {
398 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
399 }
400 ExternAbi::System { .. } => {
404 let c_variadic = self
405 .tcx
406 .type_of(item)
407 .instantiate_identity()
408 .skip_norm_wip()
409 .fn_sig(self.tcx)
410 .c_variadic();
411
412 if c_variadic {
413 DllCallingConvention::C
414 } else {
415 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
416 }
417 }
418 ExternAbi::Fastcall { .. } => {
419 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
420 }
421 ExternAbi::Vectorcall { .. } => {
422 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
423 }
424 _ => {
425 self.tcx.dcx().emit_fatal(diagnostics::RawDylibUnsupportedAbi { span });
426 }
427 }
428 } else {
429 match abi {
430 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
431 DllCallingConvention::C
432 }
433 _ => {
434 self.tcx.dcx().emit_fatal(diagnostics::RawDylibUnsupportedAbi { span });
435 }
436 }
437 };
438
439 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
440 let import_name_type = codegen_fn_attrs
441 .link_ordinal
442 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
443
444 let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
445
446 if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
447 let name = name.as_str();
448 if name.contains('\0') {
449 self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
450 } else if let Some((left, right)) = name.split_once('@')
451 && (left.is_empty() || right.is_empty() || right.contains('@'))
452 {
453 self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
454 }
455 }
456
457 let def_kind = self.tcx.def_kind(item);
458 let symbol_type = if def_kind.is_fn_like() {
459 DllImportSymbolType::Function
460 } else if #[allow(non_exhaustive_omitted_patterns)] match def_kind {
DefKind::Static { .. } => true,
_ => false,
}matches!(def_kind, DefKind::Static { .. }) {
461 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
462 DllImportSymbolType::ThreadLocal
463 } else {
464 DllImportSymbolType::Static
465 }
466 } else if def_kind == DefKind::ForeignTy {
467 return None;
468 } else {
469 ::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));
470 };
471
472 let size = match symbol_type {
473 DllImportSymbolType::Function => rustc_abi::Size::ZERO,
475 DllImportSymbolType::Static | DllImportSymbolType::ThreadLocal => {
476 let ty = self.tcx.type_of(item).instantiate_identity().skip_norm_wip();
477 self.tcx
478 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
479 .ok()
480 .map(|layout| layout.size)
481 .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"))
482 }
483 };
484
485 Some(DllImport { name, import_name_type, calling_convention, span, symbol_type, size })
486 }
487}