]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_ssa/src/back/metadata.rs
Rollup merge of #106829 - compiler-errors:more-alias-combine, r=spastorino
[rust.git] / compiler / rustc_codegen_ssa / src / back / metadata.rs
1 //! Reading of the rustc metadata for rlibs and dylibs
2
3 use std::fs::File;
4 use std::io::Write;
5 use std::path::Path;
6
7 use object::write::{self, StandardSegment, Symbol, SymbolSection};
8 use object::{
9     elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
10     SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
11 };
12
13 use snap::write::FrameEncoder;
14
15 use rustc_data_structures::memmap::Mmap;
16 use rustc_data_structures::owning_ref::OwningRef;
17 use rustc_data_structures::rustc_erase_owner;
18 use rustc_data_structures::sync::MetadataRef;
19 use rustc_metadata::fs::METADATA_FILENAME;
20 use rustc_metadata::EncodedMetadata;
21 use rustc_session::cstore::MetadataLoader;
22 use rustc_session::Session;
23 use rustc_target::abi::Endian;
24 use rustc_target::spec::{RelocModel, Target};
25
26 /// The default metadata loader. This is used by cg_llvm and cg_clif.
27 ///
28 /// # Metadata location
29 ///
30 /// <dl>
31 /// <dt>rlib</dt>
32 /// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
33 /// <dt>dylib</dt>
34 /// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
35 /// </dl>
36 pub struct DefaultMetadataLoader;
37
38 fn load_metadata_with(
39     path: &Path,
40     f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
41 ) -> Result<MetadataRef, String> {
42     let file =
43         File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
44     let data = unsafe { Mmap::map(file) }
45         .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))?;
46     let metadata = OwningRef::new(data).try_map(f)?;
47     return Ok(rustc_erase_owner!(metadata.map_owner_box()));
48 }
49
50 impl MetadataLoader for DefaultMetadataLoader {
51     fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
52         load_metadata_with(path, |data| {
53             let archive = object::read::archive::ArchiveFile::parse(&*data)
54                 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
55
56             for entry_result in archive.members() {
57                 let entry = entry_result
58                     .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
59                 if entry.name() == METADATA_FILENAME.as_bytes() {
60                     let data = entry
61                         .data(data)
62                         .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
63                     return search_for_section(path, data, ".rmeta");
64                 }
65             }
66
67             Err(format!("metadata not found in rlib '{}'", path.display()))
68         })
69     }
70
71     fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
72         load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
73     }
74 }
75
76 pub(super) fn search_for_section<'a>(
77     path: &Path,
78     bytes: &'a [u8],
79     section: &str,
80 ) -> Result<&'a [u8], String> {
81     let Ok(file) = object::File::parse(bytes) else {
82         // The parse above could fail for odd reasons like corruption, but for
83         // now we just interpret it as this target doesn't support metadata
84         // emission in object files so the entire byte slice itself is probably
85         // a metadata file. Ideally though if necessary we could at least check
86         // the prefix of bytes to see if it's an actual metadata object and if
87         // not forward the error along here.
88         return Ok(bytes);
89     };
90     file.section_by_name(section)
91         .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))?
92         .data()
93         .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
94 }
95
96 pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
97     let endianness = match sess.target.options.endian {
98         Endian::Little => Endianness::Little,
99         Endian::Big => Endianness::Big,
100     };
101     let architecture = match &sess.target.arch[..] {
102         "arm" => Architecture::Arm,
103         "aarch64" => {
104             if sess.target.pointer_width == 32 {
105                 Architecture::Aarch64_Ilp32
106             } else {
107                 Architecture::Aarch64
108             }
109         }
110         "x86" => Architecture::I386,
111         "s390x" => Architecture::S390x,
112         "mips" => Architecture::Mips,
113         "mips64" => Architecture::Mips64,
114         "x86_64" => {
115             if sess.target.pointer_width == 32 {
116                 Architecture::X86_64_X32
117             } else {
118                 Architecture::X86_64
119             }
120         }
121         "powerpc" => Architecture::PowerPc,
122         "powerpc64" => Architecture::PowerPc64,
123         "riscv32" => Architecture::Riscv32,
124         "riscv64" => Architecture::Riscv64,
125         "sparc64" => Architecture::Sparc64,
126         "avr" => Architecture::Avr,
127         "msp430" => Architecture::Msp430,
128         "hexagon" => Architecture::Hexagon,
129         "bpf" => Architecture::Bpf,
130         // Unsupported architecture.
131         _ => return None,
132     };
133     let binary_format = if sess.target.is_like_osx {
134         BinaryFormat::MachO
135     } else if sess.target.is_like_windows {
136         BinaryFormat::Coff
137     } else {
138         BinaryFormat::Elf
139     };
140
141     let mut file = write::Object::new(binary_format, architecture, endianness);
142     let e_flags = match architecture {
143         Architecture::Mips => {
144             let arch = match sess.target.options.cpu.as_ref() {
145                 "mips1" => elf::EF_MIPS_ARCH_1,
146                 "mips2" => elf::EF_MIPS_ARCH_2,
147                 "mips3" => elf::EF_MIPS_ARCH_3,
148                 "mips4" => elf::EF_MIPS_ARCH_4,
149                 "mips5" => elf::EF_MIPS_ARCH_5,
150                 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
151                 _ => elf::EF_MIPS_ARCH_32R2,
152             };
153             // The only ABI LLVM supports for 32-bit MIPS CPUs is o32.
154             let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch;
155             if sess.target.options.relocation_model != RelocModel::Static {
156                 e_flags |= elf::EF_MIPS_PIC;
157             }
158             if sess.target.options.cpu.contains("r6") {
159                 e_flags |= elf::EF_MIPS_NAN2008;
160             }
161             e_flags
162         }
163         Architecture::Mips64 => {
164             // copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
165             let e_flags = elf::EF_MIPS_CPIC
166                 | elf::EF_MIPS_PIC
167                 | if sess.target.options.cpu.contains("r6") {
168                     elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
169                 } else {
170                     elf::EF_MIPS_ARCH_64R2
171                 };
172             e_flags
173         }
174         Architecture::Riscv32 | Architecture::Riscv64 => {
175             // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
176             let mut e_flags: u32 = 0x0;
177             let features = &sess.target.options.features;
178             // Check if compressed is enabled
179             if features.contains("+c") {
180                 e_flags |= elf::EF_RISCV_RVC;
181             }
182
183             // Select the appropriate floating-point ABI
184             if features.contains("+d") {
185                 e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
186             } else if features.contains("+f") {
187                 e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
188             } else {
189                 e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
190             }
191             e_flags
192         }
193         _ => 0,
194     };
195     // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
196     let os_abi = match sess.target.options.os.as_ref() {
197         "hermit" => elf::ELFOSABI_STANDALONE,
198         "freebsd" => elf::ELFOSABI_FREEBSD,
199         "solaris" => elf::ELFOSABI_SOLARIS,
200         _ => elf::ELFOSABI_NONE,
201     };
202     let abi_version = 0;
203     file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
204     Some(file)
205 }
206
207 pub enum MetadataPosition {
208     First,
209     Last,
210 }
211
212 /// For rlibs we "pack" rustc metadata into a dummy object file.
213 ///
214 /// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
215 /// In that case linkers try to include all files located in an archive, so if metadata is stored
216 /// in an archive then it needs to be of a form that the linker is able to process.
217 /// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
218 ///
219 /// Note, though, that we don't actually want this metadata to show up in any
220 /// final output of the compiler. Instead this is purely for rustc's own
221 /// metadata tracking purposes.
222 ///
223 /// With the above in mind, each "flavor" of object format gets special
224 /// handling here depending on the target:
225 ///
226 /// * MachO - macos-like targets will insert the metadata into a section that
227 ///   is sort of fake dwarf debug info. Inspecting the source of the macos
228 ///   linker this causes these sections to be skipped automatically because
229 ///   it's not in an allowlist of otherwise well known dwarf section names to
230 ///   go into the final artifact.
231 ///
232 /// * WebAssembly - we actually don't have any container format for this
233 ///   target. WebAssembly doesn't support the `dylib` crate type anyway so
234 ///   there's no need for us to support this at this time. Consequently the
235 ///   metadata bytes are simply stored as-is into an rlib.
236 ///
237 /// * COFF - Windows-like targets create an object with a section that has
238 ///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
239 ///   ever sees the section it doesn't process it and it's removed.
240 ///
241 /// * ELF - All other targets are similar to Windows in that there's a
242 ///   `SHF_EXCLUDE` flag we can set on sections in an object file to get
243 ///   automatically removed from the final output.
244 pub fn create_wrapper_file(
245     sess: &Session,
246     section_name: Vec<u8>,
247     data: &[u8],
248 ) -> (Vec<u8>, MetadataPosition) {
249     let Some(mut file) = create_object_file(sess) else {
250         // This is used to handle all "other" targets. This includes targets
251         // in two categories:
252         //
253         // * Some targets don't have support in the `object` crate just yet
254         //   to write an object file. These targets are likely to get filled
255         //   out over time.
256         //
257         // * Targets like WebAssembly don't support dylibs, so the purpose
258         //   of putting metadata in object files, to support linking rlibs
259         //   into dylibs, is moot.
260         //
261         // In both of these cases it means that linking into dylibs will
262         // not be supported by rustc. This doesn't matter for targets like
263         // WebAssembly and for targets not supported by the `object` crate
264         // yet it means that work will need to be done in the `object` crate
265         // to add a case above.
266         return (data.to_vec(), MetadataPosition::Last);
267     };
268     let section = file.add_section(
269         file.segment_name(StandardSegment::Debug).to_vec(),
270         section_name,
271         SectionKind::Debug,
272     );
273     match file.format() {
274         BinaryFormat::Coff => {
275             file.section_mut(section).flags =
276                 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
277         }
278         BinaryFormat::Elf => {
279             file.section_mut(section).flags =
280                 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
281         }
282         _ => {}
283     };
284     file.append_section_data(section, data, 1);
285     (file.write().unwrap(), MetadataPosition::First)
286 }
287
288 // Historical note:
289 //
290 // When using link.exe it was seen that the section name `.note.rustc`
291 // was getting shortened to `.note.ru`, and according to the PE and COFF
292 // specification:
293 //
294 // > Executable images do not use a string table and do not support
295 // > section names longer than 8 characters
296 //
297 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
298 //
299 // As a result, we choose a slightly shorter name! As to why
300 // `.note.rustc` works on MinGW, see
301 // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
302 pub fn create_compressed_metadata_file(
303     sess: &Session,
304     metadata: &EncodedMetadata,
305     symbol_name: &str,
306 ) -> Vec<u8> {
307     let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
308     FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
309     let Some(mut file) = create_object_file(sess) else {
310         return compressed.to_vec();
311     };
312     let section = file.add_section(
313         file.segment_name(StandardSegment::Data).to_vec(),
314         b".rustc".to_vec(),
315         SectionKind::ReadOnlyData,
316     );
317     match file.format() {
318         BinaryFormat::Elf => {
319             // Explicitly set no flags to avoid SHF_ALLOC default for data section.
320             file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
321         }
322         _ => {}
323     };
324     let offset = file.append_section_data(section, &compressed, 1);
325
326     // For MachO and probably PE this is necessary to prevent the linker from throwing away the
327     // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
328     file.add_symbol(Symbol {
329         name: symbol_name.as_bytes().to_vec(),
330         value: offset,
331         size: compressed.len() as u64,
332         kind: SymbolKind::Data,
333         scope: SymbolScope::Dynamic,
334         weak: false,
335         section: SymbolSection::Section(section),
336         flags: SymbolFlags::None,
337     });
338
339     file.write().unwrap()
340 }