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::{AttributeKind, 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
70 || sess.target.os == Os::Linux
71 || sess.target.os == Os::Fuchsia
72 || sess.target.is_like_aix
73 || sess.target.is_like_darwin && !sess.sanitizers().is_empty()
74 {
75 f(&sess.target_tlib_path.dir, false)?;
76 }
77
78 if let Some(sdk_root) = apple_sdk_root
81 && sess.target.env == Env::MacAbi
82 {
83 f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
84 f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;
85 }
86
87 ControlFlow::Continue(())
88}
89
90pub fn try_find_native_static_library(
91 sess: &Session,
92 name: &str,
93 verbatim: bool,
94) -> Option<PathBuf> {
95 let default = sess.staticlib_components(verbatim);
96 let formats = if verbatim {
97 vec![default]
98 } else {
99 let unix = ("lib", ".a");
102 if default == unix { vec![default] } else { vec![default, unix] }
103 };
104
105 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
106 if !is_framework {
107 for (prefix, suffix) in &formats {
108 let test = dir.join(format!("{prefix}{name}{suffix}"));
109 if test.exists() {
110 return ControlFlow::Break(test);
111 }
112 }
113 }
114 ControlFlow::Continue(())
115 })
116 .break_value()
117}
118
119pub fn try_find_native_dynamic_library(
120 sess: &Session,
121 name: &str,
122 verbatim: bool,
123) -> Option<PathBuf> {
124 let default = sess.staticlib_components(verbatim);
125 let formats = if verbatim {
126 vec![default]
127 } else {
128 let meson = ("lib", ".dll.a");
132 let mingw = ("lib", ".a");
134 vec![default, meson, mingw]
135 };
136
137 walk_native_lib_search_dirs(sess, None, |dir, is_framework| {
138 if !is_framework {
139 for (prefix, suffix) in &formats {
140 let test = dir.join(format!("{prefix}{name}{suffix}"));
141 if test.exists() {
142 return ControlFlow::Break(test);
143 }
144 }
145 }
146 ControlFlow::Continue(())
147 })
148 .break_value()
149}
150
151pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf {
152 try_find_native_static_library(sess, name, verbatim)
153 .unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
154}
155
156fn find_bundled_library(
157 name: Symbol,
158 verbatim: Option<bool>,
159 kind: NativeLibKind,
160 has_cfg: bool,
161 tcx: TyCtxt<'_>,
162) -> Option<Symbol> {
163 let sess = tcx.sess;
164 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
165 && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
166 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
167 {
168 let verbatim = verbatim.unwrap_or(false);
169 return find_native_static_library(name.as_str(), verbatim, sess)
170 .file_name()
171 .and_then(|s| s.to_str())
172 .map(Symbol::intern);
173 }
174 None
175}
176
177pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
178 let mut collector = Collector { tcx, libs: Vec::new() };
179 if tcx.sess.opts.unstable_opts.link_directives {
180 for module in tcx.foreign_modules(LOCAL_CRATE).values() {
181 collector.process_module(module);
182 }
183 }
184 collector.process_command_line();
185 collector.libs
186}
187
188pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
189 match lib.cfg {
190 Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(),
191 None => true,
192 }
193}
194
195struct Collector<'tcx> {
196 tcx: TyCtxt<'tcx>,
197 libs: Vec<NativeLib>,
198}
199
200impl<'tcx> Collector<'tcx> {
201 fn process_module(&mut self, module: &ForeignModule) {
202 let ForeignModule { def_id, abi, ref foreign_items } = *module;
203 let def_id = def_id.expect_local();
204
205 let sess = self.tcx.sess;
206
207 if matches!(abi, ExternAbi::Rust) {
208 return;
209 }
210
211 for attr in
212 find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::Link(links, _) => links)
213 .iter()
214 .map(|v| v.iter())
215 .flatten()
216 {
217 let dll_imports = match attr.kind {
218 NativeLibKind::RawDylib { .. } => foreign_items
219 .iter()
220 .map(|&child_item| {
221 self.build_dll_import(
222 abi,
223 attr.import_name_type.map(|(import_name_type, _)| import_name_type),
224 child_item,
225 )
226 })
227 .collect(),
228 _ => {
229 for &child_item in foreign_items {
230 if let Some(span) = find_attr!(self.tcx.get_all_attrs(child_item), AttributeKind::LinkOrdinal {span, ..} => *span)
231 {
232 sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span });
233 }
234 }
235
236 Vec::new()
237 }
238 };
239
240 let filename = find_bundled_library(
241 attr.name,
242 attr.verbatim,
243 attr.kind,
244 attr.cfg.is_some(),
245 self.tcx,
246 );
247 self.libs.push(NativeLib {
248 name: attr.name,
249 filename,
250 kind: attr.kind,
251 cfg: attr.cfg.clone(),
252 foreign_module: Some(def_id.to_def_id()),
253 verbatim: attr.verbatim,
254 dll_imports,
255 });
256 }
257 }
258
259 fn process_command_line(&mut self) {
261 let mut renames = FxHashSet::default();
263 for lib in &self.tcx.sess.opts.libs {
264 if let NativeLibKind::Framework { .. } = lib.kind
265 && !self.tcx.sess.target.is_like_darwin
266 {
267 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
269 }
270 if let Some(ref new_name) = lib.new_name {
271 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
272 if new_name.is_empty() {
273 self.tcx.dcx().emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
274 } else if !any_duplicate {
275 self.tcx.dcx().emit_err(errors::RenamingNoLink { lib_name: &lib.name });
276 } else if !renames.insert(&lib.name) {
277 self.tcx.dcx().emit_err(errors::MultipleRenamings { lib_name: &lib.name });
278 }
279 }
280 }
281
282 for passed_lib in &self.tcx.sess.opts.libs {
290 let mut existing = self
294 .libs
295 .extract_if(.., |lib| {
296 if lib.name.as_str() == passed_lib.name {
297 if lib.has_modifiers() || passed_lib.has_modifiers() {
301 match lib.foreign_module {
302 Some(def_id) => {
303 self.tcx.dcx().emit_err(errors::NoLinkModOverride {
304 span: Some(self.tcx.def_span(def_id)),
305 })
306 }
307 None => self
308 .tcx
309 .dcx()
310 .emit_err(errors::NoLinkModOverride { span: None }),
311 };
312 }
313 if passed_lib.kind != NativeLibKind::Unspecified {
314 lib.kind = passed_lib.kind;
315 }
316 if let Some(new_name) = &passed_lib.new_name {
317 lib.name = Symbol::intern(new_name);
318 }
319 lib.verbatim = passed_lib.verbatim;
320 return true;
321 }
322 false
323 })
324 .collect::<Vec<_>>();
325 if existing.is_empty() {
326 let new_name: Option<&str> = passed_lib.new_name.as_deref();
328 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
329 let filename = find_bundled_library(
330 name,
331 passed_lib.verbatim,
332 passed_lib.kind,
333 false,
334 self.tcx,
335 );
336 self.libs.push(NativeLib {
337 name,
338 filename,
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 .fn_sig(self.tcx)
359 .inputs()
360 .map_bound(|slice| self.tcx.mk_type_list(slice)),
361 );
362
363 argument_types
364 .iter()
365 .map(|ty| {
366 let layout = self
367 .tcx
368 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
369 .expect("layout")
370 .layout;
371 (layout.size().bytes_usize() + 3) & !3
374 })
375 .sum()
376 }
377
378 fn build_dll_import(
379 &self,
380 abi: ExternAbi,
381 import_name_type: Option<PeImportNameType>,
382 item: DefId,
383 ) -> DllImport {
384 let span = self.tcx.def_span(item);
385
386 assert!(self.tcx.sess.target.is_abi_supported(abi));
389
390 let calling_convention = if self.tcx.sess.target.arch == Arch::X86 {
394 match abi {
395 ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
396 ExternAbi::Stdcall { .. } => {
397 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
398 }
399 ExternAbi::System { .. } => {
403 let c_variadic =
404 self.tcx.type_of(item).instantiate_identity().fn_sig(self.tcx).c_variadic();
405
406 if c_variadic {
407 DllCallingConvention::C
408 } else {
409 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
410 }
411 }
412 ExternAbi::Fastcall { .. } => {
413 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
414 }
415 ExternAbi::Vectorcall { .. } => {
416 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
417 }
418 _ => {
419 self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
420 }
421 }
422 } else {
423 match abi {
424 ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
425 DllCallingConvention::C
426 }
427 _ => {
428 self.tcx.dcx().emit_fatal(errors::RawDylibUnsupportedAbi { span });
429 }
430 }
431 };
432
433 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
434 let import_name_type = codegen_fn_attrs
435 .link_ordinal
436 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
437
438 let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
439
440 if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
441 let name = name.as_str();
442 if name.contains('\0') {
443 self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
444 } else if let Some((left, right)) = name.split_once('@')
445 && (left.is_empty() || right.is_empty() || right.contains('@'))
446 {
447 self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
448 }
449 }
450
451 DllImport {
452 name,
453 import_name_type,
454 calling_convention,
455 span,
456 is_fn: self.tcx.def_kind(item).is_fn_like(),
457 }
458 }
459}