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