1use std::borrow::Cow;
4use std::fs::File;
5use std::io::Write;
6use std::path::Path;
7
8use itertools::Itertools;
9use object::write::{self, StandardSegment, Symbol, SymbolSection};
10use object::{
11 Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol,
12 SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, elf, pe, xcoff,
13};
14use rustc_abi::Endian;
15use rustc_data_structures::memmap::Mmap;
16use rustc_data_structures::owned_slice::{OwnedSlice, try_slice_owned};
17use rustc_metadata::EncodedMetadata;
18use rustc_metadata::creader::MetadataLoader;
19use rustc_metadata::fs::METADATA_FILENAME;
20use rustc_middle::bug;
21use rustc_session::Session;
22use rustc_span::sym;
23use rustc_target::spec::{Abi, Os, RelocModel, Target, ef_avr_arch};
24use tracing::debug;
25
26use super::apple;
27
28#[derive(Debug)]
39pub(crate) struct DefaultMetadataLoader;
40
41static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
42
43fn load_metadata_with(
44 path: &Path,
45 f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
46) -> Result<OwnedSlice, String> {
47 let file =
48 File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
49
50 unsafe { Mmap::map(file) }
51 .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))
52 .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap)))
53}
54
55impl MetadataLoader for DefaultMetadataLoader {
56 fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
57 debug!("getting rlib metadata for {}", path.display());
58 load_metadata_with(path, |data| {
59 let archive = object::read::archive::ArchiveFile::parse(&*data)
60 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
61
62 for entry_result in archive.members() {
63 let entry = entry_result
64 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
65 if entry.name() == METADATA_FILENAME.as_bytes() {
66 let data = entry
67 .data(data)
68 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
69 if target.is_like_aix {
70 return get_metadata_xcoff(path, data);
71 } else {
72 return search_for_section(path, data, ".rmeta");
73 }
74 }
75 }
76
77 Err(format!("metadata not found in rlib '{}'", path.display()))
78 })
79 }
80
81 fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
82 debug!("getting dylib metadata for {}", path.display());
83 if target.is_like_aix {
84 load_metadata_with(path, |data| {
85 let archive = object::read::archive::ArchiveFile::parse(&*data).map_err(|e| {
86 format!("failed to parse aix dylib '{}': {}", path.display(), e)
87 })?;
88
89 match Itertools::exactly_one(archive.members()) {
91 Ok(lib) => {
92 let lib = lib.map_err(|e| {
93 format!("failed to parse aix dylib '{}': {}", path.display(), e)
94 })?;
95 let data = lib.data(data).map_err(|e| {
96 format!("failed to parse aix dylib '{}': {}", path.display(), e)
97 })?;
98 get_metadata_xcoff(path, data)
99 }
100 Err(e) => Err(format!("failed to parse aix dylib '{}': {}", path.display(), e)),
101 }
102 })
103 } else {
104 load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
105 }
106 }
107}
108
109pub(super) fn search_for_section<'a>(
110 path: &Path,
111 bytes: &'a [u8],
112 section: &str,
113) -> Result<&'a [u8], String> {
114 let Ok(file) = object::File::parse(bytes) else {
115 return Ok(bytes);
122 };
123 file.section_by_name(section)
124 .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))?
125 .data()
126 .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
127}
128
129fn add_gnu_property_note(
130 file: &mut write::Object<'static>,
131 architecture: Architecture,
132 binary_format: BinaryFormat,
133 endianness: Endianness,
134) {
135 if binary_format != BinaryFormat::Elf
137 || !matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)
138 {
139 return;
140 }
141
142 let section = file.add_section(
143 file.segment_name(StandardSegment::Data).to_vec(),
144 b".note.gnu.property".to_vec(),
145 SectionKind::Note,
146 );
147 let mut data: Vec<u8> = Vec::new();
148 let n_namsz: u32 = 4; let n_descsz: u32 = 16; let n_type: u32 = object::elf::NT_GNU_PROPERTY_TYPE_0; let header_values = [n_namsz, n_descsz, n_type];
152 header_values.iter().for_each(|v| {
153 data.extend_from_slice(&match endianness {
154 Endianness::Little => v.to_le_bytes(),
155 Endianness::Big => v.to_be_bytes(),
156 })
157 });
158 data.extend_from_slice(b"GNU\0"); let pr_type: u32 = match architecture {
160 Architecture::X86_64 => object::elf::GNU_PROPERTY_X86_FEATURE_1_AND,
161 Architecture::Aarch64 => object::elf::GNU_PROPERTY_AARCH64_FEATURE_1_AND,
162 _ => unreachable!(),
163 };
164 let pr_datasz: u32 = 4; let pr_data: u32 = 3; let pr_padding: u32 = 0;
167 let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
168 property_values.iter().for_each(|v| {
169 data.extend_from_slice(&match endianness {
170 Endianness::Little => v.to_le_bytes(),
171 Endianness::Big => v.to_be_bytes(),
172 })
173 });
174 file.append_section_data(section, &data, 8);
175}
176
177pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
178 let Ok(file) = object::File::parse(data) else {
179 return Ok(data);
180 };
181 let info_data = search_for_section(path, data, ".info")?;
182 if let Some(metadata_symbol) =
183 file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
184 {
185 let offset = metadata_symbol.address() as usize;
186 if offset < 4 {
189 return Err(format!("Invalid metadata symbol offset: {offset}"));
190 }
191 let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
193 if offset + len > (info_data.len() as usize) {
194 return Err(format!(
195 "Metadata at offset {offset} with size {len} is beyond .info section"
196 ));
197 }
198 Ok(&info_data[offset..(offset + len)])
199 } else {
200 Err(format!("Unable to find symbol {AIX_METADATA_SYMBOL_NAME}"))
201 }
202}
203
204pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
205 let endianness = match sess.target.options.endian {
206 Endian::Little => Endianness::Little,
207 Endian::Big => Endianness::Big,
208 };
209 let Some((architecture, sub_architecture)) =
210 sess.target.object_architecture(&sess.unstable_target_features)
211 else {
212 return None;
213 };
214 let binary_format = sess.target.binary_format.to_object();
215
216 let mut file = write::Object::new(binary_format, architecture, endianness);
217 file.set_sub_architecture(sub_architecture);
218 if sess.target.is_like_darwin {
219 if macho_is_arm64e(&sess.target) {
220 file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
221 }
222
223 file.set_macho_build_version(macho_object_build_version_for_target(sess))
224 }
225 if binary_format == BinaryFormat::Coff {
226 let original_mangling = file.mangling();
228 file.set_mangling(object::write::Mangling::None);
229
230 let mut feature = 0;
231
232 if file.architecture() == object::Architecture::I386 {
233 feature |= 1;
239 }
240
241 file.add_symbol(object::write::Symbol {
242 name: "@feat.00".into(),
243 value: feature,
244 size: 0,
245 kind: object::SymbolKind::Data,
246 scope: object::SymbolScope::Compilation,
247 weak: false,
248 section: object::write::SymbolSection::Absolute,
249 flags: object::SymbolFlags::None,
250 });
251
252 file.set_mangling(original_mangling);
253 }
254 let e_flags = elf_e_flags(architecture, sess);
255 let os_abi = elf_os_abi(sess);
257 let abi_version = 0;
258 add_gnu_property_note(&mut file, architecture, binary_format, endianness);
259 file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
260 Some(file)
261}
262
263pub(super) fn elf_os_abi(sess: &Session) -> u8 {
264 match sess.target.options.os {
265 Os::Hermit => elf::ELFOSABI_STANDALONE,
266 Os::FreeBsd => elf::ELFOSABI_FREEBSD,
267 Os::Solaris => elf::ELFOSABI_SOLARIS,
268 _ => elf::ELFOSABI_NONE,
269 }
270}
271
272pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
273 match architecture {
274 Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => {
275 let is_32bit = architecture == Architecture::Mips;
278 let mut e_flags = match sess.target.options.cpu.as_ref() {
279 "mips1" if is_32bit => elf::EF_MIPS_ARCH_1,
280 "mips2" if is_32bit => elf::EF_MIPS_ARCH_2,
281 "mips3" => elf::EF_MIPS_ARCH_3,
282 "mips4" => elf::EF_MIPS_ARCH_4,
283 "mips5" => elf::EF_MIPS_ARCH_5,
284 "mips32r2" if is_32bit => elf::EF_MIPS_ARCH_32R2,
285 "mips32r6" if is_32bit => elf::EF_MIPS_ARCH_32R6,
286 "mips64r2" if !is_32bit => elf::EF_MIPS_ARCH_64R2,
287 "mips64r6" if !is_32bit => elf::EF_MIPS_ARCH_64R6,
288 s if s.starts_with("mips32") && !is_32bit => {
289 sess.dcx().fatal(format!("invalid CPU `{}` for 64-bit MIPS target", s))
290 }
291 s if s.starts_with("mips64") && is_32bit => {
292 sess.dcx().fatal(format!("invalid CPU `{}` for 32-bit MIPS target", s))
293 }
294 _ if is_32bit => elf::EF_MIPS_ARCH_32R2,
295 _ => elf::EF_MIPS_ARCH_64R2,
296 };
297
298 match sess.target.options.llvm_abiname.as_ref() {
301 "o32" if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32,
302 "n32" if !is_32bit => e_flags |= elf::EF_MIPS_ABI2,
303 "n64" if !is_32bit => {}
304 "" if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32,
305 "" => sess.dcx().fatal("LLVM ABI must be specified for 64-bit MIPS targets"),
306 s if is_32bit => {
307 sess.dcx().fatal(format!("invalid LLVM ABI `{}` for 32-bit MIPS target", s))
308 }
309 s => sess.dcx().fatal(format!("invalid LLVM ABI `{}` for 64-bit MIPS target", s)),
310 };
311
312 if sess.target.options.relocation_model != RelocModel::Static {
313 e_flags |= elf::EF_MIPS_PIC | elf::EF_MIPS_CPIC;
323 }
324 if sess.target.options.cpu.contains("r6") {
325 e_flags |= elf::EF_MIPS_NAN2008;
326 }
327 e_flags
328 }
329 Architecture::Riscv32 | Architecture::Riscv64 => {
330 let mut e_flags: u32 = 0x0;
332
333 if sess.unstable_target_features.contains(&sym::zca) {
336 e_flags |= elf::EF_RISCV_RVC;
337 }
338
339 if sess.unstable_target_features.contains(&sym::ztso) {
342 e_flags |= elf::EF_RISCV_TSO;
343 }
344
345 match &*sess.target.llvm_abiname {
348 "ilp32" | "lp64" => (),
349 "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
350 "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
351 "ilp32e" | "lp64e" => e_flags |= elf::EF_RISCV_RVE,
353 _ => bug!("unknown RISC-V ABI name"),
354 }
355
356 e_flags
357 }
358 Architecture::LoongArch32 | Architecture::LoongArch64 => {
359 let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
361
362 match &*sess.target.llvm_abiname {
365 "ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
366 "ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
367 "ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
368 _ => bug!("unknown LoongArch ABI name"),
369 }
370
371 e_flags
372 }
373 Architecture::Avr => {
374 if let Some(ref cpu) = sess.opts.cg.target_cpu {
377 ef_avr_arch(cpu)
378 } else {
379 bug!("AVR CPU not explicitly specified")
380 }
381 }
382 Architecture::Csky => {
383 if matches!(sess.target.options.abi, Abi::AbiV2) {
384 elf::EF_CSKY_ABIV2
385 } else {
386 elf::EF_CSKY_ABIV1
387 }
388 }
389 Architecture::PowerPc64 => {
390 const EF_PPC64_ABI_UNKNOWN: u32 = 0;
391 const EF_PPC64_ABI_ELF_V1: u32 = 1;
392 const EF_PPC64_ABI_ELF_V2: u32 = 2;
393
394 match sess.target.options.llvm_abiname.as_ref() {
395 "elfv1" => EF_PPC64_ABI_ELF_V1,
399 "elfv2" => EF_PPC64_ABI_ELF_V2,
400 "" if sess.target.options.binary_format.to_object() == BinaryFormat::Elf => {
401 bug!("No ABI specified for this PPC64 ELF target");
402 }
403 _ => EF_PPC64_ABI_UNKNOWN,
405 }
406 }
407 _ => 0,
408 }
409}
410
411fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
430 fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 {
433 let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
434 (major << 16) | (minor << 8) | patch
435 }
436
437 let platform = apple::macho_platform(&sess.target);
438 let min_os = sess.apple_deployment_target();
439
440 let mut build_version = object::write::MachOBuildVersion::default();
441 build_version.platform = platform;
442 build_version.minos = pack_version(min_os);
443 build_version.sdk = 0;
449
450 build_version
451}
452
453fn macho_is_arm64e(target: &Target) -> bool {
455 target.llvm_target.starts_with("arm64e")
456}
457
458pub(crate) enum MetadataPosition {
459 First,
460 Last,
461}
462
463pub(crate) fn create_wrapper_file(
495 sess: &Session,
496 section_name: String,
497 data: &[u8],
498) -> (Vec<u8>, MetadataPosition) {
499 let Some(mut file) = create_object_file(sess) else {
500 if sess.target.is_like_wasm {
501 return (
502 create_metadata_file_for_wasm(sess, data, §ion_name),
503 MetadataPosition::First,
504 );
505 }
506
507 return (data.to_vec(), MetadataPosition::Last);
511 };
512 let section = if file.format() == BinaryFormat::Xcoff {
513 file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
514 } else {
515 file.add_section(
516 file.segment_name(StandardSegment::Debug).to_vec(),
517 section_name.into_bytes(),
518 SectionKind::Debug,
519 )
520 };
521 match file.format() {
522 BinaryFormat::Coff => {
523 file.section_mut(section).flags =
524 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
525 }
526 BinaryFormat::Elf => {
527 file.section_mut(section).flags =
528 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
529 }
530 BinaryFormat::Xcoff => {
531 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
533 file.section_mut(section).flags =
534 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
535 let len: u32 = data.len().try_into().unwrap();
540 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
541 file.add_symbol(Symbol {
543 name: AIX_METADATA_SYMBOL_NAME.into(),
544 value: offset + 4,
545 size: 0,
546 kind: SymbolKind::Unknown,
547 scope: SymbolScope::Compilation,
548 weak: false,
549 section: SymbolSection::Section(section),
550 flags: SymbolFlags::Xcoff {
551 n_sclass: xcoff::C_INFO,
552 x_smtyp: xcoff::C_HIDEXT,
553 x_smclas: xcoff::C_HIDEXT,
554 containing_csect: None,
555 },
556 });
557 }
558 _ => {}
559 };
560 file.append_section_data(section, data, 1);
561 (file.write().unwrap(), MetadataPosition::First)
562}
563
564pub fn create_compressed_metadata_file(
579 sess: &Session,
580 metadata: &EncodedMetadata,
581 symbol_name: &str,
582) -> Vec<u8> {
583 let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
584 packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap();
585 packed_metadata.extend(metadata.stub_or_full());
586
587 let Some(mut file) = create_object_file(sess) else {
588 if sess.target.is_like_wasm {
589 return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
590 }
591 return packed_metadata.to_vec();
592 };
593 if file.format() == BinaryFormat::Xcoff {
594 return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
595 }
596 let section = file.add_section(
597 file.segment_name(StandardSegment::Data).to_vec(),
598 b".rustc".to_vec(),
599 SectionKind::ReadOnlyData,
600 );
601 match file.format() {
602 BinaryFormat::Elf => {
603 file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
605 }
606 _ => {}
607 };
608 let offset = file.append_section_data(section, &packed_metadata, 1);
609
610 file.add_symbol(Symbol {
613 name: symbol_name.as_bytes().to_vec(),
614 value: offset,
615 size: packed_metadata.len() as u64,
616 kind: SymbolKind::Data,
617 scope: SymbolScope::Dynamic,
618 weak: false,
619 section: SymbolSection::Section(section),
620 flags: SymbolFlags::None,
621 });
622
623 file.write().unwrap()
624}
625
626pub fn create_compressed_metadata_file_for_xcoff(
640 mut file: write::Object<'_>,
641 data: &[u8],
642 symbol_name: &str,
643) -> Vec<u8> {
644 assert!(file.format() == BinaryFormat::Xcoff);
645 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
647 let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
648 let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
649 file.add_file_symbol("lib.rmeta".into());
650 file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
651 file.add_symbol(Symbol {
653 name: symbol_name.as_bytes().into(),
654 value: 0,
655 size: 0,
656 kind: SymbolKind::Data,
657 scope: SymbolScope::Dynamic,
658 weak: true,
659 section: SymbolSection::Section(data_section),
660 flags: SymbolFlags::None,
661 });
662 let len: u32 = data.len().try_into().unwrap();
663 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
664 file.add_symbol(Symbol {
666 name: AIX_METADATA_SYMBOL_NAME.into(),
667 value: offset + 4, size: 0,
669 kind: SymbolKind::Unknown,
670 scope: SymbolScope::Dynamic,
671 weak: false,
672 section: SymbolSection::Section(section),
673 flags: SymbolFlags::Xcoff {
674 n_sclass: xcoff::C_INFO,
675 x_smtyp: xcoff::C_HIDEXT,
676 x_smclas: xcoff::C_HIDEXT,
677 containing_csect: None,
678 },
679 });
680 file.append_section_data(section, data, 1);
681 file.write().unwrap()
682}
683
684pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
705 assert!(sess.target.is_like_wasm);
706 let mut module = wasm_encoder::Module::new();
707 let mut imports = wasm_encoder::ImportSection::new();
708
709 if sess.target.pointer_width == 64 {
710 imports.import(
711 "env",
712 "__linear_memory",
713 wasm_encoder::MemoryType {
714 minimum: 0,
715 maximum: None,
716 memory64: true,
717 shared: false,
718 page_size_log2: None,
719 },
720 );
721 }
722
723 if imports.len() > 0 {
724 module.section(&imports);
725 }
726 module.section(&wasm_encoder::CustomSection {
727 name: "linking".into(),
728 data: Cow::Borrowed(&[2]),
729 });
730 module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
731 module.finish()
732}