]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_llvm/src/back/archive.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / back / archive.rs
CommitLineData
c1a9b12d
SL
1//! A helper class for dealing with static archives
2
dfeec247 3use std::ffi::{CStr, CString};
c1a9b12d
SL
4use std::io;
5use std::mem;
6use std::path::{Path, PathBuf};
e9174d1e 7use std::ptr;
c1a9b12d
SL
8use std::str;
9
9fa01778 10use crate::llvm::archive_ro::{ArchiveRO, Child};
17df50a5 11use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
dfeec247 12use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
f9f354fc 13use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
17df50a5 14use rustc_data_structures::temp_dir::MaybeTempDir;
136023e0 15use rustc_middle::middle::cstore::{DllCallingConvention, DllImport};
ba9703b0 16use rustc_session::Session;
dfeec247 17use rustc_span::symbol::Symbol;
c1a9b12d 18
48663c56 19struct ArchiveConfig<'a> {
c1a9b12d
SL
20 pub sess: &'a Session,
21 pub dst: PathBuf,
22 pub src: Option<PathBuf>,
23 pub lib_search_paths: Vec<PathBuf>,
c1a9b12d
SL
24}
25
abe05a73 26/// Helper for adding many files to an archive.
c1a9b12d 27#[must_use = "must call build() to finish building the archive"]
48663c56 28pub struct LlvmArchiveBuilder<'a> {
c1a9b12d 29 config: ArchiveConfig<'a>,
c1a9b12d
SL
30 removals: Vec<String>,
31 additions: Vec<Addition>,
32 should_update_symbols: bool,
33 src_archive: Option<Option<ArchiveRO>>,
34}
35
36enum Addition {
dfeec247
XL
37 File { path: PathBuf, name_in_archive: String },
38 Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> },
c1a9b12d
SL
39}
40
416331ca
XL
41impl Addition {
42 fn path(&self) -> &Path {
43 match self {
44 Addition::File { path, .. } | Addition::Archive { path, .. } => path,
45 }
46 }
47}
48
9fa01778 49fn is_relevant_child(c: &Child<'_>) -> bool {
c1a9b12d
SL
50 match c.name() {
51 Some(name) => !name.contains("SYMDEF"),
52 None => false,
53 }
54}
55
dfeec247 56fn archive_config<'a>(sess: &'a Session, output: &Path, input: Option<&Path>) -> ArchiveConfig<'a> {
48663c56
XL
57 use rustc_codegen_ssa::back::link::archive_search_paths;
58 ArchiveConfig {
59 sess,
60 dst: output.to_path_buf(),
61 src: input.map(|p| p.to_path_buf()),
62 lib_search_paths: archive_search_paths(sess),
63 }
64}
65
17df50a5
XL
66/// Map machine type strings to values of LLVM's MachineTypes enum.
67fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
68 match cpu {
69 "x86_64" => LLVMMachineType::AMD64,
70 "x86" => LLVMMachineType::I386,
71 "aarch64" => LLVMMachineType::ARM64,
72 "arm" => LLVMMachineType::ARM,
73 _ => panic!("unsupported cpu type {}", cpu),
74 }
75}
76
48663c56 77impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
9fa01778 78 /// Creates a new static archive, ready for modifying the archive specified
c1a9b12d 79 /// by `config`.
dfeec247 80 fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> LlvmArchiveBuilder<'a> {
48663c56
XL
81 let config = archive_config(sess, output, input);
82 LlvmArchiveBuilder {
3b2f2976 83 config,
c1a9b12d
SL
84 removals: Vec::new(),
85 additions: Vec::new(),
86 should_update_symbols: false,
87 src_archive: None,
88 }
89 }
90
91 /// Removes a file from this archive
48663c56 92 fn remove_file(&mut self, file: &str) {
c1a9b12d
SL
93 self.removals.push(file.to_string());
94 }
95
96 /// Lists all files in an archive
48663c56 97 fn src_files(&mut self) -> Vec<String> {
c1a9b12d 98 if self.src_archive().is_none() {
dfeec247 99 return Vec::new();
c1a9b12d 100 }
a1dfa0c6 101
c1a9b12d 102 let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap();
a1dfa0c6 103
dfeec247
XL
104 archive
105 .iter()
106 .filter_map(|child| child.ok())
107 .filter(is_relevant_child)
108 .filter_map(|child| child.name())
109 .filter(|name| !self.removals.iter().any(|x| x == name))
110 .map(|name| name.to_owned())
111 .collect()
c1a9b12d
SL
112 }
113
c1a9b12d
SL
114 /// Adds all of the contents of a native library to this archive. This will
115 /// search in the relevant locations for a library named `name`.
17df50a5
XL
116 fn add_native_library(&mut self, name: Symbol, verbatim: bool) {
117 let location =
118 find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess);
3157f602 119 self.add_archive(&location, |_| false).unwrap_or_else(|e| {
dfeec247
XL
120 self.config.sess.fatal(&format!(
121 "failed to add native library {}: {}",
122 location.to_string_lossy(),
123 e
124 ));
b039eaaf 125 });
c1a9b12d
SL
126 }
127
128 /// Adds all of the contents of the rlib at the specified path to this
129 /// archive.
130 ///
131 /// This ignores adding the bytecode from the rlib, and if LTO is enabled
132 /// then the object file also isn't added.
dfeec247
XL
133 fn add_rlib(
134 &mut self,
135 rlib: &Path,
136 name: &str,
137 lto: bool,
138 skip_objects: bool,
139 ) -> io::Result<()> {
c1a9b12d
SL
140 // Ignoring obj file starting with the crate name
141 // as simple comparison is not enough - there
142 // might be also an extra name suffix
8faf50e0 143 let obj_start = name.to_owned();
c1a9b12d 144
3157f602 145 self.add_archive(rlib, move |fname: &str| {
f9f354fc
XL
146 // Ignore metadata files, no matter the name.
147 if fname == METADATA_FILENAME {
dfeec247 148 return true;
476ff2be
SL
149 }
150
151 // Don't include Rust objects if LTO is enabled
e74abb32 152 if lto && looks_like_rust_object_file(fname) {
dfeec247 153 return true;
476ff2be
SL
154 }
155
156 // Otherwise if this is *not* a rust object and we're skipping
157 // objects then skip this file
158 if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
dfeec247 159 return true;
476ff2be
SL
160 }
161
162 // ok, don't skip this
ba9703b0 163 false
c1a9b12d
SL
164 })
165 }
166
c1a9b12d 167 /// Adds an arbitrary file to this archive
48663c56 168 fn add_file(&mut self, file: &Path) {
c1a9b12d 169 let name = file.file_name().unwrap().to_str().unwrap();
dfeec247
XL
170 self.additions
171 .push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
c1a9b12d
SL
172 }
173
abe05a73
XL
174 /// Indicate that the next call to `build` should update all symbols in
175 /// the archive (equivalent to running 'ar s' over it).
48663c56 176 fn update_symbols(&mut self) {
c1a9b12d
SL
177 self.should_update_symbols = true;
178 }
179
180 /// Combine the provided files, rlibs, and native libraries into a single
181 /// `Archive`.
48663c56 182 fn build(mut self) {
dfeec247
XL
183 let kind = self.llvm_archive_kind().unwrap_or_else(|kind| {
184 self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", kind))
185 });
c1a9b12d 186
3157f602
XL
187 if let Err(e) = self.build_with_llvm(kind) {
188 self.config.sess.fatal(&format!("failed to build archive: {}", e));
c1a9b12d 189 }
c1a9b12d 190 }
17df50a5
XL
191
192 fn inject_dll_import_lib(
193 &mut self,
194 lib_name: &str,
195 dll_imports: &[DllImport],
196 tmpdir: &MaybeTempDir,
197 ) {
198 let output_path = {
199 let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf();
200 output_path.push(format!("{}_imports", lib_name));
201 output_path.with_extension("lib")
202 };
203
204 // we've checked for \0 characters in the library name already
205 let dll_name_z = CString::new(lib_name).unwrap();
206 // All import names are Rust identifiers and therefore cannot contain \0 characters.
207 // FIXME: when support for #[link_name] implemented, ensure that import.name values don't
208 // have any \0 characters
209 let import_name_vector: Vec<CString> = dll_imports
210 .iter()
136023e0
XL
211 .map(|import: &DllImport| {
212 if self.config.sess.target.arch == "x86" {
213 LlvmArchiveBuilder::i686_decorated_name(import)
214 } else {
215 CString::new(import.name.to_string()).unwrap()
216 }
17df50a5
XL
217 })
218 .collect();
219
220 let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
221
222 tracing::trace!("invoking LLVMRustWriteImportLibrary");
223 tracing::trace!(" dll_name {:#?}", dll_name_z);
224 tracing::trace!(" output_path {}", output_path.display());
225 tracing::trace!(
226 " import names: {}",
227 dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "),
228 );
229
230 let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector
231 .iter()
232 .map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr()))
233 .collect();
234 let result = unsafe {
235 crate::llvm::LLVMRustWriteImportLibrary(
236 dll_name_z.as_ptr(),
237 output_path_z.as_ptr(),
238 ffi_exports.as_ptr(),
239 ffi_exports.len(),
240 llvm_machine_type(&self.config.sess.target.arch) as u16,
241 !self.config.sess.target.is_like_msvc,
242 )
243 };
244
245 if result == crate::llvm::LLVMRustResult::Failure {
246 self.config.sess.fatal(&format!(
247 "Error creating import library for {}: {}",
248 lib_name,
249 llvm::last_error().unwrap_or("unknown LLVM error".to_string())
250 ));
251 }
252
253 self.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
254 self.config.sess.fatal(&format!(
255 "failed to add native library {}: {}",
256 output_path.display(),
257 e
258 ));
259 });
260 }
48663c56
XL
261}
262
263impl<'a> LlvmArchiveBuilder<'a> {
264 fn src_archive(&mut self) -> Option<&ArchiveRO> {
265 if let Some(ref a) = self.src_archive {
dfeec247 266 return a.as_ref();
48663c56
XL
267 }
268 let src = self.config.src.as_ref()?;
269 self.src_archive = Some(ArchiveRO::open(src).ok());
270 self.src_archive.as_ref().unwrap().as_ref()
271 }
272
dfeec247
XL
273 fn add_archive<F>(&mut self, archive: &Path, skip: F) -> io::Result<()>
274 where
275 F: FnMut(&str) -> bool + 'static,
48663c56 276 {
416331ca 277 let archive_ro = match ArchiveRO::open(archive) {
48663c56
XL
278 Ok(ar) => ar,
279 Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
280 };
416331ca 281 if self.additions.iter().any(|ar| ar.path() == archive) {
dfeec247 282 return Ok(());
416331ca 283 }
48663c56 284 self.additions.push(Addition::Archive {
416331ca
XL
285 path: archive.to_path_buf(),
286 archive: archive_ro,
48663c56
XL
287 skip: Box::new(skip),
288 });
289 Ok(())
290 }
c1a9b12d 291
3157f602 292 fn llvm_archive_kind(&self) -> Result<ArchiveKind, &str> {
29967ef6 293 let kind = &*self.config.sess.target.archive_format;
3157f602 294 kind.parse().map_err(|_| kind)
c1a9b12d
SL
295 }
296
297 fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> {
416331ca
XL
298 let removals = mem::take(&mut self.removals);
299 let mut additions = mem::take(&mut self.additions);
c1a9b12d
SL
300 let mut strings = Vec::new();
301 let mut members = Vec::new();
b7449926
XL
302
303 let dst = CString::new(self.config.dst.to_str().unwrap())?;
304 let should_update_symbols = self.should_update_symbols;
c1a9b12d
SL
305
306 unsafe {
307 if let Some(archive) = self.src_archive() {
308 for child in archive.iter() {
54a0048b 309 let child = child.map_err(string_to_io_error)?;
c1a9b12d
SL
310 let child_name = match child.name() {
311 Some(s) => s,
312 None => continue,
313 };
314 if removals.iter().any(|r| r == child_name) {
dfeec247 315 continue;
c1a9b12d
SL
316 }
317
54a0048b 318 let name = CString::new(child_name)?;
dfeec247
XL
319 members.push(llvm::LLVMRustArchiveMemberNew(
320 ptr::null(),
321 name.as_ptr(),
322 Some(child.raw),
323 ));
c1a9b12d
SL
324 strings.push(name);
325 }
326 }
b7449926 327 for addition in &mut additions {
c1a9b12d
SL
328 match addition {
329 Addition::File { path, name_in_archive } => {
54a0048b 330 let path = CString::new(path.to_str().unwrap())?;
b7449926 331 let name = CString::new(name_in_archive.clone())?;
dfeec247
XL
332 members.push(llvm::LLVMRustArchiveMemberNew(
333 path.as_ptr(),
334 name.as_ptr(),
335 None,
336 ));
c1a9b12d
SL
337 strings.push(path);
338 strings.push(name);
339 }
416331ca 340 Addition::Archive { archive, skip, .. } => {
7453a54e 341 for child in archive.iter() {
54a0048b 342 let child = child.map_err(string_to_io_error)?;
7453a54e 343 if !is_relevant_child(&child) {
dfeec247 344 continue;
7453a54e 345 }
c1a9b12d 346 let child_name = child.name().unwrap();
7453a54e 347 if skip(child_name) {
dfeec247 348 continue;
7453a54e
SL
349 }
350
351 // It appears that LLVM's archive writer is a little
352 // buggy if the name we pass down isn't just the
353 // filename component, so chop that off here and
354 // pass it in.
355 //
356 // See LLVM bug 25877 for more info.
dfeec247
XL
357 let child_name =
358 Path::new(child_name).file_name().unwrap().to_str().unwrap();
54a0048b 359 let name = CString::new(child_name)?;
dfeec247
XL
360 let m = llvm::LLVMRustArchiveMemberNew(
361 ptr::null(),
362 name.as_ptr(),
363 Some(child.raw),
364 );
c1a9b12d
SL
365 members.push(m);
366 strings.push(name);
367 }
c1a9b12d
SL
368 }
369 }
370 }
371
dfeec247
XL
372 let r = llvm::LLVMRustWriteArchive(
373 dst.as_ptr(),
374 members.len() as libc::size_t,
375 members.as_ptr() as *const &_,
376 should_update_symbols,
377 kind,
378 );
5bcae85e 379 let ret = if r.into_result().is_err() {
c1a9b12d
SL
380 let err = llvm::LLVMRustGetLastError();
381 let msg = if err.is_null() {
a1dfa0c6 382 "failed to write archive".into()
c1a9b12d
SL
383 } else {
384 String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
c1a9b12d
SL
385 };
386 Err(io::Error::new(io::ErrorKind::Other, msg))
387 } else {
388 Ok(())
389 };
390 for member in members {
391 llvm::LLVMRustArchiveMemberFree(member);
392 }
a1dfa0c6 393 ret
c1a9b12d
SL
394 }
395 }
136023e0
XL
396
397 fn i686_decorated_name(import: &DllImport) -> CString {
398 let name = import.name;
399 // We verified during construction that `name` does not contain any NULL characters, so the
400 // conversion to CString is guaranteed to succeed.
401 CString::new(match import.calling_convention {
402 DllCallingConvention::C => format!("_{}", name),
403 DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size),
404 DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size),
405 DllCallingConvention::Vectorcall(arg_list_size) => {
406 format!("{}@@{}", name, arg_list_size)
407 }
408 })
409 .unwrap()
410 }
c1a9b12d 411}
7453a54e
SL
412
413fn string_to_io_error(s: String) -> io::Error {
414 io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
415}