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
;
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
;
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
);
20 collector
.process_command_line();
24 pub(crate) fn relevant_lib(sess
: &Session
, lib
: &NativeLib
) -> bool
{
26 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, CRATE_NODE_ID
, None
),
31 struct Collector
<'tcx
> {
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
) {
42 let it
= self.tcx
.hir().item(id
);
43 let hir
::ItemKind
::ForeignMod { abi, items: foreign_mod_items }
= it
.kind
else {
47 if abi
== Abi
::Rust
|| abi
== Abi
::RustIntrinsic
|| abi
== Abi
::PlatformIntrinsic
{
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 {
61 let mut modifiers
= None
;
63 let mut wasm_import_module
= None
;
64 for item
in items
.iter() {
65 match item
.name_or_empty() {
68 let msg
= "multiple `name` arguments in a single `#[link]` attribute";
69 sess
.span_err(item
.span(), msg
);
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
);
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")
83 name
= Some((link_name
, span
));
87 let msg
= "multiple `kind` arguments in a single `#[link]` attribute";
88 sess
.span_err(item
.span(), msg
);
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
);
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 }
,
102 if !sess
.target
.is_like_osx
{
107 "link kind `framework` is only supported on Apple targets"
111 NativeLibKind
::Framework { as_needed: None }
114 if !sess
.target
.is_like_windows
{
119 "link kind `raw-dylib` is only supported on Windows targets"
122 } else if !features
.raw_dylib
{
127 "link kind `raw-dylib` is unstable",
131 NativeLibKind
::RawDylib
135 "unknown link kind `{kind}`, expected one of: \
136 static, dylib, framework, raw-dylib"
138 struct_span_err
!(sess
, span
, E0458
, "{}", msg
)
139 .span_label(span
, "unknown link kind")
144 kind
= Some(link_kind
);
147 if modifiers
.is_some() {
149 "multiple `modifiers` arguments in a single `#[link]` attribute";
150 sess
.span_err(item
.span(), msg
);
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
);
158 modifiers
= Some((link_modifiers
, item
.name_value_literal_span().unwrap()));
162 let msg
= "multiple `cfg` arguments in a single `#[link]` attribute";
163 sess
.span_err(item
.span(), msg
);
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
);
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
);
176 if !features
.link_cfg
{
181 "link cfg is unstable",
185 cfg
= Some(link_cfg
.clone());
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
);
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
);
200 wasm_import_module
= Some((link_wasm_import_module
, item
.span()));
203 let msg
= "unexpected `#[link]` argument, expected one of: \
204 name, kind, modifiers, cfg, wasm_import_module";
205 sess
.span_err(item
.span(), msg
);
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('
+'
)),
219 "invalid linking modifier syntax, expected '+' or '-' prefix \
220 before one of: bundle, verbatim, whole-archive, as-needed",
226 macro report_unstable_modifier($feature
: ident
) {
227 if !features
.$feature
{
232 &format
!("linking modifier `{modifier}` is unstable"),
237 let assign_modifier
= |dst
: &mut Option
<bool
>| {
240 "multiple `{modifier}` modifiers in a single `modifiers` argument"
242 sess
.span_err(span
, &msg
);
247 match (modifier
, &mut kind
) {
248 ("bundle", Some(NativeLibKind
::Static { bundle, .. }
)) => {
249 assign_modifier(bundle
)
254 "linking modifier `bundle` is only compatible with \
255 `static` linking kind",
260 report_unstable_modifier
!(native_link_modifiers_verbatim
);
261 assign_modifier(&mut verbatim
)
264 ("whole-archive", Some(NativeLibKind
::Static { whole_archive, .. }
)) => {
265 assign_modifier(whole_archive
)
267 ("whole-archive", _
) => {
270 "linking modifier `whole-archive` is only compatible with \
271 `static` linking kind",
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
)
280 ("as-needed", _
) => {
283 "linking modifier `as-needed` is only compatible with \
284 `dylib` and `framework` linking kinds",
292 "unknown linking modifier `{modifier}`, expected one of: \
293 bundle, verbatim, whole-archive, as-needed"
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
);
307 } else if name
.is_none() {
312 "`#[link]` attribute requires a `name = \"string\"` argument"
314 .span_label(m
.span
, "missing `name` argument")
318 let dll_imports
= match kind
{
319 Some(NativeLibKind
::RawDylib
) => {
320 if let Some((name
, span
)) = name
&& name
.as_str().contains('
\0'
) {
323 "link name must not contain NUL characters if link kind is `raw-dylib`",
328 .map(|child_item
| self.build_dll_import(abi
, child_item
))
333 self.libs
.push(NativeLib
{
334 name
: name
.map(|(name
, _
)| name
),
335 kind
: kind
.unwrap_or(NativeLibKind
::Unspecified
),
337 foreign_module
: Some(it
.def_id
.to_def_id()),
338 wasm_import_module
: wasm_import_module
.map(|(name
, _
)| name
),
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");
354 if let Some(ref new_name
) = lib
.new_name
{
355 let any_duplicate
= self
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 `{}`",
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",
372 } else if !renames
.insert(&lib
.name
) {
373 self.tcx
.sess
.err(format
!(
374 "multiple renamings were \
375 specified for library `{}`",
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
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
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
),
408 if passed_lib
.kind
!= NativeLibKind
::Unspecified
{
409 lib
.kind
= passed_lib
.kind
;
411 if let Some(new_name
) = &passed_lib
.new_name
{
412 lib
.name
= Some(Symbol
::intern(new_name
));
414 lib
.verbatim
= passed_lib
.verbatim
;
420 .collect
::<Vec
<_
>>();
421 if existing
.is_empty() {
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
,
428 foreign_module
: None
,
429 wasm_import_module
: None
,
430 verbatim
: passed_lib
.verbatim
,
431 dll_imports
: Vec
::new(),
434 // Move all existing libraries with the same name to the
435 // end of the command line.
436 self.libs
.append(&mut existing
);
441 fn i686_arg_list_size(&self, item
: &hir
::ForeignItemRef
) -> usize {
442 let argument_types
: &List
<Ty
<'_
>> = self.tcx
.erase_late_bound_regions(
444 .type_of(item
.id
.def_id
)
447 .map_bound(|slice
| self.tcx
.mk_type_list(slice
.iter())),
455 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }
)
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
465 fn build_dll_import(&self, abi
: Abi
, item
: &hir
::ForeignItemRef
) -> DllImport
{
466 let calling_convention
= if self.tcx
.sess
.target
.arch
== "x86" {
468 Abi
::C { .. }
| Abi
::Cdecl { .. }
=> DllCallingConvention
::C
,
469 Abi
::Stdcall { .. }
| Abi
::System { .. }
=> {
470 DllCallingConvention
::Stdcall(self.i686_arg_list_size(item
))
472 Abi
::Fastcall { .. }
=> {
473 DllCallingConvention
::Fastcall(self.i686_arg_list_size(item
))
475 // Vectorcall is intentionally not supported at this time.
477 self.tcx
.sess
.span_fatal(
479 r
#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
485 Abi
::C { .. }
| Abi
::Win64 { .. }
| Abi
::System { .. }
=> DllCallingConvention
::C
,
487 self.tcx
.sess
.span_fatal(
489 r
#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
496 name
: item
.ident
.name
,
497 ordinal
: self.tcx
.codegen_fn_attrs(item
.id
.def_id
).link_ordinal
,