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::{RelocModel, Target, ef_avr_arch};
24use tracing::debug;
25
26use super::apple;
27
28/// The default metadata loader. This is used by cg_llvm and cg_clif.
29///
30/// # Metadata location
31///
32/// <dl>
33/// <dt>rlib</dt>
34/// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
35/// <dt>dylib</dt>
36/// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
37/// </dl>
38#[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        // The parse above could fail for odd reasons like corruption, but for
115        // now we just interpret it as this target doesn't support metadata
116        // emission in object files so the entire byte slice itself is probably
117        // a metadata file. Ideally though if necessary we could at least check
118        // the prefix of bytes to see if it's an actual metadata object and if
119        // not forward the error along here.
120        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    // check bti protection
135    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; // Size of the n_name field
148    let n_descsz: u32 = 16; // Size of the n_desc field
149    let n_type: u32 = object::elf::NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
150    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"); // Owner of the program property note
158    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; //size of the pr_data field
164    let pr_data: u32 = 3; //program property descriptor
165    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        // The offset specifies the location of rustc metadata in the .info section of XCOFF.
186        // Each string stored in .info section of XCOFF is preceded by a 4-byte length field.
187        if offset < 4 {
188            return Err(format!("Invalid metadata symbol offset: {offset}"));
189        }
190        // XCOFF format uses big-endian byte order.
191        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        // 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    let e_flags = elf_e_flags(architecture, sess);
254    // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
255    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            // If the ABI is explicitly given, use it or default to O32.
287            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            // copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
303            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            // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
314            let mut e_flags: u32 = 0x0;
315
316            // Check if compressed is enabled
317            // `unstable_target_features` is used here because "c" is gated behind riscv_target_feature.
318            if sess.unstable_target_features.contains(&sym::c) {
319                e_flags |= elf::EF_RISCV_RVC;
320            }
321
322            // Set the appropriate flag based on ABI
323            // This needs to match LLVM `RISCVELFStreamer.cpp`
324            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                // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI.
329                "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            // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
337            let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
338
339            // Set the appropriate flag based on ABI
340            // This needs to match LLVM `LoongArchELFStreamer.cpp`
341            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            // Resolve the ISA revision and set
352            // the appropriate EF_AVR_ARCH flag.
353            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
370/// Mach-O files contain information about:
371/// - The platform/OS they were built for (macOS/watchOS/Mac Catalyst/iOS simulator etc).
372/// - The minimum OS version / deployment target.
373/// - The version of the SDK they were targetting.
374///
375/// In the past, this was accomplished using the LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
376/// LC_VERSION_MIN_TVOS or LC_VERSION_MIN_WATCHOS load commands, which each contain information
377/// about the deployment target and SDK version, and implicitly, by their presence, which OS they
378/// target. Simulator targets were determined if the architecture was x86_64, but there was e.g. a
379/// LC_VERSION_MIN_IPHONEOS present.
380///
381/// This is of course brittle and limited, so modern tooling emit the LC_BUILD_VERSION load
382/// command (which contains all three pieces of information in one) when the deployment target is
383/// high enough, or the target is something that wouldn't be encodable with the old load commands
384/// (such as Mac Catalyst, or Aarch64 iOS simulator).
385///
386/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
387/// returns the `MachOBuildVersion` for the target to do so.
388fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
389    /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
390    /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
391    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    // The version here does not _really_ matter, since it is only used at runtime, and we specify
403    // it when linking the final binary, so we will omit the version. This is also what LLVM does,
404    // and the tooling also allows this (and shows the SDK version as `n/a`). Finally, it is the
405    // semantically correct choice, as the SDK has not influenced the binary generated by rustc at
406    // this point in time.
407    build_version.sdk = 0;
408
409    build_version
410}
411
412/// Is Apple's CPU subtype `arm64e`s
413fn 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
422/// For rlibs we "pack" rustc metadata into a dummy object file.
423///
424/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
425/// In that case linkers try to include all files located in an archive, so if metadata is stored
426/// in an archive then it needs to be of a form that the linker is able to process.
427/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
428///
429/// Note, though, that we don't actually want this metadata to show up in any
430/// final output of the compiler. Instead this is purely for rustc's own
431/// metadata tracking purposes.
432///
433/// With the above in mind, each "flavor" of object format gets special
434/// handling here depending on the target:
435///
436/// * MachO - macos-like targets will insert the metadata into a section that
437///   is sort of fake dwarf debug info. Inspecting the source of the macos
438///   linker this causes these sections to be skipped automatically because
439///   it's not in an allowlist of otherwise well known dwarf section names to
440///   go into the final artifact.
441///
442/// * WebAssembly - this uses wasm files themselves as the object file format
443///   so an empty file with no linking metadata but a single custom section is
444///   created holding our metadata.
445///
446/// * COFF - Windows-like targets create an object with a section that has
447///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
448///   ever sees the section it doesn't process it and it's removed.
449///
450/// * ELF - All other targets are similar to Windows in that there's a
451///   `SHF_EXCLUDE` flag we can set on sections in an object file to get
452///   automatically removed from the final output.
453pub(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, &section_name),
462                MetadataPosition::First,
463            );
464        }
465
466        // Targets using this branch don't have support implemented here yet or
467        // they're not yet implemented in the `object` crate and will likely
468        // fill out this module over time.
469        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            // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
491            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            // Encode string stored in .info section of XCOFF.
495            // FIXME: The length of data here is not guaranteed to fit in a u32.
496            // We may have to split the data into multiple pieces in order to
497            // store in .info section.
498            let len: u32 = data.len().try_into().unwrap();
499            let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
500            // Add a symbol referring to the data in .info section.
501            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
523// Historical note:
524//
525// When using link.exe it was seen that the section name `.note.rustc`
526// was getting shortened to `.note.ru`, and according to the PE and COFF
527// specification:
528//
529// > Executable images do not use a string table and do not support
530// > section names longer than 8 characters
531//
532// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
533//
534// As a result, we choose a slightly shorter name! As to why
535// `.note.rustc` works on MinGW, see
536// https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
537pub 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            // Explicitly set no flags to avoid SHF_ALLOC default for data section.
563            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    // For MachO and probably PE this is necessary to prevent the linker from throwing away the
570    // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
571    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
585/// * Xcoff - On AIX, custom sections are merged into predefined sections,
586///   so custom .rustc section is not preserved during linking.
587///   For this reason, we store metadata in predefined .info section, and
588///   define a symbol to reference the metadata. To preserve metadata during
589///   linking on AIX, we have to
590///   1. Create an empty .text section, a empty .data section.
591///   2. Define an empty symbol named `symbol_name` inside .data section.
592///   3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
593///      data inside .info section.
594///   From XCOFF's view, (2) creates a csect entry in the symbol table, the
595///   symbol created by (3) is a info symbol for the preceding csect. Thus
596///   two symbols are preserved during linking and we can use the second symbol
597///   to reference the metadata.
598pub 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    // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
605    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    // Add a global symbol to data_section.
611    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    // Add a symbol referring to the rustc metadata.
624    file.add_symbol(Symbol {
625        name: AIX_METADATA_SYMBOL_NAME.into(),
626        value: offset + 4, // The metadata is preceded by a 4-byte length field.
627        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
643/// Creates a simple WebAssembly object file, which is itself a wasm module,
644/// that contains a custom section of the name `section_name` with contents
645/// `data`.
646///
647/// NB: the `object` crate does not yet have support for writing the wasm
648/// object file format. In lieu of that the `wasm-encoder` crate is used to
649/// build a wasm file by hand.
650///
651/// The wasm object file format is defined at
652/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
653/// and mainly consists of a `linking` custom section. In this case the custom
654/// section there is empty except for a version marker indicating what format
655/// it's in.
656///
657/// The main purpose of this is to contain a custom section with `section_name`,
658/// which is then appended after `linking`.
659///
660/// As a further detail the object needs to have a 64-bit memory if `wasm64` is
661/// the target or otherwise it's interpreted as a 32-bit object which is
662/// incompatible with 64-bit ones.
663pub 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}