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