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::{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 archive.members().exactly_one() {
90 Ok(lib) => {
91 let lib = lib.map_err(|e| {
92 format!("failed to parse aix dylib '{}': {}", path.display(), e)
93 })?;
94 let data = lib.data(data).map_err(|e| {
95 format!("failed to parse aix dylib '{}': {}", path.display(), e)
96 })?;
97 get_metadata_xcoff(path, data)
98 }
99 Err(e) => Err(format!("failed to parse aix dylib '{}': {}", path.display(), e)),
100 }
101 })
102 } else {
103 load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
104 }
105 }
106}
107
108pub(super) fn search_for_section<'a>(
109 path: &Path,
110 bytes: &'a [u8],
111 section: &str,
112) -> Result<&'a [u8], String> {
113 let Ok(file) = object::File::parse(bytes) else {
114 return Ok(bytes);
121 };
122 file.section_by_name(section)
123 .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))?
124 .data()
125 .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
126}
127
128fn add_gnu_property_note(
129 file: &mut write::Object<'static>,
130 architecture: Architecture,
131 binary_format: BinaryFormat,
132 endianness: Endianness,
133) {
134 if binary_format != BinaryFormat::Elf
136 || !matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)
137 {
138 return;
139 }
140
141 let section = file.add_section(
142 file.segment_name(StandardSegment::Data).to_vec(),
143 b".note.gnu.property".to_vec(),
144 SectionKind::Note,
145 );
146 let mut data: Vec<u8> = Vec::new();
147 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];
151 header_values.iter().for_each(|v| {
152 data.extend_from_slice(&match endianness {
153 Endianness::Little => v.to_le_bytes(),
154 Endianness::Big => v.to_be_bytes(),
155 })
156 });
157 data.extend_from_slice(b"GNU\0"); let pr_type: u32 = match architecture {
159 Architecture::X86_64 => object::elf::GNU_PROPERTY_X86_FEATURE_1_AND,
160 Architecture::Aarch64 => object::elf::GNU_PROPERTY_AARCH64_FEATURE_1_AND,
161 _ => unreachable!(),
162 };
163 let pr_datasz: u32 = 4; let pr_data: u32 = 3; let pr_padding: u32 = 0;
166 let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
167 property_values.iter().for_each(|v| {
168 data.extend_from_slice(&match endianness {
169 Endianness::Little => v.to_le_bytes(),
170 Endianness::Big => v.to_be_bytes(),
171 })
172 });
173 file.append_section_data(section, &data, 8);
174}
175
176pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
177 let Ok(file) = object::File::parse(data) else {
178 return Ok(data);
179 };
180 let info_data = search_for_section(path, data, ".info")?;
181 if let Some(metadata_symbol) =
182 file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
183 {
184 let offset = metadata_symbol.address() as usize;
185 if offset < 4 {
188 return Err(format!("Invalid metadata symbol offset: {offset}"));
189 }
190 let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
192 if offset + len > (info_data.len() as usize) {
193 return Err(format!(
194 "Metadata at offset {offset} with size {len} is beyond .info section"
195 ));
196 }
197 Ok(&info_data[offset..(offset + len)])
198 } else {
199 Err(format!("Unable to find symbol {AIX_METADATA_SYMBOL_NAME}"))
200 }
201}
202
203pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
204 let endianness = match sess.target.options.endian {
205 Endian::Little => Endianness::Little,
206 Endian::Big => Endianness::Big,
207 };
208 let Some((architecture, sub_architecture)) =
209 sess.target.object_architecture(&sess.unstable_target_features)
210 else {
211 return None;
212 };
213 let binary_format = sess.target.binary_format.to_object();
214
215 let mut file = write::Object::new(binary_format, architecture, endianness);
216 file.set_sub_architecture(sub_architecture);
217 if sess.target.is_like_osx {
218 if macho_is_arm64e(&sess.target) {
219 file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
220 }
221
222 file.set_macho_build_version(macho_object_build_version_for_target(sess))
223 }
224 if binary_format == BinaryFormat::Coff {
225 let original_mangling = file.mangling();
227 file.set_mangling(object::write::Mangling::None);
228
229 let mut feature = 0;
230
231 if file.architecture() == object::Architecture::I386 {
232 feature |= 1;
238 }
239
240 file.add_symbol(object::write::Symbol {
241 name: "@feat.00".into(),
242 value: feature,
243 size: 0,
244 kind: object::SymbolKind::Data,
245 scope: object::SymbolScope::Compilation,
246 weak: false,
247 section: object::write::SymbolSection::Absolute,
248 flags: object::SymbolFlags::None,
249 });
250
251 file.set_mangling(original_mangling);
252 }
253 let e_flags = elf_e_flags(architecture, sess);
254 let os_abi = elf_os_abi(sess);
256 let abi_version = 0;
257 add_gnu_property_note(&mut file, architecture, binary_format, endianness);
258 file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
259 Some(file)
260}
261
262pub(super) fn elf_os_abi(sess: &Session) -> u8 {
263 match sess.target.options.os.as_ref() {
264 "hermit" => elf::ELFOSABI_STANDALONE,
265 "freebsd" => elf::ELFOSABI_FREEBSD,
266 "solaris" => elf::ELFOSABI_SOLARIS,
267 _ => elf::ELFOSABI_NONE,
268 }
269}
270
271pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
272 match architecture {
273 Architecture::Mips => {
274 let arch = match sess.target.options.cpu.as_ref() {
275 "mips1" => elf::EF_MIPS_ARCH_1,
276 "mips2" => elf::EF_MIPS_ARCH_2,
277 "mips3" => elf::EF_MIPS_ARCH_3,
278 "mips4" => elf::EF_MIPS_ARCH_4,
279 "mips5" => elf::EF_MIPS_ARCH_5,
280 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
281 _ => elf::EF_MIPS_ARCH_32R2,
282 };
283
284 let mut e_flags = elf::EF_MIPS_CPIC | arch;
285
286 match sess.target.options.llvm_abiname.to_lowercase().as_str() {
288 "n32" => e_flags |= elf::EF_MIPS_ABI2,
289 "o32" => e_flags |= elf::EF_MIPS_ABI_O32,
290 _ => e_flags |= elf::EF_MIPS_ABI_O32,
291 };
292
293 if sess.target.options.relocation_model != RelocModel::Static {
294 e_flags |= elf::EF_MIPS_PIC;
295 }
296 if sess.target.options.cpu.contains("r6") {
297 e_flags |= elf::EF_MIPS_NAN2008;
298 }
299 e_flags
300 }
301 Architecture::Mips64 => {
302 let e_flags = elf::EF_MIPS_CPIC
304 | elf::EF_MIPS_PIC
305 | if sess.target.options.cpu.contains("r6") {
306 elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
307 } else {
308 elf::EF_MIPS_ARCH_64R2
309 };
310 e_flags
311 }
312 Architecture::Riscv32 | Architecture::Riscv64 => {
313 let mut e_flags: u32 = 0x0;
315
316 if sess.unstable_target_features.contains(&sym::c) {
319 e_flags |= elf::EF_RISCV_RVC;
320 }
321
322 match &*sess.target.llvm_abiname {
325 "ilp32" | "lp64" => (),
326 "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
327 "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
328 "ilp32e" | "lp64e" => e_flags |= elf::EF_RISCV_RVE,
330 _ => bug!("unknown RISC-V ABI name"),
331 }
332
333 e_flags
334 }
335 Architecture::LoongArch64 => {
336 let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
338
339 match &*sess.target.llvm_abiname {
342 "ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
343 "ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
344 "ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
345 _ => bug!("unknown LoongArch ABI name"),
346 }
347
348 e_flags
349 }
350 Architecture::Avr => {
351 if let Some(ref cpu) = sess.opts.cg.target_cpu {
354 ef_avr_arch(cpu)
355 } else {
356 bug!("AVR CPU not explicitly specified")
357 }
358 }
359 Architecture::Csky => {
360 let e_flags = match sess.target.options.abi.as_ref() {
361 "abiv2" => elf::EF_CSKY_ABIV2,
362 _ => elf::EF_CSKY_ABIV1,
363 };
364 e_flags
365 }
366 _ => 0,
367 }
368}
369
370fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
389 fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
392 let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
393 (major << 16) | (minor << 8) | patch
394 }
395
396 let platform = apple::macho_platform(&sess.target);
397 let min_os = apple::deployment_target(sess);
398
399 let mut build_version = object::write::MachOBuildVersion::default();
400 build_version.platform = platform;
401 build_version.minos = pack_version(min_os);
402 build_version.sdk = 0;
408
409 build_version
410}
411
412fn macho_is_arm64e(target: &Target) -> bool {
414 target.llvm_target.starts_with("arm64e")
415}
416
417pub(crate) enum MetadataPosition {
418 First,
419 Last,
420}
421
422pub(crate) fn create_wrapper_file(
454 sess: &Session,
455 section_name: String,
456 data: &[u8],
457) -> (Vec<u8>, MetadataPosition) {
458 let Some(mut file) = create_object_file(sess) else {
459 if sess.target.is_like_wasm {
460 return (
461 create_metadata_file_for_wasm(sess, data, §ion_name),
462 MetadataPosition::First,
463 );
464 }
465
466 return (data.to_vec(), MetadataPosition::Last);
470 };
471 let section = if file.format() == BinaryFormat::Xcoff {
472 file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
473 } else {
474 file.add_section(
475 file.segment_name(StandardSegment::Debug).to_vec(),
476 section_name.into_bytes(),
477 SectionKind::Debug,
478 )
479 };
480 match file.format() {
481 BinaryFormat::Coff => {
482 file.section_mut(section).flags =
483 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
484 }
485 BinaryFormat::Elf => {
486 file.section_mut(section).flags =
487 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
488 }
489 BinaryFormat::Xcoff => {
490 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
492 file.section_mut(section).flags =
493 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
494 let len: u32 = data.len().try_into().unwrap();
499 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
500 file.add_symbol(Symbol {
502 name: AIX_METADATA_SYMBOL_NAME.into(),
503 value: offset + 4,
504 size: 0,
505 kind: SymbolKind::Unknown,
506 scope: SymbolScope::Compilation,
507 weak: false,
508 section: SymbolSection::Section(section),
509 flags: SymbolFlags::Xcoff {
510 n_sclass: xcoff::C_INFO,
511 x_smtyp: xcoff::C_HIDEXT,
512 x_smclas: xcoff::C_HIDEXT,
513 containing_csect: None,
514 },
515 });
516 }
517 _ => {}
518 };
519 file.append_section_data(section, data, 1);
520 (file.write().unwrap(), MetadataPosition::First)
521}
522
523pub fn create_compressed_metadata_file(
538 sess: &Session,
539 metadata: &EncodedMetadata,
540 symbol_name: &str,
541) -> Vec<u8> {
542 let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
543 packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap();
544 packed_metadata.extend(metadata.raw_data());
545
546 let Some(mut file) = create_object_file(sess) else {
547 if sess.target.is_like_wasm {
548 return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
549 }
550 return packed_metadata.to_vec();
551 };
552 if file.format() == BinaryFormat::Xcoff {
553 return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
554 }
555 let section = file.add_section(
556 file.segment_name(StandardSegment::Data).to_vec(),
557 b".rustc".to_vec(),
558 SectionKind::ReadOnlyData,
559 );
560 match file.format() {
561 BinaryFormat::Elf => {
562 file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
564 }
565 _ => {}
566 };
567 let offset = file.append_section_data(section, &packed_metadata, 1);
568
569 file.add_symbol(Symbol {
572 name: symbol_name.as_bytes().to_vec(),
573 value: offset,
574 size: packed_metadata.len() as u64,
575 kind: SymbolKind::Data,
576 scope: SymbolScope::Dynamic,
577 weak: false,
578 section: SymbolSection::Section(section),
579 flags: SymbolFlags::None,
580 });
581
582 file.write().unwrap()
583}
584
585pub fn create_compressed_metadata_file_for_xcoff(
599 mut file: write::Object<'_>,
600 data: &[u8],
601 symbol_name: &str,
602) -> Vec<u8> {
603 assert!(file.format() == BinaryFormat::Xcoff);
604 file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
606 let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
607 let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
608 file.add_file_symbol("lib.rmeta".into());
609 file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
610 file.add_symbol(Symbol {
612 name: symbol_name.as_bytes().into(),
613 value: 0,
614 size: 0,
615 kind: SymbolKind::Data,
616 scope: SymbolScope::Dynamic,
617 weak: true,
618 section: SymbolSection::Section(data_section),
619 flags: SymbolFlags::None,
620 });
621 let len: u32 = data.len().try_into().unwrap();
622 let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
623 file.add_symbol(Symbol {
625 name: AIX_METADATA_SYMBOL_NAME.into(),
626 value: offset + 4, size: 0,
628 kind: SymbolKind::Unknown,
629 scope: SymbolScope::Dynamic,
630 weak: false,
631 section: SymbolSection::Section(section),
632 flags: SymbolFlags::Xcoff {
633 n_sclass: xcoff::C_INFO,
634 x_smtyp: xcoff::C_HIDEXT,
635 x_smclas: xcoff::C_HIDEXT,
636 containing_csect: None,
637 },
638 });
639 file.append_section_data(section, data, 1);
640 file.write().unwrap()
641}
642
643pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
664 assert!(sess.target.is_like_wasm);
665 let mut module = wasm_encoder::Module::new();
666 let mut imports = wasm_encoder::ImportSection::new();
667
668 if sess.target.pointer_width == 64 {
669 imports.import(
670 "env",
671 "__linear_memory",
672 wasm_encoder::MemoryType {
673 minimum: 0,
674 maximum: None,
675 memory64: true,
676 shared: false,
677 page_size_log2: None,
678 },
679 );
680 }
681
682 if imports.len() > 0 {
683 module.section(&imports);
684 }
685 module.section(&wasm_encoder::CustomSection {
686 name: "linking".into(),
687 data: Cow::Borrowed(&[2]),
688 });
689 module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
690 module.finish()
691}