Skip to main content

rustc_codegen_ssa/back/
rmeta_link.rs

1//! Late-metadata archive member that lists which rlib entries are Rust object files,
2//! and potentially other data collected and used when building or linking a rlib.
3//! See <https://github.com/rust-lang/rust/issues/138243>.
4
5use std::fs::File;
6use std::path::{Path, PathBuf};
7
8use object::read::archive::ArchiveFile;
9use rustc_data_structures::fx::FxHashMap;
10use rustc_data_structures::memmap::Mmap;
11use rustc_serialize::opaque::mem_encoder::MemEncoder;
12use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder};
13use rustc_serialize::{Decodable, Encodable};
14use rustc_span::Symbol;
15use rustc_structures::NativeLibKind;
16use rustc_target::spec::Target;
17use tracing::debug;
18
19use super::metadata::{get_metadata_xcoff, search_for_section};
20use crate::NativeLib;
21
22pub(crate) const FILENAME: &str = "lib.rmeta-link";
23pub(crate) const SECTION: &str = ".rmeta-link";
24
25pub struct RmetaLink {
26    pub rust_object_files: Vec<String>,
27    /// Positionally aligned with `native_libraries` in regular metadata: index `i` is the
28    /// bundled filename for native library `i`, or `None` if that library needs no bundling.
29    pub native_lib_filenames: Vec<Option<String>>,
30}
31
32impl RmetaLink {
33    pub(crate) fn encode(&self) -> Vec<u8> {
34        let mut encoder = MemEncoder::new();
35        self.rust_object_files.encode(&mut encoder);
36        self.native_lib_filenames.encode(&mut encoder);
37        let mut data = encoder.finish();
38        data.extend_from_slice(MAGIC_END_BYTES);
39        data
40    }
41
42    pub(crate) fn decode(data: &[u8]) -> Option<RmetaLink> {
43        let mut decoder = MemDecoder::new(data, 0).ok()?;
44        let rust_object_files = Vec::<String>::decode(&mut decoder);
45        let native_lib_filenames = Vec::<Option<String>>::decode(&mut decoder);
46        Some(RmetaLink { rust_object_files, native_lib_filenames })
47    }
48}
49
50/// Reads the link-time metadata from an already-parsed archive.
51pub fn read(
52    archive: &ArchiveFile<'_>,
53    archive_data: &[u8],
54    rlib_path: &Path,
55    is_like_aix: bool,
56) -> Option<RmetaLink> {
57    for entry in archive.members() {
58        let entry = entry.ok()?;
59        if entry.name() == FILENAME.as_bytes() {
60            let data = entry.data(archive_data).ok()?;
61            let section_data = if is_like_aix {
62                get_metadata_xcoff(rlib_path, data).ok()?
63            } else {
64                search_for_section(rlib_path, data, SECTION).ok()?
65            };
66            return RmetaLink::decode(section_data);
67        }
68    }
69    None
70}
71
72/// Like [`read`], but parses the archive from raw bytes.
73///
74/// Use this when the caller's `ArchiveFile` comes from a different version of the `object` crate.
75pub fn read_from_data(
76    archive_data: &[u8],
77    rlib_path: &Path,
78    is_like_aix: bool,
79) -> Option<RmetaLink> {
80    let archive = ArchiveFile::parse(archive_data).ok()?;
81    read(&archive, archive_data, rlib_path, is_like_aix)
82}
83
84#[derive(#[automatically_derived]impl ::core::default::Default for RmetaLinkCache {
    #[inline]
    fn default() -> Self {
        RmetaLinkCache { cache: ::core::default::Default::default() }
    }
}Default)]
85pub struct RmetaLinkCache {
86    cache: FxHashMap<PathBuf, Option<RmetaLink>>,
87}
88
89impl RmetaLinkCache {
90    pub fn get_or_insert_with(
91        &mut self,
92        rlib_path: &Path,
93        load: impl FnOnce() -> Option<RmetaLink>,
94    ) -> Option<&RmetaLink> {
95        self.cache.entry(rlib_path.to_path_buf()).or_insert_with(load).as_ref()
96    }
97
98    pub fn native_lib_filenames(
99        &mut self,
100        target: &Target,
101        rlib_path: &Path,
102        native_libs: &[NativeLib],
103    ) -> Vec<Option<Symbol>> {
104        if !crate_may_have_bundled_libs(native_libs) {
105            return Vec::new();
106        }
107        self.get_or_insert_with(rlib_path, || read_from_path(rlib_path, target.is_like_aix))
108            .map(|rl| {
109                rl.native_lib_filenames.iter().map(|f| f.as_deref().map(Symbol::intern)).collect()
110            })
111            .unwrap_or_default()
112    }
113}
114
115fn crate_may_have_bundled_libs(libs: &[NativeLib]) -> bool {
116    libs.iter()
117        .any(|lib| #[allow(non_exhaustive_omitted_patterns)] match lib.kind {
    NativeLibKind::Static { bundle: Some(true) | None, .. } => true,
    _ => false,
}matches!(lib.kind, NativeLibKind::Static { bundle: Some(true) | None, .. }))
118}
119
120// FIXME: this is mostly a copy-paste of `DefaultMetadataLoader::get_rlib_metadata`.
121fn read_from_path(path: &Path, is_like_aix: bool) -> Option<RmetaLink> {
122    let Ok(file) = File::open(path) else {
123        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/76456a688bc0ed897fa23a677d4ddb5b3fdb575a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs:123",
                        "rustc_codegen_ssa::back::rmeta_link",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/76456a688bc0ed897fa23a677d4ddb5b3fdb575a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs"),
                        ::tracing_core::__macro_support::Option::Some(123u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rmeta_link"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("failed to open rlib for rmeta-link: {0}",
                                                    path.display()) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("failed to open rlib for rmeta-link: {}", path.display());
124        return None;
125    };
126    let Ok(mmap) = (unsafe { Mmap::map(file) }) else {
127        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/76456a688bc0ed897fa23a677d4ddb5b3fdb575a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs:127",
                        "rustc_codegen_ssa::back::rmeta_link",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/76456a688bc0ed897fa23a677d4ddb5b3fdb575a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs"),
                        ::tracing_core::__macro_support::Option::Some(127u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rmeta_link"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("failed to mmap rlib for rmeta-link: {0}",
                                                    path.display()) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("failed to mmap rlib for rmeta-link: {}", path.display());
128        return None;
129    };
130
131    read_from_data(&mmap, path, is_like_aix)
132}