1 use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
2 use rustc_attr as attr;
3 use rustc_data_structures::fx::FxHashSet;
5 use rustc_hir::def::DefKind;
6 use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
7 use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType};
8 use rustc_session::parse::feature_err;
9 use rustc_session::utils::NativeLibKind;
10 use rustc_session::Session;
11 use rustc_span::symbol::{sym, Symbol};
12 use rustc_target::spec::abi::Abi;
15 AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, EmptyRenamingTarget,
16 FrameworkOnlyWindows, ImportNameTypeForm, ImportNameTypeRaw, ImportNameTypeX86,
17 IncompatibleWasmLink, InvalidLinkModifier, LibFrameworkApple, LinkCfgForm,
18 LinkCfgSinglePredicate, LinkFrameworkApple, LinkKindForm, LinkModifiersForm, LinkNameForm,
19 LinkOrdinalRawDylib, LinkRequiresName, MultipleCfgs, MultipleImportNameType,
20 MultipleKindsInLink, MultipleLinkModifiers, MultipleModifiers, MultipleNamesInLink,
21 MultipleRenamings, MultipleWasmImport, NoLinkModOverride, RawDylibNoNul, RenamingNoLink,
22 UnexpectedLinkArg, UnknownImportNameType, UnknownLinkKind, UnknownLinkModifier, UnsupportedAbi,
23 UnsupportedAbiI686, WasmImportForm, WholeArchiveNeedsStatic,
26 pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
27 let mut collector = Collector { tcx, libs: Vec::new() };
28 for id in tcx.hir().items() {
29 collector.process_item(id);
31 collector.process_command_line();
35 pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
37 Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
42 struct Collector<'tcx> {
47 impl<'tcx> Collector<'tcx> {
48 fn process_item(&mut self, id: rustc_hir::ItemId) {
49 if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) {
53 let it = self.tcx.hir().item(id);
54 let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else {
58 if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
62 // Process all of the #[link(..)]-style arguments
63 let sess = &self.tcx.sess;
64 let features = self.tcx.features();
65 for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
66 let Some(items) = m.meta_item_list() else {
72 let mut modifiers = None;
74 let mut wasm_import_module = None;
75 let mut import_name_type = None;
76 for item in items.iter() {
77 match item.name_or_empty() {
80 sess.emit_err(MultipleNamesInLink { span: item.span() });
83 let Some(link_name) = item.value_str() else {
84 sess.emit_err(LinkNameForm { span: item.span() });
87 let span = item.name_value_literal_span().unwrap();
88 if link_name.is_empty() {
89 sess.emit_err(EmptyLinkName { span });
91 name = Some((link_name, span));
95 sess.emit_err(MultipleKindsInLink { span: item.span() });
98 let Some(link_kind) = item.value_str() else {
99 sess.emit_err(LinkKindForm { span: item.span() });
103 let span = item.name_value_literal_span().unwrap();
104 let link_kind = match link_kind.as_str() {
105 "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
106 "dylib" => NativeLibKind::Dylib { as_needed: None },
108 if !sess.target.is_like_osx {
109 sess.emit_err(LinkFrameworkApple { span });
111 NativeLibKind::Framework { as_needed: None }
114 if !sess.target.is_like_windows {
115 sess.emit_err(FrameworkOnlyWindows { span });
116 } else if !features.raw_dylib {
121 "link kind `raw-dylib` is unstable",
125 NativeLibKind::RawDylib
128 sess.emit_err(UnknownLinkKind { span, kind });
132 kind = Some(link_kind);
135 if modifiers.is_some() {
136 sess.emit_err(MultipleLinkModifiers { span: item.span() });
139 let Some(link_modifiers) = item.value_str() else {
140 sess.emit_err(LinkModifiersForm { span: item.span() });
143 modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
147 sess.emit_err(MultipleCfgs { span: item.span() });
150 let Some(link_cfg) = item.meta_item_list() else {
151 sess.emit_err(LinkCfgForm { span: item.span() });
154 let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
155 sess.emit_err(LinkCfgSinglePredicate { span: item.span() });
158 if !features.link_cfg {
163 "link cfg is unstable",
167 cfg = Some(link_cfg.clone());
169 sym::wasm_import_module => {
170 if wasm_import_module.is_some() {
171 sess.emit_err(MultipleWasmImport { span: item.span() });
174 let Some(link_wasm_import_module) = item.value_str() else {
175 sess.emit_err(WasmImportForm { span: item.span() });
178 wasm_import_module = Some((link_wasm_import_module, item.span()));
180 sym::import_name_type => {
181 if import_name_type.is_some() {
182 sess.emit_err(MultipleImportNameType { span: item.span() });
185 let Some(link_import_name_type) = item.value_str() else {
186 sess.emit_err(ImportNameTypeForm { span: item.span() });
189 if self.tcx.sess.target.arch != "x86" {
190 sess.emit_err(ImportNameTypeX86 { span: item.span() });
194 let link_import_name_type = match link_import_name_type.as_str() {
195 "decorated" => PeImportNameType::Decorated,
196 "noprefix" => PeImportNameType::NoPrefix,
197 "undecorated" => PeImportNameType::Undecorated,
198 import_name_type => {
199 sess.emit_err(UnknownImportNameType {
206 if !features.raw_dylib {
207 let span = item.name_value_literal_span().unwrap();
212 "import name type is unstable",
216 import_name_type = Some((link_import_name_type, item.span()));
219 sess.emit_err(UnexpectedLinkArg { span: item.span() });
224 // Do this outside the above loop so we don't depend on modifiers coming after kinds
225 let mut verbatim = None;
226 if let Some((modifiers, span)) = modifiers {
227 for modifier in modifiers.as_str().split(',') {
228 let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
229 Some(m) => (m, modifier.starts_with('+')),
231 sess.emit_err(InvalidLinkModifier { span });
236 macro report_unstable_modifier($feature: ident) {
237 if !features.$feature {
242 &format!("linking modifier `{modifier}` is unstable"),
247 let assign_modifier = |dst: &mut Option<bool>| {
249 sess.emit_err(MultipleModifiers { span, modifier });
254 match (modifier, &mut kind) {
255 ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
256 assign_modifier(bundle)
259 sess.emit_err(BundleNeedsStatic { span });
263 report_unstable_modifier!(native_link_modifiers_verbatim);
264 assign_modifier(&mut verbatim)
267 ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
268 assign_modifier(whole_archive)
270 ("whole-archive", _) => {
271 sess.emit_err(WholeArchiveNeedsStatic { span });
274 ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
275 | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
276 report_unstable_modifier!(native_link_modifiers_as_needed);
277 assign_modifier(as_needed)
279 ("as-needed", _) => {
280 sess.emit_err(AsNeededCompatibility { span });
284 sess.emit_err(UnknownLinkModifier { span, modifier });
290 if let Some((_, span)) = wasm_import_module {
291 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
292 sess.emit_err(IncompatibleWasmLink { span });
294 } else if name.is_none() {
295 sess.emit_err(LinkRequiresName { span: m.span });
298 // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
299 if let Some((_, span)) = import_name_type {
300 if kind != Some(NativeLibKind::RawDylib) {
301 sess.emit_err(ImportNameTypeRaw { span });
305 let dll_imports = match kind {
306 Some(NativeLibKind::RawDylib) => {
307 if let Some((name, span)) = name && name.as_str().contains('\0') {
308 sess.emit_err(RawDylibNoNul { span });
313 self.build_dll_import(
315 import_name_type.map(|(import_name_type, _)| import_name_type),
322 for child_item in foreign_mod_items {
323 if self.tcx.def_kind(child_item.id.def_id).has_codegen_attrs()
326 .codegen_fn_attrs(child_item.id.def_id)
330 let link_ordinal_attr = self
333 .attrs(self.tcx.hir().local_def_id_to_hir_id(child_item.id.def_id))
335 .find(|a| a.has_name(sym::link_ordinal))
337 sess.emit_err(LinkOrdinalRawDylib { span: link_ordinal_attr.span });
344 self.libs.push(NativeLib {
345 name: name.map(|(name, _)| name),
346 kind: kind.unwrap_or(NativeLibKind::Unspecified),
348 foreign_module: Some(it.def_id.to_def_id()),
349 wasm_import_module: wasm_import_module.map(|(name, _)| name),
356 // Process libs passed on the command line
357 fn process_command_line(&mut self) {
358 // First, check for errors
359 let mut renames = FxHashSet::default();
360 for lib in &self.tcx.sess.opts.libs {
361 if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
362 // Cannot check this when parsing options because the target is not yet available.
363 self.tcx.sess.emit_err(LibFrameworkApple);
365 if let Some(ref new_name) = lib.new_name {
366 let any_duplicate = self
369 .filter_map(|lib| lib.name.as_ref())
370 .any(|n| n.as_str() == lib.name);
371 if new_name.is_empty() {
372 self.tcx.sess.emit_err(EmptyRenamingTarget { lib_name: &lib.name });
373 } else if !any_duplicate {
374 self.tcx.sess.emit_err(RenamingNoLink { lib_name: &lib.name });
375 } else if !renames.insert(&lib.name) {
376 self.tcx.sess.emit_err(MultipleRenamings { lib_name: &lib.name });
381 // Update kind and, optionally, the name of all native libraries
382 // (there may be more than one) with the specified name. If any
383 // library is mentioned more than once, keep the latest mention
384 // of it, so that any possible dependent libraries appear before
385 // it. (This ensures that the linker is able to see symbols from
386 // all possible dependent libraries before linking in the library
388 for passed_lib in &self.tcx.sess.opts.libs {
389 // If we've already added any native libraries with the same
390 // name, they will be pulled out into `existing`, so that we
391 // can move them to the end of the list below.
392 let mut existing = self
394 .drain_filter(|lib| {
395 if let Some(lib_name) = lib.name {
396 if lib_name.as_str() == passed_lib.name {
397 // FIXME: This whole logic is questionable, whether modifiers are
398 // involved or not, library reordering and kind overriding without
399 // explicit `:rename` in particular.
400 if lib.has_modifiers() || passed_lib.has_modifiers() {
401 match lib.foreign_module {
402 Some(def_id) => self.tcx.sess.emit_err(NoLinkModOverride {
403 span: Some(self.tcx.def_span(def_id)),
406 self.tcx.sess.emit_err(NoLinkModOverride { span: None })
410 if passed_lib.kind != NativeLibKind::Unspecified {
411 lib.kind = passed_lib.kind;
413 if let Some(new_name) = &passed_lib.new_name {
414 lib.name = Some(Symbol::intern(new_name));
416 lib.verbatim = passed_lib.verbatim;
422 .collect::<Vec<_>>();
423 if existing.is_empty() {
425 let new_name: Option<&str> = passed_lib.new_name.as_deref();
426 self.libs.push(NativeLib {
427 name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
428 kind: passed_lib.kind,
430 foreign_module: None,
431 wasm_import_module: None,
432 verbatim: passed_lib.verbatim,
433 dll_imports: Vec::new(),
436 // Move all existing libraries with the same name to the
437 // end of the command line.
438 self.libs.append(&mut existing);
443 fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
444 let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
446 .type_of(item.id.def_id)
449 .map_bound(|slice| self.tcx.mk_type_list(slice.iter())),
457 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
460 // In both stdcall and fastcall, we always round up the argument size to the
461 // nearest multiple of 4 bytes.
462 (layout.size().bytes_usize() + 3) & !3
470 import_name_type: Option<PeImportNameType>,
471 item: &hir::ForeignItemRef,
473 let calling_convention = if self.tcx.sess.target.arch == "x86" {
475 Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
476 Abi::Stdcall { .. } | Abi::System { .. } => {
477 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
479 Abi::Fastcall { .. } => {
480 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
482 Abi::Vectorcall { .. } => {
483 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
486 self.tcx.sess.emit_fatal(UnsupportedAbiI686 { span: item.span });
491 Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
493 self.tcx.sess.emit_fatal(UnsupportedAbi { span: item.span });
498 let import_name_type = self
500 .codegen_fn_attrs(item.id.def_id)
502 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
505 name: item.ident.name,
509 is_fn: self.tcx.def_kind(item.id.def_id).is_fn_like(),