]>
Commit | Line | Data |
---|---|---|
74b04a01 | 1 | use rustc_attr as attr; |
dfeec247 XL |
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; | |
136023e0 | 6 | use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; |
c295e0f8 | 7 | use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib}; |
ba9703b0 | 8 | use rustc_session::parse::feature_err; |
f9f354fc | 9 | use rustc_session::utils::NativeLibKind; |
ba9703b0 | 10 | use rustc_session::Session; |
dfeec247 | 11 | use rustc_span::symbol::{kw, sym, Symbol}; |
17df50a5 | 12 | use rustc_span::Span; |
83c7162d | 13 | use rustc_target::spec::abi::Abi; |
60c5eb7d | 14 | |
f9f354fc | 15 | crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { |
dfeec247 | 16 | let mut collector = Collector { tcx, libs: Vec::new() }; |
c295e0f8 | 17 | tcx.hir().visit_all_item_likes(&mut collector); |
ea8adc8c | 18 | collector.process_command_line(); |
ba9703b0 | 19 | collector.libs |
ea8adc8c XL |
20 | } |
21 | ||
f9f354fc | 22 | crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { |
ea8adc8c XL |
23 | match lib.cfg { |
24 | Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), | |
25 | None => true, | |
26 | } | |
27 | } | |
28 | ||
dc9dc135 XL |
29 | struct Collector<'tcx> { |
30 | tcx: TyCtxt<'tcx>, | |
f9f354fc | 31 | libs: Vec<NativeLib>, |
ea8adc8c XL |
32 | } |
33 | ||
dc9dc135 | 34 | impl ItemLikeVisitor<'tcx> for Collector<'tcx> { |
dfeec247 | 35 | fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { |
17df50a5 XL |
36 | let (abi, foreign_mod_items) = match it.kind { |
37 | hir::ItemKind::ForeignMod { abi, items } => (abi, items), | |
ea8adc8c XL |
38 | _ => return, |
39 | }; | |
40 | ||
fc512014 | 41 | if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { |
dfeec247 | 42 | return; |
ea8adc8c XL |
43 | } |
44 | ||
45 | // Process all of the #[link(..)]-style arguments | |
3dfed10e | 46 | let sess = &self.tcx.sess; |
94222f64 | 47 | for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { |
ea8adc8c XL |
48 | let items = match m.meta_item_list() { |
49 | Some(item) => item, | |
50 | None => continue, | |
51 | }; | |
f9f354fc | 52 | let mut lib = NativeLib { |
8faf50e0 | 53 | name: None, |
f9f354fc | 54 | kind: NativeLibKind::Unspecified, |
8faf50e0 | 55 | cfg: None, |
6a06907d | 56 | foreign_module: Some(it.def_id.to_def_id()), |
8faf50e0 | 57 | wasm_import_module: None, |
17df50a5 XL |
58 | verbatim: None, |
59 | dll_imports: Vec::new(), | |
ea8adc8c | 60 | }; |
8faf50e0 XL |
61 | let mut kind_specified = false; |
62 | ||
63 | for item in items.iter() { | |
3dfed10e | 64 | if item.has_name(sym::kind) { |
8faf50e0 XL |
65 | kind_specified = true; |
66 | let kind = match item.value_str() { | |
67 | Some(name) => name, | |
68 | None => continue, // skip like historical compilers | |
69 | }; | |
60c5eb7d | 70 | lib.kind = match &*kind.as_str() { |
17df50a5 XL |
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(); | |
94222f64 XL |
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 | } | |
17df50a5 XL |
88 | NativeLibKind::Static { bundle: Some(false), whole_archive: None } |
89 | } | |
90 | "dylib" => NativeLibKind::Dylib { as_needed: None }, | |
91 | "framework" => NativeLibKind::Framework { as_needed: None }, | |
f9f354fc | 92 | "raw-dylib" => NativeLibKind::RawDylib, |
8faf50e0 | 93 | k => { |
3dfed10e XL |
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(); | |
f9f354fc | 98 | NativeLibKind::Unspecified |
8faf50e0 XL |
99 | } |
100 | }; | |
3dfed10e | 101 | } else if item.has_name(sym::name) { |
8faf50e0 | 102 | lib.name = item.value_str(); |
3dfed10e | 103 | } else if item.has_name(sym::cfg) { |
8faf50e0 XL |
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() { | |
3dfed10e | 109 | sess.span_err(item.span(), "`cfg()` must have an argument"); |
8faf50e0 XL |
110 | } else if let cfg @ Some(..) = cfg[0].meta_item() { |
111 | lib.cfg = cfg.cloned(); | |
112 | } else { | |
3dfed10e | 113 | sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`"); |
8faf50e0 | 114 | } |
3dfed10e | 115 | } else if item.has_name(sym::wasm_import_module) { |
8faf50e0 XL |
116 | match item.value_str() { |
117 | Some(s) => lib.wasm_import_module = Some(s), | |
118 | None => { | |
416331ca | 119 | let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`"; |
3dfed10e | 120 | sess.span_err(item.span(), msg); |
8faf50e0 XL |
121 | } |
122 | } | |
2c00a5a8 | 123 | } else { |
8faf50e0 XL |
124 | // currently, like past compilers, ignore unknown |
125 | // directives here. | |
2c00a5a8 | 126 | } |
8faf50e0 XL |
127 | } |
128 | ||
17df50a5 XL |
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 | ||
8faf50e0 XL |
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 { | |
dfeec247 | 198 | struct_span_err!( |
3dfed10e | 199 | sess, |
dfeec247 XL |
200 | m.span, |
201 | E0459, | |
202 | "`#[link(...)]` specified without \ | |
203 | `name = \"foo\"`" | |
204 | ) | |
205 | .span_label(m.span, "missing `name` argument") | |
206 | .emit(); | |
8faf50e0 | 207 | } |
17df50a5 XL |
208 | |
209 | if lib.kind == NativeLibKind::RawDylib { | |
17df50a5 XL |
210 | lib.dll_imports.extend( |
211 | foreign_mod_items | |
212 | .iter() | |
136023e0 | 213 | .map(|child_item| self.build_dll_import(abi, child_item)), |
17df50a5 XL |
214 | ); |
215 | } | |
216 | ||
ea8adc8c XL |
217 | self.register_native_lib(Some(m.span), lib); |
218 | } | |
219 | } | |
220 | ||
dfeec247 XL |
221 | fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {} |
222 | fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {} | |
fc512014 | 223 | fn visit_foreign_item(&mut self, _it: &'tcx hir::ForeignItem<'tcx>) {} |
ea8adc8c XL |
224 | } |
225 | ||
dc9dc135 | 226 | impl Collector<'tcx> { |
f9f354fc | 227 | fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) { |
5869c6ff | 228 | if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) { |
ea8adc8c XL |
229 | match span { |
230 | Some(span) => { | |
dfeec247 XL |
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(); | |
ea8adc8c XL |
239 | } |
240 | None => { | |
241 | self.tcx.sess.err("empty library name given via `-l`"); | |
242 | } | |
243 | } | |
dfeec247 | 244 | return; |
ea8adc8c | 245 | } |
29967ef6 | 246 | let is_osx = self.tcx.sess.target.is_like_osx; |
17df50a5 | 247 | if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx { |
ea8adc8c XL |
248 | let msg = "native frameworks are only available on macOS targets"; |
249 | match span { | |
dfeec247 | 250 | Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(), |
ea8adc8c XL |
251 | None => self.tcx.sess.err(msg), |
252 | } | |
253 | } | |
0531ce1d | 254 | if lib.cfg.is_some() && !self.tcx.features().link_cfg { |
ba9703b0 XL |
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(); | |
ea8adc8c | 262 | } |
17df50a5 XL |
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 | } | |
e74abb32 | 297 | } |
17df50a5 | 298 | |
ea8adc8c XL |
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 | |
0bf4aa26 | 305 | let mut renames = FxHashSet::default(); |
17df50a5 XL |
306 | for lib in &self.tcx.sess.opts.libs { |
307 | if let Some(ref new_name) = lib.new_name { | |
dfeec247 XL |
308 | let any_duplicate = self |
309 | .libs | |
8faf50e0 XL |
310 | .iter() |
311 | .filter_map(|lib| lib.name.as_ref()) | |
3c0e092e | 312 | .any(|n| n.as_str() == lib.name); |
ea8adc8c | 313 | if new_name.is_empty() { |
dfeec247 XL |
314 | self.tcx.sess.err(&format!( |
315 | "an empty renaming target was specified for library `{}`", | |
17df50a5 | 316 | lib.name |
dfeec247 | 317 | )); |
8faf50e0 | 318 | } else if !any_duplicate { |
dfeec247 XL |
319 | self.tcx.sess.err(&format!( |
320 | "renaming of the library `{}` was specified, \ | |
416331ca | 321 | however this crate contains no `#[link(...)]` \ |
c295e0f8 | 322 | attributes referencing this library", |
17df50a5 | 323 | lib.name |
dfeec247 | 324 | )); |
17df50a5 | 325 | } else if !renames.insert(&lib.name) { |
dfeec247 XL |
326 | self.tcx.sess.err(&format!( |
327 | "multiple renamings were \ | |
c295e0f8 | 328 | specified for library `{}`", |
17df50a5 | 329 | lib.name |
dfeec247 | 330 | )); |
ea8adc8c XL |
331 | } |
332 | } | |
333 | } | |
334 | ||
b7449926 | 335 | // Update kind and, optionally, the name of all native libraries |
532ac7d7 XL |
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.) | |
17df50a5 | 342 | for passed_lib in &self.tcx.sess.opts.libs { |
532ac7d7 XL |
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. | |
dfeec247 XL |
346 | let mut existing = self |
347 | .libs | |
348 | .drain_filter(|lib| { | |
349 | if let Some(lib_name) = lib.name { | |
17df50a5 XL |
350 | if lib_name.as_str() == passed_lib.name { |
351 | if passed_lib.kind != NativeLibKind::Unspecified { | |
352 | lib.kind = passed_lib.kind; | |
dfeec247 | 353 | } |
17df50a5 | 354 | if let Some(new_name) = &passed_lib.new_name { |
dfeec247 XL |
355 | lib.name = Some(Symbol::intern(new_name)); |
356 | } | |
17df50a5 | 357 | lib.verbatim = passed_lib.verbatim; |
dfeec247 | 358 | return true; |
532ac7d7 | 359 | } |
ea8adc8c | 360 | } |
dfeec247 XL |
361 | false |
362 | }) | |
363 | .collect::<Vec<_>>(); | |
532ac7d7 | 364 | if existing.is_empty() { |
ea8adc8c | 365 | // Add if not found |
c295e0f8 | 366 | let new_name: Option<&str> = passed_lib.new_name.as_deref(); |
f9f354fc | 367 | let lib = NativeLib { |
17df50a5 XL |
368 | name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))), |
369 | kind: passed_lib.kind, | |
ea8adc8c | 370 | cfg: None, |
0531ce1d | 371 | foreign_module: None, |
8faf50e0 | 372 | wasm_import_module: None, |
17df50a5 XL |
373 | verbatim: passed_lib.verbatim, |
374 | dll_imports: Vec::new(), | |
ea8adc8c XL |
375 | }; |
376 | self.register_native_lib(None, lib); | |
532ac7d7 XL |
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); | |
ea8adc8c XL |
381 | } |
382 | } | |
383 | } | |
136023e0 | 384 | |
c295e0f8 | 385 | fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize { |
136023e0 XL |
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 | ||
c295e0f8 | 409 | fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport { |
136023e0 XL |
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 | }; | |
c295e0f8 XL |
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 | } | |
136023e0 | 443 | } |
ea8adc8c | 444 | } |