1 //! Reading of the rustc metadata for rlibs and dylibs
7 use object::write::{self, StandardSegment, Symbol, SymbolSection};
9 elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
10 SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
13 use snap::write::FrameEncoder;
15 use rustc_data_structures::memmap::Mmap;
16 use rustc_data_structures::owning_ref::OwningRef;
17 use rustc_data_structures::rustc_erase_owner;
18 use rustc_data_structures::sync::MetadataRef;
19 use rustc_metadata::fs::METADATA_FILENAME;
20 use rustc_metadata::EncodedMetadata;
21 use rustc_session::cstore::MetadataLoader;
22 use rustc_session::Session;
23 use rustc_target::abi::Endian;
24 use rustc_target::spec::{RelocModel, Target};
26 /// The default metadata loader. This is used by cg_llvm and cg_clif.
28 /// # Metadata location
32 /// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd>
34 /// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd>
36 pub struct DefaultMetadataLoader;
38 fn load_metadata_with(
40 f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
41 ) -> Result<MetadataRef, String> {
43 File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?;
44 let data = unsafe { Mmap::map(file) }
45 .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))?;
46 let metadata = OwningRef::new(data).try_map(f)?;
47 return Ok(rustc_erase_owner!(metadata.map_owner_box()));
50 impl MetadataLoader for DefaultMetadataLoader {
51 fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
52 load_metadata_with(path, |data| {
53 let archive = object::read::archive::ArchiveFile::parse(&*data)
54 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
56 for entry_result in archive.members() {
57 let entry = entry_result
58 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
59 if entry.name() == METADATA_FILENAME.as_bytes() {
62 .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
63 return search_for_section(path, data, ".rmeta");
67 Err(format!("metadata not found in rlib '{}'", path.display()))
71 fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
72 load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
76 pub(super) fn search_for_section<'a>(
80 ) -> Result<&'a [u8], String> {
81 let Ok(file) = object::File::parse(bytes) else {
82 // The parse above could fail for odd reasons like corruption, but for
83 // now we just interpret it as this target doesn't support metadata
84 // emission in object files so the entire byte slice itself is probably
85 // a metadata file. Ideally though if necessary we could at least check
86 // the prefix of bytes to see if it's an actual metadata object and if
87 // not forward the error along here.
90 file.section_by_name(section)
91 .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))?
93 .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
96 pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
97 let endianness = match sess.target.options.endian {
98 Endian::Little => Endianness::Little,
99 Endian::Big => Endianness::Big,
101 let architecture = match &sess.target.arch[..] {
102 "arm" => Architecture::Arm,
103 "aarch64" => Architecture::Aarch64,
104 "x86" => Architecture::I386,
105 "s390x" => Architecture::S390x,
106 "mips" => Architecture::Mips,
107 "mips64" => Architecture::Mips64,
109 if sess.target.pointer_width == 32 {
110 Architecture::X86_64_X32
115 "powerpc" => Architecture::PowerPc,
116 "powerpc64" => Architecture::PowerPc64,
117 "riscv32" => Architecture::Riscv32,
118 "riscv64" => Architecture::Riscv64,
119 "sparc64" => Architecture::Sparc64,
120 "avr" => Architecture::Avr,
121 "msp430" => Architecture::Msp430,
122 "hexagon" => Architecture::Hexagon,
123 "bpf" => Architecture::Bpf,
124 // Unsupported architecture.
127 let binary_format = if sess.target.is_like_osx {
129 } else if sess.target.is_like_windows {
135 let mut file = write::Object::new(binary_format, architecture, endianness);
136 let e_flags = match architecture {
137 Architecture::Mips => {
138 let arch = match sess.target.options.cpu.as_ref() {
139 "mips1" => elf::EF_MIPS_ARCH_1,
140 "mips2" => elf::EF_MIPS_ARCH_2,
141 "mips3" => elf::EF_MIPS_ARCH_3,
142 "mips4" => elf::EF_MIPS_ARCH_4,
143 "mips5" => elf::EF_MIPS_ARCH_5,
144 s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6,
145 _ => elf::EF_MIPS_ARCH_32R2,
147 // The only ABI LLVM supports for 32-bit MIPS CPUs is o32.
148 let mut e_flags = elf::EF_MIPS_CPIC | elf::EF_MIPS_ABI_O32 | arch;
149 if sess.target.options.relocation_model != RelocModel::Static {
150 e_flags |= elf::EF_MIPS_PIC;
152 if sess.target.options.cpu.contains("r6") {
153 e_flags |= elf::EF_MIPS_NAN2008;
157 Architecture::Mips64 => {
158 // copied from `mips64el-linux-gnuabi64-gcc foo.c -c`
159 let e_flags = elf::EF_MIPS_CPIC
161 | if sess.target.options.cpu.contains("r6") {
162 elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008
164 elf::EF_MIPS_ARCH_64R2
168 Architecture::Riscv32 | Architecture::Riscv64 => {
169 // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
170 let mut e_flags: u32 = 0x0;
171 let features = &sess.target.options.features;
172 // Check if compressed is enabled
173 if features.contains("+c") {
174 e_flags |= elf::EF_RISCV_RVC;
177 // Select the appropriate floating-point ABI
178 if features.contains("+d") {
179 e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
180 } else if features.contains("+f") {
181 e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
183 e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
189 // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
190 let os_abi = match sess.target.options.os.as_ref() {
191 "hermit" => elf::ELFOSABI_STANDALONE,
192 "freebsd" => elf::ELFOSABI_FREEBSD,
193 "solaris" => elf::ELFOSABI_SOLARIS,
194 _ => elf::ELFOSABI_NONE,
197 file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
201 pub enum MetadataPosition {
206 /// For rlibs we "pack" rustc metadata into a dummy object file.
208 /// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
209 /// In that case linkers try to include all files located in an archive, so if metadata is stored
210 /// in an archive then it needs to be of a form that the linker is able to process.
211 /// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
213 /// Note, though, that we don't actually want this metadata to show up in any
214 /// final output of the compiler. Instead this is purely for rustc's own
215 /// metadata tracking purposes.
217 /// With the above in mind, each "flavor" of object format gets special
218 /// handling here depending on the target:
220 /// * MachO - macos-like targets will insert the metadata into a section that
221 /// is sort of fake dwarf debug info. Inspecting the source of the macos
222 /// linker this causes these sections to be skipped automatically because
223 /// it's not in an allowlist of otherwise well known dwarf section names to
224 /// go into the final artifact.
226 /// * WebAssembly - we actually don't have any container format for this
227 /// target. WebAssembly doesn't support the `dylib` crate type anyway so
228 /// there's no need for us to support this at this time. Consequently the
229 /// metadata bytes are simply stored as-is into an rlib.
231 /// * COFF - Windows-like targets create an object with a section that has
232 /// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
233 /// ever sees the section it doesn't process it and it's removed.
235 /// * ELF - All other targets are similar to Windows in that there's a
236 /// `SHF_EXCLUDE` flag we can set on sections in an object file to get
237 /// automatically removed from the final output.
238 pub fn create_wrapper_file(
240 section_name: Vec<u8>,
242 ) -> (Vec<u8>, MetadataPosition) {
243 let Some(mut file) = create_object_file(sess) else {
244 // This is used to handle all "other" targets. This includes targets
245 // in two categories:
247 // * Some targets don't have support in the `object` crate just yet
248 // to write an object file. These targets are likely to get filled
251 // * Targets like WebAssembly don't support dylibs, so the purpose
252 // of putting metadata in object files, to support linking rlibs
253 // into dylibs, is moot.
255 // In both of these cases it means that linking into dylibs will
256 // not be supported by rustc. This doesn't matter for targets like
257 // WebAssembly and for targets not supported by the `object` crate
258 // yet it means that work will need to be done in the `object` crate
259 // to add a case above.
260 return (data.to_vec(), MetadataPosition::Last);
262 let section = file.add_section(
263 file.segment_name(StandardSegment::Debug).to_vec(),
267 match file.format() {
268 BinaryFormat::Coff => {
269 file.section_mut(section).flags =
270 SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE };
272 BinaryFormat::Elf => {
273 file.section_mut(section).flags =
274 SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
278 file.append_section_data(section, data, 1);
279 (file.write().unwrap(), MetadataPosition::First)
284 // When using link.exe it was seen that the section name `.note.rustc`
285 // was getting shortened to `.note.ru`, and according to the PE and COFF
288 // > Executable images do not use a string table and do not support
289 // > section names longer than 8 characters
291 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
293 // As a result, we choose a slightly shorter name! As to why
294 // `.note.rustc` works on MinGW, see
295 // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
296 pub fn create_compressed_metadata_file(
298 metadata: &EncodedMetadata,
301 let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
302 FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
303 let Some(mut file) = create_object_file(sess) else {
304 return compressed.to_vec();
306 let section = file.add_section(
307 file.segment_name(StandardSegment::Data).to_vec(),
309 SectionKind::ReadOnlyData,
311 match file.format() {
312 BinaryFormat::Elf => {
313 // Explicitly set no flags to avoid SHF_ALLOC default for data section.
314 file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 };
318 let offset = file.append_section_data(section, &compressed, 1);
320 // For MachO and probably PE this is necessary to prevent the linker from throwing away the
321 // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
322 file.add_symbol(Symbol {
323 name: symbol_name.as_bytes().to_vec(),
325 size: compressed.len() as u64,
326 kind: SymbolKind::Data,
327 scope: SymbolScope::Dynamic,
329 section: SymbolSection::Section(section),
330 flags: SymbolFlags::None,
333 file.write().unwrap()