]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_metadata/src/native_libs.rs
Merge commit '63734fcdd718cca089f84c42f3a42c0096cfd431' into sync_cg_clif-2022-05-15
[rust.git] / compiler / rustc_metadata / src / native_libs.rs
1 use rustc_ast::CRATE_NODE_ID;
2 use rustc_attr as attr;
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_errors::struct_span_err;
5 use rustc_hir as hir;
6 use rustc_hir::def::DefKind;
7 use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
8 use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib};
9 use rustc_session::parse::feature_err;
10 use rustc_session::utils::NativeLibKind;
11 use rustc_session::Session;
12 use rustc_span::symbol::{kw, sym, Symbol};
13 use rustc_span::Span;
14 use rustc_target::spec::abi::Abi;
15
16 crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
17     let mut collector = Collector { tcx, libs: Vec::new() };
18     for id in tcx.hir().items() {
19         collector.process_item(id);
20     }
21     collector.process_command_line();
22     collector.libs
23 }
24
25 crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
26     match lib.cfg {
27         Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
28         None => true,
29     }
30 }
31
32 struct Collector<'tcx> {
33     tcx: TyCtxt<'tcx>,
34     libs: Vec<NativeLib>,
35 }
36
37 impl<'tcx> Collector<'tcx> {
38     fn process_item(&mut self, id: rustc_hir::ItemId) {
39         if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) {
40             return;
41         }
42
43         let it = self.tcx.hir().item(id);
44         let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else {
45             return;
46         };
47
48         if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
49             return;
50         }
51
52         // Process all of the #[link(..)]-style arguments
53         let sess = &self.tcx.sess;
54         for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
55             let Some(items) = m.meta_item_list() else {
56                 continue;
57             };
58             let mut lib = NativeLib {
59                 name: None,
60                 kind: NativeLibKind::Unspecified,
61                 cfg: None,
62                 foreign_module: Some(it.def_id.to_def_id()),
63                 wasm_import_module: None,
64                 verbatim: None,
65                 dll_imports: Vec::new(),
66             };
67             let mut kind_specified = false;
68
69             for item in items.iter() {
70                 if item.has_name(sym::kind) {
71                     kind_specified = true;
72                     let Some(kind) = item.value_str() else {
73                         continue; // skip like historical compilers
74                     };
75                     lib.kind = match kind.as_str() {
76                         "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
77                         "static-nobundle" => {
78                             sess.struct_span_warn(
79                                 item.span(),
80                                 "library kind `static-nobundle` has been superseded by specifying \
81                                 modifier `-bundle` with library kind `static`",
82                             )
83                             .emit();
84                             if !self.tcx.features().static_nobundle {
85                                 feature_err(
86                                     &self.tcx.sess.parse_sess,
87                                     sym::static_nobundle,
88                                     item.span(),
89                                     "kind=\"static-nobundle\" is unstable",
90                                 )
91                                 .emit();
92                             }
93                             NativeLibKind::Static { bundle: Some(false), whole_archive: None }
94                         }
95                         "dylib" => NativeLibKind::Dylib { as_needed: None },
96                         "framework" => NativeLibKind::Framework { as_needed: None },
97                         "raw-dylib" => NativeLibKind::RawDylib,
98                         k => {
99                             struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
100                                 .span_label(item.span(), "unknown kind")
101                                 .span_label(m.span, "")
102                                 .emit();
103                             NativeLibKind::Unspecified
104                         }
105                     };
106                 } else if item.has_name(sym::name) {
107                     lib.name = item.value_str();
108                 } else if item.has_name(sym::cfg) {
109                     let Some(cfg) = item.meta_item_list() else {
110                         continue; // skip like historical compilers
111                     };
112                     if cfg.is_empty() {
113                         sess.span_err(item.span(), "`cfg()` must have an argument");
114                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
115                         lib.cfg = cfg.cloned();
116                     } else {
117                         sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
118                     }
119                 } else if item.has_name(sym::wasm_import_module) {
120                     match item.value_str() {
121                         Some(s) => lib.wasm_import_module = Some(s),
122                         None => {
123                             let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
124                             sess.span_err(item.span(), msg);
125                         }
126                     }
127                 } else {
128                     // currently, like past compilers, ignore unknown
129                     // directives here.
130                 }
131             }
132
133             // Do this outside the above loop so we don't depend on modifiers coming
134             // after kinds
135             let mut modifiers_count = 0;
136             for item in items.iter().filter(|item| item.has_name(sym::modifiers)) {
137                 if let Some(modifiers) = item.value_str() {
138                     modifiers_count += 1;
139                     let span = item.name_value_literal_span().unwrap();
140                     let mut has_duplicate_modifiers = false;
141                     for modifier in modifiers.as_str().split(',') {
142                         let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
143                             Some(m) => (m, modifier.starts_with('+')),
144                             None => {
145                                 // Note: this error also excludes the case with empty modifier
146                                 // string, like `modifiers = ""`.
147                                 sess.span_err(
148                                     span,
149                                     "invalid linking modifier syntax, expected '+' or '-' prefix \
150                                     before one of: bundle, verbatim, whole-archive, as-needed",
151                                 );
152                                 continue;
153                             }
154                         };
155
156                         match (modifier, &mut lib.kind) {
157                             ("bundle", NativeLibKind::Static { bundle, .. }) => {
158                                 if bundle.is_some() {
159                                     has_duplicate_modifiers = true;
160                                 }
161                                 *bundle = Some(value);
162                             }
163                             ("bundle", _) => {
164                                 sess.span_err(
165                                     span,
166                                     "bundle linking modifier is only compatible with \
167                                 `static` linking kind",
168                                 );
169                             }
170
171                             ("verbatim", _) => {
172                                 if lib.verbatim.is_some() {
173                                     has_duplicate_modifiers = true;
174                                 }
175                                 lib.verbatim = Some(value);
176                             }
177
178                             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
179                                 if whole_archive.is_some() {
180                                     has_duplicate_modifiers = true;
181                                 }
182                                 *whole_archive = Some(value);
183                             }
184                             ("whole-archive", _) => {
185                                 sess.span_err(
186                                     span,
187                                     "whole-archive linking modifier is only compatible with \
188                                 `static` linking kind",
189                                 );
190                             }
191
192                             ("as-needed", NativeLibKind::Dylib { as_needed })
193                             | ("as-needed", NativeLibKind::Framework { as_needed }) => {
194                                 if as_needed.is_some() {
195                                     has_duplicate_modifiers = true;
196                                 }
197                                 *as_needed = Some(value);
198                             }
199                             ("as-needed", _) => {
200                                 sess.span_err(
201                                     span,
202                                     "as-needed linking modifier is only compatible with \
203                                 `dylib` and `framework` linking kinds",
204                                 );
205                             }
206
207                             _ => {
208                                 sess.span_err(
209                                     span,
210                                     &format!(
211                                         "unrecognized linking modifier `{}`, expected one \
212                                     of: bundle, verbatim, whole-archive, as-needed",
213                                         modifier
214                                     ),
215                                 );
216                             }
217                         }
218                     }
219                     if has_duplicate_modifiers {
220                         let msg =
221                             "same modifier is used multiple times in a single `modifiers` argument";
222                         sess.span_err(item.span(), msg);
223                     }
224                 } else {
225                     let msg = "must be of the form `#[link(modifiers = \"...\")]`";
226                     sess.span_err(item.span(), msg);
227                 }
228             }
229
230             if modifiers_count > 1 {
231                 let msg = "multiple `modifiers` arguments in a single `#[link]` attribute";
232                 sess.span_err(m.span, msg);
233             }
234
235             // In general we require #[link(name = "...")] but we allow
236             // #[link(wasm_import_module = "...")] without the `name`.
237             let requires_name = kind_specified || lib.wasm_import_module.is_none();
238             if lib.name.is_none() && requires_name {
239                 struct_span_err!(
240                     sess,
241                     m.span,
242                     E0459,
243                     "`#[link(...)]` specified without \
244                                   `name = \"foo\"`"
245                 )
246                 .span_label(m.span, "missing `name` argument")
247                 .emit();
248             }
249
250             if lib.kind == NativeLibKind::RawDylib {
251                 lib.dll_imports.extend(
252                     foreign_mod_items
253                         .iter()
254                         .map(|child_item| self.build_dll_import(abi, child_item)),
255                 );
256             }
257
258             self.register_native_lib(Some(m.span), lib);
259         }
260     }
261
262     fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
263         if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) {
264             match span {
265                 Some(span) => {
266                     struct_span_err!(
267                         self.tcx.sess,
268                         span,
269                         E0454,
270                         "`#[link(name = \"\")]` given with empty name"
271                     )
272                     .span_label(span, "empty name given")
273                     .emit();
274                 }
275                 None => {
276                     self.tcx.sess.err("empty library name given via `-l`");
277                 }
278             }
279             return;
280         }
281         let is_osx = self.tcx.sess.target.is_like_osx;
282         if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
283             let msg = "native frameworks are only available on macOS targets";
284             match span {
285                 Some(span) => {
286                     struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit();
287                 }
288                 None => {
289                     self.tcx.sess.err(msg);
290                 }
291             }
292         }
293         if lib.cfg.is_some() && !self.tcx.features().link_cfg {
294             feature_err(
295                 &self.tcx.sess.parse_sess,
296                 sym::link_cfg,
297                 span.unwrap(),
298                 "kind=\"link_cfg\" is unstable",
299             )
300             .emit();
301         }
302         // this just unwraps lib.name; we already established that it isn't empty above.
303         if let (NativeLibKind::RawDylib, Some(lib_name)) = (lib.kind, lib.name) {
304             let Some(span) = span else {
305                 bug!("raw-dylib libraries are not supported on the command line");
306             };
307
308             if !self.tcx.sess.target.options.is_like_windows {
309                 self.tcx.sess.span_fatal(
310                     span,
311                     "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows",
312                 );
313             }
314
315             if lib_name.as_str().contains('\0') {
316                 self.tcx.sess.span_err(span, "library name may not contain NUL characters");
317             }
318
319             if !self.tcx.features().raw_dylib {
320                 feature_err(
321                     &self.tcx.sess.parse_sess,
322                     sym::raw_dylib,
323                     span,
324                     "kind=\"raw-dylib\" is unstable",
325                 )
326                 .emit();
327             }
328         }
329
330         self.libs.push(lib);
331     }
332
333     // Process libs passed on the command line
334     fn process_command_line(&mut self) {
335         // First, check for errors
336         let mut renames = FxHashSet::default();
337         for lib in &self.tcx.sess.opts.libs {
338             if let Some(ref new_name) = lib.new_name {
339                 let any_duplicate = self
340                     .libs
341                     .iter()
342                     .filter_map(|lib| lib.name.as_ref())
343                     .any(|n| n.as_str() == lib.name);
344                 if new_name.is_empty() {
345                     self.tcx.sess.err(&format!(
346                         "an empty renaming target was specified for library `{}`",
347                         lib.name
348                     ));
349                 } else if !any_duplicate {
350                     self.tcx.sess.err(&format!(
351                         "renaming of the library `{}` was specified, \
352                                                 however this crate contains no `#[link(...)]` \
353                                                 attributes referencing this library",
354                         lib.name
355                     ));
356                 } else if !renames.insert(&lib.name) {
357                     self.tcx.sess.err(&format!(
358                         "multiple renamings were \
359                                                 specified for library `{}`",
360                         lib.name
361                     ));
362                 }
363             }
364         }
365
366         // Update kind and, optionally, the name of all native libraries
367         // (there may be more than one) with the specified name.  If any
368         // library is mentioned more than once, keep the latest mention
369         // of it, so that any possible dependent libraries appear before
370         // it.  (This ensures that the linker is able to see symbols from
371         // all possible dependent libraries before linking in the library
372         // in question.)
373         for passed_lib in &self.tcx.sess.opts.libs {
374             // If we've already added any native libraries with the same
375             // name, they will be pulled out into `existing`, so that we
376             // can move them to the end of the list below.
377             let mut existing = self
378                 .libs
379                 .drain_filter(|lib| {
380                     if let Some(lib_name) = lib.name {
381                         if lib_name.as_str() == passed_lib.name {
382                             // FIXME: This whole logic is questionable, whether modifiers are
383                             // involved or not, library reordering and kind overriding without
384                             // explicit `:rename` in particular.
385                             if lib.has_modifiers() || passed_lib.has_modifiers() {
386                                 self.tcx.sess.span_err(
387                                     self.tcx.def_span(lib.foreign_module.unwrap()),
388                                     "overriding linking modifiers from command line is not supported"
389                                 );
390                             }
391                             if passed_lib.kind != NativeLibKind::Unspecified {
392                                 lib.kind = passed_lib.kind;
393                             }
394                             if let Some(new_name) = &passed_lib.new_name {
395                                 lib.name = Some(Symbol::intern(new_name));
396                             }
397                             lib.verbatim = passed_lib.verbatim;
398                             return true;
399                         }
400                     }
401                     false
402                 })
403                 .collect::<Vec<_>>();
404             if existing.is_empty() {
405                 // Add if not found
406                 let new_name: Option<&str> = passed_lib.new_name.as_deref();
407                 let lib = NativeLib {
408                     name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
409                     kind: passed_lib.kind,
410                     cfg: None,
411                     foreign_module: None,
412                     wasm_import_module: None,
413                     verbatim: passed_lib.verbatim,
414                     dll_imports: Vec::new(),
415                 };
416                 self.register_native_lib(None, lib);
417             } else {
418                 // Move all existing libraries with the same name to the
419                 // end of the command line.
420                 self.libs.append(&mut existing);
421             }
422         }
423     }
424
425     fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
426         let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
427             self.tcx
428                 .type_of(item.id.def_id)
429                 .fn_sig(self.tcx)
430                 .inputs()
431                 .map_bound(|slice| self.tcx.mk_type_list(slice.iter())),
432         );
433
434         argument_types
435             .iter()
436             .map(|ty| {
437                 let layout = self
438                     .tcx
439                     .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
440                     .expect("layout")
441                     .layout;
442                 // In both stdcall and fastcall, we always round up the argument size to the
443                 // nearest multiple of 4 bytes.
444                 (layout.size().bytes_usize() + 3) & !3
445             })
446             .sum()
447     }
448
449     fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport {
450         let calling_convention = if self.tcx.sess.target.arch == "x86" {
451             match abi {
452                 Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
453                 Abi::Stdcall { .. } | Abi::System { .. } => {
454                     DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
455                 }
456                 Abi::Fastcall { .. } => {
457                     DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
458                 }
459                 // Vectorcall is intentionally not supported at this time.
460                 _ => {
461                     self.tcx.sess.span_fatal(
462                         item.span,
463                         r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
464                     );
465                 }
466             }
467         } else {
468             match abi {
469                 Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
470                 _ => {
471                     self.tcx.sess.span_fatal(
472                         item.span,
473                         r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
474                     );
475                 }
476             }
477         };
478
479         DllImport {
480             name: item.ident.name,
481             ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal,
482             calling_convention,
483             span: item.span,
484         }
485     }
486 }