• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Reading of the rustc metadata for rlibs and dylibs
2 
3 use std::fs::File;
4 use std::io::Write;
5 use std::path::Path;
6 
7 use object::write::{self, StandardSegment, Symbol, SymbolSection};
8 use object::{
9     elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
10     ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
11 };
12 
13 use snap::write::FrameEncoder;
14 
15 use object::elf::NT_GNU_PROPERTY_TYPE_0;
16 use rustc_data_structures::memmap::Mmap;
17 use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
18 use rustc_metadata::fs::METADATA_FILENAME;
19 use rustc_metadata::EncodedMetadata;
20 use rustc_session::cstore::MetadataLoader;
21 use rustc_session::Session;
22 use rustc_target::abi::Endian;
23 use rustc_target::spec::{ef_avr_arch, RelocModel, Target};
24 
25 /// The default metadata loader. This is used by cg_llvm and cg_clif.
26 ///
27 /// # Metadata location
28 ///
29 /// <dl>
30 /// <dt>rlib</dt>
31 /// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
32 /// <dt>dylib</dt>
33 /// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
34 /// </dl>
35 #[derive(Debug)]
36 pub struct DefaultMetadataLoader;
37 
38 static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
39 
load_metadata_with( path: &Path, f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, ) -> Result<OwnedSlice, String>40 fn load_metadata_with(
41     path: &Path,
42     f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
43 ) -> Result<OwnedSlice, String> {
44     let file =
45         File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
46 
47     unsafe { Mmap::map(file) }
48         .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))
49         .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap)))
50 }
51 
52 impl MetadataLoader for DefaultMetadataLoader {
get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String>53     fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
54         load_metadata_with(path, |data| {
55             let archive = object::read::archive::ArchiveFile::parse(&*data)
56                 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
57 
58             for entry_result in archive.members() {
59                 let entry = entry_result
60                     .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
61                 if entry.name() == METADATA_FILENAME.as_bytes() {
62                     let data = entry
63                         .data(data)
64                         .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
65                     if target.is_like_aix {
66                         return get_metadata_xcoff(path, data);
67                     } else {
68                         return search_for_section(path, data, ".rmeta");
69                     }
70                 }
71             }
72 
73             Err(format!("metadata not found in rlib '{}'", path.display()))
74         })
75     }
76 
get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String>77     fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
78         if target.is_like_aix {
79             load_metadata_with(path, |data| get_metadata_xcoff(path, data))
80         } else {
81             load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
82         }
83     }
84 }
85 
search_for_section<'a>( path: &Path, bytes: &'a [u8], section: &str, ) -> Result<&'a [u8], String>86 pub(super) fn search_for_section<'a>(
87     path: &Path,
88     bytes: &'a [u8],
89     section: &str,
90 ) -> Result<&'a [u8], String> {
91     let Ok(file) = object::File::parse(bytes) else {
92         // The parse above could fail for odd reasons like corruption, but for
93         // now we just interpret it as this target doesn't support metadata
94         // emission in object files so the entire byte slice itself is probably
95         // a metadata file. Ideally though if necessary we could at least check
96         // the prefix of bytes to see if it's an actual metadata object and if
97         // not forward the error along here.
98         return Ok(bytes);
99     };
100     file.section_by_name(section)
101         .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))?
102         .data()
103         .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
104 }
105 
add_gnu_property_note( file: &mut write::Object<'static>, architecture: Architecture, binary_format: BinaryFormat, endianness: Endianness, )106 fn add_gnu_property_note(
107     file: &mut write::Object<'static>,
108     architecture: Architecture,
109     binary_format: BinaryFormat,
110     endianness: Endianness,
111 ) {
112     // check bti protection
113     if binary_format != BinaryFormat::Elf
114         || !matches!(architecture, Architecture::X86_64 | Architecture::Aarch64)
115     {
116         return;
117     }
118 
119     let section = file.add_section(
120         file.segment_name(StandardSegment::Data).to_vec(),
121         b".note.gnu.property".to_vec(),
122         SectionKind::Note,
123     );
124     let mut data: Vec<u8> = Vec::new();
125     let n_namsz: u32 = 4; // Size of the n_name field
126     let n_descsz: u32 = 16; // Size of the n_desc field
127     let n_type: u32 = NT_GNU_PROPERTY_TYPE_0; // Type of note descriptor
128     let header_values = [n_namsz, n_descsz, n_type];
129     header_values.iter().for_each(|v| {
130         data.extend_from_slice(&match endianness {
131             Endianness::Little => v.to_le_bytes(),
132             Endianness::Big => v.to_be_bytes(),
133         })
134     });
135     data.extend_from_slice(b"GNU\0"); // Owner of the program property note
136     let pr_type: u32 = match architecture {
137         Architecture::X86_64 => 0xc0000002,
138         Architecture::Aarch64 => 0xc0000000,
139         _ => unreachable!(),
140     };
141     let pr_datasz: u32 = 4; //size of the pr_data field
142     let pr_data: u32 = 3; //program property descriptor
143     let pr_padding: u32 = 0;
144     let property_values = [pr_type, pr_datasz, pr_data, pr_padding];
145     property_values.iter().for_each(|v| {
146         data.extend_from_slice(&match endianness {
147             Endianness::Little => v.to_le_bytes(),
148             Endianness::Big => v.to_be_bytes(),
149         })
150     });
151     file.append_section_data(section, &data, 8);
152 }
153 
get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String>154 pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
155     let Ok(file) = object::File::parse(data) else {
156         return Ok(data);
157     };
158     let info_data = search_for_section(path, data, ".info")?;
159     if let Some(metadata_symbol) =
160         file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
161     {
162         let offset = metadata_symbol.address() as usize;
163         if offset < 4 {
164             return Err(format!("Invalid metadata symbol offset: {}", offset));
165         }
166         // The offset specifies the location of rustc metadata in the comment section.
167         // The metadata is preceded by a 4-byte length field.
168         let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
169         if offset + len > (info_data.len() as usize) {
170             return Err(format!(
171                 "Metadata at offset {} with size {} is beyond .info section",
172                 offset, len
173             ));
174         }
175         return Ok(&info_data[offset..(offset + len)]);
176     } else {
177         return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME));
178     };
179 }
180 
create_object_file(sess: &Session) -> Option<write::Object<'static>>181 pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
182     let endianness = match sess.target.options.endian {
183         Endian::Little => Endianness::Little,
184         Endian::Big => Endianness::Big,
185     };
186     let architecture = match &sess.target.arch[..] {
187         "arm" => Architecture::Arm,
188         "aarch64" => {
189             if sess.target.pointer_width == 32 {
190                 Architecture::Aarch64_Ilp32
191             } else {
192                 Architecture::Aarch64
193             }
194         }
195         "x86" => Architecture::I386,
196         "s390x" => Architecture::S390x,
197         "mips" => Architecture::Mips,
198         "mips64" => Architecture::Mips64,
199         "x86_64" => {
200             if sess.target.pointer_width == 32 {
201                 Architecture::X86_64_X32
202             } else {
203                 Architecture::X86_64
204             }
205         }
206         "powerpc" => Architecture::PowerPc,
207         "powerpc64" => Architecture::PowerPc64,
208         "riscv32" => Architecture::Riscv32,
209         "riscv64" => Architecture::Riscv64,
210         "sparc64" => Architecture::Sparc64,
211         "avr" => Architecture::Avr,
212         "msp430" => Architecture::Msp430,
213         "hexagon" => Architecture::Hexagon,
214         "bpf" => Architecture::Bpf,
215         "loongarch64" => Architecture::LoongArch64,
216         // Unsupported architecture.
217         _ => return None,
218     };
219     let binary_format = if sess.target.is_like_osx {
220         BinaryFormat::MachO
221     } else if sess.target.is_like_windows {
222         BinaryFormat::Coff
223     } else if sess.target.is_like_aix {
224         BinaryFormat::Xcoff
225     } else {
226         BinaryFormat::Elf
227     };
228 
229     let mut file = write::Object::new(binary_format, architecture, endianness);
230     if sess.target.is_like_osx {
231         if let Some(build_version) = macho_object_build_version_for_target(&sess.target) {
232             file.set_macho_build_version(build_version)
233         }
234     }
235     let e_flags = match architecture {
236         Architecture::Mips => {
237             let arch = match sess.target.options.cpu.as_ref() {
238                 "mips1" => elf::EF_MIPS_ARCH_1,
239                 "mips2" => elf::EF_MIPS_ARCH_2,
240                 "mips3" => elf::EF_MIPS_ARCH_3,
241                 "mips4" => elf::EF_MIPS_ARCH_4,
242                 "mips5" => elf::EF_MIPS_ARCH_5,
243                 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
244                 _ => elf::EF_MIPS_ARCH_32R2,
245             };
246             // The only ABI LLVM supports for 32-bit MIPS CPUs is o32.
247             let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch;
248             if sess.target.options.relocation_model != RelocModel::Static {
249                 e_flags |= elf::EF_MIPS_PIC;
250             }
251             if sess.target.options.cpu.contains("r6") {
252                 e_flags |= elf::EF_MIPS_NAN2008;
253             }
254             e_flags
255         }
256         Architecture::Mips64 => {
257             // copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
258             let e_flags = elf::EF_MIPS_CPIC
259                 | elf::EF_MIPS_PIC
260                 | if sess.target.options.cpu.contains("r6") {
261                     elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
262                 } else {
263                     elf::EF_MIPS_ARCH_64R2
264                 };
265             e_flags
266         }
267         Architecture::Riscv32 | Architecture::Riscv64 => {
268             // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
269             let mut e_flags: u32 = 0x0;
270             let features = &sess.target.options.features;
271             // Check if compressed is enabled
272             if features.contains("+c") {
273                 e_flags |= elf::EF_RISCV_RVC;
274             }
275 
276             // Select the appropriate floating-point ABI
277             if features.contains("+d") {
278                 e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
279             } else if features.contains("+f") {
280                 e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
281             } else {
282                 e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
283             }
284             e_flags
285         }
286         Architecture::LoongArch64 => {
287             // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
288             let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
289             let features = &sess.target.options.features;
290 
291             // Select the appropriate floating-point ABI
292             if features.contains("+d") {
293                 e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT;
294             } else if features.contains("+f") {
295                 e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT;
296             } else {
297                 e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT;
298             }
299             e_flags
300         }
301         Architecture::Avr => {
302             // Resolve the ISA revision and set
303             // the appropriate EF_AVR_ARCH flag.
304             ef_avr_arch(&sess.target.options.cpu)
305         }
306         _ => 0,
307     };
308     // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
309     let os_abi = match sess.target.options.os.as_ref() {
310         "hermit" => elf::ELFOSABI_STANDALONE,
311         "freebsd" => elf::ELFOSABI_FREEBSD,
312         "solaris" => elf::ELFOSABI_SOLARIS,
313         _ => elf::ELFOSABI_NONE,
314     };
315     let abi_version = 0;
316     add_gnu_property_note(&mut file, architecture, binary_format, endianness);
317     file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
318     Some(file)
319 }
320 
321 /// Apple's LD, when linking for Mac Catalyst, requires object files to
322 /// contain information about what they were built for (LC_BUILD_VERSION):
323 /// the platform (macOS/watchOS etc), minimum OS version, and SDK version.
324 /// This returns a `MachOBuildVersion` if necessary for the target.
macho_object_build_version_for_target( target: &Target, ) -> Option<object::write::MachOBuildVersion>325 fn macho_object_build_version_for_target(
326     target: &Target,
327 ) -> Option<object::write::MachOBuildVersion> {
328     if !target.llvm_target.ends_with("-macabi") {
329         return None;
330     }
331     /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
332     /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
333     fn pack_version((major, minor): (u32, u32)) -> u32 {
334         (major << 16) | (minor << 8)
335     }
336 
337     let platform = object::macho::PLATFORM_MACCATALYST;
338     let min_os = (14, 0);
339     let sdk = (16, 2);
340 
341     let mut build_version = object::write::MachOBuildVersion::default();
342     build_version.platform = platform;
343     build_version.minos = pack_version(min_os);
344     build_version.sdk = pack_version(sdk);
345     Some(build_version)
346 }
347 
348 pub enum MetadataPosition {
349     First,
350     Last,
351 }
352 
353 /// For rlibs we "pack" rustc metadata into a dummy object file.
354 ///
355 /// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
356 /// In that case linkers try to include all files located in an archive, so if metadata is stored
357 /// in an archive then it needs to be of a form that the linker is able to process.
358 /// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
359 ///
360 /// Note, though, that we don't actually want this metadata to show up in any
361 /// final output of the compiler. Instead this is purely for rustc's own
362 /// metadata tracking purposes.
363 ///
364 /// With the above in mind, each "flavor" of object format gets special
365 /// handling here depending on the target:
366 ///
367 /// * MachO - macos-like targets will insert the metadata into a section that
368 ///   is sort of fake dwarf debug info. Inspecting the source of the macos
369 ///   linker this causes these sections to be skipped automatically because
370 ///   it's not in an allowlist of otherwise well known dwarf section names to
371 ///   go into the final artifact.
372 ///
373 /// * WebAssembly - we actually don't have any container format for this
374 ///   target. WebAssembly doesn't support the `dylib` crate type anyway so
375 ///   there's no need for us to support this at this time. Consequently the
376 ///   metadata bytes are simply stored as-is into an rlib.
377 ///
378 /// * COFF - Windows-like targets create an object with a section that has
379 ///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
380 ///   ever sees the section it doesn't process it and it's removed.
381 ///
382 /// * ELF - All other targets are similar to Windows in that there's a
383 ///   `SHF_EXCLUDE` flag we can set on sections in an object file to get
384 ///   automatically removed from the final output.
create_wrapper_file( sess: &Session, section_name: Vec<u8>, data: &[u8], ) -> (Vec<u8>, MetadataPosition)385 pub fn create_wrapper_file(
386     sess: &Session,
387     section_name: Vec<u8>,
388     data: &[u8],
389 ) -> (Vec<u8>, MetadataPosition) {
390     let Some(mut file) = create_object_file(sess) else {
391         // This is used to handle all "other" targets. This includes targets
392         // in two categories:
393         //
394         // * Some targets don't have support in the `object` crate just yet
395         //   to write an object file. These targets are likely to get filled
396         //   out over time.
397         //
398         // * Targets like WebAssembly don't support dylibs, so the purpose
399         //   of putting metadata in object files, to support linking rlibs
400         //   into dylibs, is moot.
401         //
402         // In both of these cases it means that linking into dylibs will
403         // not be supported by rustc. This doesn't matter for targets like
404         // WebAssembly and for targets not supported by the `object` crate
405         // yet it means that work will need to be done in the `object` crate
406         // to add a case above.
407         return (data.to_vec(), MetadataPosition::Last);
408     };
409     let section = if file.format() == BinaryFormat::Xcoff {
410         file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
411     } else {
412         file.add_section(
413             file.segment_name(StandardSegment::Debug).to_vec(),
414             section_name,
415             SectionKind::Debug,
416         )
417     };
418     match file.format() {
419         BinaryFormat::Coff => {
420             file.section_mut(section).flags =
421                 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
422         }
423         BinaryFormat::Elf => {
424             file.section_mut(section).flags =
425                 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
426         }
427         BinaryFormat::Xcoff => {
428             // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
429             file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
430             file.section_mut(section).flags =
431                 SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
432 
433             let len = data.len() as u32;
434             let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
435             // Add a symbol referring to the data in .info section.
436             file.add_symbol(Symbol {
437                 name: AIX_METADATA_SYMBOL_NAME.into(),
438                 value: offset + 4,
439                 size: 0,
440                 kind: SymbolKind::Unknown,
441                 scope: SymbolScope::Compilation,
442                 weak: false,
443                 section: SymbolSection::Section(section),
444                 flags: SymbolFlags::Xcoff {
445                     n_sclass: xcoff::C_INFO,
446                     x_smtyp: xcoff::C_HIDEXT,
447                     x_smclas: xcoff::C_HIDEXT,
448                     containing_csect: None,
449                 },
450             });
451         }
452         _ => {}
453     };
454     file.append_section_data(section, data, 1);
455     (file.write().unwrap(), MetadataPosition::First)
456 }
457 
458 // Historical note:
459 //
460 // When using link.exe it was seen that the section name `.note.rustc`
461 // was getting shortened to `.note.ru`, and according to the PE and COFF
462 // specification:
463 //
464 // > Executable images do not use a string table and do not support
465 // > section names longer than 8 characters
466 //
467 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
468 //
469 // As a result, we choose a slightly shorter name! As to why
470 // `.note.rustc` works on MinGW, see
471 // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
create_compressed_metadata_file( sess: &Session, metadata: &EncodedMetadata, symbol_name: &str, ) -> Vec<u8>472 pub fn create_compressed_metadata_file(
473     sess: &Session,
474     metadata: &EncodedMetadata,
475     symbol_name: &str,
476 ) -> Vec<u8> {
477     let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
478     // Our length will be backfilled once we're done writing
479     compressed.write_all(&[0; 4]).unwrap();
480     FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
481     let meta_len = rustc_metadata::METADATA_HEADER.len();
482     let data_len = (compressed.len() - meta_len - 4) as u32;
483     compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes());
484 
485     let Some(mut file) = create_object_file(sess) else {
486         return compressed.to_vec();
487     };
488     if file.format() == BinaryFormat::Xcoff {
489         return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
490     }
491     let section = file.add_section(
492         file.segment_name(StandardSegment::Data).to_vec(),
493         b".rustc".to_vec(),
494         SectionKind::ReadOnlyData,
495     );
496     match file.format() {
497         BinaryFormat::Elf => {
498             // Explicitly set no flags to avoid SHF_ALLOC default for data section.
499             file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
500         }
501         _ => {}
502     };
503     let offset = file.append_section_data(section, &compressed, 1);
504 
505     // For MachO and probably PE this is necessary to prevent the linker from throwing away the
506     // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
507     file.add_symbol(Symbol {
508         name: symbol_name.as_bytes().to_vec(),
509         value: offset,
510         size: compressed.len() as u64,
511         kind: SymbolKind::Data,
512         scope: SymbolScope::Dynamic,
513         weak: false,
514         section: SymbolSection::Section(section),
515         flags: SymbolFlags::None,
516     });
517 
518     file.write().unwrap()
519 }
520 
521 /// * Xcoff - On AIX, custom sections are merged into predefined sections,
522 ///   so custom .rustc section is not preserved during linking.
523 ///   For this reason, we store metadata in predefined .info section, and
524 ///   define a symbol to reference the metadata. To preserve metadata during
525 ///   linking on AIX, we have to
526 ///   1. Create an empty .text section, a empty .data section.
527 ///   2. Define an empty symbol named `symbol_name` inside .data section.
528 ///   3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
529 ///      data inside .info section.
530 ///   From XCOFF's view, (2) creates a csect entry in the symbol table, the
531 ///   symbol created by (3) is a info symbol for the preceding csect. Thus
532 ///   two symbols are preserved during linking and we can use the second symbol
533 ///   to reference the metadata.
create_compressed_metadata_file_for_xcoff( mut file: write::Object<'_>, data: &[u8], symbol_name: &str, ) -> Vec<u8>534 pub fn create_compressed_metadata_file_for_xcoff(
535     mut file: write::Object<'_>,
536     data: &[u8],
537     symbol_name: &str,
538 ) -> Vec<u8> {
539     assert!(file.format() == BinaryFormat::Xcoff);
540     // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
541     file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
542     let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
543     let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
544     file.add_file_symbol("lib.rmeta".into());
545     file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
546     // Add a global symbol to data_section.
547     file.add_symbol(Symbol {
548         name: symbol_name.as_bytes().into(),
549         value: 0,
550         size: 0,
551         kind: SymbolKind::Data,
552         scope: SymbolScope::Dynamic,
553         weak: true,
554         section: SymbolSection::Section(data_section),
555         flags: SymbolFlags::None,
556     });
557     let len = data.len() as u32;
558     let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
559     // Add a symbol referring to the rustc metadata.
560     file.add_symbol(Symbol {
561         name: AIX_METADATA_SYMBOL_NAME.into(),
562         value: offset + 4, // The metadata is preceded by a 4-byte length field.
563         size: 0,
564         kind: SymbolKind::Unknown,
565         scope: SymbolScope::Dynamic,
566         weak: false,
567         section: SymbolSection::Section(section),
568         flags: SymbolFlags::Xcoff {
569             n_sclass: xcoff::C_INFO,
570             x_smtyp: xcoff::C_HIDEXT,
571             x_smclas: xcoff::C_HIDEXT,
572             containing_csect: None,
573         },
574     });
575     file.append_section_data(section, data, 1);
576     file.write().unwrap()
577 }
578