]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/metadata.rs
Rollup merge of #69776 - ssomers:fix69769, r=Mark-Simulacrum
[rust.git] / src / librustc_codegen_llvm / metadata.rs
1 use crate::llvm;
2 use crate::llvm::archive_ro::ArchiveRO;
3 use crate::llvm::{mk_section_iter, False, ObjectFile};
4 use rustc::middle::cstore::MetadataLoader;
5 use rustc_target::spec::Target;
6
7 use log::debug;
8 use rustc_codegen_ssa::METADATA_FILENAME;
9 use rustc_data_structures::owning_ref::OwningRef;
10 use rustc_data_structures::rustc_erase_owner;
11
12 use rustc_fs_util::path_to_c_string;
13 use std::path::Path;
14 use std::slice;
15
16 pub use rustc_data_structures::sync::MetadataRef;
17
18 pub struct LlvmMetadataLoader;
19
20 impl MetadataLoader for LlvmMetadataLoader {
21     fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
22         // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
23         // internally to read the file. We also avoid even using a memcpy by
24         // just keeping the archive along while the metadata is in use.
25         let archive =
26             ArchiveRO::open(filename).map(|ar| OwningRef::new(Box::new(ar))).map_err(|e| {
27                 debug!("llvm didn't like `{}`: {}", filename.display(), e);
28                 format!("failed to read rlib metadata in '{}': {}", filename.display(), e)
29             })?;
30         let buf: OwningRef<_, [u8]> = archive.try_map(|ar| {
31             ar.iter()
32                 .filter_map(|s| s.ok())
33                 .find(|sect| sect.name() == Some(METADATA_FILENAME))
34                 .map(|s| s.data())
35                 .ok_or_else(|| {
36                     debug!("didn't find '{}' in the archive", METADATA_FILENAME);
37                     format!("failed to read rlib metadata: '{}'", filename.display())
38                 })
39         })?;
40         Ok(rustc_erase_owner!(buf))
41     }
42
43     fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
44         unsafe {
45             let buf = path_to_c_string(filename);
46             let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr())
47                 .ok_or_else(|| format!("error reading library: '{}'", filename.display()))?;
48             let of =
49                 ObjectFile::new(mb).map(|of| OwningRef::new(Box::new(of))).ok_or_else(|| {
50                     format!("provided path not an object file: '{}'", filename.display())
51                 })?;
52             let buf = of.try_map(|of| search_meta_section(of, target, filename))?;
53             Ok(rustc_erase_owner!(buf))
54         }
55     }
56 }
57
58 fn search_meta_section<'a>(
59     of: &'a ObjectFile,
60     target: &Target,
61     filename: &Path,
62 ) -> Result<&'a [u8], String> {
63     unsafe {
64         let si = mk_section_iter(of.llof);
65         while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
66             let mut name_buf = None;
67             let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
68             let name = name_buf.map_or(
69                 String::new(), // We got a NULL ptr, ignore `name_len`.
70                 |buf| {
71                     String::from_utf8(
72                         slice::from_raw_parts(buf.as_ptr() as *const u8, name_len as usize)
73                             .to_vec(),
74                     )
75                     .unwrap()
76                 },
77             );
78             debug!("get_metadata_section: name {}", name);
79             if read_metadata_section_name(target) == name {
80                 let cbuf = llvm::LLVMGetSectionContents(si.llsi);
81                 let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
82                 // The buffer is valid while the object file is around
83                 let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz);
84                 return Ok(buf);
85             }
86             llvm::LLVMMoveToNextSection(si.llsi);
87         }
88     }
89     Err(format!("metadata not found: '{}'", filename.display()))
90 }
91
92 pub fn metadata_section_name(target: &Target) -> &'static str {
93     // Historical note:
94     //
95     // When using link.exe it was seen that the section name `.note.rustc`
96     // was getting shortened to `.note.ru`, and according to the PE and COFF
97     // specification:
98     //
99     // > Executable images do not use a string table and do not support
100     // > section names longer than 8 characters
101     //
102     // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
103     //
104     // As a result, we choose a slightly shorter name! As to why
105     // `.note.rustc` works on MinGW, that's another good question...
106
107     if target.options.is_like_osx { "__DATA,.rustc" } else { ".rustc" }
108 }
109
110 fn read_metadata_section_name(_target: &Target) -> &'static str {
111     ".rustc"
112 }