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