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::find_attr;
9use rustc_middle::query::LocalCrate;
10use rustc_middle::ty::{self, List, Ty, TyCtxt};
11use rustc_session::Session;
12use rustc_session::config::CrateType;
13use rustc_session::cstore::{DllCallingConvention, DllImport, ForeignModule, NativeLib};
14use rustc_session::search_paths::PathKind;
15use rustc_span::Symbol;
16use rustc_span::def_id::{DefId, LOCAL_CRATE};
17use rustc_target::spec::{Abi, Arch, BinaryFormat, Env, LinkSelfContainedComponents, Os};
18
19use crate::errors;
20
21pub struct NativeLibSearchFallback<'a> {
25 pub self_contained_components: LinkSelfContainedComponents,
26 pub apple_sdk_root: Option<&'a Path>,
27}
28
29pub fn walk_native_lib_search_dirs<R>(
30 sess: &Session,
31 fallback: Option<NativeLibSearchFallback<'_>>,
32 mut f: impl FnMut(&Path, bool ) -> ControlFlow<R>,
33) -> ControlFlow<R> {
34 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Native) {
36 f(&search_path.dir, false)?;
37 }
38 for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) {
39 if search_path.kind != PathKind::All {
41 f(&search_path.dir, true)?;
42 }
43 }
44
45 let Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }) = fallback
46 else {
47 return ControlFlow::Continue(());
48 };
49
50 if self_contained_components.intersects(
53 LinkSelfContainedComponents::LIBC
54 | LinkSelfContainedComponents::UNWIND
55 | LinkSelfContainedComponents::MINGW,
56 ) {
57 f(&sess.target_tlib_path.dir.join("self-contained"), false)?;
58 }
59
60 if sess.target.abi == Abi::Fortanix
72 || sess.target.os == Os::Linux
73 || sess.target.os == Os::Fuchsia
74 || sess.target.is_like_aix
75 || sess.target.is_like_darwin && !sess.sanitizers().is_empty()
76 || sess.target.os == Os::Windows
77 && sess.target.env == Env::Gnu
78 && sess.target.abi == Abi::Llvm
79 {
80 f(&sess.target_tlib_path.dir, false)?;
81 }
82
83 if let Some(sdk_root) = apple_sdk_root
86 && sess.target.env == Env::MacAbi
87 {
88 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
89 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
90 }
91
92 ControlFlow::Continue(())
93}
94
95pub fn try_find_native_static_library(
96 sess: &Session,
97 name: &str,
98 verbatim: bool,
99) -> Option<PathBuf> {
100 let default = sess.staticlib_components(verbatim);
101 let formats = if verbatim {
102 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
103 } else {
104 let unix = ("lib", ".a");
107 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] }
108 };
109
110 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
111 if !is_framework {
112 for (prefix, suffix) in &formats {
113 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
114 if test.exists() {
115 return ControlFlow::Break(test);
116 }
117 }
118 }
119 ControlFlow::Continue(())
120 })
121 .break_value()
122}
123
124pub fn try_find_native_dynamic_library(
125 sess: &Session,
126 name: &str,
127 verbatim: bool,
128) -> Option<PathBuf> {
129 let default = sess.staticlib_components(verbatim);
130 let formats = if verbatim {
131 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[default]))vec![default]
132 } else {
133 let meson = ("lib", ".dll.a");
137 let mingw = ("lib", ".a");
139 ::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]
140 };
141
142 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
143 if !is_framework {
144 for (prefix, suffix) in &formats {
145 let test = dir.join(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix, name, suffix))
})format!("{prefix}{name}{suffix}"));
146 if test.exists() {
147 return ControlFlow::Break(test);
148 }
149 }
150 }
151 ControlFlow::Continue(())
152 })
153 .break_value()
154}
155
156pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
157 try_find_native_static_library(sess, name, verbatim)
158 .unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
159}
160
161fn find_bundled_library(
162 name: Symbol,
163 verbatim: Option<bool>,
164 kind: NativeLibKind,
165 has_cfg: bool,
166 tcx: TyCtxt<'_>,
167) -> Option<Symbol> {
168 let sess = tcx.sess;
169 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind
170 && 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))
171 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
172 {
173 let verbatim = verbatim.unwrap_or(false);
174 return find_native_static_library(name.as_str(), verbatim, sess)
175 .file_name()
176 .and_then(|s| s.to_str())
177 .map(Symbol::intern);
178 }
179 None
180}
181
182pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
183 let mut collector = Collector { tcx, libs: Vec::new() };
184 if tcx.sess.opts.unstable_opts.link_directives {
185 for module in tcx.foreign_modules(LOCAL_CRATE).values() {
186 collector.process_module(module);
187 }
188 }
189 collector.process_command_line();
190 collector.libs
191}
192
193pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
194 match lib.cfg {
195 Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(),
196 None => true,
197 }
198}
199
200struct Collector<'tcx> {
201 tcx: TyCtxt<'tcx>,
202 libs: Vec<NativeLib>,
203}
204
205impl<'tcx> Collector<'tcx> {
206 fn process_module(&mut self, module: &ForeignModule) {
207 let ForeignModule { def_id, abi, ref foreign_items } = *module;
208 let def_id = def_id.expect_local();
209
210 let sess = self.tcx.sess;
211
212 if #[allow(non_exhaustive_omitted_patterns)] match abi {
ExternAbi::Rust => true,
_ => false,
}matches!(abi, ExternAbi::Rust) {
213 return;
214 }
215
216 for attr in
217 {
#[allow(deprecated)]
{
{
'done:
{
for i in self.tcx.get_all_attrs(def_id) {
#[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()
218 {
219 let dll_imports = match attr.kind {
220 NativeLibKind::RawDylib { .. } => foreign_items
221 .iter()
222 .map(|&child_item| {
223 self.build_dll_import(
224 abi,
225 attr.import_name_type.map(|(import_name_type, _)| import_name_type),
226 child_item,
227 )
228 })
229 .collect(),
230 _ => {
231 for &child_item in foreign_items {
232 if let Some(span) =
233 {
#[allow(deprecated)]
{
{
'done:
{
for i in self.tcx.get_all_attrs(child_item) {
#[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)
234 {
235 sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span });
236 }
237 }
238
239 Vec::new()
240 }
241 };
242
243 let filename = find_bundled_library(
244 attr.name,
245 attr.verbatim,
246 attr.kind,
247 attr.cfg.is_some(),
248 self.tcx,
249 );
250 self.libs.push(NativeLib {
251 name: attr.name,
252 filename,
253 kind: attr.kind,
254 cfg: attr.cfg.clone(),
255 foreign_module: Some(def_id.to_def_id()),
256 verbatim: attr.verbatim,
257 dll_imports,
258 });
259 }
260 }
261
262 fn process_command_line(&mut self) {
264 let mut renames = FxHashSet::default();
266 for lib in &self.tcx.sess.opts.libs {
267 if let NativeLibKind::Framework { .. } = lib.kind
268 && !self.tcx.sess.target.is_like_darwin
269 {
270 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
272 }
273 if let Some(ref new_name) = lib.new_name {
274 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
275 if new_name.is_empty() {
276 self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
277 } else if !any_duplicate {
278 self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
279 } else if !renames.insert(&lib.name) {
280 self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
281 }
282 }
283 }
284
285 for passed_lib in &self.tcx.sess.opts.libs {
293 let mut existing = self
297 .libs
298 .extract_if(.., |lib| {
299 if lib.name.as_str() == passed_lib.name {
300 if lib.has_modifiers() || passed_lib.has_modifiers() {
304 match lib.foreign_module {
305 Some(def_id) => {
306 self.tcx.dcx().emit_err(errors::NoLinkModOverride {
307 span: Some(self.tcx.def_span(def_id)),
308 })
309 }
310 None => self
311 .tcx
312 .dcx()
313 .emit_err(errors::NoLinkModOverride { span: None }),
314 };
315 }
316 if passed_lib.kind != NativeLibKind::Unspecified {
317 lib.kind = passed_lib.kind;
318 }
319 if let Some(new_name) = &passed_lib.new_name {
320 lib.name = Symbol::intern(new_name);
321 }
322 lib.verbatim = passed_lib.verbatim;
323 return true;
324 }
325 false
326 })
327 .collect::<Vec<_>>();
328 if existing.is_empty() {
329 let new_name: Option<&str> = passed_lib.new_name.as_deref();
331 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
332 let filename = find_bundled_library(
333 name,
334 passed_lib.verbatim,
335 passed_lib.kind,
336 false,
337 self.tcx,
338 );
339 self.libs.push(NativeLib {
340 name,
341 filename,
342 kind: passed_lib.kind,
343 cfg: None,
344 foreign_module: None,
345 verbatim: passed_lib.verbatim,
346 dll_imports: Vec::new(),
347 });
348 } else {
349 self.libs.append(&mut existing);
352 }
353 }
354 }
355
356 fn i686_arg_list_size(&self, item: DefId) -> usize {
357 let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
358 self.tcx
359 .type_of(item)
360 .instantiate_identity()
361 .fn_sig(self.tcx)
362 .inputs()
363 .map_bound(|slice| self.tcx.mk_type_list(slice)),
364 );
365
366 argument_types
367 .iter()
368 .map(|ty| {
369 let layout = self
370 .tcx
371 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
372 .expect("layout")
373 .layout;
374 (layout.size().bytes_usize() + 3) & !3
377 })
378 .sum()
379 }
380
381 fn build_dll_import(
382 &self,
383 abi: ExternAbi,
384 import_name_type: Option<PeImportNameType>,
385 item: DefId,
386 ) -> DllImport {
387 let span = self.tcx.def_span(item);
388
389 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));
392
393 let calling_convention = if self.tcx.sess.target.arch == Arch::X86 {
397 match abi {
398 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
399 ExternAbi::Stdcall { .. } => {
400 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
401 }
402 ExternAbi::System { .. } => {
406 let c_variadic =
407 self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
408
409 if c_variadic {
410 DllCallingConvention::C
411 } else {
412 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
413 }
414 }
415 ExternAbi::Fastcall { .. } => {
416 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
417 }
418 ExternAbi::Vectorcall { .. } => {
419 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
420 }
421 _ => {
422 self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
423 }
424 }
425 } else {
426 match abi {
427 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
428 DllCallingConvention::C
429 }
430 _ => {
431 self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
432 }
433 }
434 };
435
436 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
437 let import_name_type = codegen_fn_attrs
438 .link_ordinal
439 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
440
441 let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
442
443 if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
444 let name = name.as_str();
445 if name.contains('\0') {
446 self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
447 } else if let Some((left, right)) = name.split_once('@')
448 && (left.is_empty() || right.is_empty() || right.contains('@'))
449 {
450 self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
451 }
452 }
453
454 DllImport {
455 name,
456 import_name_type,
457 calling_convention,
458 span,
459 is_fn: self.tcx.def_kind(item).is_fn_like(),
460 }
461 }
462}