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
;
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}
;
14 use rustc_target
::spec
::abi
::Abi
;
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();
23 crate fn relevant_lib(sess
: &Session
, lib
: &NativeLib
) -> bool
{
25 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, CRATE_NODE_ID
, None
),
30 struct Collector
<'tcx
> {
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 {
41 if abi
== Abi
::Rust
|| abi
== Abi
::RustIntrinsic
|| abi
== Abi
::PlatformIntrinsic
{
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 {
51 let mut lib
= NativeLib
{
53 kind
: NativeLibKind
::Unspecified
,
55 foreign_module
: Some(it
.def_id
.to_def_id()),
56 wasm_import_module
: None
,
58 dll_imports
: Vec
::new(),
60 let mut kind_specified
= false;
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
68 lib
.kind
= match kind
.as_str() {
69 "static" => NativeLibKind
::Static { bundle: None, whole_archive: None }
,
70 "static-nobundle" => {
71 sess
.struct_span_warn(
73 "library kind `static-nobundle` has been superseded by specifying \
74 modifier `-bundle` with library kind `static`",
77 if !self.tcx
.features().static_nobundle
{
79 &self.tcx
.sess
.parse_sess
,
82 "kind=\"static-nobundle\" is unstable",
86 NativeLibKind
::Static { bundle: Some(false), whole_archive: None }
88 "dylib" => NativeLibKind
::Dylib { as_needed: None }
,
89 "framework" => NativeLibKind
::Framework { as_needed: None }
,
90 "raw-dylib" => NativeLibKind
::RawDylib
,
92 struct_span_err
!(sess
, item
.span(), E0458
, "unknown kind: `{}`", k
)
93 .span_label(item
.span(), "unknown kind")
94 .span_label(m
.span
, "")
96 NativeLibKind
::Unspecified
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
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();
110 sess
.span_err(cfg
[0].span(), "invalid argument for `cfg(..)`");
112 } else if item
.has_name(sym
::wasm_import_module
) {
113 match item
.value_str() {
114 Some(s
) => lib
.wasm_import_module
= Some(s
),
116 let msg
= "must be of the form `#[link(wasm_import_module = \"...\")]`";
117 sess
.span_err(item
.span(), msg
);
121 // currently, like past compilers, ignore unknown
126 // Do this outside the above loop so we don't depend on modifiers coming
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('
+'
)),
138 // Note: this error also excludes the case with empty modifier
139 // string, like `modifiers = ""`.
142 "invalid linking modifier syntax, expected '+' or '-' prefix \
143 before one of: bundle, verbatim, whole-archive, as-needed",
149 match (modifier
, &mut lib
.kind
) {
150 ("bundle", NativeLibKind
::Static { bundle, .. }
) => {
151 if bundle
.is_some() {
152 has_duplicate_modifiers
= true;
154 *bundle
= Some(value
);
159 "bundle linking modifier is only compatible with \
160 `static` linking kind",
165 if lib
.verbatim
.is_some() {
166 has_duplicate_modifiers
= true;
168 lib
.verbatim
= Some(value
);
171 ("whole-archive", NativeLibKind
::Static { whole_archive, .. }
) => {
172 if whole_archive
.is_some() {
173 has_duplicate_modifiers
= true;
175 *whole_archive
= Some(value
);
177 ("whole-archive", _
) => {
180 "whole-archive linking modifier is only compatible with \
181 `static` linking kind",
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;
190 *as_needed
= Some(value
);
192 ("as-needed", _
) => {
195 "as-needed linking modifier is only compatible with \
196 `dylib` and `framework` linking kinds",
204 "unrecognized linking modifier `{}`, expected one \
205 of: bundle, verbatim, whole-archive, as-needed",
212 if has_duplicate_modifiers
{
214 "same modifier is used multiple times in a single `modifiers` argument";
215 sess
.span_err(item
.span(), msg
);
218 let msg
= "must be of the form `#[link(modifiers = \"...\")]`";
219 sess
.span_err(item
.span(), msg
);
223 if modifiers_count
> 1 {
224 let msg
= "multiple `modifiers` arguments in a single `#[link]` attribute";
225 sess
.span_err(m
.span
, msg
);
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
{
236 "`#[link(...)]` specified without \
239 .span_label(m
.span
, "missing `name` argument")
243 if lib
.kind
== NativeLibKind
::RawDylib
{
244 lib
.dll_imports
.extend(
247 .map(|child_item
| self.build_dll_import(abi
, child_item
)),
251 self.register_native_lib(Some(m
.span
), lib
);
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
>) {}
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
) {
269 "`#[link(name = \"\")]` given with empty name"
271 .span_label(span
, "empty name given")
275 self.tcx
.sess
.err("empty library name given via `-l`");
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";
285 struct_span_err
!(self.tcx
.sess
, span
, E0455
, "{}", msg
).emit();
288 self.tcx
.sess
.err(msg
);
292 if lib
.cfg
.is_some() && !self.tcx
.features().link_cfg
{
294 &self.tcx
.sess
.parse_sess
,
297 "kind=\"link_cfg\" is unstable",
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");
307 if !self.tcx
.sess
.target
.options
.is_like_windows
{
308 self.tcx
.sess
.span_fatal(
310 "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows",
314 if lib_name
.as_str().contains('
\0'
) {
315 self.tcx
.sess
.span_err(span
, "library name may not contain NUL characters");
318 if !self.tcx
.features().raw_dylib
{
320 &self.tcx
.sess
.parse_sess
,
323 "kind=\"raw-dylib\" is unstable",
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
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 `{}`",
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",
355 } else if !renames
.insert(&lib
.name
) {
356 self.tcx
.sess
.err(&format
!(
357 "multiple renamings were \
358 specified for library `{}`",
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
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
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"
390 if passed_lib
.kind
!= NativeLibKind
::Unspecified
{
391 lib
.kind
= passed_lib
.kind
;
393 if let Some(new_name
) = &passed_lib
.new_name
{
394 lib
.name
= Some(Symbol
::intern(new_name
));
396 lib
.verbatim
= passed_lib
.verbatim
;
402 .collect
::<Vec
<_
>>();
403 if existing
.is_empty() {
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
,
410 foreign_module
: None
,
411 wasm_import_module
: None
,
412 verbatim
: passed_lib
.verbatim
,
413 dll_imports
: Vec
::new(),
415 self.register_native_lib(None
, lib
);
417 // Move all existing libraries with the same name to the
418 // end of the command line.
419 self.libs
.append(&mut existing
);
424 fn i686_arg_list_size(&self, item
: &hir
::ForeignItemRef
) -> usize {
425 let argument_types
: &List
<Ty
<'_
>> = self.tcx
.erase_late_bound_regions(
427 .type_of(item
.id
.def_id
)
430 .map_bound(|slice
| self.tcx
.mk_type_list(slice
.iter())),
438 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }
)
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
448 fn build_dll_import(&self, abi
: Abi
, item
: &hir
::ForeignItemRef
) -> DllImport
{
449 let calling_convention
= if self.tcx
.sess
.target
.arch
== "x86" {
451 Abi
::C { .. }
| Abi
::Cdecl { .. }
=> DllCallingConvention
::C
,
452 Abi
::Stdcall { .. }
| Abi
::System { .. }
=> {
453 DllCallingConvention
::Stdcall(self.i686_arg_list_size(item
))
455 Abi
::Fastcall { .. }
=> {
456 DllCallingConvention
::Fastcall(self.i686_arg_list_size(item
))
458 // Vectorcall is intentionally not supported at this time.
460 self.tcx
.sess
.span_fatal(
462 r
#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
468 Abi
::C { .. }
| Abi
::Win64 { .. }
| Abi
::System { .. }
=> DllCallingConvention
::C
,
470 self.tcx
.sess
.span_fatal(
472 r
#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
479 name
: item
.ident
.name
,
480 ordinal
: self.tcx
.codegen_fn_attrs(item
.id
.def_id
).link_ordinal
,