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