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, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope, elf, pe,
13 xcoff,
14};
15use rustc_abi::Endian;
16use rustc_data_structures::memmap::Mmap;
17use rustc_data_structures::owned_slice::{OwnedSlice, try_slice_owned};
18use rustc_metadata::EncodedMetadata;
19use rustc_metadata::creader::MetadataLoader;
20use rustc_metadata::fs::METADATA_FILENAME;
21use rustc_middle::bug;
22use rustc_session::Session;
23use rustc_span::sym;
24use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
25use tracing::debug;
26
27use super::apple;
28
29#[derive(Debug)]
40pub(crate) struct DefaultMetadataLoader;
41
42static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
43
44fn load_metadata_with(
45 path: &Path,
46 f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
47) -> Result<OwnedSlice, String> {
48 let file =
49 File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
50
51 unsafe { Mmap::map(file) }
52 .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))
53 .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap)))
54}
55
56impl MetadataLoader for DefaultMetadataLoader {
57 fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
58 debug!("getting rlib metadata for {}", path.display());
59 load_metadata_with(path, |data| {
60 let archive = object::read::archive::ArchiveFile::parse(&*data)
61 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
62
63 for entry_result in archive.members() {
64 let entry = entry_result
65 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
66 if entry.name() == METADATA_FILENAME.as_bytes() {
67 let data = entry
68 .data(data)
69 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
70 if target.is_like_aix {
71 return get_metadata_xcoff(path, data);
72 } else {
73 return search_for_section(path, data, ".rmeta");
74 }
75 }
76 }
77
78 Err(format!("metadata not found in rlib '{}'", path.display()))
79 })
80 }
81
82 fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
83 debug!("getting dylib metadata for {}", path.display());
84 if target.is_like_aix {
85 load_metadata_with(path, |data| {
86 let archive = object::read::archive::ArchiveFile::parse(&*data).map_err(|e| {
87 format!("failed to parse aix dylib '{}': {}", path.display(), e)
88 })?;
89
90 match archive.members().exactly_one() {
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 (architecture, sub_architecture) = match &sess.target.arch[..] {
210 "arm" => (Architecture::Arm, None),
211 "aarch64" => (
212 if sess.target.pointer_width == 32 {
213 Architecture::Aarch64_Ilp32
214 } else {
215 Architecture::Aarch64
216 },
217 None,
218 ),
219 "x86" => (Architecture::I386, None),
220 "s390x" => (Architecture::S390x, None),
221 "mips" | "mips32r6" => (Architecture::Mips, None),
222 "mips64" | "mips64r6" => (Architecture::Mips64, None),
223 "x86_64" => (
224 if sess.target.pointer_width == 32 {
225 Architecture::X86_64_X32
226 } else {
227 Architecture::X86_64
228 },
229 None,
230 ),
231 "powerpc" => (Architecture::PowerPc, None),
232 "powerpc64" => (Architecture::PowerPc64, None),
233 "riscv32" => (Architecture::Riscv32, None),
234 "riscv64" => (Architecture::Riscv64, None),
235 "sparc" => {
236 if sess.unstable_target_features.contains(&sym::v8plus) {
237 (Architecture::Sparc32Plus, None)
239 } else {
240 (Architecture::Sparc, None)
242 }
243 }
244 "sparc64" => (Architecture::Sparc64, None),
245 "avr" => (Architecture::Avr, None),
246 "msp430" => (Architecture::Msp430, None),
247 "hexagon" => (Architecture::Hexagon, None),
248 "bpf" => (Architecture::Bpf, None),
249 "loongarch64" => (Architecture::LoongArch64, None),
250 "csky" => (Architecture::Csky, None),
251 "arm64ec" => (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)),
252 _ => return None,
254 };
255 let binary_format = if sess.target.is_like_osx {
256 BinaryFormat::MachO
257 } else if sess.target.is_like_windows {
258 BinaryFormat::Coff
259 } else if sess.target.is_like_aix {
260 BinaryFormat::Xcoff
261 } else {
262 BinaryFormat::Elf
263 };
264
265 let mut file = write::Object::new(binary_format, architecture, endianness);
266 file.set_sub_architecture(sub_architecture);
267 if sess.target.is_like_osx {
268 if macho_is_arm64e(&sess.target) {
269 file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
270 }
271
272 file.set_macho_build_version(macho_object_build_version_for_target(sess))
273 }
274 if binary_format == BinaryFormat::Coff {
275 let original_mangling = file.mangling();
277 file.set_mangling(object::write::Mangling::None);
278
279 let mut feature = 0;
280
281 if file.architecture() == object::Architecture::I386 {
282 feature |= 1;
288 }
289
290 file.add_symbol(object::write::Symbol {
291 name: "@feat.00".into(),
292 value: feature,
293 size: 0,
294 kind: object::SymbolKind::Data,
295 scope: object::SymbolScope::Compilation,
296 weak: false,
297 section: object::write::SymbolSection::Absolute,
298 flags: object::SymbolFlags::None,
299 });
300
301 file.set_mangling(original_mangling);
302 }
303 let e_flags = match architecture {
304 Architecture::Mips => {
305 let arch = match sess.target.options.cpu.as_ref() {
306 "mips1" => elf::EF_MIPS_ARCH_1,
307 "mips2" => elf::EF_MIPS_ARCH_2,
308 "mips3" => elf::EF_MIPS_ARCH_3,
309 "mips4" => elf::EF_MIPS_ARCH_4,
310 "mips5" => elf::EF_MIPS_ARCH_5,
311 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
312 _ => elf::EF_MIPS_ARCH_32R2,
313 };
314
315 let mut e_flags = elf::EF_MIPS_CPIC | arch;
316
317 match sess.target.options.llvm_abiname.to_lowercase().as_str() {
319 "n32" => e_flags |= elf::EF_MIPS_ABI2,
320 "o32" => e_flags |= elf::EF_MIPS_ABI_O32,
321 _ => e_flags |= elf::EF_MIPS_ABI_O32,
322 };
323
324 if sess.target.options.relocation_model != RelocModel::Static {
325 e_flags |= elf::EF_MIPS_PIC;
326 }
327 if sess.target.options.cpu.contains("r6") {
328 e_flags |= elf::EF_MIPS_NAN2008;
329 }
330 e_flags
331 }
332 Architecture::Mips64 => {
333 let e_flags = elf::EF_MIPS_CPIC
335 | elf::EF_MIPS_PIC
336 | if sess.target.options.cpu.contains("r6") {
337 elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
338 } else {
339 elf::EF_MIPS_ARCH_64R2
340 };
341 e_flags
342 }
343 Architecture::Riscv32 | Architecture::Riscv64 => {
344 let mut e_flags: u32 = 0x0;
346
347 if sess.unstable_target_features.contains(&sym::c) {
350 e_flags |= elf::EF_RISCV_RVC;
351 }
352
353 match &*sess.target.llvm_abiname {
356 "ilp32" | "lp64" => (),
357 "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
358 "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
359 "ilp32e" | "lp64e" => e_flags |= elf::EF_RISCV_RVE,
361 _ => bug!("unknown RISC-V ABI name"),
362 }
363
364 e_flags
365 }
366 Architecture::LoongArch64 => {
367 let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
369
370 match &*sess.target.llvm_abiname {
373 "ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
374 "ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
375 "ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
376 _ => bug!("unknown LoongArch ABI name"),
377 }
378
379 e_flags
380 }
381 Architecture::Avr => {
382 ef_avr_arch(&sess.target.options.cpu)
385 }
386 Architecture::Csky => {
387 let e_flags = match sess.target.options.abi.as_ref() {
388 "abiv2" => elf::EF_CSKY_ABIV2,
389 _ => elf::EF_CSKY_ABIV1,
390 };
391 e_flags
392 }
393 _ => 0,
394 };
395 let os_abi = match sess.target.options.os.as_ref() {
397 "hermit" => elf::ELFOSABI_STANDALONE,
398 "freebsd" => elf::ELFOSABI_FREEBSD,
399 "solaris" => elf::ELFOSABI_SOLARIS,
400 _ => elf::ELFOSABI_NONE,
401 };
402 let abi_version = 0;
403 add_gnu_property_note(&mut file, architecture, binary_format, endianness);
404 file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
405 Some(file)
406}
407
408fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
427 fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
430 let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
431 (major << 16) | (minor << 8) | patch
432 }
433
434 let platform = apple::macho_platform(&sess.target);
435 let min_os = apple::deployment_target(sess);
436
437 let mut build_version = object::write::MachOBuildVersion::default();
438 build_version.platform = platform;
439 build_version.minos = pack_version(min_os);
440 build_version.sdk = 0;
446
447 build_version
448}
449
450fn macho_is_arm64e(target: &Target) -> bool {
452 target.llvm_target.starts_with("arm64e")
453}
454
455pub(crate) enum MetadataPosition {
456 First,
457 Last,
458}
459
460pub(crate) fn create_wrapper_file(
492 sess: &Session,
493 section_name: String,
494 data: &[u8],
495) -> (Vec<u8>, MetadataPosition) {
496 let Some(mut file) = create_object_file(sess) else {
497 if sess.target.is_like_wasm {
498 return (
499 create_metadata_file_for_wasm(sess, data, §ion_name),
500 MetadataPosition::First,
501 );
502 }
503
504 return (data.to_vec(), MetadataPosition::Last);
508 };
509 let section = if file.format() == BinaryFormat::Xcoff {
510 file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
511 } else {
512 file.add_section(
513 file.segment_name(StandardSegment::Debug).to_vec(),
514 section_name.into_bytes(),
515 SectionKind::Debug,
516 )
517 };
518 match file.format() {
519 BinaryFormat::Coff => {
520 file.section_mut(section).flags =
521 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
522 }
523 BinaryFormat::Elf => {
524 file.section_mut(section).flags =
525 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
526 }
527 BinaryFormat::Xcoff => {
528 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
530 file.section_mut(section).flags =
531 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
532 let len: u32 = data.len().try_into().unwrap();
537 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
538 file.add_symbol(Symbol {
540 name: AIX_METADATA_SYMBOL_NAME.into(),
541 value: offset + 4,
542 size: 0,
543 kind: SymbolKind::Unknown,
544 scope: SymbolScope::Compilation,
545 weak: false,
546 section: SymbolSection::Section(section),
547 flags: SymbolFlags::Xcoff {
548 n_sclass: xcoff::C_INFO,
549 x_smtyp: xcoff::C_HIDEXT,
550 x_smclas: xcoff::C_HIDEXT,
551 containing_csect: None,
552 },
553 });
554 }
555 _ => {}
556 };
557 file.append_section_data(section, data, 1);
558 (file.write().unwrap(), MetadataPosition::First)
559}
560
561pub fn create_compressed_metadata_file(
576 sess: &Session,
577 metadata: &EncodedMetadata,
578 symbol_name: &str,
579) -> Vec<u8> {
580 let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
581 packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap();
582 packed_metadata.extend(metadata.raw_data());
583
584 let Some(mut file) = create_object_file(sess) else {
585 if sess.target.is_like_wasm {
586 return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
587 }
588 return packed_metadata.to_vec();
589 };
590 if file.format() == BinaryFormat::Xcoff {
591 return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
592 }
593 let section = file.add_section(
594 file.segment_name(StandardSegment::Data).to_vec(),
595 b".rustc".to_vec(),
596 SectionKind::ReadOnlyData,
597 );
598 match file.format() {
599 BinaryFormat::Elf => {
600 file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
602 }
603 _ => {}
604 };
605 let offset = file.append_section_data(section, &packed_metadata, 1);
606
607 file.add_symbol(Symbol {
610 name: symbol_name.as_bytes().to_vec(),
611 value: offset,
612 size: packed_metadata.len() as u64,
613 kind: SymbolKind::Data,
614 scope: SymbolScope::Dynamic,
615 weak: false,
616 section: SymbolSection::Section(section),
617 flags: SymbolFlags::None,
618 });
619
620 file.write().unwrap()
621}
622
623pub fn create_compressed_metadata_file_for_xcoff(
637 mut file: write::Object<'_>,
638 data: &[u8],
639 symbol_name: &str,
640) -> Vec<u8> {
641 assert!(file.format() == BinaryFormat::Xcoff);
642 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
644 let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
645 let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
646 file.add_file_symbol("lib.rmeta".into());
647 file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
648 file.add_symbol(Symbol {
650 name: symbol_name.as_bytes().into(),
651 value: 0,
652 size: 0,
653 kind: SymbolKind::Data,
654 scope: SymbolScope::Dynamic,
655 weak: true,
656 section: SymbolSection::Section(data_section),
657 flags: SymbolFlags::None,
658 });
659 let len: u32 = data.len().try_into().unwrap();
660 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
661 file.add_symbol(Symbol {
663 name: AIX_METADATA_SYMBOL_NAME.into(),
664 value: offset + 4, size: 0,
666 kind: SymbolKind::Unknown,
667 scope: SymbolScope::Dynamic,
668 weak: false,
669 section: SymbolSection::Section(section),
670 flags: SymbolFlags::Xcoff {
671 n_sclass: xcoff::C_INFO,
672 x_smtyp: xcoff::C_HIDEXT,
673 x_smclas: xcoff::C_HIDEXT,
674 containing_csect: None,
675 },
676 });
677 file.append_section_data(section, data, 1);
678 file.write().unwrap()
679}
680
681pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
702 assert!(sess.target.is_like_wasm);
703 let mut module = wasm_encoder::Module::new();
704 let mut imports = wasm_encoder::ImportSection::new();
705
706 if sess.target.pointer_width == 64 {
707 imports.import(
708 "env",
709 "__linear_memory",
710 wasm_encoder::MemoryType {
711 minimum: 0,
712 maximum: None,
713 memory64: true,
714 shared: false,
715 page_size_log2: None,
716 },
717 );
718 }
719
720 if imports.len() > 0 {
721 module.section(&imports);
722 }
723 module.section(&wasm_encoder::CustomSection {
724 name: "linking".into(),
725 data: Cow::Borrowed(&[2]),
726 });
727 module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
728 module.finish()
729}