]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_metadata/src/native_libs.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / compiler / rustc_metadata / src / native_libs.rs
1 use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
2 use rustc_attr as attr;
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_hir as hir;
5 use rustc_hir::def::DefKind;
6 use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
7 use rustc_session::config::CrateType;
8 use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType};
9 use rustc_session::parse::feature_err;
10 use rustc_session::search_paths::PathKind;
11 use rustc_session::utils::NativeLibKind;
12 use rustc_session::Session;
13 use rustc_span::symbol::{sym, Symbol};
14 use rustc_target::spec::abi::Abi;
15
16 use crate::errors;
17
18 use std::path::PathBuf;
19
20 pub fn find_native_static_library(
21 name: &str,
22 verbatim: bool,
23 search_paths: &[PathBuf],
24 sess: &Session,
25 ) -> PathBuf {
26 let formats = if verbatim {
27 vec![("".into(), "".into())]
28 } else {
29 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
30 // On Windows, static libraries sometimes show up as libfoo.a and other
31 // times show up as foo.lib
32 let unix = ("lib".into(), ".a".into());
33 if os == unix { vec![os] } else { vec![os, unix] }
34 };
35
36 for path in search_paths {
37 for (prefix, suffix) in &formats {
38 let test = path.join(format!("{prefix}{name}{suffix}"));
39 if test.exists() {
40 return test;
41 }
42 }
43 }
44
45 sess.emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
46 }
47
48 fn find_bundled_library(
49 name: Option<Symbol>,
50 verbatim: Option<bool>,
51 kind: NativeLibKind,
52 has_cfg: bool,
53 sess: &Session,
54 ) -> Option<Symbol> {
55 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
56 && sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
57 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
58 {
59 let verbatim = verbatim.unwrap_or(false);
60 let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
61 return find_native_static_library(name.unwrap().as_str(), verbatim, search_paths, sess)
62 .file_name()
63 .and_then(|s| s.to_str())
64 .map(Symbol::intern);
65 }
66 None
67 }
68
69 pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
70 let mut collector = Collector { tcx, libs: Vec::new() };
71 for id in tcx.hir().items() {
72 collector.process_item(id);
73 }
74 collector.process_command_line();
75 collector.libs
76 }
77
78 pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
79 match lib.cfg {
80 Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
81 None => true,
82 }
83 }
84
85 struct Collector<'tcx> {
86 tcx: TyCtxt<'tcx>,
87 libs: Vec<NativeLib>,
88 }
89
90 impl<'tcx> Collector<'tcx> {
91 fn process_item(&mut self, id: rustc_hir::ItemId) {
92 if !matches!(self.tcx.def_kind(id.owner_id), DefKind::ForeignMod) {
93 return;
94 }
95
96 let it = self.tcx.hir().item(id);
97 let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else {
98 return;
99 };
100
101 if matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) {
102 return;
103 }
104
105 // Process all of the #[link(..)]-style arguments
106 let sess = self.tcx.sess;
107 let features = self.tcx.features();
108
109 if !sess.opts.unstable_opts.link_directives {
110 return;
111 }
112
113 for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
114 let Some(items) = m.meta_item_list() else {
115 continue;
116 };
117
118 let mut name = None;
119 let mut kind = None;
120 let mut modifiers = None;
121 let mut cfg = None;
122 let mut wasm_import_module = None;
123 let mut import_name_type = None;
124 for item in items.iter() {
125 match item.name_or_empty() {
126 sym::name => {
127 if name.is_some() {
128 sess.emit_err(errors::MultipleNamesInLink { span: item.span() });
129 continue;
130 }
131 let Some(link_name) = item.value_str() else {
132 sess.emit_err(errors::LinkNameForm { span: item.span() });
133 continue;
134 };
135 let span = item.name_value_literal_span().unwrap();
136 if link_name.is_empty() {
137 sess.emit_err(errors::EmptyLinkName { span });
138 }
139 name = Some((link_name, span));
140 }
141 sym::kind => {
142 if kind.is_some() {
143 sess.emit_err(errors::MultipleKindsInLink { span: item.span() });
144 continue;
145 }
146 let Some(link_kind) = item.value_str() else {
147 sess.emit_err(errors::LinkKindForm { span: item.span() });
148 continue;
149 };
150
151 let span = item.name_value_literal_span().unwrap();
152 let link_kind = match link_kind.as_str() {
153 "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
154 "dylib" => NativeLibKind::Dylib { as_needed: None },
155 "framework" => {
156 if !sess.target.is_like_osx {
157 sess.emit_err(errors::LinkFrameworkApple { span });
158 }
159 NativeLibKind::Framework { as_needed: None }
160 }
161 "raw-dylib" => {
162 if !sess.target.is_like_windows {
163 sess.emit_err(errors::FrameworkOnlyWindows { span });
164 } else if !features.raw_dylib && sess.target.arch == "x86" {
165 feature_err(
166 &sess.parse_sess,
167 sym::raw_dylib,
168 span,
169 "link kind `raw-dylib` is unstable on x86",
170 )
171 .emit();
172 }
173 NativeLibKind::RawDylib
174 }
175 kind => {
176 sess.emit_err(errors::UnknownLinkKind { span, kind });
177 continue;
178 }
179 };
180 kind = Some(link_kind);
181 }
182 sym::modifiers => {
183 if modifiers.is_some() {
184 sess.emit_err(errors::MultipleLinkModifiers { span: item.span() });
185 continue;
186 }
187 let Some(link_modifiers) = item.value_str() else {
188 sess.emit_err(errors::LinkModifiersForm { span: item.span() });
189 continue;
190 };
191 modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
192 }
193 sym::cfg => {
194 if cfg.is_some() {
195 sess.emit_err(errors::MultipleCfgs { span: item.span() });
196 continue;
197 }
198 let Some(link_cfg) = item.meta_item_list() else {
199 sess.emit_err(errors::LinkCfgForm { span: item.span() });
200 continue;
201 };
202 let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
203 sess.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
204 continue;
205 };
206 if !features.link_cfg {
207 feature_err(
208 &sess.parse_sess,
209 sym::link_cfg,
210 item.span(),
211 "link cfg is unstable",
212 )
213 .emit();
214 }
215 cfg = Some(link_cfg.clone());
216 }
217 sym::wasm_import_module => {
218 if wasm_import_module.is_some() {
219 sess.emit_err(errors::MultipleWasmImport { span: item.span() });
220 continue;
221 }
222 let Some(link_wasm_import_module) = item.value_str() else {
223 sess.emit_err(errors::WasmImportForm { span: item.span() });
224 continue;
225 };
226 wasm_import_module = Some((link_wasm_import_module, item.span()));
227 }
228 sym::import_name_type => {
229 if import_name_type.is_some() {
230 sess.emit_err(errors::MultipleImportNameType { span: item.span() });
231 continue;
232 }
233 let Some(link_import_name_type) = item.value_str() else {
234 sess.emit_err(errors::ImportNameTypeForm { span: item.span() });
235 continue;
236 };
237 if self.tcx.sess.target.arch != "x86" {
238 sess.emit_err(errors::ImportNameTypeX86 { span: item.span() });
239 continue;
240 }
241
242 let link_import_name_type = match link_import_name_type.as_str() {
243 "decorated" => PeImportNameType::Decorated,
244 "noprefix" => PeImportNameType::NoPrefix,
245 "undecorated" => PeImportNameType::Undecorated,
246 import_name_type => {
247 sess.emit_err(errors::UnknownImportNameType {
248 span: item.span(),
249 import_name_type,
250 });
251 continue;
252 }
253 };
254 if !features.raw_dylib {
255 let span = item.name_value_literal_span().unwrap();
256 feature_err(
257 &sess.parse_sess,
258 sym::raw_dylib,
259 span,
260 "import name type is unstable",
261 )
262 .emit();
263 }
264 import_name_type = Some((link_import_name_type, item.span()));
265 }
266 _ => {
267 sess.emit_err(errors::UnexpectedLinkArg { span: item.span() });
268 }
269 }
270 }
271
272 // Do this outside the above loop so we don't depend on modifiers coming after kinds
273 let mut verbatim = None;
274 if let Some((modifiers, span)) = modifiers {
275 for modifier in modifiers.as_str().split(',') {
276 let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
277 Some(m) => (m, modifier.starts_with('+')),
278 None => {
279 sess.emit_err(errors::InvalidLinkModifier { span });
280 continue;
281 }
282 };
283
284 macro report_unstable_modifier($feature: ident) {
285 if !features.$feature {
286 feature_err(
287 &sess.parse_sess,
288 sym::$feature,
289 span,
290 &format!("linking modifier `{modifier}` is unstable"),
291 )
292 .emit();
293 }
294 }
295 let assign_modifier = |dst: &mut Option<bool>| {
296 if dst.is_some() {
297 sess.emit_err(errors::MultipleModifiers { span, modifier });
298 } else {
299 *dst = Some(value);
300 }
301 };
302 match (modifier, &mut kind) {
303 ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
304 assign_modifier(bundle)
305 }
306 ("bundle", _) => {
307 sess.emit_err(errors::BundleNeedsStatic { span });
308 }
309
310 ("verbatim", _) => assign_modifier(&mut verbatim),
311
312 ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
313 assign_modifier(whole_archive)
314 }
315 ("whole-archive", _) => {
316 sess.emit_err(errors::WholeArchiveNeedsStatic { span });
317 }
318
319 ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
320 | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
321 report_unstable_modifier!(native_link_modifiers_as_needed);
322 assign_modifier(as_needed)
323 }
324 ("as-needed", _) => {
325 sess.emit_err(errors::AsNeededCompatibility { span });
326 }
327
328 _ => {
329 sess.emit_err(errors::UnknownLinkModifier { span, modifier });
330 }
331 }
332 }
333 }
334
335 if let Some((_, span)) = wasm_import_module {
336 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
337 sess.emit_err(errors::IncompatibleWasmLink { span });
338 }
339 } else if name.is_none() {
340 sess.emit_err(errors::LinkRequiresName { span: m.span });
341 }
342
343 // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
344 if let Some((_, span)) = import_name_type {
345 if kind != Some(NativeLibKind::RawDylib) {
346 sess.emit_err(errors::ImportNameTypeRaw { span });
347 }
348 }
349
350 let dll_imports = match kind {
351 Some(NativeLibKind::RawDylib) => {
352 if let Some((name, span)) = name && name.as_str().contains('\0') {
353 sess.emit_err(errors::RawDylibNoNul { span });
354 }
355 foreign_mod_items
356 .iter()
357 .map(|child_item| {
358 self.build_dll_import(
359 abi,
360 import_name_type.map(|(import_name_type, _)| import_name_type),
361 child_item,
362 )
363 })
364 .collect()
365 }
366 _ => {
367 for child_item in foreign_mod_items {
368 if self.tcx.def_kind(child_item.id.owner_id).has_codegen_attrs()
369 && self
370 .tcx
371 .codegen_fn_attrs(child_item.id.owner_id)
372 .link_ordinal
373 .is_some()
374 {
375 let link_ordinal_attr = self
376 .tcx
377 .hir()
378 .attrs(child_item.id.owner_id.into())
379 .iter()
380 .find(|a| a.has_name(sym::link_ordinal))
381 .unwrap();
382 sess.emit_err(errors::LinkOrdinalRawDylib {
383 span: link_ordinal_attr.span,
384 });
385 }
386 }
387
388 Vec::new()
389 }
390 };
391
392 let name = name.map(|(name, _)| name);
393 let kind = kind.unwrap_or(NativeLibKind::Unspecified);
394 let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
395 self.libs.push(NativeLib {
396 name,
397 filename,
398 kind,
399 cfg,
400 foreign_module: Some(it.owner_id.to_def_id()),
401 wasm_import_module: wasm_import_module.map(|(name, _)| name),
402 verbatim,
403 dll_imports,
404 });
405 }
406 }
407
408 // Process libs passed on the command line
409 fn process_command_line(&mut self) {
410 // First, check for errors
411 let mut renames = FxHashSet::default();
412 for lib in &self.tcx.sess.opts.libs {
413 if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
414 // Cannot check this when parsing options because the target is not yet available.
415 self.tcx.sess.emit_err(errors::LibFrameworkApple);
416 }
417 if let Some(ref new_name) = lib.new_name {
418 let any_duplicate = self
419 .libs
420 .iter()
421 .filter_map(|lib| lib.name.as_ref())
422 .any(|n| n.as_str() == lib.name);
423 if new_name.is_empty() {
424 self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
425 } else if !any_duplicate {
426 self.tcx.sess.emit_err(errors::RenamingNoLink { lib_name: &lib.name });
427 } else if !renames.insert(&lib.name) {
428 self.tcx.sess.emit_err(errors::MultipleRenamings { lib_name: &lib.name });
429 }
430 }
431 }
432
433 // Update kind and, optionally, the name of all native libraries
434 // (there may be more than one) with the specified name. If any
435 // library is mentioned more than once, keep the latest mention
436 // of it, so that any possible dependent libraries appear before
437 // it. (This ensures that the linker is able to see symbols from
438 // all possible dependent libraries before linking in the library
439 // in question.)
440 for passed_lib in &self.tcx.sess.opts.libs {
441 // If we've already added any native libraries with the same
442 // name, they will be pulled out into `existing`, so that we
443 // can move them to the end of the list below.
444 let mut existing = self
445 .libs
446 .drain_filter(|lib| {
447 if let Some(lib_name) = lib.name {
448 if lib_name.as_str() == passed_lib.name {
449 // FIXME: This whole logic is questionable, whether modifiers are
450 // involved or not, library reordering and kind overriding without
451 // explicit `:rename` in particular.
452 if lib.has_modifiers() || passed_lib.has_modifiers() {
453 match lib.foreign_module {
454 Some(def_id) => {
455 self.tcx.sess.emit_err(errors::NoLinkModOverride {
456 span: Some(self.tcx.def_span(def_id)),
457 })
458 }
459 None => self
460 .tcx
461 .sess
462 .emit_err(errors::NoLinkModOverride { span: None }),
463 };
464 }
465 if passed_lib.kind != NativeLibKind::Unspecified {
466 lib.kind = passed_lib.kind;
467 }
468 if let Some(new_name) = &passed_lib.new_name {
469 lib.name = Some(Symbol::intern(new_name));
470 }
471 lib.verbatim = passed_lib.verbatim;
472 return true;
473 }
474 }
475 false
476 })
477 .collect::<Vec<_>>();
478 if existing.is_empty() {
479 // Add if not found
480 let new_name: Option<&str> = passed_lib.new_name.as_deref();
481 let name = Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name)));
482 let sess = self.tcx.sess;
483 let filename =
484 find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
485 self.libs.push(NativeLib {
486 name,
487 filename,
488 kind: passed_lib.kind,
489 cfg: None,
490 foreign_module: None,
491 wasm_import_module: None,
492 verbatim: passed_lib.verbatim,
493 dll_imports: Vec::new(),
494 });
495 } else {
496 // Move all existing libraries with the same name to the
497 // end of the command line.
498 self.libs.append(&mut existing);
499 }
500 }
501 }
502
503 fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
504 let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
505 self.tcx
506 .type_of(item.id.owner_id)
507 .subst_identity()
508 .fn_sig(self.tcx)
509 .inputs()
510 .map_bound(|slice| self.tcx.mk_type_list(slice)),
511 );
512
513 argument_types
514 .iter()
515 .map(|ty| {
516 let layout = self
517 .tcx
518 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
519 .expect("layout")
520 .layout;
521 // In both stdcall and fastcall, we always round up the argument size to the
522 // nearest multiple of 4 bytes.
523 (layout.size().bytes_usize() + 3) & !3
524 })
525 .sum()
526 }
527
528 fn build_dll_import(
529 &self,
530 abi: Abi,
531 import_name_type: Option<PeImportNameType>,
532 item: &hir::ForeignItemRef,
533 ) -> DllImport {
534 let calling_convention = if self.tcx.sess.target.arch == "x86" {
535 match abi {
536 Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
537 Abi::Stdcall { .. } | Abi::System { .. } => {
538 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
539 }
540 Abi::Fastcall { .. } => {
541 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
542 }
543 Abi::Vectorcall { .. } => {
544 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
545 }
546 _ => {
547 self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span });
548 }
549 }
550 } else {
551 match abi {
552 Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
553 _ => {
554 self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span });
555 }
556 }
557 };
558
559 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item.id.owner_id);
560 let import_name_type = codegen_fn_attrs
561 .link_ordinal
562 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
563
564 DllImport {
565 name: codegen_fn_attrs.link_name.unwrap_or(item.ident.name),
566 import_name_type,
567 calling_convention,
568 span: item.span,
569 is_fn: self.tcx.def_kind(item.id.owner_id).is_fn_like(),
570 }
571 }
572 }