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