Skip to main content

rustc_metadata/
native_libs.rs

1use rustc_abi::ExternAbi;
2use rustc_attr_ir::{PeImportNameType, find_attr};
3use rustc_attr_parsing::eval_config_entry;
4use rustc_crate_store::{
5    DllCallingConvention, DllImport, DllImportSymbolType, ForeignModule, NativeLib,
6};
7use rustc_data_structures::fx::FxHashSet;
8use rustc_hir::def::DefKind;
9use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
10use rustc_middle::query::LocalCrate;
11use rustc_middle::ty::{self, List, Ty, TyCtxt};
12use rustc_session::Session;
13use rustc_span::def_id::{DefId, LOCAL_CRATE};
14use rustc_span::{Symbol, bug};
15use rustc_structures::NativeLibKind;
16use rustc_target::spec::{Arch, BinaryFormat, CfgAbi};
17
18use crate::diagnostics;
19
20pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
21    let mut collector = Collector { tcx, libs: Vec::new() };
22    if tcx.sess.opts.unstable_opts.link_directives {
23        for module in tcx.foreign_modules(LOCAL_CRATE).values() {
24            collector.process_module(module);
25        }
26    }
27    collector.process_command_line();
28    for lib in &mut collector.libs {
29        // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked,
30        // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations
31        // and enforce pointer authentication constraints.
32        if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest {
33            if let NativeLibKind::Static { .. } = lib.kind {
34                if !tcx.sess.opts.unstable_opts.ui_testing {
35                    let diag = if lib.foreign_module.is_none() {
36                        diagnostics::StaticLinkingNotSupported::UserRequested {
37                            lib_name: lib.name,
38                            target: tcx.sess.target.llvm_target.as_ref(),
39                        }
40                    } else {
41                        diagnostics::StaticLinkingNotSupported::FromDependency {
42                            lib_name: lib.name,
43                            target: tcx.sess.target.llvm_target.as_ref(),
44                        }
45                    };
46                    tcx.dcx().emit_warn(diag);
47                }
48
49                lib.kind = NativeLibKind::Dylib { as_needed: None };
50            }
51        }
52    }
53    collector.libs
54}
55
56pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
57    match lib.cfg {
58        Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(),
59        None => true,
60    }
61}
62
63struct Collector<'tcx> {
64    tcx: TyCtxt<'tcx>,
65    libs: Vec<NativeLib>,
66}
67
68impl<'tcx> Collector<'tcx> {
69    fn process_module(&mut self, module: &ForeignModule) {
70        let ForeignModule { def_id, abi, ref foreign_items } = *module;
71        let def_id = def_id.expect_local();
72
73        let sess = self.tcx.sess;
74
75        if #[allow(non_exhaustive_omitted_patterns)] match abi {
    ExternAbi::Rust => true,
    _ => false,
}matches!(abi, ExternAbi::Rust) {
76            return;
77        }
78
79        for attr in {
    {
        'done:
            {
            for i in ::rustc_attr_ir::HasAttrs::get_attrs(def_id, &self.tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(Link(links, _)) => {
                        break 'done Some(links);
                    }
                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(self.tcx, def_id, Link(links, _) => links).into_flat_iter() {
80            let dll_imports = match attr.kind {
81                NativeLibKind::RawDylib { .. } => foreign_items
82                    .iter()
83                    .filter_map(|&child_item| {
84                        self.build_dll_import(
85                            abi,
86                            attr.import_name_type.map(|(import_name_type, _)| import_name_type),
87                            child_item,
88                        )
89                    })
90                    .collect(),
91                _ => {
92                    for &child_item in foreign_items {
93                        if let Some(span) =
94                            {
    {
        'done:
            {
            for i in
                ::rustc_attr_ir::HasAttrs::get_attrs(child_item, &self.tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(LinkOrdinal { span, .. })
                        => {
                        break 'done Some(*span);
                    }
                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(self.tcx, child_item, LinkOrdinal {span, ..} => *span)
95                        {
96                            sess.dcx().emit_err(diagnostics::LinkOrdinalRawDylib { span });
97                        }
98                    }
99
100                    Vec::new()
101                }
102            };
103
104            self.libs.push(NativeLib {
105                name: attr.name,
106                kind: attr.kind,
107                cfg: attr.cfg.clone(),
108                foreign_module: Some(def_id.to_def_id()),
109                verbatim: attr.verbatim,
110                dll_imports,
111            });
112        }
113    }
114
115    // Process libs passed on the command line
116    fn process_command_line(&mut self) {
117        // First, check for errors
118        let mut renames = FxHashSet::default();
119        for lib in &self.tcx.sess.opts.libs {
120            if let NativeLibKind::Framework { .. } = lib.kind
121                && !self.tcx.sess.target.is_like_darwin
122            {
123                // Cannot check this when parsing options because the target is not yet available.
124                self.tcx.dcx().emit_err(diagnostics::LibFrameworkApple);
125            }
126            if let Some(ref new_name) = lib.new_name {
127                let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
128                if new_name.is_empty() {
129                    self.tcx
130                        .dcx()
131                        .emit_err(diagnostics::EmptyRenamingTarget { lib_name: &lib.name });
132                } else if !any_duplicate {
133                    self.tcx.dcx().emit_err(diagnostics::RenamingNoLink { lib_name: &lib.name });
134                } else if !renames.insert(&lib.name) {
135                    self.tcx.dcx().emit_err(diagnostics::MultipleRenamings { lib_name: &lib.name });
136                }
137            }
138        }
139
140        // Update kind and, optionally, the name of all native libraries
141        // (there may be more than one) with the specified name. If any
142        // library is mentioned more than once, keep the latest mention
143        // of it, so that any possible dependent libraries appear before
144        // it. (This ensures that the linker is able to see symbols from
145        // all possible dependent libraries before linking in the library
146        // in question.)
147        for passed_lib in &self.tcx.sess.opts.libs {
148            // If we've already added any native libraries with the same
149            // name, they will be pulled out into `existing`, so that we
150            // can move them to the end of the list below.
151            let mut existing = self
152                .libs
153                .extract_if(.., |lib| {
154                    if lib.name.as_str() == passed_lib.name {
155                        // FIXME: This whole logic is questionable, whether modifiers are
156                        // involved or not, library reordering and kind overriding without
157                        // explicit `:rename` in particular.
158                        if lib.has_modifiers() || passed_lib.has_modifiers() {
159                            match lib.foreign_module {
160                                Some(def_id) => {
161                                    self.tcx.dcx().emit_err(diagnostics::NoLinkModOverride {
162                                        span: Some(self.tcx.def_span(def_id)),
163                                    })
164                                }
165                                None => self
166                                    .tcx
167                                    .dcx()
168                                    .emit_err(diagnostics::NoLinkModOverride { span: None }),
169                            };
170                        }
171                        if passed_lib.kind != NativeLibKind::Unspecified {
172                            lib.kind = passed_lib.kind;
173                        }
174                        if let Some(new_name) = &passed_lib.new_name {
175                            lib.name = Symbol::intern(new_name);
176                        }
177                        lib.verbatim = passed_lib.verbatim;
178                        return true;
179                    }
180                    false
181                })
182                .collect::<Vec<_>>();
183            if existing.is_empty() {
184                // Add if not found
185                let new_name: Option<&str> = passed_lib.new_name.as_deref();
186                let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
187                self.libs.push(NativeLib {
188                    name,
189                    kind: passed_lib.kind,
190                    cfg: None,
191                    foreign_module: None,
192                    verbatim: passed_lib.verbatim,
193                    dll_imports: Vec::new(),
194                });
195            } else {
196                // Move all existing libraries with the same name to the
197                // end of the command line.
198                self.libs.append(&mut existing);
199            }
200        }
201    }
202
203    fn i686_arg_list_size(&self, item: DefId) -> usize {
204        let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased(
205            self.tcx
206                .type_of(item)
207                .instantiate_identity()
208                .skip_norm_wip()
209                .fn_sig(self.tcx)
210                .inputs()
211                .map_bound(|slice| self.tcx.mk_type_list(slice)),
212        );
213
214        argument_types
215            .iter()
216            .map(|ty| {
217                let layout = self
218                    .tcx
219                    .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
220                    .expect("layout")
221                    .layout;
222                // In both stdcall and fastcall, we always round up the argument size to the
223                // nearest multiple of 4 bytes.
224                (layout.size().bytes_usize() + 3) & !3
225            })
226            .sum()
227    }
228
229    fn build_dll_import(
230        &self,
231        abi: ExternAbi,
232        import_name_type: Option<PeImportNameType>,
233        item: DefId,
234    ) -> Option<DllImport> {
235        let span = self.tcx.def_span(item);
236
237        // This `extern` block should have been checked for general ABI support before, but let's
238        // double-check that.
239        if !self.tcx.sess.target.is_abi_supported(abi) {
    ::core::panicking::panic("assertion failed: self.tcx.sess.target.is_abi_supported(abi)")
};assert!(self.tcx.sess.target.is_abi_supported(abi));
240
241        // This logic is similar to `AbiMap::canonize_abi` (in rustc_target/src/spec/abi_map.rs) but
242        // we need more detail than those adjustments, and we can't support all ABIs that are
243        // generally supported.
244        let calling_convention = if self.tcx.sess.target.arch == Arch::X86 {
245            match abi {
246                ExternAbi::C { .. } | ExternAbi::Cdecl { .. } => DllCallingConvention::C,
247                ExternAbi::Stdcall { .. } => {
248                    DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
249                }
250                // On Windows, `extern "system"` behaves like msvc's `__stdcall`.
251                // `__stdcall` only applies on x86 and on non-variadic functions:
252                // https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170
253                ExternAbi::System { .. } => {
254                    let c_variadic = self
255                        .tcx
256                        .type_of(item)
257                        .instantiate_identity()
258                        .skip_norm_wip()
259                        .fn_sig(self.tcx)
260                        .c_variadic();
261
262                    if c_variadic {
263                        DllCallingConvention::C
264                    } else {
265                        DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
266                    }
267                }
268                ExternAbi::Fastcall { .. } => {
269                    DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
270                }
271                ExternAbi::Vectorcall { .. } => {
272                    DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
273                }
274                _ => {
275                    self.tcx.dcx().emit_err(diagnostics::RawDylibUnsupportedAbi { span });
276                    return None;
277                }
278            }
279        } else {
280            match abi {
281                ExternAbi::C { .. } | ExternAbi::Win64 { .. } | ExternAbi::System { .. } => {
282                    DllCallingConvention::C
283                }
284                _ => {
285                    self.tcx.dcx().emit_err(diagnostics::RawDylibUnsupportedAbi { span });
286                    return None;
287                }
288            }
289        };
290
291        let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
292        let import_name_type = codegen_fn_attrs
293            .link_ordinal
294            .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
295
296        let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
297
298        if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
299            let name = name.as_str();
300            if name.contains('\0') {
301                self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
302            } else if let Some((left, right)) = name.split_once('@')
303                && (left.is_empty() || right.is_empty() || right.contains('@'))
304            {
305                self.tcx.dcx().emit_err(diagnostics::RawDylibMalformed { span });
306            }
307        }
308
309        let def_kind = self.tcx.def_kind(item);
310        let symbol_type = if def_kind.is_fn_like() {
311            DllImportSymbolType::Function
312        } else if #[allow(non_exhaustive_omitted_patterns)] match def_kind {
    DefKind::Static { .. } => true,
    _ => false,
}matches!(def_kind, DefKind::Static { .. }) {
313            if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
314                DllImportSymbolType::ThreadLocal
315            } else {
316                DllImportSymbolType::Static
317            }
318        } else if def_kind == DefKind::ForeignTy {
319            return None;
320        } else {
321            ::rustc_span::macros::bug_impl(None,
    format_args!("Unexpected type for raw-dylib: {0}", def_kind.descr(item)),
    Location::caller());bug!("Unexpected type for raw-dylib: {}", def_kind.descr(item));
322        };
323
324        let size = match symbol_type {
325            // We cannot determine the size of a function at compile time, but it shouldn't matter anyway.
326            DllImportSymbolType::Function => rustc_abi::Size::ZERO,
327            DllImportSymbolType::Static | DllImportSymbolType::ThreadLocal => {
328                let ty = self.tcx.type_of(item).instantiate_identity().skip_norm_wip();
329                self.tcx
330                    .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
331                    .ok()
332                    .map(|layout| layout.size)
333                    .unwrap_or_else(|| ::rustc_span::macros::bug_impl(None,
    format_args!("Non-function symbols must have a size"), Location::caller())bug!("Non-function symbols must have a size"))
334            }
335        };
336
337        Some(DllImport { name, import_name_type, calling_convention, span, symbol_type, size })
338    }
339}