]>
Commit | Line | Data |
---|---|---|
17df50a5 XL |
1 | //! Reading of the rustc metadata for rlibs and dylibs |
2 | ||
3 | use std::fs::File; | |
a2a8927a | 4 | use std::io::Write; |
17df50a5 XL |
5 | use std::path::Path; |
6 | ||
a2a8927a XL |
7 | use object::write::{self, StandardSegment, Symbol, SymbolSection}; |
8 | use object::{ | |
9 | elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, | |
10 | SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, | |
11 | }; | |
12 | ||
13 | use snap::write::FrameEncoder; | |
14 | ||
17df50a5 XL |
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; | |
064997fb | 19 | use rustc_metadata::fs::METADATA_FILENAME; |
a2a8927a | 20 | use rustc_metadata::EncodedMetadata; |
c295e0f8 | 21 | use rustc_session::cstore::MetadataLoader; |
a2a8927a XL |
22 | use rustc_session::Session; |
23 | use rustc_target::abi::Endian; | |
04454e1e | 24 | use rustc_target::spec::{RelocModel, Target}; |
17df50a5 | 25 | |
17df50a5 XL |
26 | /// The default metadata loader. This is used by cg_llvm and cg_clif. |
27 | /// | |
28 | /// # Metadata location | |
29 | /// | |
30 | /// <dl> | |
31 | /// <dt>rlib</dt> | |
32 | /// <dd>The metadata can be found in the `lib.rmeta` file inside of the ar archive.</dd> | |
33 | /// <dt>dylib</dt> | |
34 | /// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd> | |
35 | /// </dl> | |
36 | pub struct DefaultMetadataLoader; | |
37 | ||
38 | fn load_metadata_with( | |
39 | path: &Path, | |
40 | f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, | |
41 | ) -> Result<MetadataRef, String> { | |
42 | let file = | |
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())); | |
48 | } | |
49 | ||
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))?; | |
55 | ||
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() { | |
60 | let data = entry | |
61 | .data(data) | |
62 | .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; | |
63 | return search_for_metadata(path, data, ".rmeta"); | |
64 | } | |
65 | } | |
66 | ||
67 | Err(format!("metadata not found in rlib '{}'", path.display())) | |
68 | }) | |
69 | } | |
70 | ||
71 | fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> { | |
72 | load_metadata_with(path, |data| search_for_metadata(path, data, ".rustc")) | |
73 | } | |
74 | } | |
75 | ||
76 | fn search_for_metadata<'a>( | |
77 | path: &Path, | |
78 | bytes: &'a [u8], | |
79 | section: &str, | |
80 | ) -> Result<&'a [u8], String> { | |
5e7ed085 | 81 | let Ok(file) = object::File::parse(bytes) else { |
17df50a5 XL |
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. | |
5e7ed085 | 88 | return Ok(bytes); |
17df50a5 XL |
89 | }; |
90 | file.section_by_name(section) | |
91 | .ok_or_else(|| format!("no `{}` section in '{}'", section, path.display()))? | |
92 | .data() | |
93 | .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e)) | |
94 | } | |
a2a8927a | 95 | |
04454e1e | 96 | pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> { |
a2a8927a XL |
97 | let endianness = match sess.target.options.endian { |
98 | Endian::Little => Endianness::Little, | |
99 | Endian::Big => Endianness::Big, | |
100 | }; | |
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, | |
108 | "x86_64" => { | |
109 | if sess.target.pointer_width == 32 { | |
110 | Architecture::X86_64_X32 | |
111 | } else { | |
112 | Architecture::X86_64 | |
113 | } | |
114 | } | |
115 | "powerpc" => Architecture::PowerPc, | |
116 | "powerpc64" => Architecture::PowerPc64, | |
117 | "riscv32" => Architecture::Riscv32, | |
118 | "riscv64" => Architecture::Riscv64, | |
119 | "sparc64" => Architecture::Sparc64, | |
f2b60f7d FG |
120 | "avr" => Architecture::Avr, |
121 | "msp430" => Architecture::Msp430, | |
122 | "hexagon" => Architecture::Hexagon, | |
123 | "bpf" => Architecture::Bpf, | |
a2a8927a XL |
124 | // Unsupported architecture. |
125 | _ => return None, | |
126 | }; | |
127 | let binary_format = if sess.target.is_like_osx { | |
128 | BinaryFormat::MachO | |
129 | } else if sess.target.is_like_windows { | |
130 | BinaryFormat::Coff | |
131 | } else { | |
132 | BinaryFormat::Elf | |
133 | }; | |
134 | ||
135 | let mut file = write::Object::new(binary_format, architecture, endianness); | |
923072b8 | 136 | let e_flags = match architecture { |
a2a8927a | 137 | Architecture::Mips => { |
04454e1e FG |
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, | |
146 | }; | |
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; | |
151 | } | |
152 | if sess.target.options.cpu.contains("r6") { | |
153 | e_flags |= elf::EF_MIPS_NAN2008; | |
154 | } | |
923072b8 | 155 | e_flags |
a2a8927a XL |
156 | } |
157 | Architecture::Mips64 => { | |
158 | // copied from `mips64el-linux-gnuabi64-gcc foo.c -c` | |
5099ac24 FG |
159 | let e_flags = elf::EF_MIPS_CPIC |
160 | | elf::EF_MIPS_PIC | |
161 | | if sess.target.options.cpu.contains("r6") { | |
162 | elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008 | |
163 | } else { | |
164 | elf::EF_MIPS_ARCH_64R2 | |
165 | }; | |
923072b8 | 166 | e_flags |
a2a8927a XL |
167 | } |
168 | Architecture::Riscv64 if sess.target.options.features.contains("+d") => { | |
169 | // copied from `riscv64-linux-gnu-gcc foo.c -c`, note though | |
170 | // that the `+d` target feature represents whether the double | |
171 | // float abi is enabled. | |
172 | let e_flags = elf::EF_RISCV_RVC | elf::EF_RISCV_FLOAT_ABI_DOUBLE; | |
923072b8 | 173 | e_flags |
a2a8927a | 174 | } |
923072b8 | 175 | _ => 0, |
a2a8927a | 176 | }; |
923072b8 FG |
177 | // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI` |
178 | let os_abi = match sess.target.options.os.as_ref() { | |
179 | "hermit" => elf::ELFOSABI_STANDALONE, | |
180 | "freebsd" => elf::ELFOSABI_FREEBSD, | |
181 | "solaris" => elf::ELFOSABI_SOLARIS, | |
182 | _ => elf::ELFOSABI_NONE, | |
183 | }; | |
184 | let abi_version = 0; | |
185 | file.flags = FileFlags::Elf { os_abi, abi_version, e_flags }; | |
a2a8927a XL |
186 | Some(file) |
187 | } | |
188 | ||
5e7ed085 FG |
189 | pub enum MetadataPosition { |
190 | First, | |
191 | Last, | |
192 | } | |
193 | ||
f2b60f7d FG |
194 | // For rlibs we "pack" rustc metadata into a dummy object file. |
195 | // | |
196 | // Historically it was needed because rustc linked rlibs as whole-archive in some cases. | |
197 | // In that case linkers try to include all files located in an archive, so if metadata is stored | |
198 | // in an archive then it needs to be of a form that the linker is able to process. | |
199 | // Now it's not clear whether metadata still needs to be wrapped into an object file or not. | |
a2a8927a XL |
200 | // |
201 | // Note, though, that we don't actually want this metadata to show up in any | |
202 | // final output of the compiler. Instead this is purely for rustc's own | |
203 | // metadata tracking purposes. | |
204 | // | |
205 | // With the above in mind, each "flavor" of object format gets special | |
206 | // handling here depending on the target: | |
207 | // | |
208 | // * MachO - macos-like targets will insert the metadata into a section that | |
209 | // is sort of fake dwarf debug info. Inspecting the source of the macos | |
210 | // linker this causes these sections to be skipped automatically because | |
211 | // it's not in an allowlist of otherwise well known dwarf section names to | |
212 | // go into the final artifact. | |
213 | // | |
214 | // * WebAssembly - we actually don't have any container format for this | |
215 | // target. WebAssembly doesn't support the `dylib` crate type anyway so | |
216 | // there's no need for us to support this at this time. Consequently the | |
217 | // metadata bytes are simply stored as-is into an rlib. | |
218 | // | |
219 | // * COFF - Windows-like targets create an object with a section that has | |
220 | // the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker | |
221 | // ever sees the section it doesn't process it and it's removed. | |
222 | // | |
223 | // * ELF - All other targets are similar to Windows in that there's a | |
224 | // `SHF_EXCLUDE` flag we can set on sections in an object file to get | |
225 | // automatically removed from the final output. | |
5e7ed085 | 226 | pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataPosition) { |
5099ac24 | 227 | let Some(mut file) = create_object_file(sess) else { |
a2a8927a XL |
228 | // This is used to handle all "other" targets. This includes targets |
229 | // in two categories: | |
230 | // | |
231 | // * Some targets don't have support in the `object` crate just yet | |
232 | // to write an object file. These targets are likely to get filled | |
233 | // out over time. | |
234 | // | |
235 | // * Targets like WebAssembly don't support dylibs, so the purpose | |
236 | // of putting metadata in object files, to support linking rlibs | |
237 | // into dylibs, is moot. | |
238 | // | |
239 | // In both of these cases it means that linking into dylibs will | |
240 | // not be supported by rustc. This doesn't matter for targets like | |
241 | // WebAssembly and for targets not supported by the `object` crate | |
242 | // yet it means that work will need to be done in the `object` crate | |
243 | // to add a case above. | |
5e7ed085 | 244 | return (metadata.to_vec(), MetadataPosition::Last); |
a2a8927a XL |
245 | }; |
246 | let section = file.add_section( | |
247 | file.segment_name(StandardSegment::Debug).to_vec(), | |
248 | b".rmeta".to_vec(), | |
249 | SectionKind::Debug, | |
250 | ); | |
251 | match file.format() { | |
252 | BinaryFormat::Coff => { | |
253 | file.section_mut(section).flags = | |
254 | SectionFlags::Coff { characteristics: pe::IMAGE_SCN_LNK_REMOVE }; | |
255 | } | |
256 | BinaryFormat::Elf => { | |
257 | file.section_mut(section).flags = | |
258 | SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 }; | |
259 | } | |
260 | _ => {} | |
261 | }; | |
262 | file.append_section_data(section, metadata, 1); | |
5e7ed085 | 263 | (file.write().unwrap(), MetadataPosition::First) |
a2a8927a XL |
264 | } |
265 | ||
266 | // Historical note: | |
267 | // | |
268 | // When using link.exe it was seen that the section name `.note.rustc` | |
269 | // was getting shortened to `.note.ru`, and according to the PE and COFF | |
270 | // specification: | |
271 | // | |
272 | // > Executable images do not use a string table and do not support | |
273 | // > section names longer than 8 characters | |
274 | // | |
275 | // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format | |
276 | // | |
277 | // As a result, we choose a slightly shorter name! As to why | |
278 | // `.note.rustc` works on MinGW, see | |
279 | // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197 | |
280 | pub fn create_compressed_metadata_file( | |
281 | sess: &Session, | |
282 | metadata: &EncodedMetadata, | |
283 | symbol_name: &str, | |
284 | ) -> Vec<u8> { | |
285 | let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); | |
286 | FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap(); | |
5099ac24 | 287 | let Some(mut file) = create_object_file(sess) else { |
a2a8927a XL |
288 | return compressed.to_vec(); |
289 | }; | |
290 | let section = file.add_section( | |
291 | file.segment_name(StandardSegment::Data).to_vec(), | |
292 | b".rustc".to_vec(), | |
293 | SectionKind::ReadOnlyData, | |
294 | ); | |
295 | match file.format() { | |
296 | BinaryFormat::Elf => { | |
297 | // Explicitly set no flags to avoid SHF_ALLOC default for data section. | |
298 | file.section_mut(section).flags = SectionFlags::Elf { sh_flags: 0 }; | |
299 | } | |
300 | _ => {} | |
301 | }; | |
302 | let offset = file.append_section_data(section, &compressed, 1); | |
303 | ||
304 | // For MachO and probably PE this is necessary to prevent the linker from throwing away the | |
305 | // .rustc section. For ELF this isn't necessary, but it also doesn't harm. | |
306 | file.add_symbol(Symbol { | |
307 | name: symbol_name.as_bytes().to_vec(), | |
308 | value: offset, | |
309 | size: compressed.len() as u64, | |
310 | kind: SymbolKind::Data, | |
311 | scope: SymbolScope::Dynamic, | |
312 | weak: false, | |
313 | section: SymbolSection::Section(section), | |
314 | flags: SymbolFlags::None, | |
315 | }); | |
316 | ||
317 | file.write().unwrap() | |
318 | } |