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
;
17 AsNeededCompatibility
, BundleNeedsStatic
, EmptyLinkName
, EmptyRenamingTarget
,
18 FrameworkOnlyWindows
, ImportNameTypeForm
, ImportNameTypeRaw
, ImportNameTypeX86
,
19 IncompatibleWasmLink
, InvalidLinkModifier
, LibFrameworkApple
, LinkCfgForm
,
20 LinkCfgSinglePredicate
, LinkFrameworkApple
, LinkKindForm
, LinkModifiersForm
, LinkNameForm
,
21 LinkOrdinalRawDylib
, LinkRequiresName
, MissingNativeLibrary
, MultipleCfgs
,
22 MultipleImportNameType
, MultipleKindsInLink
, MultipleLinkModifiers
, MultipleModifiers
,
23 MultipleNamesInLink
, MultipleRenamings
, MultipleWasmImport
, NoLinkModOverride
, RawDylibNoNul
,
24 RenamingNoLink
, UnexpectedLinkArg
, UnknownImportNameType
, UnknownLinkKind
, UnknownLinkModifier
,
25 UnsupportedAbi
, UnsupportedAbiI686
, WasmImportForm
, WholeArchiveNeedsStatic
,
28 use std
::path
::PathBuf
;
30 pub fn find_native_static_library(
33 search_paths
: &[PathBuf
],
36 let formats
= if verbatim
{
37 vec
![("".into(), "".into())]
39 let os
= (sess
.target
.staticlib_prefix
.clone(), sess
.target
.staticlib_suffix
.clone());
40 // On Windows, static libraries sometimes show up as libfoo.a and other
41 // times show up as foo.lib
42 let unix
= ("lib".into(), ".a".into());
43 if os
== unix { vec![os] }
else { vec![os, unix] }
46 for path
in search_paths
{
47 for (prefix
, suffix
) in &formats
{
48 let test
= path
.join(format
!("{}{}{}", prefix
, name
, suffix
));
55 sess
.emit_fatal(MissingNativeLibrary
::new(name
, verbatim
));
58 fn find_bundled_library(
60 verbatim
: Option
<bool
>,
64 if sess
.opts
.unstable_opts
.packed_bundled_libs
&&
65 sess
.crate_types().iter().any(|ct
| ct
== &CrateType
::Rlib
|| ct
== &CrateType
::Staticlib
) &&
66 let NativeLibKind
::Static { bundle: Some(true) | None, .. }
= kind
{
67 find_native_static_library(
68 name
.unwrap().as_str(),
69 verbatim
.unwrap_or(false),
70 &sess
.target_filesearch(PathKind
::Native
).search_path_dirs(),
72 ).file_name().and_then(|s
| s
.to_str()).map(Symbol
::intern
)
78 pub(crate) fn collect(tcx
: TyCtxt
<'_
>) -> Vec
<NativeLib
> {
79 let mut collector
= Collector { tcx, libs: Vec::new() }
;
80 for id
in tcx
.hir().items() {
81 collector
.process_item(id
);
83 collector
.process_command_line();
87 pub(crate) fn relevant_lib(sess
: &Session
, lib
: &NativeLib
) -> bool
{
89 Some(ref cfg
) => attr
::cfg_matches(cfg
, &sess
.parse_sess
, CRATE_NODE_ID
, None
),
94 struct Collector
<'tcx
> {
99 impl<'tcx
> Collector
<'tcx
> {
100 fn process_item(&mut self, id
: rustc_hir
::ItemId
) {
101 if !matches
!(self.tcx
.def_kind(id
.owner_id
), DefKind
::ForeignMod
) {
105 let it
= self.tcx
.hir().item(id
);
106 let hir
::ItemKind
::ForeignMod { abi, items: foreign_mod_items }
= it
.kind
else {
110 if abi
== Abi
::Rust
|| abi
== Abi
::RustIntrinsic
|| abi
== Abi
::PlatformIntrinsic
{
114 // Process all of the #[link(..)]-style arguments
115 let sess
= &self.tcx
.sess
;
116 let features
= self.tcx
.features();
117 for m
in self.tcx
.hir().attrs(it
.hir_id()).iter().filter(|a
| a
.has_name(sym
::link
)) {
118 let Some(items
) = m
.meta_item_list() else {
124 let mut modifiers
= None
;
126 let mut wasm_import_module
= None
;
127 let mut import_name_type
= None
;
128 for item
in items
.iter() {
129 match item
.name_or_empty() {
132 sess
.emit_err(MultipleNamesInLink { span: item.span() }
);
135 let Some(link_name
) = item
.value_str() else {
136 sess
.emit_err(LinkNameForm { span: item.span() }
);
139 let span
= item
.name_value_literal_span().unwrap();
140 if link_name
.is_empty() {
141 sess
.emit_err(EmptyLinkName { span }
);
143 name
= Some((link_name
, span
));
147 sess
.emit_err(MultipleKindsInLink { span: item.span() }
);
150 let Some(link_kind
) = item
.value_str() else {
151 sess
.emit_err(LinkKindForm { span: item.span() }
);
155 let span
= item
.name_value_literal_span().unwrap();
156 let link_kind
= match link_kind
.as_str() {
157 "static" => NativeLibKind
::Static { bundle: None, whole_archive: None }
,
158 "dylib" => NativeLibKind
::Dylib { as_needed: None }
,
160 if !sess
.target
.is_like_osx
{
161 sess
.emit_err(LinkFrameworkApple { span }
);
163 NativeLibKind
::Framework { as_needed: None }
166 if !sess
.target
.is_like_windows
{
167 sess
.emit_err(FrameworkOnlyWindows { span }
);
168 } else if !features
.raw_dylib
&& sess
.target
.arch
== "x86" {
173 "link kind `raw-dylib` is unstable on x86",
177 NativeLibKind
::RawDylib
180 sess
.emit_err(UnknownLinkKind { span, kind }
);
184 kind
= Some(link_kind
);
187 if modifiers
.is_some() {
188 sess
.emit_err(MultipleLinkModifiers { span: item.span() }
);
191 let Some(link_modifiers
) = item
.value_str() else {
192 sess
.emit_err(LinkModifiersForm { span: item.span() }
);
195 modifiers
= Some((link_modifiers
, item
.name_value_literal_span().unwrap()));
199 sess
.emit_err(MultipleCfgs { span: item.span() }
);
202 let Some(link_cfg
) = item
.meta_item_list() else {
203 sess
.emit_err(LinkCfgForm { span: item.span() }
);
206 let [NestedMetaItem
::MetaItem(link_cfg
)] = link_cfg
else {
207 sess
.emit_err(LinkCfgSinglePredicate { span: item.span() }
);
210 if !features
.link_cfg
{
215 "link cfg is unstable",
219 cfg
= Some(link_cfg
.clone());
221 sym
::wasm_import_module
=> {
222 if wasm_import_module
.is_some() {
223 sess
.emit_err(MultipleWasmImport { span: item.span() }
);
226 let Some(link_wasm_import_module
) = item
.value_str() else {
227 sess
.emit_err(WasmImportForm { span: item.span() }
);
230 wasm_import_module
= Some((link_wasm_import_module
, item
.span()));
232 sym
::import_name_type
=> {
233 if import_name_type
.is_some() {
234 sess
.emit_err(MultipleImportNameType { span: item.span() }
);
237 let Some(link_import_name_type
) = item
.value_str() else {
238 sess
.emit_err(ImportNameTypeForm { span: item.span() }
);
241 if self.tcx
.sess
.target
.arch
!= "x86" {
242 sess
.emit_err(ImportNameTypeX86 { span: item.span() }
);
246 let link_import_name_type
= match link_import_name_type
.as_str() {
247 "decorated" => PeImportNameType
::Decorated
,
248 "noprefix" => PeImportNameType
::NoPrefix
,
249 "undecorated" => PeImportNameType
::Undecorated
,
250 import_name_type
=> {
251 sess
.emit_err(UnknownImportNameType
{
258 if !features
.raw_dylib
{
259 let span
= item
.name_value_literal_span().unwrap();
264 "import name type is unstable",
268 import_name_type
= Some((link_import_name_type
, item
.span()));
271 sess
.emit_err(UnexpectedLinkArg { span: item.span() }
);
276 // Do this outside the above loop so we don't depend on modifiers coming after kinds
277 let mut verbatim
= None
;
278 if let Some((modifiers
, span
)) = modifiers
{
279 for modifier
in modifiers
.as_str().split('
,'
) {
280 let (modifier
, value
) = match modifier
.strip_prefix(&['
+'
, '
-'
]) {
281 Some(m
) => (m
, modifier
.starts_with('
+'
)),
283 sess
.emit_err(InvalidLinkModifier { span }
);
288 macro report_unstable_modifier($feature
: ident
) {
289 if !features
.$feature
{
294 &format
!("linking modifier `{modifier}` is unstable"),
299 let assign_modifier
= |dst
: &mut Option
<bool
>| {
301 sess
.emit_err(MultipleModifiers { span, modifier }
);
306 match (modifier
, &mut kind
) {
307 ("bundle", Some(NativeLibKind
::Static { bundle, .. }
)) => {
308 assign_modifier(bundle
)
311 sess
.emit_err(BundleNeedsStatic { span }
);
314 ("verbatim", _
) => assign_modifier(&mut verbatim
),
316 ("whole-archive", Some(NativeLibKind
::Static { whole_archive, .. }
)) => {
317 assign_modifier(whole_archive
)
319 ("whole-archive", _
) => {
320 sess
.emit_err(WholeArchiveNeedsStatic { span }
);
323 ("as-needed", Some(NativeLibKind
::Dylib { as_needed }
))
324 | ("as-needed", Some(NativeLibKind
::Framework { as_needed }
)) => {
325 report_unstable_modifier
!(native_link_modifiers_as_needed
);
326 assign_modifier(as_needed
)
328 ("as-needed", _
) => {
329 sess
.emit_err(AsNeededCompatibility { span }
);
333 sess
.emit_err(UnknownLinkModifier { span, modifier }
);
339 if let Some((_
, span
)) = wasm_import_module
{
340 if name
.is_some() || kind
.is_some() || modifiers
.is_some() || cfg
.is_some() {
341 sess
.emit_err(IncompatibleWasmLink { span }
);
343 } else if name
.is_none() {
344 sess
.emit_err(LinkRequiresName { span: m.span }
);
347 // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
348 if let Some((_
, span
)) = import_name_type
{
349 if kind
!= Some(NativeLibKind
::RawDylib
) {
350 sess
.emit_err(ImportNameTypeRaw { span }
);
354 let dll_imports
= match kind
{
355 Some(NativeLibKind
::RawDylib
) => {
356 if let Some((name
, span
)) = name
&& name
.as_str().contains('
\0'
) {
357 sess
.emit_err(RawDylibNoNul { span }
);
362 self.build_dll_import(
364 import_name_type
.map(|(import_name_type
, _
)| import_name_type
),
371 for child_item
in foreign_mod_items
{
372 if self.tcx
.def_kind(child_item
.id
.owner_id
).has_codegen_attrs()
375 .codegen_fn_attrs(child_item
.id
.owner_id
)
379 let link_ordinal_attr
= self
382 .attrs(child_item
.id
.owner_id
.into())
384 .find(|a
| a
.has_name(sym
::link_ordinal
))
386 sess
.emit_err(LinkOrdinalRawDylib { span: link_ordinal_attr.span }
);
394 let name
= name
.map(|(name
, _
)| name
);
395 let kind
= kind
.unwrap_or(NativeLibKind
::Unspecified
);
396 let filename
= find_bundled_library(name
, verbatim
, kind
, sess
);
397 self.libs
.push(NativeLib
{
402 foreign_module
: Some(it
.owner_id
.to_def_id()),
403 wasm_import_module
: wasm_import_module
.map(|(name
, _
)| name
),
410 // Process libs passed on the command line
411 fn process_command_line(&mut self) {
412 // First, check for errors
413 let mut renames
= FxHashSet
::default();
414 for lib
in &self.tcx
.sess
.opts
.libs
{
415 if let NativeLibKind
::Framework { .. }
= lib
.kind
&& !self.tcx
.sess
.target
.is_like_osx
{
416 // Cannot check this when parsing options because the target is not yet available.
417 self.tcx
.sess
.emit_err(LibFrameworkApple
);
419 if let Some(ref new_name
) = lib
.new_name
{
420 let any_duplicate
= self
423 .filter_map(|lib
| lib
.name
.as_ref())
424 .any(|n
| n
.as_str() == lib
.name
);
425 if new_name
.is_empty() {
426 self.tcx
.sess
.emit_err(EmptyRenamingTarget { lib_name: &lib.name }
);
427 } else if !any_duplicate
{
428 self.tcx
.sess
.emit_err(RenamingNoLink { lib_name: &lib.name }
);
429 } else if !renames
.insert(&lib
.name
) {
430 self.tcx
.sess
.emit_err(MultipleRenamings { lib_name: &lib.name }
);
435 // Update kind and, optionally, the name of all native libraries
436 // (there may be more than one) with the specified name. If any
437 // library is mentioned more than once, keep the latest mention
438 // of it, so that any possible dependent libraries appear before
439 // it. (This ensures that the linker is able to see symbols from
440 // all possible dependent libraries before linking in the library
442 for passed_lib
in &self.tcx
.sess
.opts
.libs
{
443 // If we've already added any native libraries with the same
444 // name, they will be pulled out into `existing`, so that we
445 // can move them to the end of the list below.
446 let mut existing
= self
448 .drain_filter(|lib
| {
449 if let Some(lib_name
) = lib
.name
{
450 if lib_name
.as_str() == passed_lib
.name
{
451 // FIXME: This whole logic is questionable, whether modifiers are
452 // involved or not, library reordering and kind overriding without
453 // explicit `:rename` in particular.
454 if lib
.has_modifiers() || passed_lib
.has_modifiers() {
455 match lib
.foreign_module
{
456 Some(def_id
) => self.tcx
.sess
.emit_err(NoLinkModOverride
{
457 span
: Some(self.tcx
.def_span(def_id
)),
460 self.tcx
.sess
.emit_err(NoLinkModOverride { span: None }
)
464 if passed_lib
.kind
!= NativeLibKind
::Unspecified
{
465 lib
.kind
= passed_lib
.kind
;
467 if let Some(new_name
) = &passed_lib
.new_name
{
468 lib
.name
= Some(Symbol
::intern(new_name
));
470 lib
.verbatim
= passed_lib
.verbatim
;
476 .collect
::<Vec
<_
>>();
477 if existing
.is_empty() {
479 let new_name
: Option
<&str> = passed_lib
.new_name
.as_deref();
480 let name
= Some(Symbol
::intern(new_name
.unwrap_or(&passed_lib
.name
)));
481 let sess
= self.tcx
.sess
;
483 find_bundled_library(name
, passed_lib
.verbatim
, passed_lib
.kind
, sess
);
484 self.libs
.push(NativeLib
{
487 kind
: passed_lib
.kind
,
489 foreign_module
: None
,
490 wasm_import_module
: None
,
491 verbatim
: passed_lib
.verbatim
,
492 dll_imports
: Vec
::new(),
495 // Move all existing libraries with the same name to the
496 // end of the command line.
497 self.libs
.append(&mut existing
);
502 fn i686_arg_list_size(&self, item
: &hir
::ForeignItemRef
) -> usize {
503 let argument_types
: &List
<Ty
<'_
>> = self.tcx
.erase_late_bound_regions(
505 .type_of(item
.id
.owner_id
)
508 .map_bound(|slice
| self.tcx
.mk_type_list(slice
.iter())),
516 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty }
)
519 // In both stdcall and fastcall, we always round up the argument size to the
520 // nearest multiple of 4 bytes.
521 (layout
.size().bytes_usize() + 3) & !3
529 import_name_type
: Option
<PeImportNameType
>,
530 item
: &hir
::ForeignItemRef
,
532 let calling_convention
= if self.tcx
.sess
.target
.arch
== "x86" {
534 Abi
::C { .. }
| Abi
::Cdecl { .. }
=> DllCallingConvention
::C
,
535 Abi
::Stdcall { .. }
| Abi
::System { .. }
=> {
536 DllCallingConvention
::Stdcall(self.i686_arg_list_size(item
))
538 Abi
::Fastcall { .. }
=> {
539 DllCallingConvention
::Fastcall(self.i686_arg_list_size(item
))
541 Abi
::Vectorcall { .. }
=> {
542 DllCallingConvention
::Vectorcall(self.i686_arg_list_size(item
))
545 self.tcx
.sess
.emit_fatal(UnsupportedAbiI686 { span: item.span }
);
550 Abi
::C { .. }
| Abi
::Win64 { .. }
| Abi
::System { .. }
=> DllCallingConvention
::C
,
552 self.tcx
.sess
.emit_fatal(UnsupportedAbi { span: item.span }
);
557 let codegen_fn_attrs
= self.tcx
.codegen_fn_attrs(item
.id
.owner_id
);
558 let import_name_type
= codegen_fn_attrs
560 .map_or(import_name_type
, |ord
| Some(PeImportNameType
::Ordinal(ord
)));
563 name
: codegen_fn_attrs
.link_name
.unwrap_or(item
.ident
.name
),
567 is_fn
: self.tcx
.def_kind(item
.id
.owner_id
).is_fn_like(),