1//! Reading of the rustc metadata for rlibs and dylibs
23use std::borrow::Cow;
4use std::fs::File;
5use std::io::Write;
6use std::path::Path;
78use itertools::Itertools;
9use object::write::{self, StandardSegment, Symbol, SymbolSection};
10use object::{
11Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol,
12SectionFlags, 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::{CfgAbi, Env, LlvmAbi, Os, RelocModel, Target, ef_avr_arch};
24use tracing::debug;
2526use super::apple;
27use crate::diagnostics;
2829/// The default metadata loader. This is used by cg_llvm and cg_clif.
30///
31/// # Metadata location
32///
33/// <dl>
34/// <dt>rlib</dt>
35/// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
36/// <dt>dylib</dt>
37/// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
38/// </dl>
39#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DefaultMetadataLoader {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "DefaultMetadataLoader")
}
}Debug)]
40pub struct DefaultMetadataLoader;
4142static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
4344fn load_metadata_with(
45 path: &Path,
46 f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
47) -> Result<OwnedSlice, String> {
48let file =
49 File::open(path).map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to open file \'{0}\': {1}",
path.display(), e))
})format!("failed to open file '{}': {}", path.display(), e))?;
5051unsafe { Mmap::map(file) }
52 .map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to mmap file \'{0}\': {1}",
path.display(), e))
})format!("failed to mmap file '{}': {}", path.display(), e))
53 .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap)))
54}
5556impl MetadataLoaderfor DefaultMetadataLoader {
57fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
58{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/metadata.rs:58",
"rustc_codegen_ssa::back::metadata",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/metadata.rs"),
::tracing_core::__macro_support::Option::Some(58u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::metadata"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("getting rlib metadata for {0}",
path.display()) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("getting rlib metadata for {}", path.display());
59load_metadata_with(path, |data| {
60let archive = object::read::archive::ArchiveFile::parse(&*data)
61 .map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse rlib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse rlib '{}': {}", path.display(), e))?;
6263for entry_result in archive.members() {
64let entry = entry_result
65 .map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse rlib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse rlib '{}': {}", path.display(), e))?;
66if entry.name() == METADATA_FILENAME.as_bytes() {
67let data = entry
68 .data(data)
69 .map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse rlib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse rlib '{}': {}", path.display(), e))?;
70if target.is_like_aix {
71return get_metadata_xcoff(path, data);
72 } else {
73return search_for_section(path, data, ".rmeta");
74 }
75 }
76 }
7778Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("metadata not found in rlib \'{0}\'",
path.display()))
})format!("metadata not found in rlib '{}'", path.display()))
79 })
80 }
8182fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
83{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/metadata.rs:83",
"rustc_codegen_ssa::back::metadata",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/metadata.rs"),
::tracing_core::__macro_support::Option::Some(83u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::metadata"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("getting dylib metadata for {0}",
path.display()) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("getting dylib metadata for {}", path.display());
84if target.is_like_aix {
85load_metadata_with(path, |data| {
86let archive = object::read::archive::ArchiveFile::parse(&*data).map_err(|e| {
87::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse aix dylib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse aix dylib '{}': {}", path.display(), e)88 })?;
8990match archive.members().exactly_one() {
91Ok(lib) => {
92let lib = lib.map_err(|e| {
93::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse aix dylib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse aix dylib '{}': {}", path.display(), e)94 })?;
95let data = lib.data(data).map_err(|e| {
96::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse aix dylib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse aix dylib '{}': {}", path.display(), e)97 })?;
98get_metadata_xcoff(path, data)
99 }
100Err(e) => Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to parse aix dylib \'{0}\': {1}",
path.display(), e))
})format!("failed to parse aix dylib '{}': {}", path.display(), e)),
101 }
102 })
103 } else {
104load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
105 }
106 }
107}
108109pub(super) fn search_for_section<'a>(
110 path: &Path,
111 bytes: &'a [u8],
112 section: &str,
113) -> Result<&'a [u8], String> {
114let Ok(file) = object::File::parse(bytes) else {
115// The parse above could fail for odd reasons like corruption, but for
116 // now we just interpret it as this target doesn't support metadata
117 // emission in object files so the entire byte slice itself is probably
118 // a metadata file. Ideally though if necessary we could at least check
119 // the prefix of bytes to see if it's an actual metadata object and if
120 // not forward the error along here.
121return Ok(bytes);
122 };
123 file.section_by_name(section)
124 .ok_or_else(|| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("no `{0}` section in \'{1}\'",
section, path.display()))
})format!("no `{}` section in '{}'", section, path.display()))?
125.data()
126 .map_err(|e| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to read {0} section in \'{1}\': {2}",
section, path.display(), e))
})format!("failed to read {} section in '{}': {}", section, path.display(), e))
127}
128129fn add_gnu_property_note(
130 file: &mut write::Object<'static>,
131 architecture: Architecture,
132 binary_format: BinaryFormat,
133 endianness: Endianness,
134) {
135// check bti protection
136if binary_format != BinaryFormat::Elf137 || !#[allow(non_exhaustive_omitted_patterns)] match architecture {
Architecture::X86_64 | Architecture::Aarch64 => true,
_ => false,
}matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)138 {
139return;
140 }
141142let section = file.add_section(
143file.segment_name(StandardSegment::Data).to_vec(),
144b".note.gnu.property".to_vec(),
145 SectionKind::Note,
146 );
147let mut data: Vec<u8> = Vec::new();
148let n_namsz: u32 = 4; // Size of the n_name field
149let n_descsz: u32 = 16; // Size of the n_desc field
150let n_type: u32 = object::elf::NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
151let header_values = [n_namsz, n_descsz, n_type];
152header_values.iter().for_each(|v| {
153data.extend_from_slice(&match endianness {
154 Endianness::Little => v.to_le_bytes(),
155 Endianness::Big => v.to_be_bytes(),
156 })
157 });
158data.extend_from_slice(b"GNU\0"); // Owner of the program property note
159let 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_ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
163 };
164let pr_datasz: u32 = 4; //size of the pr_data field
165let pr_data: u32 = 3; //program property descriptor
166let pr_padding: u32 = 0;
167let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
168property_values.iter().for_each(|v| {
169data.extend_from_slice(&match endianness {
170 Endianness::Little => v.to_le_bytes(),
171 Endianness::Big => v.to_be_bytes(),
172 })
173 });
174file.append_section_data(section, &data, 8);
175}
176177pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
178let Ok(file) = object::File::parse(data) else {
179return Ok(data);
180 };
181let info_data = search_for_section(path, data, ".info")?;
182if let Some(metadata_symbol) =
183file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
184 {
185let offset = metadata_symbol.address() as usize;
186// The offset specifies the location of rustc metadata in the .info section of XCOFF.
187 // Each string stored in .info section of XCOFF is preceded by a 4-byte length field.
188if offset < 4 {
189return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Invalid metadata symbol offset: {0}",
offset))
})format!("Invalid metadata symbol offset: {offset}"));
190 }
191// XCOFF format uses big-endian byte order.
192let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
193if offset + len > (info_data.len() as usize) {
194return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Metadata at offset {0} with size {1} is beyond .info section",
offset, len))
})format!(
195"Metadata at offset {offset} with size {len} is beyond .info section"
196));
197 }
198Ok(&info_data[offset..(offset + len)])
199 } else {
200Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Unable to find symbol {0}",
AIX_METADATA_SYMBOL_NAME))
})format!("Unable to find symbol {AIX_METADATA_SYMBOL_NAME}"))
201 }
202}
203204pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
205let endianness = match sess.target.options.endian {
206 Endian::Little => Endianness::Little,
207 Endian::Big => Endianness::Big,
208 };
209let Some((architecture, sub_architecture)) =
210sess.target.object_architecture(&sess.unstable_target_features)
211else {
212return None;
213 };
214let binary_format = sess.target.binary_format.to_object();
215216let mut file = write::Object::new(binary_format, architecture, endianness);
217file.set_sub_architecture(sub_architecture);
218if sess.target.is_like_darwin {
219if macho_is_arm64e(&sess.target) {
220file.set_macho_cpu_subtype(
221 object::macho::CPU_SUBTYPE_ARM64E | object::macho::CPU_SUBTYPE_PTRAUTH_ABI,
222 );
223 }
224225file.set_macho_build_version(macho_object_build_version_for_target(sess))
226 }
227if binary_format == BinaryFormat::Coff {
228// Disable the default mangler to avoid mangling the special "@feat.00" symbol name.
229let original_mangling = file.mangling();
230file.set_mangling(object::write::Mangling::None);
231232let mut feature = 0;
233234if file.architecture() == object::Architecture::I386 {
235// When linking with /SAFESEH on x86, lld requires that all linker inputs be marked as
236 // safe exception handling compatible. Metadata files masquerade as regular COFF
237 // objects and are treated as linker inputs, despite containing no actual code. Thus,
238 // they still need to be marked as safe exception handling compatible. See #96498.
239 // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
240feature |= 1;
241 }
242243file.add_symbol(object::write::Symbol {
244 name: "@feat.00".into(),
245 value: feature,
246 size: 0,
247 kind: object::SymbolKind::Data,
248 scope: object::SymbolScope::Compilation,
249 weak: false,
250 section: object::write::SymbolSection::Absolute,
251 flags: object::SymbolFlags::None,
252 });
253254file.set_mangling(original_mangling);
255 }
256let e_flags = elf_e_flags(architecture, sess);
257// adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
258let os_abi = elf_os_abi(sess);
259let abi_version = 0;
260// Ferrocene change:
261 // aarch64-unknown_nto_qnx710 uses GNU LD 2.32, and that version does not
262 // support the linker section added here.
263 // Adding the section makes the linker emit warnings.
264let is_aarch64_qnx71 =
265 (architecture == Architecture::Aarch64) && (sess.target.env == Env::Nto71);
266if !is_aarch64_qnx71 {
267add_gnu_property_note(&mut file, architecture, binary_format, endianness);
268 }
269file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
270Some(file)
271}
272273pub(super) fn elf_os_abi(sess: &Session) -> u8 {
274match sess.target.options.os {
275 Os::Hermit => elf::ELFOSABI_STANDALONE,
276 Os::FreeBsd => elf::ELFOSABI_FREEBSD,
277 Os::Solaris => elf::ELFOSABI_SOLARIS,
278_ => elf::ELFOSABI_NONE,
279 }
280}
281282pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
283match architecture {
284 Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => {
285// "N32" indicates an "ILP32" data model on a 64-bit MIPS CPU
286 // like SPARC's "v8+", x86_64's "x32", or the watchOS "arm64_32".
287let is_32bit = architecture == Architecture::Mips;
288let mut e_flags = match sess.target.options.cpu.as_ref() {
289"mips1" if is_32bit => elf::EF_MIPS_ARCH_1,
290"mips2" if is_32bit => elf::EF_MIPS_ARCH_2,
291"mips3" => elf::EF_MIPS_ARCH_3,
292"mips4" => elf::EF_MIPS_ARCH_4,
293"mips5" => elf::EF_MIPS_ARCH_5,
294"mips32r2" if is_32bit => elf::EF_MIPS_ARCH_32R2,
295"mips32r6" if is_32bit => elf::EF_MIPS_ARCH_32R6,
296"mips64r2" if !is_32bit => elf::EF_MIPS_ARCH_64R2,
297"mips64r6" if !is_32bit => elf::EF_MIPS_ARCH_64R6,
298 s if s.starts_with("mips32") && !is_32bit => {
299sess.dcx().fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid CPU `{0}` for 64-bit MIPS target",
s))
})format!("invalid CPU `{}` for 64-bit MIPS target", s))
300 }
301 s if s.starts_with("mips64") && is_32bit => {
302sess.dcx().fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid CPU `{0}` for 32-bit MIPS target",
s))
})format!("invalid CPU `{}` for 32-bit MIPS target", s))
303 }
304_ if is_32bit => elf::EF_MIPS_ARCH_32R2,
305_ => elf::EF_MIPS_ARCH_64R2,
306 };
307308// Use the explicitly given ABI.
309match &sess.target.options.llvm_abiname {
310 LlvmAbi::O32if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32,
311 LlvmAbi::N32if !is_32bit => e_flags |= elf::EF_MIPS_ABI2,
312 LlvmAbi::N64if !is_32bit => {}
313// The rest is invalid (which is already ensured by the target spec check).
314 s => ::rustc_middle::util::bug::bug_fmt(format_args!("invalid LLVM ABI `{0}` for MIPS target",
s))bug!("invalid LLVM ABI `{}` for MIPS target", s),
315 };
316317if sess.target.options.relocation_model != RelocModel::Static {
318// PIC means position-independent code. CPIC means "calls PIC".
319 // CPIC was mutually exclusive with PIC according to
320 // the SVR4 MIPS ABI https://refspecs.linuxfoundation.org/elf/mipsabi.pdf
321 // and should have only appeared on static objects with dynamically calls.
322 // At some point someone (GCC?) decided to set CPIC even for PIC.
323 // Nowadays various things expect both set on the same object file
324 // and may even error if you mix CPIC and non-CPIC object files,
325 // despite that being the entire point of the CPIC ABI extension!
326 // As we are in Rome, we do as the Romans do.
327e_flags |= elf::EF_MIPS_PIC | elf::EF_MIPS_CPIC;
328 }
329if sess.target.options.cpu.contains("r6") {
330e_flags |= elf::EF_MIPS_NAN2008;
331 }
332e_flags333 }
334 Architecture::Riscv32 | Architecture::Riscv64 => {
335// Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
336let mut e_flags: u32 = 0x0;
337338// Check if compression is enabled
339if sess.target_features.contains(&sym::zca) {
340e_flags |= elf::EF_RISCV_RVC;
341 }
342343// Check if RVTSO is enabled
344if sess.target_features.contains(&sym::ztso) {
345e_flags |= elf::EF_RISCV_TSO;
346 }
347348// Set the appropriate flag based on ABI
349 // This needs to match LLVM `RISCVELFStreamer.cpp`
350match &sess.target.llvm_abiname {
351 LlvmAbi::Ilp32 | LlvmAbi::Lp64 => (),
352 LlvmAbi::Ilp32f | LlvmAbi::Lp64f => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
353 LlvmAbi::Ilp32d | LlvmAbi::Lp64d => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
354// Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI.
355LlvmAbi::Ilp32e | LlvmAbi::Lp64e => e_flags |= elf::EF_RISCV_RVE,
356_ => ::rustc_middle::util::bug::bug_fmt(format_args!("unknown RISC-V ABI name"))bug!("unknown RISC-V ABI name"),
357 }
358359e_flags360 }
361 Architecture::LoongArch32 | Architecture::LoongArch64 => {
362// Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
363let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
364365// Set the appropriate flag based on ABI
366 // This needs to match LLVM `LoongArchELFStreamer.cpp`
367match &sess.target.llvm_abiname {
368 LlvmAbi::Ilp32s | LlvmAbi::Lp64s => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
369 LlvmAbi::Ilp32f | LlvmAbi::Lp64f => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
370 LlvmAbi::Ilp32d | LlvmAbi::Lp64d => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
371_ => ::rustc_middle::util::bug::bug_fmt(format_args!("unknown LoongArch ABI name"))bug!("unknown LoongArch ABI name"),
372 }
373374e_flags375 }
376 Architecture::Avr => {
377// Resolve the ISA revision and set
378 // the appropriate EF_AVR_ARCH flag.
379if let Some(ref cpu) = sess.opts.cg.target_cpu {
380ef_avr_arch(cpu)
381 } else {
382sess.dcx().emit_fatal(diagnostics::CpuRequired)
383 }
384 }
385 Architecture::Csky => {
386if #[allow(non_exhaustive_omitted_patterns)] match sess.target.options.cfg_abi {
CfgAbi::AbiV2 => true,
_ => false,
}matches!(sess.target.options.cfg_abi, CfgAbi::AbiV2) {
387 elf::EF_CSKY_ABIV2388 } else {
389 elf::EF_CSKY_ABIV1390 }
391 }
392 Architecture::PowerPc64 => {
393const EF_PPC64_ABI_UNKNOWN: u32 = 0;
394const EF_PPC64_ABI_ELF_V1: u32 = 1;
395const EF_PPC64_ABI_ELF_V2: u32 = 2;
396397match sess.target.options.llvm_abiname {
398// If the flags do not correctly indicate the ABI,
399 // linkers such as ld.lld assume that the ppc64 object files are always ELFv2
400 // which leads to broken binaries if ELFv1 is used for the object files.
401LlvmAbi::ElfV1 => EF_PPC64_ABI_ELF_V1,
402 LlvmAbi::ElfV2 => EF_PPC64_ABI_ELF_V2,
403_ if sess.target.options.binary_format.to_object() == BinaryFormat::Elf => {
404::rustc_middle::util::bug::bug_fmt(format_args!("invalid ABI specified for this PPC64 ELF target"));bug!("invalid ABI specified for this PPC64 ELF target");
405 }
406// Fall back
407_ => EF_PPC64_ABI_UNKNOWN,
408 }
409 }
410 Architecture::Sparc32Plus => elf::EF_SPARC_32PLUS,
411_ => 0,
412 }
413}
414415/// Mach-O files contain information about:
416/// - The platform/OS they were built for (macOS/watchOS/Mac Catalyst/iOS simulator etc).
417/// - The minimum OS version / deployment target.
418/// - The version of the SDK they were targetting.
419///
420/// In the past, this was accomplished using the LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
421/// LC_VERSION_MIN_TVOS or LC_VERSION_MIN_WATCHOS load commands, which each contain information
422/// about the deployment target and SDK version, and implicitly, by their presence, which OS they
423/// target. Simulator targets were determined if the architecture was x86_64, but there was e.g. a
424/// LC_VERSION_MIN_IPHONEOS present.
425///
426/// This is of course brittle and limited, so modern tooling emit the LC_BUILD_VERSION load
427/// command (which contains all three pieces of information in one) when the deployment target is
428/// high enough, or the target is something that wouldn't be encodable with the old load commands
429/// (such as Mac Catalyst, or Aarch64 iOS simulator).
430///
431/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
432/// returns the `MachOBuildVersion` for the target to do so.
433fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
434/// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
435 /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
436fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 {
437let (major, minor, patch) = (majoras u32, minoras u32, patchas u32);
438 (major << 16) | (minor << 8) | patch439 }
440441let platform = apple::macho_platform(&sess.target);
442let min_os = sess.apple_deployment_target();
443444let mut build_version = object::write::MachOBuildVersion::default();
445build_version.platform = platform;
446build_version.minos = pack_version(min_os);
447// The version here does not _really_ matter, since it is only used at runtime, and we specify
448 // it when linking the final binary, so we will omit the version. This is also what LLVM does,
449 // and the tooling also allows this (and shows the SDK version as `n/a`). Finally, it is the
450 // semantically correct choice, as the SDK has not influenced the binary generated by rustc at
451 // this point in time.
452build_version.sdk = 0;
453454build_version455}
456457/// Is Apple's CPU subtype `arm64e`s
458fn macho_is_arm64e(target: &Target) -> bool {
459target.llvm_target.starts_with("arm64e")
460}
461462pub(crate) enum MetadataPosition {
463 First,
464 Last,
465}
466467/// For rlibs we "pack" rustc metadata into a dummy object file.
468///
469/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
470/// In that case linkers try to include all files located in an archive, so if metadata is stored
471/// in an archive then it needs to be of a form that the linker is able to process.
472/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
473///
474/// Note, though, that we don't actually want this metadata to show up in any
475/// final output of the compiler. Instead this is purely for rustc's own
476/// metadata tracking purposes.
477///
478/// With the above in mind, each "flavor" of object format gets special
479/// handling here depending on the target:
480///
481/// * MachO - macos-like targets will insert the metadata into a section that
482/// is sort of fake dwarf debug info. Inspecting the source of the macos
483/// linker this causes these sections to be skipped automatically because
484/// it's not in an allowlist of otherwise well known dwarf section names to
485/// go into the final artifact.
486///
487/// * WebAssembly - this uses wasm files themselves as the object file format
488/// so an empty file with no linking metadata but a single custom section is
489/// created holding our metadata.
490///
491/// * COFF - Windows-like targets create an object with a section that has
492/// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
493/// ever sees the section it doesn't process it and it's removed.
494///
495/// * ELF - All other targets are similar to Windows in that there's a
496/// `SHF_EXCLUDE` flag we can set on sections in an object file to get
497/// automatically removed from the final output.
498pub(crate) fn create_wrapper_file(
499 sess: &Session,
500 section_name: String,
501 data: &[u8],
502) -> (Vec<u8>, MetadataPosition) {
503let Some(mut file) = create_object_file(sess) else {
504if sess.target.is_like_wasm {
505return (
506create_metadata_file_for_wasm(sess, data, §ion_name),
507 MetadataPosition::First,
508 );
509 }
510511// Targets using this branch don't have support implemented here yet or
512 // they're not yet implemented in the `object` crate and will likely
513 // fill out this module over time.
514return (data.to_vec(), MetadataPosition::Last);
515 };
516let section = if file.format() == BinaryFormat::Xcoff {
517file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
518 } else {
519file.add_section(
520file.segment_name(StandardSegment::Debug).to_vec(),
521section_name.into_bytes(),
522 SectionKind::Debug,
523 )
524 };
525match file.format() {
526 BinaryFormat::Coff => {
527file.section_mut(section).flags =
528 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
529 }
530 BinaryFormat::Elf => {
531file.section_mut(section).flags =
532 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDEas u64 };
533 }
534 BinaryFormat::Xcoff => {
535// AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
536file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
537file.section_mut(section).flags =
538 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFOas u32 };
539// Encode string stored in .info section of XCOFF.
540 // FIXME: The length of data here is not guaranteed to fit in a u32.
541 // We may have to split the data into multiple pieces in order to
542 // store in .info section.
543let len: u32 = data.len().try_into().unwrap();
544let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
545// Add a symbol referring to the data in .info section.
546file.add_symbol(Symbol {
547 name: AIX_METADATA_SYMBOL_NAME.into(),
548 value: offset + 4,
549 size: 0,
550 kind: SymbolKind::Unknown,
551 scope: SymbolScope::Compilation,
552 weak: false,
553 section: SymbolSection::Section(section),
554 flags: SymbolFlags::Xcoff {
555 n_sclass: xcoff::C_INFO,
556 x_smtyp: xcoff::C_HIDEXT,
557 x_smclas: xcoff::C_HIDEXT,
558 containing_csect: None,
559 },
560 });
561 }
562_ => {}
563 };
564file.append_section_data(section, data, 1);
565 (file.write().unwrap(), MetadataPosition::First)
566}
567568// Historical note:
569//
570// When using link.exe it was seen that the section name `.note.rustc`
571// was getting shortened to `.note.ru`, and according to the PE and COFF
572// specification:
573//
574// > Executable images do not use a string table and do not support
575// > section names longer than 8 characters
576//
577// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
578//
579// As a result, we choose a slightly shorter name! As to why
580// `.note.rustc` works on MinGW, see
581// https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
582pub fn create_compressed_metadata_file(
583 sess: &Session,
584 metadata: &EncodedMetadata,
585 symbol_name: &str,
586) -> Vec<u8> {
587let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
588packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap();
589packed_metadata.extend(metadata.stub_or_full());
590591let Some(mut file) = create_object_file(sess) else {
592if sess.target.is_like_wasm {
593return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
594 }
595return packed_metadata.to_vec();
596 };
597if file.format() == BinaryFormat::Xcoff {
598return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
599 }
600let section = file.add_section(
601file.segment_name(StandardSegment::Data).to_vec(),
602b".rustc".to_vec(),
603 SectionKind::ReadOnlyData,
604 );
605match file.format() {
606 BinaryFormat::Elf => {
607// Explicitly set no flags to avoid SHF_ALLOC default for data section.
608file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
609 }
610_ => {}
611 };
612let offset = file.append_section_data(section, &packed_metadata, 1);
613614// For MachO and probably PE this is necessary to prevent the linker from throwing away the
615 // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
616file.add_symbol(Symbol {
617 name: symbol_name.as_bytes().to_vec(),
618 value: offset,
619 size: packed_metadata.len() as u64,
620 kind: SymbolKind::Data,
621 scope: SymbolScope::Dynamic,
622 weak: false,
623 section: SymbolSection::Section(section),
624 flags: SymbolFlags::None,
625 });
626627file.write().unwrap()
628}
629630/// * Xcoff - On AIX, custom sections are merged into predefined sections,
631/// so custom .rustc section is not preserved during linking.
632/// For this reason, we store metadata in predefined .info section, and
633/// define a symbol to reference the metadata. To preserve metadata during
634/// linking on AIX, we have to
635/// 1. Create an empty .text section, a empty .data section.
636/// 2. Define an empty symbol named `symbol_name` inside .data section.
637/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
638/// data inside .info section.
639/// From XCOFF's view, (2) creates a csect entry in the symbol table, the
640/// symbol created by (3) is a info symbol for the preceding csect. Thus
641/// two symbols are preserved during linking and we can use the second symbol
642/// to reference the metadata.
643pub fn create_compressed_metadata_file_for_xcoff(
644mut file: write::Object<'_>,
645 data: &[u8],
646 symbol_name: &str,
647) -> Vec<u8> {
648if !(file.format() == BinaryFormat::Xcoff) {
::core::panicking::panic("assertion failed: file.format() == BinaryFormat::Xcoff")
};assert!(file.format() == BinaryFormat::Xcoff);
649// AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
650file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
651let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
652let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
653file.add_file_symbol("lib.rmeta".into());
654file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFOas u32 };
655// Add a global symbol to data_section.
656file.add_symbol(Symbol {
657 name: symbol_name.as_bytes().into(),
658 value: 0,
659 size: 0,
660 kind: SymbolKind::Data,
661 scope: SymbolScope::Dynamic,
662 weak: true,
663 section: SymbolSection::Section(data_section),
664 flags: SymbolFlags::None,
665 });
666let len: u32 = data.len().try_into().unwrap();
667let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
668// Add a symbol referring to the rustc metadata.
669file.add_symbol(Symbol {
670 name: AIX_METADATA_SYMBOL_NAME.into(),
671 value: offset + 4, // The metadata is preceded by a 4-byte length field.
672size: 0,
673 kind: SymbolKind::Unknown,
674 scope: SymbolScope::Dynamic,
675 weak: false,
676 section: SymbolSection::Section(section),
677 flags: SymbolFlags::Xcoff {
678 n_sclass: xcoff::C_INFO,
679 x_smtyp: xcoff::C_HIDEXT,
680 x_smclas: xcoff::C_HIDEXT,
681 containing_csect: None,
682 },
683 });
684file.append_section_data(section, data, 1);
685file.write().unwrap()
686}
687688/// Creates a simple WebAssembly object file, which is itself a wasm module,
689/// that contains a custom section of the name `section_name` with contents
690/// `data`.
691///
692/// NB: the `object` crate does not yet have support for writing the wasm
693/// object file format. In lieu of that the `wasm-encoder` crate is used to
694/// build a wasm file by hand.
695///
696/// The wasm object file format is defined at
697/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
698/// and mainly consists of a `linking` custom section. In this case the custom
699/// section there is empty except for a version marker indicating what format
700/// it's in.
701///
702/// The main purpose of this is to contain a custom section with `section_name`,
703/// which is then appended after `linking`.
704///
705/// As a further detail the object needs to have a 64-bit memory if `wasm64` is
706/// the target or otherwise it's interpreted as a 32-bit object which is
707/// incompatible with 64-bit ones.
708pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
709if !sess.target.is_like_wasm {
::core::panicking::panic("assertion failed: sess.target.is_like_wasm")
};assert!(sess.target.is_like_wasm);
710let mut module = wasm_encoder::Module::new();
711let mut imports = wasm_encoder::ImportSection::new();
712713if sess.target.pointer_width == 64 {
714imports.import(
715"env",
716"__linear_memory",
717 wasm_encoder::MemoryType {
718 minimum: 0,
719 maximum: None,
720 memory64: true,
721 shared: false,
722 page_size_log2: None,
723 },
724 );
725 }
726727if imports.len() > 0 {
728module.section(&imports);
729 }
730module.section(&wasm_encoder::CustomSection {
731 name: "linking".into(),
732 data: Cow::Borrowed(&[2]),
733 });
734module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
735module.finish()
736}