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
::config
::CrateType
;
8 use rustc_session
::cstore
::{DllCallingConvention, DllImport, NativeLib, PeImportNameType}
;
9 use rustc_session
::parse
::feature_err
;
10 use rustc_session
::search_paths
::PathKind
;
11 use rustc_session
::utils
::NativeLibKind
;
12 use rustc_session
::Session
;
13 use rustc_span
::symbol
::{sym, Symbol}
;
14 use rustc_target
::spec
::abi
::Abi
;
18 use std
::path
::PathBuf
;
20 pub fn find_native_static_library(
23 search_paths
: &[PathBuf
],
26 let formats
= if verbatim
{
27 vec
![("".into(), "".into())]
29 let os
= (sess
.target
.staticlib_prefix
.clone(), sess
.target
.staticlib_suffix
.clone());
30 // On Windows, static libraries sometimes show up as libfoo.a and other
31 // times show up as foo.lib
32 let unix
= ("lib".into(), ".a".into());
33 if os
== unix { vec![os] }
else { vec![os, unix] }
36 for path
in search_paths
{
37 for (prefix
, suffix
) in &formats
{
38 let test
= path
.join(format
!("{prefix}{name}{suffix}"));
45 sess
.emit_fatal(errors
::MissingNativeLibrary
::new(name
, verbatim
));
48 fn find_bundled_library(
50 verbatim
: Option
<bool
>,
55 if let NativeLibKind
::Static { bundle: Some(true) | None, whole_archive }
= kind
56 && sess
.crate_types().iter().any(|t
| matches
!(t
, &CrateType
::Rlib
| CrateType
::Staticlib
))
57 && (sess
.opts
.unstable_opts
.packed_bundled_libs
|| has_cfg
|| whole_archive
== Some(true))
59 let verbatim
= verbatim
.unwrap_or(false);
60 let search_paths
= &sess
.target_filesearch(PathKind
::Native
).search_path_dirs();
61 return find_native_static_library(name
.unwrap().as_str(), verbatim
, search_paths
, sess
)
63 .and_then(|s
| s
.to_str())
69 pub(crate) fn collect(tcx
: TyCtxt
<'_
>) -> Vec
<NativeLib
> {
70 let mut collector
= Collector { tcx, libs: Vec::new() }
;
71 for id
in tcx
.hir().items() {
72 collector
.process_item(id
);
74 collector
.process_command_line();
78 pub(crate) fn relevant_lib(sess
: &Session
, lib
: &NativeLib
) -> bool
{
80 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, CRATE_NODE_ID
, None
),
85 struct Collector
<'tcx
> {
90 impl<'tcx
> Collector
<'tcx
> {
91 fn process_item(&mut self, id
: rustc_hir
::ItemId
) {
92 if !matches
!(self.tcx
.def_kind(id
.owner_id
), DefKind
::ForeignMod
) {
96 let it
= self.tcx
.hir().item(id
);
97 let hir
::ItemKind
::ForeignMod { abi, items: foreign_mod_items }
= it
.kind
else {
101 if matches
!(abi
, Abi
::Rust
| Abi
::RustIntrinsic
| Abi
::PlatformIntrinsic
) {
105 // Process all of the #[link(..)]-style arguments
106 let sess
= self.tcx
.sess
;
107 let features
= self.tcx
.features();
109 if !sess
.opts
.unstable_opts
.link_directives
{
113 for m
in self.tcx
.hir().attrs(it
.hir_id()).iter().filter(|a
| a
.has_name(sym
::link
)) {
114 let Some(items
) = m
.meta_item_list() else {
120 let mut modifiers
= None
;
122 let mut wasm_import_module
= None
;
123 let mut import_name_type
= None
;
124 for item
in items
.iter() {
125 match item
.name_or_empty() {
128 sess
.emit_err(errors
::MultipleNamesInLink { span: item.span() }
);
131 let Some(link_name
) = item
.value_str() else {
132 sess
.emit_err(errors
::LinkNameForm { span: item.span() }
);
135 let span
= item
.name_value_literal_span().unwrap();
136 if link_name
.is_empty() {
137 sess
.emit_err(errors
::EmptyLinkName { span }
);
139 name
= Some((link_name
, span
));
143 sess
.emit_err(errors
::MultipleKindsInLink { span: item.span() }
);
146 let Some(link_kind
) = item
.value_str() else {
147 sess
.emit_err(errors
::LinkKindForm { span: item.span() }
);
151 let span
= item
.name_value_literal_span().unwrap();
152 let link_kind
= match link_kind
.as_str() {
153 "static" => NativeLibKind
::Static { bundle: None, whole_archive: None }
,
154 "dylib" => NativeLibKind
::Dylib { as_needed: None }
,
156 if !sess
.target
.is_like_osx
{
157 sess
.emit_err(errors
::LinkFrameworkApple { span }
);
159 NativeLibKind
::Framework { as_needed: None }
162 if !sess
.target
.is_like_windows
{
163 sess
.emit_err(errors
::FrameworkOnlyWindows { span }
);
164 } else if !features
.raw_dylib
&& sess
.target
.arch
== "x86" {
169 "link kind `raw-dylib` is unstable on x86",
173 NativeLibKind
::RawDylib
176 sess
.emit_err(errors
::UnknownLinkKind { span, kind }
);
180 kind
= Some(link_kind
);
183 if modifiers
.is_some() {
184 sess
.emit_err(errors
::MultipleLinkModifiers { span: item.span() }
);
187 let Some(link_modifiers
) = item
.value_str() else {
188 sess
.emit_err(errors
::LinkModifiersForm { span: item.span() }
);
191 modifiers
= Some((link_modifiers
, item
.name_value_literal_span().unwrap()));
195 sess
.emit_err(errors
::MultipleCfgs { span: item.span() }
);
198 let Some(link_cfg
) = item
.meta_item_list() else {
199 sess
.emit_err(errors
::LinkCfgForm { span: item.span() }
);
202 let [NestedMetaItem
::MetaItem(link_cfg
)] = link_cfg
else {
203 sess
.emit_err(errors
::LinkCfgSinglePredicate { span: item.span() }
);
206 if !features
.link_cfg
{
211 "link cfg is unstable",
215 cfg
= Some(link_cfg
.clone());
217 sym
::wasm_import_module
=> {
218 if wasm_import_module
.is_some() {
219 sess
.emit_err(errors
::MultipleWasmImport { span: item.span() }
);
222 let Some(link_wasm_import_module
) = item
.value_str() else {
223 sess
.emit_err(errors
::WasmImportForm { span: item.span() }
);
226 wasm_import_module
= Some((link_wasm_import_module
, item
.span()));
228 sym
::import_name_type
=> {
229 if import_name_type
.is_some() {
230 sess
.emit_err(errors
::MultipleImportNameType { span: item.span() }
);
233 let Some(link_import_name_type
) = item
.value_str() else {
234 sess
.emit_err(errors
::ImportNameTypeForm { span: item.span() }
);
237 if self.tcx
.sess
.target
.arch
!= "x86" {
238 sess
.emit_err(errors
::ImportNameTypeX86 { span: item.span() }
);
242 let link_import_name_type
= match link_import_name_type
.as_str() {
243 "decorated" => PeImportNameType
::Decorated
,
244 "noprefix" => PeImportNameType
::NoPrefix
,
245 "undecorated" => PeImportNameType
::Undecorated
,
246 import_name_type
=> {
247 sess
.emit_err(errors
::UnknownImportNameType
{
254 if !features
.raw_dylib
{
255 let span
= item
.name_value_literal_span().unwrap();
260 "import name type is unstable",
264 import_name_type
= Some((link_import_name_type
, item
.span()));
267 sess
.emit_err(errors
::UnexpectedLinkArg { span: item.span() }
);
272 // Do this outside the above loop so we don't depend on modifiers coming after kinds
273 let mut verbatim
= None
;
274 if let Some((modifiers
, span
)) = modifiers
{
275 for modifier
in modifiers
.as_str().split('
,'
) {
276 let (modifier
, value
) = match modifier
.strip_prefix(&['
+'
, '
-'
]) {
277 Some(m
) => (m
, modifier
.starts_with('
+'
)),
279 sess
.emit_err(errors
::InvalidLinkModifier { span }
);
284 macro report_unstable_modifier($feature
: ident
) {
285 if !features
.$feature
{
290 &format
!("linking modifier `{modifier}` is unstable"),
295 let assign_modifier
= |dst
: &mut Option
<bool
>| {
297 sess
.emit_err(errors
::MultipleModifiers { span, modifier }
);
302 match (modifier
, &mut kind
) {
303 ("bundle", Some(NativeLibKind
::Static { bundle, .. }
)) => {
304 assign_modifier(bundle
)
307 sess
.emit_err(errors
::BundleNeedsStatic { span }
);
310 ("verbatim", _
) => assign_modifier(&mut verbatim
),
312 ("whole-archive", Some(NativeLibKind
::Static { whole_archive, .. }
)) => {
313 assign_modifier(whole_archive
)
315 ("whole-archive", _
) => {
316 sess
.emit_err(errors
::WholeArchiveNeedsStatic { span }
);
319 ("as-needed", Some(NativeLibKind
::Dylib { as_needed }
))
320 | ("as-needed", Some(NativeLibKind
::Framework { as_needed }
)) => {
321 report_unstable_modifier
!(native_link_modifiers_as_needed
);
322 assign_modifier(as_needed
)
324 ("as-needed", _
) => {
325 sess
.emit_err(errors
::AsNeededCompatibility { span }
);
329 sess
.emit_err(errors
::UnknownLinkModifier { span, modifier }
);
335 if let Some((_
, span
)) = wasm_import_module
{
336 if name
.is_some() || kind
.is_some() || modifiers
.is_some() || cfg
.is_some() {
337 sess
.emit_err(errors
::IncompatibleWasmLink { span }
);
339 } else if name
.is_none() {
340 sess
.emit_err(errors
::LinkRequiresName { span: m.span }
);
343 // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
344 if let Some((_
, span
)) = import_name_type
{
345 if kind
!= Some(NativeLibKind
::RawDylib
) {
346 sess
.emit_err(errors
::ImportNameTypeRaw { span }
);
350 let dll_imports
= match kind
{
351 Some(NativeLibKind
::RawDylib
) => {
352 if let Some((name
, span
)) = name
&& name
.as_str().contains('
\0'
) {
353 sess
.emit_err(errors
::RawDylibNoNul { span }
);
358 self.build_dll_import(
360 import_name_type
.map(|(import_name_type
, _
)| import_name_type
),
367 for child_item
in foreign_mod_items
{
368 if self.tcx
.def_kind(child_item
.id
.owner_id
).has_codegen_attrs()
371 .codegen_fn_attrs(child_item
.id
.owner_id
)
375 let link_ordinal_attr
= self
378 .attrs(child_item
.id
.owner_id
.into())
380 .find(|a
| a
.has_name(sym
::link_ordinal
))
382 sess
.emit_err(errors
::LinkOrdinalRawDylib
{
383 span
: link_ordinal_attr
.span
,
392 let name
= name
.map(|(name
, _
)| name
);
393 let kind
= kind
.unwrap_or(NativeLibKind
::Unspecified
);
394 let filename
= find_bundled_library(name
, verbatim
, kind
, cfg
.is_some(), sess
);
395 self.libs
.push(NativeLib
{
400 foreign_module
: Some(it
.owner_id
.to_def_id()),
401 wasm_import_module
: wasm_import_module
.map(|(name
, _
)| name
),
408 // Process libs passed on the command line
409 fn process_command_line(&mut self) {
410 // First, check for errors
411 let mut renames
= FxHashSet
::default();
412 for lib
in &self.tcx
.sess
.opts
.libs
{
413 if let NativeLibKind
::Framework { .. }
= lib
.kind
&& !self.tcx
.sess
.target
.is_like_osx
{
414 // Cannot check this when parsing options because the target is not yet available.
415 self.tcx
.sess
.emit_err(errors
::LibFrameworkApple
);
417 if let Some(ref new_name
) = lib
.new_name
{
418 let any_duplicate
= self
421 .filter_map(|lib
| lib
.name
.as_ref())
422 .any(|n
| n
.as_str() == lib
.name
);
423 if new_name
.is_empty() {
424 self.tcx
.sess
.emit_err(errors
::EmptyRenamingTarget { lib_name: &lib.name }
);
425 } else if !any_duplicate
{
426 self.tcx
.sess
.emit_err(errors
::RenamingNoLink { lib_name: &lib.name }
);
427 } else if !renames
.insert(&lib
.name
) {
428 self.tcx
.sess
.emit_err(errors
::MultipleRenamings { lib_name: &lib.name }
);
433 // Update kind and, optionally, the name of all native libraries
434 // (there may be more than one) with the specified name. If any
435 // library is mentioned more than once, keep the latest mention
436 // of it, so that any possible dependent libraries appear before
437 // it. (This ensures that the linker is able to see symbols from
438 // all possible dependent libraries before linking in the library
440 for passed_lib
in &self.tcx
.sess
.opts
.libs
{
441 // If we've already added any native libraries with the same
442 // name, they will be pulled out into `existing`, so that we
443 // can move them to the end of the list below.
444 let mut existing
= self
446 .drain_filter(|lib
| {
447 if let Some(lib_name
) = lib
.name
{
448 if lib_name
.as_str() == passed_lib
.name
{
449 // FIXME: This whole logic is questionable, whether modifiers are
450 // involved or not, library reordering and kind overriding without
451 // explicit `:rename` in particular.
452 if lib
.has_modifiers() || passed_lib
.has_modifiers() {
453 match lib
.foreign_module
{
455 self.tcx
.sess
.emit_err(errors
::NoLinkModOverride
{
456 span
: Some(self.tcx
.def_span(def_id
)),
462 .emit_err(errors
::NoLinkModOverride { span: None }
),
465 if passed_lib
.kind
!= NativeLibKind
::Unspecified
{
466 lib
.kind
= passed_lib
.kind
;
468 if let Some(new_name
) = &passed_lib
.new_name
{
469 lib
.name
= Some(Symbol
::intern(new_name
));
471 lib
.verbatim
= passed_lib
.verbatim
;
477 .collect
::<Vec
<_
>>();
478 if existing
.is_empty() {
480 let new_name
: Option
<&str> = passed_lib
.new_name
.as_deref();
481 let name
= Some(Symbol
::intern(new_name
.unwrap_or(&passed_lib
.name
)));
482 let sess
= self.tcx
.sess
;
484 find_bundled_library(name
, passed_lib
.verbatim
, passed_lib
.kind
, false, sess
);
485 self.libs
.push(NativeLib
{
488 kind
: passed_lib
.kind
,
490 foreign_module
: None
,
491 wasm_import_module
: None
,
492 verbatim
: passed_lib
.verbatim
,
493 dll_imports
: Vec
::new(),
496 // Move all existing libraries with the same name to the
497 // end of the command line.
498 self.libs
.append(&mut existing
);
503 fn i686_arg_list_size(&self, item
: &hir
::ForeignItemRef
) -> usize {
504 let argument_types
: &List
<Ty
<'_
>> = self.tcx
.erase_late_bound_regions(
506 .type_of(item
.id
.owner_id
)
510 .map_bound(|slice
| self.tcx
.mk_type_list(slice
)),
518 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }
)
521 // In both stdcall and fastcall, we always round up the argument size to the
522 // nearest multiple of 4 bytes.
523 (layout
.size().bytes_usize() + 3) & !3
531 import_name_type
: Option
<PeImportNameType
>,
532 item
: &hir
::ForeignItemRef
,
534 let calling_convention
= if self.tcx
.sess
.target
.arch
== "x86" {
536 Abi
::C { .. }
| Abi
::Cdecl { .. }
=> DllCallingConvention
::C
,
537 Abi
::Stdcall { .. }
| Abi
::System { .. }
=> {
538 DllCallingConvention
::Stdcall(self.i686_arg_list_size(item
))
540 Abi
::Fastcall { .. }
=> {
541 DllCallingConvention
::Fastcall(self.i686_arg_list_size(item
))
543 Abi
::Vectorcall { .. }
=> {
544 DllCallingConvention
::Vectorcall(self.i686_arg_list_size(item
))
547 self.tcx
.sess
.emit_fatal(errors
::UnsupportedAbiI686 { span: item.span }
);
552 Abi
::C { .. }
| Abi
::Win64 { .. }
| Abi
::System { .. }
=> DllCallingConvention
::C
,
554 self.tcx
.sess
.emit_fatal(errors
::UnsupportedAbi { span: item.span }
);
559 let codegen_fn_attrs
= self.tcx
.codegen_fn_attrs(item
.id
.owner_id
);
560 let import_name_type
= codegen_fn_attrs
562 .map_or(import_name_type
, |ord
| Some(PeImportNameType
::Ordinal(ord
)));
565 name
: codegen_fn_attrs
.link_name
.unwrap_or(item
.ident
.name
),
569 is_fn
: self.tcx
.def_kind(item
.id
.owner_id
).is_fn_like(),