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