//! Reading and writing of the rustc metadata for rlibs and dylibs use std::fs::File; use std::path::Path; use rustc_codegen_ssa::METADATA_FILENAME; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::rustc_erase_owner; use rustc_data_structures::sync::MetadataRef; use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader}; use rustc_middle::ty::TyCtxt; use rustc_session::config; use rustc_target::spec::Target; use crate::backend::WriteMetadata; pub(crate) struct CraneliftMetadataLoader; fn load_metadata_with( path: &Path, f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, ) -> Result { let file = File::open(path).map_err(|e| format!("{:?}", e))?; let data = unsafe { Mmap::map(file) }.map_err(|e| format!("{:?}", e))?; let metadata = OwningRef::new(data).try_map(f)?; return Ok(rustc_erase_owner!(metadata.map_owner_box())); } impl MetadataLoader for CraneliftMetadataLoader { fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result { load_metadata_with(path, |data| { let archive = object::read::archive::ArchiveFile::parse(&*data) .map_err(|e| format!("{:?}", e))?; for entry_result in archive.members() { let entry = entry_result.map_err(|e| format!("{:?}", e))?; if entry.name() == METADATA_FILENAME.as_bytes() { return Ok(entry.data()); } } Err("couldn't find metadata entry".to_string()) }) } fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { use object::{Object, ObjectSection}; load_metadata_with(path, |data| { let file = object::File::parse(&data).map_err(|e| format!("parse: {:?}", e))?; file.section_by_name(".rustc") .ok_or("no .rustc section")? .data() .map_err(|e| format!("failed to read .rustc section: {:?}", e)) }) } } // Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112 pub(crate) fn write_metadata( tcx: TyCtxt<'_>, product: &mut P, ) -> EncodedMetadata { use snap::write::FrameEncoder; use std::io::Write; #[derive(PartialEq, Eq, PartialOrd, Ord)] enum MetadataKind { None, Uncompressed, Compressed, } let kind = tcx .sess .crate_types() .iter() .map(|ty| match *ty { config::CrateType::Executable | config::CrateType::Staticlib | config::CrateType::Cdylib => MetadataKind::None, config::CrateType::Rlib => MetadataKind::Uncompressed, config::CrateType::Dylib | config::CrateType::ProcMacro => MetadataKind::Compressed, }) .max() .unwrap_or(MetadataKind::None); if kind == MetadataKind::None { return EncodedMetadata::new(); } let metadata = tcx.encode_metadata(); if kind == MetadataKind::Uncompressed { return metadata; } assert!(kind == MetadataKind::Compressed); let mut compressed = tcx.metadata_encoding_version(); FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap(); product.add_rustc_section( rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx), compressed, tcx.sess.target.is_like_osx, ); metadata }