]>
Commit | Line | Data |
---|---|---|
5e7ed085 | 1 | use rustc_ast::CRATE_NODE_ID; |
74b04a01 | 2 | use rustc_attr as attr; |
dfeec247 XL |
3 | use rustc_data_structures::fx::FxHashSet; |
4 | use rustc_errors::struct_span_err; | |
5 | use rustc_hir as hir; | |
04454e1e | 6 | use rustc_hir::def::DefKind; |
136023e0 | 7 | use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; |
c295e0f8 | 8 | use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib}; |
ba9703b0 | 9 | use rustc_session::parse::feature_err; |
f9f354fc | 10 | use rustc_session::utils::NativeLibKind; |
ba9703b0 | 11 | use rustc_session::Session; |
dfeec247 | 12 | use rustc_span::symbol::{kw, sym, Symbol}; |
17df50a5 | 13 | use rustc_span::Span; |
83c7162d | 14 | use rustc_target::spec::abi::Abi; |
60c5eb7d | 15 | |
f9f354fc | 16 | crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { |
dfeec247 | 17 | let mut collector = Collector { tcx, libs: Vec::new() }; |
04454e1e FG |
18 | for id in tcx.hir().items() { |
19 | collector.process_item(id); | |
20 | } | |
ea8adc8c | 21 | collector.process_command_line(); |
ba9703b0 | 22 | collector.libs |
ea8adc8c XL |
23 | } |
24 | ||
f9f354fc | 25 | crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { |
ea8adc8c | 26 | match lib.cfg { |
5e7ed085 | 27 | Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), |
ea8adc8c XL |
28 | None => true, |
29 | } | |
30 | } | |
31 | ||
dc9dc135 XL |
32 | struct Collector<'tcx> { |
33 | tcx: TyCtxt<'tcx>, | |
f9f354fc | 34 | libs: Vec<NativeLib>, |
ea8adc8c XL |
35 | } |
36 | ||
04454e1e FG |
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); | |
5e7ed085 FG |
44 | let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else { |
45 | return; | |
ea8adc8c XL |
46 | }; |
47 | ||
fc512014 | 48 | if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { |
dfeec247 | 49 | return; |
ea8adc8c XL |
50 | } |
51 | ||
52 | // Process all of the #[link(..)]-style arguments | |
3dfed10e | 53 | let sess = &self.tcx.sess; |
94222f64 | 54 | for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { |
5e7ed085 FG |
55 | let Some(items) = m.meta_item_list() else { |
56 | continue; | |
ea8adc8c | 57 | }; |
f9f354fc | 58 | let mut lib = NativeLib { |
8faf50e0 | 59 | name: None, |
f9f354fc | 60 | kind: NativeLibKind::Unspecified, |
8faf50e0 | 61 | cfg: None, |
6a06907d | 62 | foreign_module: Some(it.def_id.to_def_id()), |
8faf50e0 | 63 | wasm_import_module: None, |
17df50a5 XL |
64 | verbatim: None, |
65 | dll_imports: Vec::new(), | |
ea8adc8c | 66 | }; |
8faf50e0 XL |
67 | let mut kind_specified = false; |
68 | ||
69 | for item in items.iter() { | |
3dfed10e | 70 | if item.has_name(sym::kind) { |
8faf50e0 | 71 | kind_specified = true; |
5e7ed085 FG |
72 | let Some(kind) = item.value_str() else { |
73 | continue; // skip like historical compilers | |
8faf50e0 | 74 | }; |
a2a8927a | 75 | lib.kind = match kind.as_str() { |
17df50a5 XL |
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(); | |
94222f64 XL |
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 | } | |
17df50a5 XL |
93 | NativeLibKind::Static { bundle: Some(false), whole_archive: None } |
94 | } | |
95 | "dylib" => NativeLibKind::Dylib { as_needed: None }, | |
96 | "framework" => NativeLibKind::Framework { as_needed: None }, | |
f9f354fc | 97 | "raw-dylib" => NativeLibKind::RawDylib, |
8faf50e0 | 98 | k => { |
3dfed10e XL |
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(); | |
f9f354fc | 103 | NativeLibKind::Unspecified |
8faf50e0 XL |
104 | } |
105 | }; | |
3dfed10e | 106 | } else if item.has_name(sym::name) { |
8faf50e0 | 107 | lib.name = item.value_str(); |
3dfed10e | 108 | } else if item.has_name(sym::cfg) { |
5e7ed085 FG |
109 | let Some(cfg) = item.meta_item_list() else { |
110 | continue; // skip like historical compilers | |
8faf50e0 XL |
111 | }; |
112 | if cfg.is_empty() { | |
3dfed10e | 113 | sess.span_err(item.span(), "`cfg()` must have an argument"); |
8faf50e0 XL |
114 | } else if let cfg @ Some(..) = cfg[0].meta_item() { |
115 | lib.cfg = cfg.cloned(); | |
116 | } else { | |
3dfed10e | 117 | sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`"); |
8faf50e0 | 118 | } |
3dfed10e | 119 | } else if item.has_name(sym::wasm_import_module) { |
8faf50e0 XL |
120 | match item.value_str() { |
121 | Some(s) => lib.wasm_import_module = Some(s), | |
122 | None => { | |
416331ca | 123 | let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`"; |
3dfed10e | 124 | sess.span_err(item.span(), msg); |
8faf50e0 XL |
125 | } |
126 | } | |
2c00a5a8 | 127 | } else { |
8faf50e0 XL |
128 | // currently, like past compilers, ignore unknown |
129 | // directives here. | |
2c00a5a8 | 130 | } |
8faf50e0 XL |
131 | } |
132 | ||
17df50a5 XL |
133 | // Do this outside the above loop so we don't depend on modifiers coming |
134 | // after kinds | |
5e7ed085 FG |
135 | let mut modifiers_count = 0; |
136 | for item in items.iter().filter(|item| item.has_name(sym::modifiers)) { | |
17df50a5 | 137 | if let Some(modifiers) = item.value_str() { |
5e7ed085 | 138 | modifiers_count += 1; |
17df50a5 | 139 | let span = item.name_value_literal_span().unwrap(); |
5e7ed085 | 140 | let mut has_duplicate_modifiers = false; |
17df50a5 | 141 | for modifier in modifiers.as_str().split(',') { |
a2a8927a | 142 | let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { |
17df50a5 XL |
143 | Some(m) => (m, modifier.starts_with('+')), |
144 | None => { | |
5e7ed085 FG |
145 | // Note: this error also excludes the case with empty modifier |
146 | // string, like `modifiers = ""`. | |
17df50a5 XL |
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, .. }) => { | |
5e7ed085 FG |
158 | if bundle.is_some() { |
159 | has_duplicate_modifiers = true; | |
160 | } | |
17df50a5 XL |
161 | *bundle = Some(value); |
162 | } | |
5e7ed085 FG |
163 | ("bundle", _) => { |
164 | sess.span_err( | |
165 | span, | |
166 | "bundle linking modifier is only compatible with \ | |
17df50a5 | 167 | `static` linking kind", |
5e7ed085 FG |
168 | ); |
169 | } | |
17df50a5 | 170 | |
5e7ed085 FG |
171 | ("verbatim", _) => { |
172 | if lib.verbatim.is_some() { | |
173 | has_duplicate_modifiers = true; | |
174 | } | |
175 | lib.verbatim = Some(value); | |
176 | } | |
17df50a5 XL |
177 | |
178 | ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { | |
5e7ed085 FG |
179 | if whole_archive.is_some() { |
180 | has_duplicate_modifiers = true; | |
181 | } | |
17df50a5 XL |
182 | *whole_archive = Some(value); |
183 | } | |
5e7ed085 FG |
184 | ("whole-archive", _) => { |
185 | sess.span_err( | |
186 | span, | |
187 | "whole-archive linking modifier is only compatible with \ | |
17df50a5 | 188 | `static` linking kind", |
5e7ed085 FG |
189 | ); |
190 | } | |
17df50a5 XL |
191 | |
192 | ("as-needed", NativeLibKind::Dylib { as_needed }) | |
193 | | ("as-needed", NativeLibKind::Framework { as_needed }) => { | |
5e7ed085 FG |
194 | if as_needed.is_some() { |
195 | has_duplicate_modifiers = true; | |
196 | } | |
17df50a5 XL |
197 | *as_needed = Some(value); |
198 | } | |
5e7ed085 FG |
199 | ("as-needed", _) => { |
200 | sess.span_err( | |
201 | span, | |
202 | "as-needed linking modifier is only compatible with \ | |
17df50a5 | 203 | `dylib` and `framework` linking kinds", |
5e7ed085 FG |
204 | ); |
205 | } | |
17df50a5 | 206 | |
5e7ed085 FG |
207 | _ => { |
208 | sess.span_err( | |
209 | span, | |
210 | &format!( | |
211 | "unrecognized linking modifier `{}`, expected one \ | |
17df50a5 | 212 | of: bundle, verbatim, whole-archive, as-needed", |
5e7ed085 FG |
213 | modifier |
214 | ), | |
215 | ); | |
216 | } | |
17df50a5 XL |
217 | } |
218 | } | |
5e7ed085 FG |
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 | } | |
17df50a5 XL |
224 | } else { |
225 | let msg = "must be of the form `#[link(modifiers = \"...\")]`"; | |
226 | sess.span_err(item.span(), msg); | |
227 | } | |
228 | } | |
229 | ||
5e7ed085 FG |
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 | ||
8faf50e0 XL |
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 { | |
dfeec247 | 239 | struct_span_err!( |
3dfed10e | 240 | sess, |
dfeec247 XL |
241 | m.span, |
242 | E0459, | |
243 | "`#[link(...)]` specified without \ | |
244 | `name = \"foo\"`" | |
245 | ) | |
246 | .span_label(m.span, "missing `name` argument") | |
247 | .emit(); | |
8faf50e0 | 248 | } |
17df50a5 XL |
249 | |
250 | if lib.kind == NativeLibKind::RawDylib { | |
17df50a5 XL |
251 | lib.dll_imports.extend( |
252 | foreign_mod_items | |
253 | .iter() | |
136023e0 | 254 | .map(|child_item| self.build_dll_import(abi, child_item)), |
17df50a5 XL |
255 | ); |
256 | } | |
257 | ||
ea8adc8c XL |
258 | self.register_native_lib(Some(m.span), lib); |
259 | } | |
260 | } | |
261 | ||
f9f354fc | 262 | fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) { |
5869c6ff | 263 | if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) { |
ea8adc8c XL |
264 | match span { |
265 | Some(span) => { | |
dfeec247 XL |
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(); | |
ea8adc8c XL |
274 | } |
275 | None => { | |
276 | self.tcx.sess.err("empty library name given via `-l`"); | |
277 | } | |
278 | } | |
dfeec247 | 279 | return; |
ea8adc8c | 280 | } |
29967ef6 | 281 | let is_osx = self.tcx.sess.target.is_like_osx; |
17df50a5 | 282 | if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx { |
ea8adc8c XL |
283 | let msg = "native frameworks are only available on macOS targets"; |
284 | match span { | |
5e7ed085 FG |
285 | Some(span) => { |
286 | struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(); | |
287 | } | |
288 | None => { | |
289 | self.tcx.sess.err(msg); | |
290 | } | |
ea8adc8c XL |
291 | } |
292 | } | |
0531ce1d | 293 | if lib.cfg.is_some() && !self.tcx.features().link_cfg { |
ba9703b0 XL |
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(); | |
ea8adc8c | 301 | } |
17df50a5 XL |
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) { | |
5e7ed085 FG |
304 | let Some(span) = span else { |
305 | bug!("raw-dylib libraries are not supported on the command line"); | |
17df50a5 XL |
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 | ); | |
17df50a5 XL |
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 | } | |
e74abb32 | 328 | } |
17df50a5 | 329 | |
ea8adc8c XL |
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 | |
0bf4aa26 | 336 | let mut renames = FxHashSet::default(); |
17df50a5 XL |
337 | for lib in &self.tcx.sess.opts.libs { |
338 | if let Some(ref new_name) = lib.new_name { | |
dfeec247 XL |
339 | let any_duplicate = self |
340 | .libs | |
8faf50e0 XL |
341 | .iter() |
342 | .filter_map(|lib| lib.name.as_ref()) | |
3c0e092e | 343 | .any(|n| n.as_str() == lib.name); |
ea8adc8c | 344 | if new_name.is_empty() { |
dfeec247 XL |
345 | self.tcx.sess.err(&format!( |
346 | "an empty renaming target was specified for library `{}`", | |
17df50a5 | 347 | lib.name |
dfeec247 | 348 | )); |
8faf50e0 | 349 | } else if !any_duplicate { |
dfeec247 XL |
350 | self.tcx.sess.err(&format!( |
351 | "renaming of the library `{}` was specified, \ | |
416331ca | 352 | however this crate contains no `#[link(...)]` \ |
c295e0f8 | 353 | attributes referencing this library", |
17df50a5 | 354 | lib.name |
dfeec247 | 355 | )); |
17df50a5 | 356 | } else if !renames.insert(&lib.name) { |
dfeec247 XL |
357 | self.tcx.sess.err(&format!( |
358 | "multiple renamings were \ | |
c295e0f8 | 359 | specified for library `{}`", |
17df50a5 | 360 | lib.name |
dfeec247 | 361 | )); |
ea8adc8c XL |
362 | } |
363 | } | |
364 | } | |
365 | ||
b7449926 | 366 | // Update kind and, optionally, the name of all native libraries |
532ac7d7 XL |
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.) | |
17df50a5 | 373 | for passed_lib in &self.tcx.sess.opts.libs { |
532ac7d7 XL |
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. | |
dfeec247 XL |
377 | let mut existing = self |
378 | .libs | |
379 | .drain_filter(|lib| { | |
380 | if let Some(lib_name) = lib.name { | |
17df50a5 | 381 | if lib_name.as_str() == passed_lib.name { |
5e7ed085 FG |
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() { | |
04454e1e FG |
386 | let msg = "overriding linking modifiers from command line is not supported"; |
387 | match lib.foreign_module { | |
388 | Some(def_id) => self.tcx.sess.span_err(self.tcx.def_span(def_id), msg), | |
389 | None => self.tcx.sess.err(msg), | |
390 | }; | |
5e7ed085 | 391 | } |
17df50a5 XL |
392 | if passed_lib.kind != NativeLibKind::Unspecified { |
393 | lib.kind = passed_lib.kind; | |
dfeec247 | 394 | } |
17df50a5 | 395 | if let Some(new_name) = &passed_lib.new_name { |
dfeec247 XL |
396 | lib.name = Some(Symbol::intern(new_name)); |
397 | } | |
17df50a5 | 398 | lib.verbatim = passed_lib.verbatim; |
dfeec247 | 399 | return true; |
532ac7d7 | 400 | } |
ea8adc8c | 401 | } |
dfeec247 XL |
402 | false |
403 | }) | |
404 | .collect::<Vec<_>>(); | |
532ac7d7 | 405 | if existing.is_empty() { |
ea8adc8c | 406 | // Add if not found |
c295e0f8 | 407 | let new_name: Option<&str> = passed_lib.new_name.as_deref(); |
f9f354fc | 408 | let lib = NativeLib { |
17df50a5 XL |
409 | name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))), |
410 | kind: passed_lib.kind, | |
ea8adc8c | 411 | cfg: None, |
0531ce1d | 412 | foreign_module: None, |
8faf50e0 | 413 | wasm_import_module: None, |
17df50a5 XL |
414 | verbatim: passed_lib.verbatim, |
415 | dll_imports: Vec::new(), | |
ea8adc8c XL |
416 | }; |
417 | self.register_native_lib(None, lib); | |
532ac7d7 XL |
418 | } else { |
419 | // Move all existing libraries with the same name to the | |
420 | // end of the command line. | |
421 | self.libs.append(&mut existing); | |
ea8adc8c XL |
422 | } |
423 | } | |
424 | } | |
136023e0 | 425 | |
c295e0f8 | 426 | fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize { |
136023e0 XL |
427 | let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions( |
428 | self.tcx | |
429 | .type_of(item.id.def_id) | |
430 | .fn_sig(self.tcx) | |
431 | .inputs() | |
432 | .map_bound(|slice| self.tcx.mk_type_list(slice.iter())), | |
433 | ); | |
434 | ||
435 | argument_types | |
436 | .iter() | |
437 | .map(|ty| { | |
438 | let layout = self | |
439 | .tcx | |
440 | .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }) | |
441 | .expect("layout") | |
442 | .layout; | |
443 | // In both stdcall and fastcall, we always round up the argument size to the | |
444 | // nearest multiple of 4 bytes. | |
5e7ed085 | 445 | (layout.size().bytes_usize() + 3) & !3 |
136023e0 XL |
446 | }) |
447 | .sum() | |
448 | } | |
449 | ||
c295e0f8 | 450 | fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport { |
136023e0 XL |
451 | let calling_convention = if self.tcx.sess.target.arch == "x86" { |
452 | match abi { | |
5099ac24 | 453 | Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, |
136023e0 XL |
454 | Abi::Stdcall { .. } | Abi::System { .. } => { |
455 | DllCallingConvention::Stdcall(self.i686_arg_list_size(item)) | |
456 | } | |
5099ac24 FG |
457 | Abi::Fastcall { .. } => { |
458 | DllCallingConvention::Fastcall(self.i686_arg_list_size(item)) | |
459 | } | |
136023e0 XL |
460 | // Vectorcall is intentionally not supported at this time. |
461 | _ => { | |
462 | self.tcx.sess.span_fatal( | |
463 | item.span, | |
464 | r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#, | |
465 | ); | |
466 | } | |
467 | } | |
468 | } else { | |
469 | match abi { | |
5099ac24 | 470 | Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C, |
136023e0 XL |
471 | _ => { |
472 | self.tcx.sess.span_fatal( | |
473 | item.span, | |
474 | r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#, | |
475 | ); | |
476 | } | |
477 | } | |
478 | }; | |
c295e0f8 XL |
479 | |
480 | DllImport { | |
481 | name: item.ident.name, | |
482 | ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal, | |
483 | calling_convention, | |
484 | span: item.span, | |
485 | } | |
136023e0 | 486 | } |
ea8adc8c | 487 | } |