Skip to main content

rustc_codegen_ssa/back/
metadata.rs

1//! Reading of the rustc metadata for rlibs and dylibs
2
3use 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::{CfgAbi, LlvmAbi, Os, RelocModel, Target, ef_avr_arch};
24use tracing::debug;
25
26use super::apple;
27use crate::diagnostics;
28
29/// 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;
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| ::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))?;
50
51    unsafe { 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}
55
56impl MetadataLoader for DefaultMetadataLoader {
57    fn 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 /rustc-dev/0fc141305da7a8a222f65aef1f1acc739c46282b/compiler/rustc_codegen_ssa/src/back/metadata.rs:58",
                        "rustc_codegen_ssa::back::metadata",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/0fc141305da7a8a222f65aef1f1acc739c46282b/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());
59        load_metadata_with(path, |data| {
60            let 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))?;
62
63            for entry_result in archive.members() {
64                let 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))?;
66                if entry.name() == METADATA_FILENAME.as_bytes() {
67                    let 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))?;
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(::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    }
81
82    fn 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 /rustc-dev/0fc141305da7a8a222f65aef1f1acc739c46282b/compiler/rustc_codegen_ssa/src/back/metadata.rs:83",
                        "rustc_codegen_ssa::back::metadata",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/0fc141305da7a8a222f65aef1f1acc739c46282b/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());
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                    ::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                })?;
89
90                match archive.members().exactly_one() {
91                    Ok(lib) => {
92                        let 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                        })?;
95                        let 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                        })?;
98                        get_metadata_xcoff(path, data)
99                    }
100                    Err(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 {
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        // 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.
121        return 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}
128
129fn add_gnu_property_note(
130    file: &mut write::Object<'static>,
131    architecture: Architecture,
132    endianness: Endianness,
133) {
134    // Only X86_64 and Aarch64 require a GNU property note.
135    if !#[allow(non_exhaustive_omitted_patterns)] match architecture {
    Architecture::X86_64 | Architecture::Aarch64 => true,
    _ => false,
}matches!(architecture, Architecture::X86_64 | Architecture::Aarch64) {
136        return;
137    }
138
139    let section = file.add_section(
140        file.segment_name(StandardSegment::Data).to_vec(),
141        b".note.gnu.property".to_vec(),
142        SectionKind::Note,
143    );
144    let mut data: Vec<u8> = Vec::new();
145    let n_namsz: u32 = 4; // Size of the n_name field
146    let n_descsz: u32 = 16; // Size of the n_desc field
147    let n_type: u32 = object::elf::NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
148    let header_values = [n_namsz, n_descsz, n_type];
149    header_values.iter().for_each(|v| {
150        data.extend_from_slice(&match endianness {
151            Endianness::Little => v.to_le_bytes(),
152            Endianness::Big => v.to_be_bytes(),
153        })
154    });
155    data.extend_from_slice(b"GNU\0"); // Owner of the program property note
156    let pr_type: u32 = match architecture {
157        Architecture::X86_64 => object::elf::GNU_PROPERTY_X86_FEATURE_1_AND,
158        Architecture::Aarch64 => object::elf::GNU_PROPERTY_AARCH64_FEATURE_1_AND,
159        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
160    };
161    let pr_datasz: u32 = 4; //size of the pr_data field
162    let pr_data: u32 = 3; //program property descriptor
163    let pr_padding: u32 = 0;
164    let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
165    property_values.iter().for_each(|v| {
166        data.extend_from_slice(&match endianness {
167            Endianness::Little => v.to_le_bytes(),
168            Endianness::Big => v.to_be_bytes(),
169        })
170    });
171    file.append_section_data(section, &data, 8);
172}
173
174pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
175    let Ok(file) = object::File::parse(data) else {
176        return Ok(data);
177    };
178    let info_data = search_for_section(path, data, ".info")?;
179    if let Some(metadata_symbol) =
180        file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
181    {
182        let offset = metadata_symbol.address() as usize;
183        // The offset specifies the location of rustc metadata in the .info section of XCOFF.
184        // Each string stored in .info section of XCOFF is preceded by a 4-byte length field.
185        if offset < 4 {
186            return Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Invalid metadata symbol offset: {0}",
                offset))
    })format!("Invalid metadata symbol offset: {offset}"));
187        }
188        // XCOFF format uses big-endian byte order.
189        let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
190        if offset + len > (info_data.len() as usize) {
191            return Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Metadata at offset {0} with size {1} is beyond .info section",
                offset, len))
    })format!(
192                "Metadata at offset {offset} with size {len} is beyond .info section"
193            ));
194        }
195        Ok(&info_data[offset..(offset + len)])
196    } else {
197        Err(::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}"))
198    }
199}
200
201pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
202    let endianness = match sess.target.options.endian {
203        Endian::Little => Endianness::Little,
204        Endian::Big => Endianness::Big,
205    };
206    let Some((architecture, sub_architecture)) =
207        sess.target.object_architecture(&sess.internal_target_features)
208    else {
209        return None;
210    };
211    let binary_format = sess.target.binary_format.to_object();
212
213    let mut file = write::Object::new(binary_format, architecture, endianness);
214    file.set_sub_architecture(sub_architecture);
215    if sess.target.is_like_darwin {
216        if macho_is_arm64e(&sess.target) {
217            file.set_macho_cpu_subtype(
218                object::macho::CPU_SUBTYPE_ARM64E | object::macho::CPU_SUBTYPE_PTRAUTH_ABI,
219            );
220        }
221
222        file.set_macho_build_version(macho_object_build_version_for_target(sess))
223    }
224    if binary_format == BinaryFormat::Coff {
225        // Disable the default mangler to avoid mangling the special "@feat.00" symbol name.
226        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            // When linking with /SAFESEH on x86, lld requires that all linker inputs be marked as
233            // safe exception handling compatible. Metadata files masquerade as regular COFF
234            // objects and are treated as linker inputs, despite containing no actual code. Thus,
235            // they still need to be marked as safe exception handling compatible. See #96498.
236            // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
237            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    if binary_format == BinaryFormat::Elf {
254        let e_flags = elf_e_flags(architecture, sess);
255        // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
256        let os_abi = elf_os_abi(sess);
257        let abi_version = 0;
258        add_gnu_property_note(&mut file, architecture, endianness);
259        file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
260    }
261    Some(file)
262}
263
264pub(super) fn elf_os_abi(sess: &Session) -> u8 {
265    match sess.target.options.os {
266        Os::Hermit => elf::ELFOSABI_STANDALONE,
267        Os::FreeBsd => elf::ELFOSABI_FREEBSD,
268        Os::Solaris => elf::ELFOSABI_SOLARIS,
269        _ => elf::ELFOSABI_NONE,
270    }
271}
272
273pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
274    match architecture {
275        Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => {
276            // "N32" indicates an "ILP32" data model on a 64-bit MIPS CPU
277            // like SPARC's "v8+", x86_64's "x32", or the watchOS "arm64_32".
278            let is_32bit = architecture == Architecture::Mips;
279            let mut e_flags = match sess.target.options.cpu.as_ref() {
280                "mips1" if is_32bit => elf::EF_MIPS_ARCH_1,
281                "mips2" if is_32bit => elf::EF_MIPS_ARCH_2,
282                "mips3" => elf::EF_MIPS_ARCH_3,
283                "mips4" => elf::EF_MIPS_ARCH_4,
284                "mips5" => elf::EF_MIPS_ARCH_5,
285                "mips32r2" if is_32bit => elf::EF_MIPS_ARCH_32R2,
286                "mips32r6" if is_32bit => elf::EF_MIPS_ARCH_32R6,
287                "mips64r2" if !is_32bit => elf::EF_MIPS_ARCH_64R2,
288                "mips64r6" if !is_32bit => elf::EF_MIPS_ARCH_64R6,
289                s if s.starts_with("mips32") && !is_32bit => {
290                    sess.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))
291                }
292                s if s.starts_with("mips64") && is_32bit => {
293                    sess.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))
294                }
295                _ if is_32bit => elf::EF_MIPS_ARCH_32R2,
296                _ => elf::EF_MIPS_ARCH_64R2,
297            };
298
299            // Use the explicitly given ABI.
300            match &sess.target.options.llvm_abiname {
301                LlvmAbi::O32 if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32,
302                LlvmAbi::N32 if !is_32bit => e_flags |= elf::EF_MIPS_ABI2,
303                LlvmAbi::N64 if !is_32bit => {}
304                // The rest is invalid (which is already ensured by the target spec check).
305                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),
306            };
307
308            if sess.target.options.relocation_model != RelocModel::Static {
309                // PIC means position-independent code. CPIC means "calls PIC".
310                // CPIC was mutually exclusive with PIC according to
311                // the SVR4 MIPS ABI https://refspecs.linuxfoundation.org/elf/mipsabi.pdf
312                // and should have only appeared on static objects with dynamically calls.
313                // At some point someone (GCC?) decided to set CPIC even for PIC.
314                // Nowadays various things expect both set on the same object file
315                // and may even error if you mix CPIC and non-CPIC object files,
316                // despite that being the entire point of the CPIC ABI extension!
317                // As we are in Rome, we do as the Romans do.
318                e_flags |= elf::EF_MIPS_PIC | elf::EF_MIPS_CPIC;
319            }
320            if sess.target.options.cpu.contains("r6") {
321                e_flags |= elf::EF_MIPS_NAN2008;
322            }
323            e_flags
324        }
325        Architecture::Riscv32 | Architecture::Riscv64 => {
326            // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
327            let mut e_flags: u32 = 0x0;
328
329            // Check if compression is enabled
330            if sess.internal_target_features.contains(&sym::zca) {
331                e_flags |= elf::EF_RISCV_RVC;
332            }
333
334            // Check if RVTSO is enabled
335            if sess.internal_target_features.contains(&sym::ztso) {
336                e_flags |= elf::EF_RISCV_TSO;
337            }
338
339            // Set the appropriate flag based on ABI
340            // This needs to match LLVM `RISCVELFStreamer.cpp`
341            match &sess.target.llvm_abiname {
342                LlvmAbi::Ilp32 | LlvmAbi::Lp64 => (),
343                LlvmAbi::Ilp32f | LlvmAbi::Lp64f => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
344                LlvmAbi::Ilp32d | LlvmAbi::Lp64d => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
345                // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI.
346                LlvmAbi::Ilp32e | LlvmAbi::Lp64e => e_flags |= elf::EF_RISCV_RVE,
347                _ => ::rustc_middle::util::bug::bug_fmt(format_args!("unknown RISC-V ABI name"))bug!("unknown RISC-V ABI name"),
348            }
349
350            e_flags
351        }
352        Architecture::LoongArch32 | Architecture::LoongArch64 => {
353            // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
354            let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
355
356            // Set the appropriate flag based on ABI
357            // This needs to match LLVM `LoongArchELFStreamer.cpp`
358            match &sess.target.llvm_abiname {
359                LlvmAbi::Ilp32s | LlvmAbi::Lp64s => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
360                LlvmAbi::Ilp32f | LlvmAbi::Lp64f => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
361                LlvmAbi::Ilp32d | LlvmAbi::Lp64d => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
362                _ => ::rustc_middle::util::bug::bug_fmt(format_args!("unknown LoongArch ABI name"))bug!("unknown LoongArch ABI name"),
363            }
364
365            e_flags
366        }
367        Architecture::Avr => {
368            // Resolve the ISA revision and set
369            // the appropriate EF_AVR_ARCH flag.
370            if let Some(ref cpu) = sess.opts.cg.target_cpu {
371                ef_avr_arch(cpu)
372            } else {
373                sess.dcx().emit_fatal(diagnostics::CpuRequired)
374            }
375        }
376        Architecture::Csky => {
377            if #[allow(non_exhaustive_omitted_patterns)] match sess.target.options.cfg_abi {
    CfgAbi::AbiV2 => true,
    _ => false,
}matches!(sess.target.options.cfg_abi, CfgAbi::AbiV2) {
378                elf::EF_CSKY_ABIV2
379            } else {
380                elf::EF_CSKY_ABIV1
381            }
382        }
383        Architecture::PowerPc64 => {
384            const EF_PPC64_ABI_ELF_V1: u32 = 1;
385            const EF_PPC64_ABI_ELF_V2: u32 = 2;
386
387            match sess.target.options.llvm_abiname {
388                // If the flags do not correctly indicate the ABI,
389                // linkers such as ld.lld assume that the ppc64 object files are always ELFv2
390                // which leads to broken binaries if ELFv1 is used for the object files.
391                LlvmAbi::ElfV1 => EF_PPC64_ABI_ELF_V1,
392                LlvmAbi::ElfV2 => EF_PPC64_ABI_ELF_V2,
393                _ => ::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"),
394            }
395        }
396        Architecture::Sparc32Plus => elf::EF_SPARC_32PLUS,
397        _ => 0,
398    }
399}
400
401/// Mach-O files contain information about:
402/// - The platform/OS they were built for (macOS/watchOS/Mac Catalyst/iOS simulator etc).
403/// - The minimum OS version / deployment target.
404/// - The version of the SDK they were targetting.
405///
406/// In the past, this was accomplished using the LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
407/// LC_VERSION_MIN_TVOS or LC_VERSION_MIN_WATCHOS load commands, which each contain information
408/// about the deployment target and SDK version, and implicitly, by their presence, which OS they
409/// target. Simulator targets were determined if the architecture was x86_64, but there was e.g. a
410/// LC_VERSION_MIN_IPHONEOS present.
411///
412/// This is of course brittle and limited, so modern tooling emit the LC_BUILD_VERSION load
413/// command (which contains all three pieces of information in one) when the deployment target is
414/// high enough, or the target is something that wouldn't be encodable with the old load commands
415/// (such as Mac Catalyst, or Aarch64 iOS simulator).
416///
417/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
418/// returns the `MachOBuildVersion` for the target to do so.
419fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
420    /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
421    /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
422    fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 {
423        let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
424        (major << 16) | (minor << 8) | patch
425    }
426
427    let platform = apple::macho_platform(&sess.target);
428    let min_os = sess.apple_deployment_target();
429
430    let mut build_version = object::write::MachOBuildVersion::default();
431    build_version.platform = platform;
432    build_version.minos = pack_version(min_os);
433    // The version here does not _really_ matter, since it is only used at runtime, and we specify
434    // it when linking the final binary, so we will omit the version. This is also what LLVM does,
435    // and the tooling also allows this (and shows the SDK version as `n/a`). Finally, it is the
436    // semantically correct choice, as the SDK has not influenced the binary generated by rustc at
437    // this point in time.
438    build_version.sdk = 0;
439
440    build_version
441}
442
443/// Is Apple's CPU subtype `arm64e`s
444fn macho_is_arm64e(target: &Target) -> bool {
445    target.llvm_target.starts_with("arm64e")
446}
447
448pub(crate) enum MetadataPosition {
449    First,
450    Last,
451}
452
453/// For rlibs we "pack" rustc metadata into a dummy object file.
454///
455/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
456/// In that case linkers try to include all files located in an archive, so if metadata is stored
457/// in an archive then it needs to be of a form that the linker is able to process.
458/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
459///
460/// Note, though, that we don't actually want this metadata to show up in any
461/// final output of the compiler. Instead this is purely for rustc's own
462/// metadata tracking purposes.
463///
464/// With the above in mind, each "flavor" of object format gets special
465/// handling here depending on the target:
466///
467/// * MachO - macos-like targets will insert the metadata into a section that
468///   is sort of fake dwarf debug info. Inspecting the source of the macos
469///   linker this causes these sections to be skipped automatically because
470///   it's not in an allowlist of otherwise well known dwarf section names to
471///   go into the final artifact.
472///
473/// * WebAssembly - this uses wasm files themselves as the object file format
474///   so an empty file with no linking metadata but a single custom section is
475///   created holding our metadata.
476///
477/// * COFF - Windows-like targets create an object with a section that has
478///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
479///   ever sees the section it doesn't process it and it's removed.
480///
481/// * ELF - All other targets are similar to Windows in that there's a
482///   `SHF_EXCLUDE` flag we can set on sections in an object file to get
483///   automatically removed from the final output.
484pub(crate) fn create_wrapper_file(
485    sess: &Session,
486    section_name: String,
487    data: &[u8],
488) -> (Vec<u8>, MetadataPosition) {
489    let Some(mut file) = create_object_file(sess) else {
490        if sess.target.is_like_wasm {
491            return (
492                create_metadata_file_for_wasm(sess, data, &section_name),
493                MetadataPosition::First,
494            );
495        }
496
497        // Targets using this branch don't have support implemented here yet or
498        // they're not yet implemented in the `object` crate and will likely
499        // fill out this module over time.
500        return (data.to_vec(), MetadataPosition::Last);
501    };
502    let section = if file.format() == BinaryFormat::Xcoff {
503        file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
504    } else {
505        file.add_section(
506            file.segment_name(StandardSegment::Debug).to_vec(),
507            section_name.into_bytes(),
508            SectionKind::Debug,
509        )
510    };
511    match file.format() {
512        BinaryFormat::Coff => {
513            file.section_mut(section).flags =
514                SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
515        }
516        BinaryFormat::Elf => {
517            file.section_mut(section).flags =
518                SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
519        }
520        BinaryFormat::Xcoff => {
521            // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
522            file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
523            file.section_mut(section).flags =
524                SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
525            // Encode string stored in .info section of XCOFF.
526            // FIXME: The length of data here is not guaranteed to fit in a u32.
527            // We may have to split the data into multiple pieces in order to
528            // store in .info section.
529            let len: u32 = data.len().try_into().unwrap();
530            let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
531            // Add a symbol referring to the data in .info section.
532            file.add_symbol(Symbol {
533                name: AIX_METADATA_SYMBOL_NAME.into(),
534                value: offset + 4,
535                size: 0,
536                kind: SymbolKind::Unknown,
537                scope: SymbolScope::Compilation,
538                weak: false,
539                section: SymbolSection::Section(section),
540                flags: SymbolFlags::Xcoff {
541                    n_sclass: xcoff::C_INFO,
542                    x_smtyp: xcoff::C_HIDEXT,
543                    x_smclas: xcoff::C_HIDEXT,
544                    containing_csect: None,
545                },
546            });
547        }
548        _ => {}
549    };
550    file.append_section_data(section, data, 1);
551    (file.write().unwrap(), MetadataPosition::First)
552}
553
554// Historical note:
555//
556// When using link.exe it was seen that the section name `.note.rustc`
557// was getting shortened to `.note.ru`, and according to the PE and COFF
558// specification:
559//
560// > Executable images do not use a string table and do not support
561// > section names longer than 8 characters
562//
563// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
564//
565// As a result, we choose a slightly shorter name! As to why
566// `.note.rustc` works on MinGW, see
567// https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
568pub fn create_compressed_metadata_file(
569    sess: &Session,
570    metadata: &EncodedMetadata,
571    symbol_name: &str,
572) -> Vec<u8> {
573    let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
574    packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap();
575    packed_metadata.extend(metadata.stub_or_full());
576
577    let Some(mut file) = create_object_file(sess) else {
578        if sess.target.is_like_wasm {
579            return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
580        }
581        return packed_metadata.to_vec();
582    };
583    if file.format() == BinaryFormat::Xcoff {
584        return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
585    }
586    let section = file.add_section(
587        file.segment_name(StandardSegment::Data).to_vec(),
588        b".rustc".to_vec(),
589        SectionKind::ReadOnlyData,
590    );
591    match file.format() {
592        BinaryFormat::Elf => {
593            // Explicitly set no flags to avoid SHF_ALLOC default for data section.
594            file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
595        }
596        _ => {}
597    };
598    let offset = file.append_section_data(section, &packed_metadata, 1);
599
600    // For MachO and probably PE this is necessary to prevent the linker from throwing away the
601    // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
602    file.add_symbol(Symbol {
603        name: symbol_name.as_bytes().to_vec(),
604        value: offset,
605        size: packed_metadata.len() as u64,
606        kind: SymbolKind::Data,
607        scope: SymbolScope::Dynamic,
608        weak: false,
609        section: SymbolSection::Section(section),
610        flags: SymbolFlags::None,
611    });
612
613    file.write().unwrap()
614}
615
616/// * Xcoff - On AIX, custom sections are merged into predefined sections,
617///   so custom .rustc section is not preserved during linking.
618///   For this reason, we store metadata in predefined .info section, and
619///   define a symbol to reference the metadata. To preserve metadata during
620///   linking on AIX, we have to
621///   1. Create an empty .text section, a empty .data section.
622///   2. Define an empty symbol named `symbol_name` inside .data section.
623///   3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
624///      data inside .info section.
625///   From XCOFF's view, (2) creates a csect entry in the symbol table, the
626///   symbol created by (3) is a info symbol for the preceding csect. Thus
627///   two symbols are preserved during linking and we can use the second symbol
628///   to reference the metadata.
629pub fn create_compressed_metadata_file_for_xcoff(
630    mut file: write::Object<'_>,
631    data: &[u8],
632    symbol_name: &str,
633) -> Vec<u8> {
634    if !(file.format() == BinaryFormat::Xcoff) {
    ::core::panicking::panic("assertion failed: file.format() == BinaryFormat::Xcoff")
};assert!(file.format() == BinaryFormat::Xcoff);
635    // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
636    file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
637    let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
638    let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
639    file.add_file_symbol("lib.rmeta".into());
640    file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
641    // Add a global symbol to data_section.
642    file.add_symbol(Symbol {
643        name: symbol_name.as_bytes().into(),
644        value: 0,
645        size: 0,
646        kind: SymbolKind::Data,
647        scope: SymbolScope::Dynamic,
648        weak: true,
649        section: SymbolSection::Section(data_section),
650        flags: SymbolFlags::None,
651    });
652    let len: u32 = data.len().try_into().unwrap();
653    let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
654    // Add a symbol referring to the rustc metadata.
655    file.add_symbol(Symbol {
656        name: AIX_METADATA_SYMBOL_NAME.into(),
657        value: offset + 4, // The metadata is preceded by a 4-byte length field.
658        size: 0,
659        kind: SymbolKind::Unknown,
660        scope: SymbolScope::Dynamic,
661        weak: false,
662        section: SymbolSection::Section(section),
663        flags: SymbolFlags::Xcoff {
664            n_sclass: xcoff::C_INFO,
665            x_smtyp: xcoff::C_HIDEXT,
666            x_smclas: xcoff::C_HIDEXT,
667            containing_csect: None,
668        },
669    });
670    file.append_section_data(section, data, 1);
671    file.write().unwrap()
672}
673
674/// Creates a simple WebAssembly object file, which is itself a wasm module,
675/// that contains a custom section of the name `section_name` with contents
676/// `data`.
677///
678/// NB: the `object` crate does not yet have support for writing the wasm
679/// object file format. In lieu of that the `wasm-encoder` crate is used to
680/// build a wasm file by hand.
681///
682/// The wasm object file format is defined at
683/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
684/// and mainly consists of a `linking` custom section. In this case the custom
685/// section there is empty except for a version marker indicating what format
686/// it's in.
687///
688/// The main purpose of this is to contain a custom section with `section_name`,
689/// which is then appended after `linking`.
690///
691/// As a further detail the object needs to have a 64-bit memory if `wasm64` is
692/// the target or otherwise it's interpreted as a 32-bit object which is
693/// incompatible with 64-bit ones.
694pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
695    if !sess.target.is_like_wasm {
    ::core::panicking::panic("assertion failed: sess.target.is_like_wasm")
};assert!(sess.target.is_like_wasm);
696    let mut module = wasm_encoder::Module::new();
697    let mut imports = wasm_encoder::ImportSection::new();
698
699    if sess.target.pointer_width == 64 {
700        imports.import(
701            "env",
702            "__linear_memory",
703            wasm_encoder::MemoryType {
704                minimum: 0,
705                maximum: None,
706                memory64: true,
707                shared: false,
708                page_size_log2: None,
709            },
710        );
711    }
712
713    if imports.len() > 0 {
714        module.section(&imports);
715    }
716    module.section(&wasm_encoder::CustomSection {
717        name: "linking".into(),
718        data: Cow::Borrowed(&[2]),
719    });
720    module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
721    module.finish()
722}