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