1use std::env;
2use std::error::Error;
3use std::ffi::OsString;
4use std::fs::{self, File};
5use std::io::{self, BufWriter, Write};
6use std::path::{Path, PathBuf};
7
8use ar_archive_writer::{
9 ArchiveKind, COFFShortExport, MachineTypes, NewArchiveMember, write_archive_to_stream,
10};
11pub use ar_archive_writer::{DEFAULT_OBJECT_READER, ObjectReader};
12use object::read::archive::{ArchiveFile, ArchiveKind as ObjectArchiveKind};
13use object::read::macho::FatArch;
14use rustc_data_structures::fx::FxIndexSet;
15use rustc_data_structures::memmap::Mmap;
16use rustc_fs_util::TempDirBuilder;
17use rustc_metadata::EncodedMetadata;
18use rustc_session::Session;
19use rustc_span::Symbol;
20use rustc_target::spec::Arch;
21use tracing::trace;
22
23use super::metadata::{create_compressed_metadata_file, search_for_section};
24use super::rmeta_link::{self, RmetaLink};
25use crate::common;
26pub use crate::errors::ExtractBundledLibsError;
28use crate::errors::{
29 ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
30 ErrorWritingDEFFile, UnknownArchiveKind,
31};
32
33pub struct ImportLibraryItem {
36 pub name: String,
38 pub ordinal: Option<u16>,
40 pub symbol_name: Option<String>,
42 pub is_data: bool,
44}
45
46impl ImportLibraryItem {
47 fn into_coff_short_export(self, sess: &Session) -> COFFShortExport {
48 let import_name = (sess.target.arch == Arch::Arm64EC).then(|| self.name.clone());
49 COFFShortExport {
50 name: self.name,
51 ext_name: None,
52 symbol_name: self.symbol_name,
53 import_name,
54 export_as: None,
55 ordinal: self.ordinal.unwrap_or(0),
56 noname: self.ordinal.is_some(),
57 data: self.is_data,
58 private: false,
59 constant: false,
60 }
61 }
62}
63
64pub trait ArchiveBuilderBuilder {
65 fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
66
67 fn create_dylib_metadata_wrapper(
68 &self,
69 sess: &Session,
70 metadata: &EncodedMetadata,
71 symbol_name: &str,
72 ) -> Vec<u8> {
73 create_compressed_metadata_file(sess, metadata, symbol_name)
74 }
75
76 fn create_dll_import_lib(
82 &self,
83 sess: &Session,
84 lib_name: &str,
85 items: Vec<ImportLibraryItem>,
86 output_path: &Path,
87 ) {
88 if common::is_mingw_gnu_toolchain(&sess.target) {
89 create_mingw_dll_import_lib(sess, lib_name, items, output_path);
95 } else {
96 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/archive.rs:96",
"rustc_codegen_ssa::back::archive", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/archive.rs"),
::tracing_core::__macro_support::Option::Some(96u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::archive"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("creating import library")
as &dyn Value))])
});
} else { ; }
};trace!("creating import library");
97 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/archive.rs:97",
"rustc_codegen_ssa::back::archive", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/archive.rs"),
::tracing_core::__macro_support::Option::Some(97u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::archive"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!(" dll_name {0:#?}",
lib_name) as &dyn Value))])
});
} else { ; }
};trace!(" dll_name {:#?}", lib_name);
98 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/archive.rs:98",
"rustc_codegen_ssa::back::archive", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/archive.rs"),
::tracing_core::__macro_support::Option::Some(98u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::archive"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!(" output_path {0}",
output_path.display()) as &dyn Value))])
});
} else { ; }
};trace!(" output_path {}", output_path.display());
99 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/archive.rs:99",
"rustc_codegen_ssa::back::archive", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/archive.rs"),
::tracing_core::__macro_support::Option::Some(99u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::archive"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!(" import names: {0}",
items.iter().map(|ImportLibraryItem { name, .. }|
name.clone()).collect::<Vec<_>>().join(", ")) as
&dyn Value))])
});
} else { ; }
};trace!(
100 " import names: {}",
101 items
102 .iter()
103 .map(|ImportLibraryItem { name, .. }| name.clone())
104 .collect::<Vec<_>>()
105 .join(", "),
106 );
107
108 let mut file = match fs::File::create_new(&output_path) {
115 Ok(file) => file,
116 Err(error) => sess
117 .dcx()
118 .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
119 };
120
121 let exports =
122 items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
123 let machine = match &sess.target.arch {
124 Arch::X86_64 => MachineTypes::AMD64,
125 Arch::X86 => MachineTypes::I386,
126 Arch::AArch64 => MachineTypes::ARM64,
127 Arch::Arm64EC => MachineTypes::ARM64EC,
128 Arch::Arm => MachineTypes::ARMNT,
129 cpu => {
::core::panicking::panic_fmt(format_args!("unsupported cpu type {0}",
cpu));
}panic!("unsupported cpu type {cpu}"),
130 };
131
132 if let Err(error) = ar_archive_writer::write_import_library(
133 &mut file,
134 lib_name,
135 &exports,
136 machine,
137 !sess.target.is_like_msvc,
138 true,
143 &[],
144 ) {
145 sess.dcx()
146 .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
147 }
148 }
149 }
150
151 fn extract_bundled_libs<'a>(
152 &'a self,
153 rlib: &'a Path,
154 outdir: &Path,
155 bundled_lib_file_names: &FxIndexSet<Symbol>,
156 ) -> Result<(), ExtractBundledLibsError<'a>> {
157 let archive_map = unsafe {
158 Mmap::map(
159 File::open(rlib)
160 .map_err(|e| ExtractBundledLibsError::OpenFile { rlib, error: Box::new(e) })?,
161 )
162 .map_err(|e| ExtractBundledLibsError::MmapFile { rlib, error: Box::new(e) })?
163 };
164 let archive = ArchiveFile::parse(&*archive_map)
165 .map_err(|e| ExtractBundledLibsError::ParseArchive { rlib, error: Box::new(e) })?;
166
167 for entry in archive.members() {
168 let entry = entry
169 .map_err(|e| ExtractBundledLibsError::ReadEntry { rlib, error: Box::new(e) })?;
170 let data = entry
171 .data(&*archive_map)
172 .map_err(|e| ExtractBundledLibsError::ArchiveMember { rlib, error: Box::new(e) })?;
173 let name = std::str::from_utf8(entry.name())
174 .map_err(|e| ExtractBundledLibsError::ConvertName { rlib, error: Box::new(e) })?;
175 if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
176 continue; }
178 let data = search_for_section(rlib, data, ".bundled_lib").map_err(|e| {
179 ExtractBundledLibsError::ExtractSection { rlib, error: Box::<dyn Error>::from(e) }
180 })?;
181 std::fs::write(&outdir.join(&name), data)
182 .map_err(|e| ExtractBundledLibsError::WriteFile { rlib, error: Box::new(e) })?;
183 }
184 Ok(())
185 }
186}
187
188fn create_mingw_dll_import_lib(
189 sess: &Session,
190 lib_name: &str,
191 items: Vec<ImportLibraryItem>,
192 output_path: &Path,
193) {
194 let def_file_path = output_path.with_extension("def");
195
196 let def_file_content = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("EXPORTS\n{0}",
items.into_iter().map(|ImportLibraryItem { name, ordinal, ..
}|
{
match ordinal {
Some(n) =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} @{1} NONAME", name,
n))
}),
None => name,
}
}).collect::<Vec<String>>().join("\n")))
})format!(
197 "EXPORTS\n{}",
198 items
199 .into_iter()
200 .map(|ImportLibraryItem { name, ordinal, .. }| {
201 match ordinal {
202 Some(n) => format!("{name} @{n} NONAME"),
203 None => name,
204 }
205 })
206 .collect::<Vec<String>>()
207 .join("\n")
208 );
209
210 match std::fs::write(&def_file_path, def_file_content) {
211 Ok(_) => {}
212 Err(e) => {
213 sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
214 }
215 };
216
217 let dlltool = find_binutils_dlltool(sess);
221 let cwd = output_path.parent().unwrap_or(output_path);
224 let temp_prefix = lib_name;
225 let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch {
228 Arch::X86_64 => ("i386:x86-64", "--64"),
229 Arch::X86 => ("i386", "--32"),
230 Arch::AArch64 => ("arm64", "--64"),
231 Arch::Arm => ("arm", "--32"),
232 arch => { ::core::panicking::panic_fmt(format_args!("unsupported arch {0}", arch)); }panic!("unsupported arch {arch}"),
233 };
234 let mut dlltool_cmd = std::process::Command::new(&dlltool);
235 dlltool_cmd
236 .arg("-d")
237 .arg(def_file_path)
238 .arg("-D")
239 .arg(lib_name)
240 .arg("-l")
241 .arg(&output_path)
242 .arg("-m")
243 .arg(dlltool_target_arch)
244 .arg("-f")
245 .arg(dlltool_target_bitness)
246 .arg("--no-leading-underscore")
247 .arg("--temp-prefix")
248 .arg(temp_prefix)
249 .current_dir(cwd);
250
251 match dlltool_cmd.output() {
252 Err(e) => {
253 sess.dcx().emit_fatal(ErrorCallingDllTool {
254 dlltool_path: dlltool.to_string_lossy(),
255 error: e,
256 });
257 }
258 Ok(output) if !output.stderr.is_empty() => {
260 sess.dcx().emit_fatal(DlltoolFailImportLibrary {
261 dlltool_path: dlltool.to_string_lossy(),
262 dlltool_args: dlltool_cmd
263 .get_args()
264 .map(|arg| arg.to_string_lossy())
265 .collect::<Vec<_>>()
266 .join(" "),
267 stdout: String::from_utf8_lossy(&output.stdout),
268 stderr: String::from_utf8_lossy(&output.stderr),
269 })
270 }
271 _ => {}
272 }
273}
274
275fn find_binutils_dlltool(sess: &Session) -> OsString {
276 if !(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc)
{
::core::panicking::panic("assertion failed: sess.target.options.is_like_windows && !sess.target.options.is_like_msvc")
};assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
277 if let Some(dlltool_path) = &sess.opts.cg.dlltool {
278 return dlltool_path.clone().into_os_string();
279 }
280
281 let tool_name: OsString = if sess.host.options.is_like_windows {
282 "dlltool.exe"
284 } else {
285 match sess.target.arch {
287 Arch::X86_64 => "x86_64-w64-mingw32-dlltool",
288 Arch::X86 => "i686-w64-mingw32-dlltool",
289 Arch::AArch64 => "aarch64-w64-mingw32-dlltool",
290
291 _ => "dlltool",
293 }
294 }
295 .into();
296
297 for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
299 let full_path = dir.join(&tool_name);
300 if full_path.is_file() {
301 return full_path.into_os_string();
302 }
303 }
304
305 tool_name
309}
310
311pub trait ArchiveBuilder {
312 fn add_file(&mut self, path: &Path);
313
314 fn add_archive(
315 &mut self,
316 archive: &Path,
317 skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
318 ) -> io::Result<()>;
319
320 fn build(self: Box<Self>, output: &Path) -> bool;
321}
322
323fn target_archive_format_to_object_kind(format: &str) -> Option<ObjectArchiveKind> {
324 match format {
325 "gnu" => Some(ObjectArchiveKind::Gnu),
326 "bsd" => Some(ObjectArchiveKind::Bsd),
327 "darwin" => Some(ObjectArchiveKind::Bsd64),
328 "coff" => Some(ObjectArchiveKind::Coff),
329 "aix_big" => Some(ObjectArchiveKind::AixBig),
330 _ => None,
331 }
332}
333
334fn archive_kinds_compatible(actual: ObjectArchiveKind, expected: ObjectArchiveKind) -> bool {
335 if actual == expected {
336 return true;
337 }
338 #[allow(non_exhaustive_omitted_patterns)] match (actual, expected) {
(ObjectArchiveKind::Unknown, _) |
(ObjectArchiveKind::Gnu64, ObjectArchiveKind::Gnu) |
(ObjectArchiveKind::Gnu, ObjectArchiveKind::Gnu64) |
(ObjectArchiveKind::Bsd64, ObjectArchiveKind::Bsd) |
(ObjectArchiveKind::Bsd, ObjectArchiveKind::Bsd64) |
(ObjectArchiveKind::Gnu, ObjectArchiveKind::Coff) |
(ObjectArchiveKind::Coff, ObjectArchiveKind::Gnu) |
(ObjectArchiveKind::Gnu64, ObjectArchiveKind::Coff) => true,
_ => false,
}matches!(
339 (actual, expected),
340 (ObjectArchiveKind::Unknown, _)
343 | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Gnu)
345 | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Gnu64)
346 | (ObjectArchiveKind::Bsd64, ObjectArchiveKind::Bsd)
347 | (ObjectArchiveKind::Bsd, ObjectArchiveKind::Bsd64)
348 | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Coff)
351 | (ObjectArchiveKind::Coff, ObjectArchiveKind::Gnu)
352 | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Coff)
353 )
354}
355
356fn archive_kind_display_name(kind: ObjectArchiveKind) -> String {
357 match kind {
358 ObjectArchiveKind::Gnu | ObjectArchiveKind::Gnu64 => "GNU".to_string(),
359 ObjectArchiveKind::Bsd => "BSD".to_string(),
360 ObjectArchiveKind::Bsd64 => "Darwin".to_string(),
361 ObjectArchiveKind::Coff => "COFF".to_string(),
362 ObjectArchiveKind::AixBig => "AIX big".to_string(),
363 _ => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", kind))
})format!("{kind:?}"),
364 }
365}
366
367pub struct ArArchiveBuilderBuilder;
368
369impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
370 fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
371 Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER))
372 }
373}
374
375#[must_use = "must call build() to finish building the archive"]
376pub struct ArArchiveBuilder<'a> {
377 sess: &'a Session,
378 object_reader: &'static ObjectReader,
379
380 src_archives: Vec<(PathBuf, Mmap)>,
381 entries: Vec<(Vec<u8>, ArchiveEntry)>,
384}
385
386#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ArchiveEntry {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
ArchiveEntry::FromArchive {
archive_index: __self_0, file_range: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"FromArchive", "archive_index", __self_0, "file_range",
&__self_1),
ArchiveEntry::File(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "File",
&__self_0),
}
}
}Debug)]
387enum ArchiveEntry {
388 FromArchive { archive_index: usize, file_range: (u64, u64) },
389 File(PathBuf),
390}
391
392impl<'a> ArArchiveBuilder<'a> {
393 pub fn new(sess: &'a Session, object_reader: &'static ObjectReader) -> ArArchiveBuilder<'a> {
394 ArArchiveBuilder { sess, object_reader, src_archives: ::alloc::vec::Vec::new()vec![], entries: ::alloc::vec::Vec::new()vec![] }
395 }
396}
397
398fn try_filter_fat_archs(
399 archs: &[impl FatArch],
400 target_arch: object::Architecture,
401 archive_path: &Path,
402 archive_map_data: &[u8],
403) -> io::Result<Option<PathBuf>> {
404 let desired = match archs.iter().find(|a| a.architecture() == target_arch) {
405 Some(a) => a,
406 None => return Ok(None),
407 };
408
409 let (mut new_f, extracted_path) = tempfile::Builder::new()
410 .suffix(archive_path.file_name().unwrap())
411 .tempfile()?
412 .keep()
413 .unwrap();
414
415 new_f.write_all(
416 desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
417 )?;
418
419 Ok(Some(extracted_path))
420}
421
422pub fn try_extract_macho_fat_archive(
423 sess: &Session,
424 archive_path: &Path,
425) -> io::Result<Option<PathBuf>> {
426 let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
427 let target_arch = match sess.target.arch {
428 Arch::AArch64 => object::Architecture::Aarch64,
429 Arch::X86_64 => object::Architecture::X86_64,
430 _ => return Ok(None),
431 };
432
433 if let Ok(h) = object::read::macho::MachOFatFile32::parse(&*archive_map) {
434 let archs = h.arches();
435 try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
436 } else if let Ok(h) = object::read::macho::MachOFatFile64::parse(&*archive_map) {
437 let archs = h.arches();
438 try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
439 } else {
440 Ok(None)
442 }
443}
444
445impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
446 fn add_archive(
447 &mut self,
448 archive_path: &Path,
449 mut skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
450 ) -> io::Result<()> {
451 let mut archive_path = archive_path.to_path_buf();
452 if self.sess.target.llvm_target.contains("-apple-macosx")
453 && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)?
454 {
455 archive_path = new_archive_path
456 }
457
458 if self.src_archives.iter().any(|archive| archive.0 == archive_path) {
459 return Ok(());
460 }
461
462 let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
463 let archive = ArchiveFile::parse(&*archive_map)
464 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
465 let metadata_link =
466 skip.as_ref().and_then(|_| rmeta_link::read(&archive, &archive_map, &archive_path));
467 let archive_index = self.src_archives.len();
468
469 if let Some(expected_kind) =
470 target_archive_format_to_object_kind(&self.sess.target.archive_format)
471 {
472 let actual_kind = archive.kind();
473 if !archive_kinds_compatible(actual_kind, expected_kind) {
474 self.sess.dcx().emit_warn(crate::errors::IncompatibleArchiveFormat {
475 path: archive_path.clone(),
476 actual: archive_kind_display_name(actual_kind),
477 expected: archive_kind_display_name(expected_kind),
478 });
479 }
480 }
481
482 for entry in archive.members() {
483 let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
484 let file_name = String::from_utf8(entry.name().to_vec())
485 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
486 let drop = skip.as_mut().is_some_and(|f| f(&file_name, metadata_link.as_ref()));
487 if !drop {
488 if entry.is_thin() {
489 let member_path = archive_path.parent().unwrap().join(Path::new(&file_name));
490 self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path)));
491 } else {
492 self.entries.push((
493 file_name.into_bytes(),
494 ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
495 ));
496 }
497 }
498 }
499
500 self.src_archives.push((archive_path, archive_map));
501 Ok(())
502 }
503
504 fn add_file(&mut self, file: &Path) {
506 self.entries.push((
507 file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
508 ArchiveEntry::File(file.to_owned()),
509 ));
510 }
511
512 fn build(self: Box<Self>, output: &Path) -> bool {
515 let sess = self.sess;
516 match self.build_inner(output) {
517 Ok(any_members) => any_members,
518 Err(error) => {
519 sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
520 }
521 }
522 }
523}
524
525impl<'a> ArArchiveBuilder<'a> {
526 fn build_inner(self, output: &Path) -> io::Result<bool> {
527 let archive_kind = match &*self.sess.target.archive_format {
528 "gnu" => ArchiveKind::Gnu,
529 "bsd" => ArchiveKind::Bsd,
530 "darwin" => ArchiveKind::Darwin,
531 "coff" => ArchiveKind::Coff,
532 "aix_big" => ArchiveKind::AixBig,
533 kind => {
534 self.sess.dcx().emit_fatal(UnknownArchiveKind { kind });
535 }
536 };
537
538 let mut entries = Vec::new();
539
540 for (entry_name, entry) in self.entries {
541 let data =
542 match entry {
543 ArchiveEntry::FromArchive { archive_index, file_range } => {
544 let src_archive = &self.src_archives[archive_index];
545 let archive_data = &src_archive.1;
546 let start = file_range.0 as usize;
547 let end = start + file_range.1 as usize;
548 let Some(data) = archive_data.get(start..end) else {
549 return Err(io_error_context(
550 "invalid archive member",
551 io::Error::new(
552 io::ErrorKind::InvalidData,
553 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("archive member at offset {3} with size {0} exceeds archive size {1} in `{2}`",
file_range.1, archive_data.len(), src_archive.0.display(),
start))
})format!(
554 "archive member at offset {start} with size {} \
555 exceeds archive size {} in `{}`",
556 file_range.1,
557 archive_data.len(),
558 src_archive.0.display(),
559 ),
560 ),
561 ));
562 };
563
564 Box::new(data) as Box<dyn AsRef<[u8]>>
565 }
566 ArchiveEntry::File(file) => unsafe {
567 Box::new(
568 Mmap::map(File::open(file).map_err(|err| {
569 io_error_context("failed to open object file", err)
570 })?)
571 .map_err(|err| io_error_context("failed to map object file", err))?,
572 ) as Box<dyn AsRef<[u8]>>
573 },
574 };
575
576 entries.push(NewArchiveMember {
577 buf: data,
578 object_reader: self.object_reader,
579 member_name: String::from_utf8(entry_name).unwrap(),
580 mtime: 0,
581 uid: 0,
582 gid: 0,
583 perms: 0o644,
584 })
585 }
586
587 let archive_tmpdir = TempDirBuilder::new()
596 .suffix(".temp-archive")
597 .tempdir_in(output.parent().unwrap_or_else(|| Path::new("")))
598 .map_err(|err| {
599 io_error_context("couldn't create a directory for the temp file", err)
600 })?;
601 let archive_tmpfile_path = archive_tmpdir.path().join("tmp.a");
602 let archive_tmpfile = File::create_new(&archive_tmpfile_path)
603 .map_err(|err| io_error_context("couldn't create the temp file", err))?;
604
605 let mut archive_tmpfile = BufWriter::new(archive_tmpfile);
606 write_archive_to_stream(
607 &mut archive_tmpfile,
608 &entries,
609 archive_kind,
610 false,
611 Some(self.sess.target.arch == Arch::Arm64EC),
612 )?;
613 archive_tmpfile.flush()?;
614 drop(archive_tmpfile);
615
616 let any_entries = !entries.is_empty();
617 drop(entries);
618 drop(self.src_archives);
621
622 fs::rename(archive_tmpfile_path, output)
623 .map_err(|err| io_error_context("failed to rename archive file", err))?;
624 archive_tmpdir
625 .close()
626 .map_err(|err| io_error_context("failed to remove temporary directory", err))?;
627
628 Ok(any_entries)
629 }
630}
631
632fn io_error_context(context: &str, err: io::Error) -> io::Error {
633 io::Error::new(io::ErrorKind::Other, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: {1}", context, err))
})format!("{context}: {err}"))
634}