]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_ssa/src/back/metadata.rs
New upstream version 1.71.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
49aad941 15use object::elf::NT_GNU_PROPERTY_TYPE_0;
17df50a5 16use rustc_data_structures::memmap::Mmap;
49aad941 17use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
064997fb 18use rustc_metadata::fs::METADATA_FILENAME;
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 24
17df50a5
XL
25/// The default metadata loader. This is used by cg_llvm and cg_clif.
26///
27/// # Metadata location
28///
29/// <dl>
30/// <dt>rlib</dt>
31/// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
32/// <dt>dylib</dt>
33/// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
34/// </dl>
9ffffee4 35#[derive(Debug)]
17df50a5
XL
36pub struct DefaultMetadataLoader;
37
38fn load_metadata_with(
39 path: &Path,
40 f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
49aad941 41) -> Result<OwnedSlice, String> {
17df50a5
XL
42 let file =
43 File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
353b0b11
FG
44
45 unsafe { Mmap::map(file) }
46 .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))
47 .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap)))
17df50a5
XL
48}
49
50impl MetadataLoader for DefaultMetadataLoader {
49aad941 51 fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
17df50a5
XL
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))?;
487cf647 63 return search_for_section(path, data, ".rmeta");
17df50a5
XL
64 }
65 }
66
67 Err(format!("metadata not found in rlib '{}'", path.display()))
68 })
69 }
70
49aad941 71 fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
487cf647 72 load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
17df50a5
XL
73 }
74}
75
487cf647 76pub(super) fn search_for_section<'a>(
17df50a5
XL
77 path: &Path,
78 bytes: &'a [u8],
79 section: &str,
80) -> Result<&'a [u8], String> {
5e7ed085 81 let Ok(file) = object::File::parse(bytes) else {
17df50a5
XL
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.
5e7ed085 88 return Ok(bytes);
17df50a5
XL
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}
a2a8927a 95
49aad941
FG
96fn add_gnu_property_note(
97 file: &mut write::Object<'static>,
98 architecture: Architecture,
99 binary_format: BinaryFormat,
100 endianness: Endianness,
101) {
102 // check bti protection
103 if binary_format != BinaryFormat::Elf
104 || !matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)
105 {
106 return;
107 }
108
109 let section = file.add_section(
110 file.segment_name(StandardSegment::Data).to_vec(),
111 b".note.gnu.property".to_vec(),
112 SectionKind::Note,
113 );
114 let mut data: Vec<u8> = Vec::new();
115 let n_namsz: u32 = 4; // Size of the n_name field
116 let n_descsz: u32 = 16; // Size of the n_desc field
117 let n_type: u32 = NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
118 let header_values = [n_namsz, n_descsz, n_type];
119 header_values.iter().for_each(|v| {
120 data.extend_from_slice(&match endianness {
121 Endianness::Little => v.to_le_bytes(),
122 Endianness::Big => v.to_be_bytes(),
123 })
124 });
125 data.extend_from_slice(b"GNU\0"); // Owner of the program property note
126 let pr_type: u32 = match architecture {
127 Architecture::X86_64 => 0xc0000002,
128 Architecture::Aarch64 => 0xc0000000,
129 _ => unreachable!(),
130 };
131 let pr_datasz: u32 = 4; //size of the pr_data field
132 let pr_data: u32 = 3; //program property descriptor
133 let pr_padding: u32 = 0;
134 let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
135 property_values.iter().for_each(|v| {
136 data.extend_from_slice(&match endianness {
137 Endianness::Little => v.to_le_bytes(),
138 Endianness::Big => v.to_be_bytes(),
139 })
140 });
141 file.append_section_data(section, &data, 8);
142}
143
04454e1e 144pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
a2a8927a
XL
145 let endianness = match sess.target.options.endian {
146 Endian::Little => Endianness::Little,
147 Endian::Big => Endianness::Big,
148 };
149 let architecture = match &sess.target.arch[..] {
150 "arm" => Architecture::Arm,
9c376795
FG
151 "aarch64" => {
152 if sess.target.pointer_width == 32 {
153 Architecture::Aarch64_Ilp32
154 } else {
155 Architecture::Aarch64
156 }
157 }
a2a8927a
XL
158 "x86" => Architecture::I386,
159 "s390x" => Architecture::S390x,
160 "mips" => Architecture::Mips,
161 "mips64" => Architecture::Mips64,
162 "x86_64" => {
163 if sess.target.pointer_width == 32 {
164 Architecture::X86_64_X32
165 } else {
166 Architecture::X86_64
167 }
168 }
169 "powerpc" => Architecture::PowerPc,
170 "powerpc64" => Architecture::PowerPc64,
171 "riscv32" => Architecture::Riscv32,
172 "riscv64" => Architecture::Riscv64,
173 "sparc64" => Architecture::Sparc64,
f2b60f7d
FG
174 "avr" => Architecture::Avr,
175 "msp430" => Architecture::Msp430,
176 "hexagon" => Architecture::Hexagon,
177 "bpf" => Architecture::Bpf,
353b0b11 178 "loongarch64" => Architecture::LoongArch64,
a2a8927a
XL
179 // Unsupported architecture.
180 _ => return None,
181 };
182 let binary_format = if sess.target.is_like_osx {
183 BinaryFormat::MachO
184 } else if sess.target.is_like_windows {
185 BinaryFormat::Coff
186 } else {
187 BinaryFormat::Elf
188 };
189
190 let mut file = write::Object::new(binary_format, architecture, endianness);
49aad941
FG
191 if sess.target.is_like_osx {
192 if let Some(build_version) = macho_object_build_version_for_target(&sess.target) {
193 file.set_macho_build_version(build_version)
194 }
195 }
923072b8 196 let e_flags = match architecture {
a2a8927a 197 Architecture::Mips => {
04454e1e
FG
198 let arch = match sess.target.options.cpu.as_ref() {
199 "mips1" => elf::EF_MIPS_ARCH_1,
200 "mips2" => elf::EF_MIPS_ARCH_2,
201 "mips3" => elf::EF_MIPS_ARCH_3,
202 "mips4" => elf::EF_MIPS_ARCH_4,
203 "mips5" => elf::EF_MIPS_ARCH_5,
204 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
205 _ => elf::EF_MIPS_ARCH_32R2,
206 };
207 // The only ABI LLVM supports for 32-bit MIPS CPUs is o32.
208 let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch;
209 if sess.target.options.relocation_model != RelocModel::Static {
210 e_flags |= elf::EF_MIPS_PIC;
211 }
212 if sess.target.options.cpu.contains("r6") {
213 e_flags |= elf::EF_MIPS_NAN2008;
214 }
923072b8 215 e_flags
a2a8927a
XL
216 }
217 Architecture::Mips64 => {
218 // copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
5099ac24
FG
219 let e_flags = elf::EF_MIPS_CPIC
220 | elf::EF_MIPS_PIC
221 | if sess.target.options.cpu.contains("r6") {
222 elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
223 } else {
224 elf::EF_MIPS_ARCH_64R2
225 };
923072b8 226 e_flags
a2a8927a 227 }
9c376795
FG
228 Architecture::Riscv32 | Architecture::Riscv64 => {
229 // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
230 let mut e_flags: u32 = 0x0;
231 let features = &sess.target.options.features;
232 // Check if compressed is enabled
233 if features.contains("+c") {
234 e_flags |= elf::EF_RISCV_RVC;
235 }
236
237 // Select the appropriate floating-point ABI
238 if features.contains("+d") {
239 e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
240 } else if features.contains("+f") {
241 e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
242 } else {
243 e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
244 }
923072b8 245 e_flags
a2a8927a 246 }
353b0b11
FG
247 Architecture::LoongArch64 => {
248 // Source: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_e_flags_identifies_abi_type_and_version
249 elf::EF_LARCH_OBJABI_V1 | elf::EF_LARCH_ABI_DOUBLE_FLOAT
250 }
923072b8 251 _ => 0,
a2a8927a 252 };
923072b8
FG
253 // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
254 let os_abi = match sess.target.options.os.as_ref() {
255 "hermit" => elf::ELFOSABI_STANDALONE,
256 "freebsd" => elf::ELFOSABI_FREEBSD,
257 "solaris" => elf::ELFOSABI_SOLARIS,
258 _ => elf::ELFOSABI_NONE,
259 };
260 let abi_version = 0;
49aad941 261 add_gnu_property_note(&mut file, architecture, binary_format, endianness);
923072b8 262 file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
a2a8927a
XL
263 Some(file)
264}
265
49aad941
FG
266/// Apple's LD, when linking for Mac Catalyst, requires object files to
267/// contain information about what they were built for (LC_BUILD_VERSION):
268/// the platform (macOS/watchOS etc), minimum OS version, and SDK version.
269/// This returns a `MachOBuildVersion` if necessary for the target.
270fn macho_object_build_version_for_target(
271 target: &Target,
272) -> Option<object::write::MachOBuildVersion> {
273 if !target.llvm_target.ends_with("-macabi") {
274 return None;
275 }
276 /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
277 /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
278 fn pack_version((major, minor): (u32, u32)) -> u32 {
279 (major << 16) | (minor << 8)
280 }
281
282 let platform = object::macho::PLATFORM_MACCATALYST;
283 let min_os = (14, 0);
284 let sdk = (16, 2);
285
286 let mut build_version = object::write::MachOBuildVersion::default();
287 build_version.platform = platform;
288 build_version.minos = pack_version(min_os);
289 build_version.sdk = pack_version(sdk);
290 Some(build_version)
291}
292
5e7ed085
FG
293pub enum MetadataPosition {
294 First,
295 Last,
296}
297
487cf647
FG
298/// For rlibs we "pack" rustc metadata into a dummy object file.
299///
300/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
301/// In that case linkers try to include all files located in an archive, so if metadata is stored
302/// in an archive then it needs to be of a form that the linker is able to process.
303/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
304///
305/// Note, though, that we don't actually want this metadata to show up in any
306/// final output of the compiler. Instead this is purely for rustc's own
307/// metadata tracking purposes.
308///
309/// With the above in mind, each "flavor" of object format gets special
310/// handling here depending on the target:
311///
312/// * MachO - macos-like targets will insert the metadata into a section that
313/// is sort of fake dwarf debug info. Inspecting the source of the macos
314/// linker this causes these sections to be skipped automatically because
315/// it's not in an allowlist of otherwise well known dwarf section names to
316/// go into the final artifact.
317///
318/// * WebAssembly - we actually don't have any container format for this
319/// target. WebAssembly doesn't support the `dylib` crate type anyway so
320/// there's no need for us to support this at this time. Consequently the
321/// metadata bytes are simply stored as-is into an rlib.
322///
323/// * COFF - Windows-like targets create an object with a section that has
324/// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
325/// ever sees the section it doesn't process it and it's removed.
326///
327/// * ELF - All other targets are similar to Windows in that there's a
328/// `SHF_EXCLUDE` flag we can set on sections in an object file to get
329/// automatically removed from the final output.
330pub fn create_wrapper_file(
331 sess: &Session,
332 section_name: Vec<u8>,
333 data: &[u8],
334) -> (Vec<u8>, MetadataPosition) {
5099ac24 335 let Some(mut file) = create_object_file(sess) else {
a2a8927a
XL
336 // This is used to handle all "other" targets. This includes targets
337 // in two categories:
338 //
339 // * Some targets don't have support in the `object` crate just yet
340 // to write an object file. These targets are likely to get filled
341 // out over time.
342 //
343 // * Targets like WebAssembly don't support dylibs, so the purpose
344 // of putting metadata in object files, to support linking rlibs
345 // into dylibs, is moot.
346 //
347 // In both of these cases it means that linking into dylibs will
348 // not be supported by rustc. This doesn't matter for targets like
349 // WebAssembly and for targets not supported by the `object` crate
350 // yet it means that work will need to be done in the `object` crate
351 // to add a case above.
487cf647 352 return (data.to_vec(), MetadataPosition::Last);
a2a8927a
XL
353 };
354 let section = file.add_section(
355 file.segment_name(StandardSegment::Debug).to_vec(),
487cf647 356 section_name,
a2a8927a
XL
357 SectionKind::Debug,
358 );
359 match file.format() {
360 BinaryFormat::Coff => {
361 file.section_mut(section).flags =
362 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
363 }
364 BinaryFormat::Elf => {
365 file.section_mut(section).flags =
366 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
367 }
368 _ => {}
369 };
487cf647 370 file.append_section_data(section, data, 1);
5e7ed085 371 (file.write().unwrap(), MetadataPosition::First)
a2a8927a
XL
372}
373
374// Historical note:
375//
376// When using link.exe it was seen that the section name `.note.rustc`
377// was getting shortened to `.note.ru`, and according to the PE and COFF
378// specification:
379//
380// > Executable images do not use a string table and do not support
381// > section names longer than 8 characters
382//
383// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
384//
385// As a result, we choose a slightly shorter name! As to why
386// `.note.rustc` works on MinGW, see
387// https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
388pub fn create_compressed_metadata_file(
389 sess: &Session,
390 metadata: &EncodedMetadata,
391 symbol_name: &str,
392) -> Vec<u8> {
393 let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
353b0b11
FG
394 // Our length will be backfilled once we're done writing
395 compressed.write_all(&[0; 4]).unwrap();
a2a8927a 396 FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
353b0b11
FG
397 let meta_len = rustc_metadata::METADATA_HEADER.len();
398 let data_len = (compressed.len() - meta_len - 4) as u32;
399 compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes());
400
5099ac24 401 let Some(mut file) = create_object_file(sess) else {
a2a8927a
XL
402 return compressed.to_vec();
403 };
404 let section = file.add_section(
405 file.segment_name(StandardSegment::Data).to_vec(),
406 b".rustc".to_vec(),
407 SectionKind::ReadOnlyData,
408 );
409 match file.format() {
410 BinaryFormat::Elf => {
411 // Explicitly set no flags to avoid SHF_ALLOC default for data section.
412 file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
413 }
414 _ => {}
415 };
416 let offset = file.append_section_data(section, &compressed, 1);
417
418 // For MachO and probably PE this is necessary to prevent the linker from throwing away the
419 // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
420 file.add_symbol(Symbol {
421 name: symbol_name.as_bytes().to_vec(),
422 value: offset,
423 size: compressed.len() as u64,
424 kind: SymbolKind::Data,
425 scope: SymbolScope::Dynamic,
426 weak: false,
427 section: SymbolSection::Section(section),
428 flags: SymbolFlags::None,
429 });
430
431 file.write().unwrap()
432}