// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use rustc::util::common; use rustc::middle::cstore::MetadataLoader; use rustc_back::target::Target; use llvm; use llvm::{False, ObjectFile, mk_section_iter}; use llvm::archive_ro::ArchiveRO; use rustc_data_structures::owning_ref::OwningRef; use std::path::Path; use std::ptr; use std::slice; pub use rustc_data_structures::sync::MetadataRef; pub const METADATA_FILENAME: &str = "rust.metadata.bin"; pub struct LlvmMetadataLoader; impl MetadataLoader for LlvmMetadataLoader { fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap // internally to read the file. We also avoid even using a memcpy by // just keeping the archive along while the metadata is in use. let archive = ArchiveRO::open(filename) .map(|ar| OwningRef::new(box ar)) .map_err(|e| { debug!("llvm didn't like `{}`: {}", filename.display(), e); format!("failed to read rlib metadata in '{}': {}", filename.display(), e) })?; let buf: OwningRef<_, [u8]> = archive .try_map(|ar| { ar.iter() .filter_map(|s| s.ok()) .find(|sect| sect.name() == Some(METADATA_FILENAME)) .map(|s| s.data()) .ok_or_else(|| { debug!("didn't find '{}' in the archive", METADATA_FILENAME); format!("failed to read rlib metadata: '{}'", filename.display()) }) })?; Ok(rustc_erase_owner!(buf)) } fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result { unsafe { let buf = common::path2cstr(filename); let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); if mb as isize == 0 { return Err(format!("error reading library: '{}'", filename.display())); } let of = ObjectFile::new(mb) .map(|of| OwningRef::new(box of)) .ok_or_else(|| format!("provided path not an object file: '{}'", filename.display()))?; let buf = of.try_map(|of| search_meta_section(of, target, filename))?; Ok(rustc_erase_owner!(buf)) } } } fn search_meta_section<'a>(of: &'a ObjectFile, target: &Target, filename: &Path) -> Result<&'a [u8], String> { unsafe { let si = mk_section_iter(of.llof); while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { let mut name_buf = ptr::null(); let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec(); let name = String::from_utf8(name).unwrap(); debug!("get_metadata_section: name {}", name); if read_metadata_section_name(target) == name { let cbuf = llvm::LLVMGetSectionContents(si.llsi); let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; // The buffer is valid while the object file is around let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz); return Ok(buf); } llvm::LLVMMoveToNextSection(si.llsi); } } Err(format!("metadata not found: '{}'", filename.display())) } pub fn metadata_section_name(target: &Target) -> &'static str { // Historical note: // // When using link.exe it was seen that the section name `.note.rustc` // was getting shortened to `.note.ru`, and according to the PE and COFF // specification: // // > Executable images do not use a string table and do not support // > section names longer than 8 characters // // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx // // As a result, we choose a slightly shorter name! As to why // `.note.rustc` works on MinGW, that's another good question... if target.options.is_like_osx { "__DATA,.rustc" } else { ".rustc" } } fn read_metadata_section_name(_target: &Target) -> &'static str { ".rustc" }