From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_metadata/src/native_libs.rs | 504 +++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 compiler/rustc_metadata/src/native_libs.rs (limited to 'compiler/rustc_metadata/src/native_libs.rs') diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs new file mode 100644 index 000000000..9f6079ecb --- /dev/null +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -0,0 +1,504 @@ +use rustc_ast::{NestedMetaItem, CRATE_NODE_ID}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; +use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib}; +use rustc_session::parse::feature_err; +use rustc_session::utils::NativeLibKind; +use rustc_session::Session; +use rustc_span::symbol::{sym, Symbol}; +use rustc_target::spec::abi::Abi; + +pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec { + let mut collector = Collector { tcx, libs: Vec::new() }; + for id in tcx.hir().items() { + collector.process_item(id); + } + collector.process_command_line(); + collector.libs +} + +pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { + match lib.cfg { + Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), + None => true, + } +} + +struct Collector<'tcx> { + tcx: TyCtxt<'tcx>, + libs: Vec, +} + +impl<'tcx> Collector<'tcx> { + fn process_item(&mut self, id: rustc_hir::ItemId) { + if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) { + return; + } + + let it = self.tcx.hir().item(id); + let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else { + return; + }; + + if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { + return; + } + + // Process all of the #[link(..)]-style arguments + let sess = &self.tcx.sess; + let features = self.tcx.features(); + for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { + let Some(items) = m.meta_item_list() else { + continue; + }; + + let mut name = None; + let mut kind = None; + let mut modifiers = None; + let mut cfg = None; + let mut wasm_import_module = None; + for item in items.iter() { + match item.name_or_empty() { + sym::name => { + if name.is_some() { + let msg = "multiple `name` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_name) = item.value_str() else { + let msg = "link name must be of the form `name = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + let span = item.name_value_literal_span().unwrap(); + if link_name.is_empty() { + struct_span_err!(sess, span, E0454, "link name must not be empty") + .span_label(span, "empty link name") + .emit(); + } + name = Some((link_name, span)); + } + sym::kind => { + if kind.is_some() { + let msg = "multiple `kind` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_kind) = item.value_str() else { + let msg = "link kind must be of the form `kind = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + + let span = item.name_value_literal_span().unwrap(); + let link_kind = match link_kind.as_str() { + "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "dylib" => NativeLibKind::Dylib { as_needed: None }, + "framework" => { + if !sess.target.is_like_osx { + struct_span_err!( + sess, + span, + E0455, + "link kind `framework` is only supported on Apple targets" + ) + .emit(); + } + NativeLibKind::Framework { as_needed: None } + } + "raw-dylib" => { + if !sess.target.is_like_windows { + struct_span_err!( + sess, + span, + E0455, + "link kind `raw-dylib` is only supported on Windows targets" + ) + .emit(); + } else if !features.raw_dylib { + feature_err( + &sess.parse_sess, + sym::raw_dylib, + span, + "link kind `raw-dylib` is unstable", + ) + .emit(); + } + NativeLibKind::RawDylib + } + kind => { + let msg = format!( + "unknown link kind `{kind}`, expected one of: \ + static, dylib, framework, raw-dylib" + ); + struct_span_err!(sess, span, E0458, "{}", msg) + .span_label(span, "unknown link kind") + .emit(); + continue; + } + }; + kind = Some(link_kind); + } + sym::modifiers => { + if modifiers.is_some() { + let msg = + "multiple `modifiers` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_modifiers) = item.value_str() else { + let msg = "link modifiers must be of the form `modifiers = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap())); + } + sym::cfg => { + if cfg.is_some() { + let msg = "multiple `cfg` arguments in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_cfg) = item.meta_item_list() else { + let msg = "link cfg must be of the form `cfg(/* predicate */)`"; + sess.span_err(item.span(), msg); + continue; + }; + let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else { + let msg = "link cfg must have a single predicate argument"; + sess.span_err(item.span(), msg); + continue; + }; + if !features.link_cfg { + feature_err( + &sess.parse_sess, + sym::link_cfg, + item.span(), + "link cfg is unstable", + ) + .emit(); + } + cfg = Some(link_cfg.clone()); + } + sym::wasm_import_module => { + if wasm_import_module.is_some() { + let msg = "multiple `wasm_import_module` arguments \ + in a single `#[link]` attribute"; + sess.span_err(item.span(), msg); + continue; + } + let Some(link_wasm_import_module) = item.value_str() else { + let msg = "wasm import module must be of the form \ + `wasm_import_module = \"string\"`"; + sess.span_err(item.span(), msg); + continue; + }; + wasm_import_module = Some((link_wasm_import_module, item.span())); + } + _ => { + let msg = "unexpected `#[link]` argument, expected one of: \ + name, kind, modifiers, cfg, wasm_import_module"; + sess.span_err(item.span(), msg); + } + } + } + + // Do this outside the above loop so we don't depend on modifiers coming after kinds + let mut verbatim = None; + if let Some((modifiers, span)) = modifiers { + for modifier in modifiers.as_str().split(',') { + let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { + Some(m) => (m, modifier.starts_with('+')), + None => { + sess.span_err( + span, + "invalid linking modifier syntax, expected '+' or '-' prefix \ + before one of: bundle, verbatim, whole-archive, as-needed", + ); + continue; + } + }; + + macro report_unstable_modifier($feature: ident) { + if !features.$feature { + feature_err( + &sess.parse_sess, + sym::$feature, + span, + &format!("linking modifier `{modifier}` is unstable"), + ) + .emit(); + } + } + let assign_modifier = |dst: &mut Option| { + if dst.is_some() { + let msg = format!( + "multiple `{modifier}` modifiers in a single `modifiers` argument" + ); + sess.span_err(span, &msg); + } else { + *dst = Some(value); + } + }; + match (modifier, &mut kind) { + ("bundle", Some(NativeLibKind::Static { bundle, .. })) => { + assign_modifier(bundle) + } + ("bundle", _) => { + sess.span_err( + span, + "linking modifier `bundle` is only compatible with \ + `static` linking kind", + ); + } + + ("verbatim", _) => { + report_unstable_modifier!(native_link_modifiers_verbatim); + assign_modifier(&mut verbatim) + } + + ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => { + assign_modifier(whole_archive) + } + ("whole-archive", _) => { + sess.span_err( + span, + "linking modifier `whole-archive` is only compatible with \ + `static` linking kind", + ); + } + + ("as-needed", Some(NativeLibKind::Dylib { as_needed })) + | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => { + report_unstable_modifier!(native_link_modifiers_as_needed); + assign_modifier(as_needed) + } + ("as-needed", _) => { + sess.span_err( + span, + "linking modifier `as-needed` is only compatible with \ + `dylib` and `framework` linking kinds", + ); + } + + _ => { + sess.span_err( + span, + format!( + "unknown linking modifier `{modifier}`, expected one of: \ + bundle, verbatim, whole-archive, as-needed" + ), + ); + } + } + } + } + + if let Some((_, span)) = wasm_import_module { + if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() { + let msg = "`wasm_import_module` is incompatible with \ + other arguments in `#[link]` attributes"; + sess.span_err(span, msg); + } + } else if name.is_none() { + struct_span_err!( + sess, + m.span, + E0459, + "`#[link]` attribute requires a `name = \"string\"` argument" + ) + .span_label(m.span, "missing `name` argument") + .emit(); + } + + let dll_imports = match kind { + Some(NativeLibKind::RawDylib) => { + if let Some((name, span)) = name && name.as_str().contains('\0') { + sess.span_err( + span, + "link name must not contain NUL characters if link kind is `raw-dylib`", + ); + } + foreign_mod_items + .iter() + .map(|child_item| self.build_dll_import(abi, child_item)) + .collect() + } + _ => Vec::new(), + }; + self.libs.push(NativeLib { + name: name.map(|(name, _)| name), + kind: kind.unwrap_or(NativeLibKind::Unspecified), + cfg, + foreign_module: Some(it.def_id.to_def_id()), + wasm_import_module: wasm_import_module.map(|(name, _)| name), + verbatim, + dll_imports, + }); + } + } + + // Process libs passed on the command line + fn process_command_line(&mut self) { + // First, check for errors + let mut renames = FxHashSet::default(); + for lib in &self.tcx.sess.opts.libs { + if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx { + // Cannot check this when parsing options because the target is not yet available. + self.tcx.sess.err("library kind `framework` is only supported on Apple targets"); + } + if let Some(ref new_name) = lib.new_name { + let any_duplicate = self + .libs + .iter() + .filter_map(|lib| lib.name.as_ref()) + .any(|n| n.as_str() == lib.name); + if new_name.is_empty() { + self.tcx.sess.err(format!( + "an empty renaming target was specified for library `{}`", + lib.name + )); + } else if !any_duplicate { + self.tcx.sess.err(format!( + "renaming of the library `{}` was specified, \ + however this crate contains no `#[link(...)]` \ + attributes referencing this library", + lib.name + )); + } else if !renames.insert(&lib.name) { + self.tcx.sess.err(format!( + "multiple renamings were \ + specified for library `{}`", + lib.name + )); + } + } + } + + // Update kind and, optionally, the name of all native libraries + // (there may be more than one) with the specified name. If any + // library is mentioned more than once, keep the latest mention + // of it, so that any possible dependent libraries appear before + // it. (This ensures that the linker is able to see symbols from + // all possible dependent libraries before linking in the library + // in question.) + for passed_lib in &self.tcx.sess.opts.libs { + // If we've already added any native libraries with the same + // name, they will be pulled out into `existing`, so that we + // can move them to the end of the list below. + let mut existing = self + .libs + .drain_filter(|lib| { + if let Some(lib_name) = lib.name { + if lib_name.as_str() == passed_lib.name { + // FIXME: This whole logic is questionable, whether modifiers are + // involved or not, library reordering and kind overriding without + // explicit `:rename` in particular. + if lib.has_modifiers() || passed_lib.has_modifiers() { + let msg = "overriding linking modifiers from command line is not supported"; + match lib.foreign_module { + Some(def_id) => self.tcx.sess.span_err(self.tcx.def_span(def_id), msg), + None => self.tcx.sess.err(msg), + }; + } + if passed_lib.kind != NativeLibKind::Unspecified { + lib.kind = passed_lib.kind; + } + if let Some(new_name) = &passed_lib.new_name { + lib.name = Some(Symbol::intern(new_name)); + } + lib.verbatim = passed_lib.verbatim; + return true; + } + } + false + }) + .collect::>(); + if existing.is_empty() { + // Add if not found + let new_name: Option<&str> = passed_lib.new_name.as_deref(); + self.libs.push(NativeLib { + name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))), + kind: passed_lib.kind, + cfg: None, + foreign_module: None, + wasm_import_module: None, + verbatim: passed_lib.verbatim, + dll_imports: Vec::new(), + }); + } else { + // Move all existing libraries with the same name to the + // end of the command line. + self.libs.append(&mut existing); + } + } + } + + fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize { + let argument_types: &List> = self.tcx.erase_late_bound_regions( + self.tcx + .type_of(item.id.def_id) + .fn_sig(self.tcx) + .inputs() + .map_bound(|slice| self.tcx.mk_type_list(slice.iter())), + ); + + argument_types + .iter() + .map(|ty| { + let layout = self + .tcx + .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }) + .expect("layout") + .layout; + // In both stdcall and fastcall, we always round up the argument size to the + // nearest multiple of 4 bytes. + (layout.size().bytes_usize() + 3) & !3 + }) + .sum() + } + + fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport { + let calling_convention = if self.tcx.sess.target.arch == "x86" { + match abi { + Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, + Abi::Stdcall { .. } | Abi::System { .. } => { + DllCallingConvention::Stdcall(self.i686_arg_list_size(item)) + } + Abi::Fastcall { .. } => { + DllCallingConvention::Fastcall(self.i686_arg_list_size(item)) + } + Abi::Vectorcall { .. } => { + DllCallingConvention::Vectorcall(self.i686_arg_list_size(item)) + } + _ => { + self.tcx.sess.span_fatal( + item.span, + r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#, + ); + } + } + } else { + match abi { + Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C, + _ => { + self.tcx.sess.span_fatal( + item.span, + r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#, + ); + } + } + }; + + DllImport { + name: item.ident.name, + ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal, + calling_convention, + span: item.span, + } + } +} -- cgit v1.2.3