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