Skip to main content

rustc_metadata/
native_libs.rs

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