1 //! A helper class for dealing with static archives
3 use std
::ffi
::{CStr, CString}
;
6 use std
::path
::{Path, PathBuf}
;
10 use crate::llvm
::archive_ro
::{ArchiveRO, Child}
;
11 use crate::llvm
::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}
;
12 use rustc_codegen_ssa
::back
::archive
::{find_library, ArchiveBuilder}
;
13 use rustc_codegen_ssa
::{looks_like_rust_object_file, METADATA_FILENAME}
;
14 use rustc_data_structures
::temp_dir
::MaybeTempDir
;
15 use rustc_middle
::middle
::cstore
::DllImport
;
16 use rustc_session
::Session
;
17 use rustc_span
::symbol
::Symbol
;
19 struct ArchiveConfig
<'a
> {
20 pub sess
: &'a Session
,
22 pub src
: Option
<PathBuf
>,
23 pub lib_search_paths
: Vec
<PathBuf
>,
26 /// Helper for adding many files to an archive.
27 #[must_use = "must call build() to finish building the archive"]
28 pub struct LlvmArchiveBuilder
<'a
> {
29 config
: ArchiveConfig
<'a
>,
30 removals
: Vec
<String
>,
31 additions
: Vec
<Addition
>,
32 should_update_symbols
: bool
,
33 src_archive
: Option
<Option
<ArchiveRO
>>,
37 File { path: PathBuf, name_in_archive: String }
,
38 Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> }
,
42 fn path(&self) -> &Path
{
44 Addition
::File { path, .. }
| Addition
::Archive { path, .. }
=> path
,
49 fn is_relevant_child(c
: &Child
<'_
>) -> bool
{
51 Some(name
) => !name
.contains("SYMDEF"),
56 fn archive_config
<'a
>(sess
: &'a Session
, output
: &Path
, input
: Option
<&Path
>) -> ArchiveConfig
<'a
> {
57 use rustc_codegen_ssa
::back
::link
::archive_search_paths
;
60 dst
: output
.to_path_buf(),
61 src
: input
.map(|p
| p
.to_path_buf()),
62 lib_search_paths
: archive_search_paths(sess
),
66 /// Map machine type strings to values of LLVM's MachineTypes enum.
67 fn llvm_machine_type(cpu
: &str) -> LLVMMachineType
{
69 "x86_64" => LLVMMachineType
::AMD64
,
70 "x86" => LLVMMachineType
::I386
,
71 "aarch64" => LLVMMachineType
::ARM64
,
72 "arm" => LLVMMachineType
::ARM
,
73 _
=> panic
!("unsupported cpu type {}", cpu
),
77 impl<'a
> ArchiveBuilder
<'a
> for LlvmArchiveBuilder
<'a
> {
78 /// Creates a new static archive, ready for modifying the archive specified
80 fn new(sess
: &'a Session
, output
: &Path
, input
: Option
<&Path
>) -> LlvmArchiveBuilder
<'a
> {
81 let config
= archive_config(sess
, output
, input
);
85 additions
: Vec
::new(),
86 should_update_symbols
: false,
91 /// Removes a file from this archive
92 fn remove_file(&mut self, file
: &str) {
93 self.removals
.push(file
.to_string());
96 /// Lists all files in an archive
97 fn src_files(&mut self) -> Vec
<String
> {
98 if self.src_archive().is_none() {
102 let archive
= self.src_archive
.as_ref().unwrap().as_ref().unwrap();
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())
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`.
116 fn add_native_library(&mut self, name
: Symbol
, verbatim
: bool
) {
118 find_library(name
, verbatim
, &self.config
.lib_search_paths
, self.config
.sess
);
119 self.add_archive(&location
, |_
| false).unwrap_or_else(|e
| {
120 self.config
.sess
.fatal(&format
!(
121 "failed to add native library {}: {}",
122 location
.to_string_lossy(),
128 /// Adds all of the contents of the rlib at the specified path to this
131 /// This ignores adding the bytecode from the rlib, and if LTO is enabled
132 /// then the object file also isn't added.
139 ) -> io
::Result
<()> {
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
143 let obj_start
= name
.to_owned();
145 self.add_archive(rlib
, move |fname
: &str| {
146 // Ignore metadata files, no matter the name.
147 if fname
== METADATA_FILENAME
{
151 // Don't include Rust objects if LTO is enabled
152 if lto
&& looks_like_rust_object_file(fname
) {
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")) {
162 // ok, don't skip this
167 /// Adds an arbitrary file to this archive
168 fn add_file(&mut self, file
: &Path
) {
169 let name
= file
.file_name().unwrap().to_str().unwrap();
171 .push(Addition
::File { path: file.to_path_buf(), name_in_archive: name.to_owned() }
);
174 /// Indicate that the next call to `build` should update all symbols in
175 /// the archive (equivalent to running 'ar s' over it).
176 fn update_symbols(&mut self) {
177 self.should_update_symbols
= true;
180 /// Combine the provided files, rlibs, and native libraries into a single
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
))
187 if let Err(e
) = self.build_with_llvm(kind
) {
188 self.config
.sess
.fatal(&format
!("failed to build archive: {}", e
));
192 fn inject_dll_import_lib(
195 dll_imports
: &[DllImport
],
196 tmpdir
: &MaybeTempDir
,
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")
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
211 .map(if self.config
.sess
.target
.arch
== "x86" {
212 |import
: &DllImport
| CString
::new(format
!("_{}", import
.name
.to_string())).unwrap()
214 |import
: &DllImport
| CString
::new(import
.name
.to_string()).unwrap()
218 let output_path_z
= rustc_fs_util
::path_to_c_string(&output_path
);
220 tracing
::trace
!("invoking LLVMRustWriteImportLibrary");
221 tracing
::trace
!(" dll_name {:#?}", dll_name_z
);
222 tracing
::trace
!(" output_path {}", output_path
.display());
225 dll_imports
.iter().map(|import
| import
.name
.to_string()).collect
::<Vec
<_
>>().join(", "),
228 let ffi_exports
: Vec
<LLVMRustCOFFShortExport
> = import_name_vector
230 .map(|name_z
| LLVMRustCOFFShortExport
::from_name(name_z
.as_ptr()))
232 let result
= unsafe {
233 crate::llvm
::LLVMRustWriteImportLibrary(
235 output_path_z
.as_ptr(),
236 ffi_exports
.as_ptr(),
238 llvm_machine_type(&self.config
.sess
.target
.arch
) as u16,
239 !self.config
.sess
.target
.is_like_msvc
,
243 if result
== crate::llvm
::LLVMRustResult
::Failure
{
244 self.config
.sess
.fatal(&format
!(
245 "Error creating import library for {}: {}",
247 llvm
::last_error().unwrap_or("unknown LLVM error".to_string())
251 self.add_archive(&output_path
, |_
| false).unwrap_or_else(|e
| {
252 self.config
.sess
.fatal(&format
!(
253 "failed to add native library {}: {}",
254 output_path
.display(),
261 impl<'a
> LlvmArchiveBuilder
<'a
> {
262 fn src_archive(&mut self) -> Option
<&ArchiveRO
> {
263 if let Some(ref a
) = self.src_archive
{
266 let src
= self.config
.src
.as_ref()?
;
267 self.src_archive
= Some(ArchiveRO
::open(src
).ok());
268 self.src_archive
.as_ref().unwrap().as_ref()
271 fn add_archive
<F
>(&mut self, archive
: &Path
, skip
: F
) -> io
::Result
<()>
273 F
: FnMut(&str) -> bool
+ '
static,
275 let archive_ro
= match ArchiveRO
::open(archive
) {
277 Err(e
) => return Err(io
::Error
::new(io
::ErrorKind
::Other
, e
)),
279 if self.additions
.iter().any(|ar
| ar
.path() == archive
) {
282 self.additions
.push(Addition
::Archive
{
283 path
: archive
.to_path_buf(),
285 skip
: Box
::new(skip
),
290 fn llvm_archive_kind(&self) -> Result
<ArchiveKind
, &str> {
291 let kind
= &*self.config
.sess
.target
.archive_format
;
292 kind
.parse().map_err(|_
| kind
)
295 fn build_with_llvm(&mut self, kind
: ArchiveKind
) -> io
::Result
<()> {
296 let removals
= mem
::take(&mut self.removals
);
297 let mut additions
= mem
::take(&mut self.additions
);
298 let mut strings
= Vec
::new();
299 let mut members
= Vec
::new();
301 let dst
= CString
::new(self.config
.dst
.to_str().unwrap())?
;
302 let should_update_symbols
= self.should_update_symbols
;
305 if let Some(archive
) = self.src_archive() {
306 for child
in archive
.iter() {
307 let child
= child
.map_err(string_to_io_error
)?
;
308 let child_name
= match child
.name() {
312 if removals
.iter().any(|r
| r
== child_name
) {
316 let name
= CString
::new(child_name
)?
;
317 members
.push(llvm
::LLVMRustArchiveMemberNew(
325 for addition
in &mut additions
{
327 Addition
::File { path, name_in_archive }
=> {
328 let path
= CString
::new(path
.to_str().unwrap())?
;
329 let name
= CString
::new(name_in_archive
.clone())?
;
330 members
.push(llvm
::LLVMRustArchiveMemberNew(
338 Addition
::Archive { archive, skip, .. }
=> {
339 for child
in archive
.iter() {
340 let child
= child
.map_err(string_to_io_error
)?
;
341 if !is_relevant_child(&child
) {
344 let child_name
= child
.name().unwrap();
345 if skip(child_name
) {
349 // It appears that LLVM's archive writer is a little
350 // buggy if the name we pass down isn't just the
351 // filename component, so chop that off here and
354 // See LLVM bug 25877 for more info.
356 Path
::new(child_name
).file_name().unwrap().to_str().unwrap();
357 let name
= CString
::new(child_name
)?
;
358 let m
= llvm
::LLVMRustArchiveMemberNew(
370 let r
= llvm
::LLVMRustWriteArchive(
372 members
.len() as libc
::size_t
,
373 members
.as_ptr() as *const &_
,
374 should_update_symbols
,
377 let ret
= if r
.into_result().is_err() {
378 let err
= llvm
::LLVMRustGetLastError();
379 let msg
= if err
.is_null() {
380 "failed to write archive".into()
382 String
::from_utf8_lossy(CStr
::from_ptr(err
).to_bytes())
384 Err(io
::Error
::new(io
::ErrorKind
::Other
, msg
))
388 for member
in members
{
389 llvm
::LLVMRustArchiveMemberFree(member
);
396 fn string_to_io_error(s
: String
) -> io
::Error
{
397 io
::Error
::new(io
::ErrorKind
::Other
, format
!("bad archive: {}", s
))