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::{FxHashSet, 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;
25use super::symbol_edit::apply_hide;
26use crate::common;
27pub use crate::errors::ExtractBundledLibsError;
29use crate::errors::{
30 ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
31 ErrorWritingDEFFile, UnknownArchiveKind,
32};
33
34pub struct ImportLibraryItem {
37 pub name: String,
39 pub ordinal: Option<u16>,
41 pub symbol_name: Option<String>,
43 pub is_data: bool,
45}
46
47impl ImportLibraryItem {
48 fn into_coff_short_export(self, sess: &Session) -> COFFShortExport {
49 let import_name = (sess.target.arch == Arch::Arm64EC).then(|| self.name.clone());
50 COFFShortExport {
51 name: self.name,
52 ext_name: None,
53 symbol_name: self.symbol_name,
54 import_name,
55 export_as: None,
56 ordinal: self.ordinal.unwrap_or(0),
57 noname: self.ordinal.is_some(),
58 data: self.is_data,
59 private: false,
60 constant: false,
61 }
62 }
63}
64
65pub trait ArchiveBuilderBuilder {
66 fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
67
68 fn create_dylib_metadata_wrapper(
69 &self,
70 sess: &Session,
71 metadata: &EncodedMetadata,
72 symbol_name: &str,
73 ) -> Vec<u8> {
74 create_compressed_metadata_file(sess, metadata, symbol_name)
75 }
76
77 fn create_dll_import_lib(
83 &self,
84 sess: &Session,
85 lib_name: &str,
86 items: Vec<ImportLibraryItem>,
87 output_path: &Path,
88 ) {
89 if common::is_mingw_gnu_toolchain(&sess.target) {
90 create_mingw_dll_import_lib(sess, lib_name, items, output_path);
96 } else {
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!("creating import library")
as &dyn Value))])
});
} else { ; }
};trace!("creating import library");
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!(" dll_name {0:#?}",
lib_name) as &dyn Value))])
});
} else { ; }
};trace!(" dll_name {:#?}", lib_name);
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!(" output_path {0}",
output_path.display()) as &dyn Value))])
});
} else { ; }
};trace!(" output_path {}", output_path.display());
100 {
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:100",
"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(100u32),
::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!(
101 " import names: {}",
102 items
103 .iter()
104 .map(|ImportLibraryItem { name, .. }| name.clone())
105 .collect::<Vec<_>>()
106 .join(", "),
107 );
108
109 let mut file = match fs::File::create_new(&output_path) {
116 Ok(file) => file,
117 Err(error) => sess
118 .dcx()
119 .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
120 };
121
122 let exports =
123 items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
124 let machine = match &sess.target.arch {
125 Arch::X86_64 => MachineTypes::AMD64,
126 Arch::X86 => MachineTypes::I386,
127 Arch::AArch64 => MachineTypes::ARM64,
128 Arch::Arm64EC => MachineTypes::ARM64EC,
129 Arch::Arm => MachineTypes::ARMNT,
130 cpu => {
::core::panicking::panic_fmt(format_args!("unsupported cpu type {0}",
cpu));
}panic!("unsupported cpu type {cpu}"),
131 };
132
133 if let Err(error) = ar_archive_writer::write_import_library(
134 &mut file,
135 lib_name,
136 &exports,
137 machine,
138 !sess.target.is_like_msvc,
139 true,
144 &[],
145 ) {
146 sess.dcx()
147 .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
148 }
149 }
150 }
151
152 fn extract_bundled_libs<'a>(
153 &'a self,
154 rlib: &'a Path,
155 outdir: &Path,
156 bundled_lib_file_names: &FxIndexSet<Symbol>,
157 ) -> Result<(), ExtractBundledLibsError<'a>> {
158 let archive_map = unsafe {
159 Mmap::map(
160 File::open(rlib)
161 .map_err(|e| ExtractBundledLibsError::OpenFile { rlib, error: Box::new(e) })?,
162 )
163 .map_err(|e| ExtractBundledLibsError::MmapFile { rlib, error: Box::new(e) })?
164 };
165 let archive = ArchiveFile::parse(&*archive_map)
166 .map_err(|e| ExtractBundledLibsError::ParseArchive { rlib, error: Box::new(e) })?;
167
168 for entry in archive.members() {
169 let entry = entry
170 .map_err(|e| ExtractBundledLibsError::ReadEntry { rlib, error: Box::new(e) })?;
171 let data = entry
172 .data(&*archive_map)
173 .map_err(|e| ExtractBundledLibsError::ArchiveMember { rlib, error: Box::new(e) })?;
174 let name = std::str::from_utf8(entry.name())
175 .map_err(|e| ExtractBundledLibsError::ConvertName { rlib, error: Box::new(e) })?;
176 if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
177 continue; }
179 let data = search_for_section(rlib, data, ".bundled_lib").map_err(|e| {
180 ExtractBundledLibsError::ExtractSection { rlib, error: Box::<dyn Error>::from(e) }
181 })?;
182 std::fs::write(&outdir.join(&name), data)
183 .map_err(|e| ExtractBundledLibsError::WriteFile { rlib, error: Box::new(e) })?;
184 }
185 Ok(())
186 }
187}
188
189fn create_mingw_dll_import_lib(
190 sess: &Session,
191 lib_name: &str,
192 items: Vec<ImportLibraryItem>,
193 output_path: &Path,
194) {
195 let def_file_path = output_path.with_extension("def");
196
197 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!(
198 "EXPORTS\n{}",
199 items
200 .into_iter()
201 .map(|ImportLibraryItem { name, ordinal, .. }| {
202 match ordinal {
203 Some(n) => format!("{name} @{n} NONAME"),
204 None => name,
205 }
206 })
207 .collect::<Vec<String>>()
208 .join("\n")
209 );
210
211 match std::fs::write(&def_file_path, def_file_content) {
212 Ok(_) => {}
213 Err(e) => {
214 sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e });
215 }
216 };
217
218 let dlltool = find_binutils_dlltool(sess);
222 let cwd = output_path.parent().unwrap_or(output_path);
225 let temp_prefix = lib_name;
226 let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch {
229 Arch::X86_64 => ("i386:x86-64", "--64"),
230 Arch::X86 => ("i386", "--32"),
231 Arch::AArch64 => ("arm64", "--64"),
232 Arch::Arm => ("arm", "--32"),
233 arch => { ::core::panicking::panic_fmt(format_args!("unsupported arch {0}", arch)); }panic!("unsupported arch {arch}"),
234 };
235 let mut dlltool_cmd = std::process::Command::new(&dlltool);
236 dlltool_cmd
237 .arg("-d")
238 .arg(def_file_path)
239 .arg("-D")
240 .arg(lib_name)
241 .arg("-l")
242 .arg(&output_path)
243 .arg("-m")
244 .arg(dlltool_target_arch)
245 .arg("-f")
246 .arg(dlltool_target_bitness)
247 .arg("--no-leading-underscore")
248 .arg("--temp-prefix")
249 .arg(temp_prefix)
250 .current_dir(cwd);
251
252 match dlltool_cmd.output() {
253 Err(e) => {
254 sess.dcx().emit_fatal(ErrorCallingDllTool {
255 dlltool_path: dlltool.to_string_lossy(),
256 error: e,
257 });
258 }
259 Ok(output) if !output.stderr.is_empty() => {
261 sess.dcx().emit_fatal(DlltoolFailImportLibrary {
262 dlltool_path: dlltool.to_string_lossy(),
263 dlltool_args: dlltool_cmd
264 .get_args()
265 .map(|arg| arg.to_string_lossy())
266 .collect::<Vec<_>>()
267 .join(" "),
268 stdout: String::from_utf8_lossy(&output.stdout),
269 stderr: String::from_utf8_lossy(&output.stderr),
270 })
271 }
272 _ => {}
273 }
274}
275
276fn find_binutils_dlltool(sess: &Session) -> OsString {
277 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);
278 if let Some(dlltool_path) = &sess.opts.cg.dlltool {
279 return dlltool_path.clone().into_os_string();
280 }
281
282 let tool_name: OsString = if sess.host.options.is_like_windows {
283 "dlltool.exe"
285 } else {
286 match sess.target.arch {
288 Arch::X86_64 => "x86_64-w64-mingw32-dlltool",
289 Arch::X86 => "i686-w64-mingw32-dlltool",
290 Arch::AArch64 => "aarch64-w64-mingw32-dlltool",
291
292 _ => "dlltool",
294 }
295 }
296 .into();
297
298 for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
300 let full_path = dir.join(&tool_name);
301 if full_path.is_file() {
302 return full_path.into_os_string();
303 }
304 }
305
306 tool_name
310}
311
312pub enum AddArchiveKind<'a> {
313 Rlib(&'a dyn Fn(&str, ArchiveEntryKind) -> bool),
314 Other,
315}
316
317pub trait ArchiveBuilder {
318 fn add_file(&mut self, path: &Path, kind: ArchiveEntryKind);
319
320 fn add_archive(&mut self, archive: &Path, kind: AddArchiveKind<'_>) -> io::Result<()>;
321
322 fn build(self: Box<Self>, output: &Path, exported_symbols: Option<FxHashSet<String>>) -> bool;
323}
324
325fn target_archive_format_to_object_kind(format: &str) -> Option<ObjectArchiveKind> {
326 match format {
327 "gnu" => Some(ObjectArchiveKind::Gnu),
328 "bsd" => Some(ObjectArchiveKind::Bsd),
329 "darwin" => Some(ObjectArchiveKind::Bsd64),
330 "coff" => Some(ObjectArchiveKind::Coff),
331 "aix_big" => Some(ObjectArchiveKind::AixBig),
332 _ => None,
333 }
334}
335
336fn archive_kinds_compatible(actual: ObjectArchiveKind, expected: ObjectArchiveKind) -> bool {
337 if actual == expected {
338 return true;
339 }
340 #[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!(
341 (actual, expected),
342 (ObjectArchiveKind::Unknown, _)
345 | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Gnu)
347 | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Gnu64)
348 | (ObjectArchiveKind::Bsd64, ObjectArchiveKind::Bsd)
349 | (ObjectArchiveKind::Bsd, ObjectArchiveKind::Bsd64)
350 | (ObjectArchiveKind::Gnu, ObjectArchiveKind::Coff)
353 | (ObjectArchiveKind::Coff, ObjectArchiveKind::Gnu)
354 | (ObjectArchiveKind::Gnu64, ObjectArchiveKind::Coff)
355 )
356}
357
358fn archive_kind_display_name(kind: ObjectArchiveKind) -> String {
359 match kind {
360 ObjectArchiveKind::Gnu | ObjectArchiveKind::Gnu64 => "GNU".to_string(),
361 ObjectArchiveKind::Bsd => "BSD".to_string(),
362 ObjectArchiveKind::Bsd64 => "Darwin".to_string(),
363 ObjectArchiveKind::Coff => "COFF".to_string(),
364 ObjectArchiveKind::AixBig => "AIX big".to_string(),
365 _ => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", kind))
})format!("{kind:?}"),
366 }
367}
368
369pub struct ArArchiveBuilderBuilder;
370
371impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
372 fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
373 Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER))
374 }
375}
376
377#[must_use = "must call build() to finish building the archive"]
378pub struct ArArchiveBuilder<'a> {
379 sess: &'a Session,
380 object_reader: &'static ObjectReader,
381
382 src_archives: Vec<(PathBuf, Mmap)>,
383 entries: Vec<(Vec<u8>, ArchiveEntry)>,
386}
387
388#[derive(#[automatically_derived]
impl ::core::clone::Clone for ArchiveEntryKind {
#[inline]
fn clone(&self) -> ArchiveEntryKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ArchiveEntryKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for ArchiveEntryKind {
#[inline]
fn eq(&self, other: &ArchiveEntryKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for ArchiveEntryKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ArchiveEntryKind::RustObj => "RustObj",
ArchiveEntryKind::Other => "Other",
})
}
}Debug)]
389pub enum ArchiveEntryKind {
390 RustObj,
392 Other,
394}
395
396#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ArchiveEntrySource {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
ArchiveEntrySource::Archive {
archive_index: __self_0, file_range: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"Archive", "archive_index", __self_0, "file_range",
&__self_1),
ArchiveEntrySource::File(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "File",
&__self_0),
}
}
}Debug)]
397enum ArchiveEntrySource {
398 Archive { archive_index: usize, file_range: (u64, u64) },
399 File(PathBuf),
400}
401
402#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ArchiveEntry {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "ArchiveEntry",
"source", &self.source, "kind", &&self.kind)
}
}Debug)]
403struct ArchiveEntry {
404 source: ArchiveEntrySource,
405 kind: ArchiveEntryKind,
406}
407
408impl<'a> ArArchiveBuilder<'a> {
409 pub fn new(sess: &'a Session, object_reader: &'static ObjectReader) -> ArArchiveBuilder<'a> {
410 ArArchiveBuilder { sess, object_reader, src_archives: ::alloc::vec::Vec::new()vec![], entries: ::alloc::vec::Vec::new()vec![] }
411 }
412}
413
414fn try_filter_fat_archs(
415 archs: &[impl FatArch],
416 target_arch: object::Architecture,
417 archive_path: &Path,
418 archive_map_data: &[u8],
419) -> io::Result<Option<PathBuf>> {
420 let desired = match archs.iter().find(|a| a.architecture() == target_arch) {
421 Some(a) => a,
422 None => return Ok(None),
423 };
424
425 let (mut new_f, extracted_path) = tempfile::Builder::new()
426 .suffix(archive_path.file_name().unwrap())
427 .tempfile()?
428 .keep()
429 .unwrap();
430
431 new_f.write_all(
432 desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
433 )?;
434
435 Ok(Some(extracted_path))
436}
437
438pub fn try_extract_macho_fat_archive(
439 sess: &Session,
440 archive_path: &Path,
441) -> io::Result<Option<PathBuf>> {
442 let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
443 let target_arch = match sess.target.arch {
444 Arch::AArch64 => object::Architecture::Aarch64,
445 Arch::X86_64 => object::Architecture::X86_64,
446 _ => return Ok(None),
447 };
448
449 if let Ok(h) = object::read::macho::MachOFatFile32::parse(&*archive_map) {
450 let archs = h.arches();
451 try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
452 } else if let Ok(h) = object::read::macho::MachOFatFile64::parse(&*archive_map) {
453 let archs = h.arches();
454 try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
455 } else {
456 Ok(None)
458 }
459}
460
461impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
462 fn add_archive(&mut self, archive_path: &Path, ar_kind: AddArchiveKind<'_>) -> io::Result<()> {
463 let mut archive_path = archive_path.to_path_buf();
464 if self.sess.target.llvm_target.contains("-apple-macosx")
465 && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)?
466 {
467 archive_path = new_archive_path
468 }
469
470 if self.src_archives.iter().any(|archive| archive.0 == archive_path) {
471 return Ok(());
472 }
473
474 let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
475 let archive = ArchiveFile::parse(&*archive_map)
476 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
477 let metadata_link = match ar_kind {
478 AddArchiveKind::Rlib(..) => rmeta_link::read(&archive, &archive_map, &archive_path),
479 AddArchiveKind::Other => None,
480 };
481 let archive_index = self.src_archives.len();
482
483 if let Some(expected_kind) =
484 target_archive_format_to_object_kind(&self.sess.target.archive_format)
485 {
486 let actual_kind = archive.kind();
487 if !archive_kinds_compatible(actual_kind, expected_kind) {
488 self.sess.dcx().emit_warn(crate::errors::IncompatibleArchiveFormat {
489 path: archive_path.clone(),
490 actual: archive_kind_display_name(actual_kind),
491 expected: archive_kind_display_name(expected_kind),
492 });
493 }
494 }
495
496 for entry in archive.members() {
497 let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
498 let file_name = String::from_utf8(entry.name().to_vec())
499 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
500 let kind = if metadata_link
501 .as_ref()
502 .is_some_and(|m| m.rust_object_files.iter().any(|f| f == &file_name))
503 {
504 ArchiveEntryKind::RustObj
505 } else {
506 ArchiveEntryKind::Other
507 };
508 let drop = match ar_kind {
509 AddArchiveKind::Rlib(skip) => skip(&file_name, kind),
510 AddArchiveKind::Other => false,
511 };
512 if !drop {
513 let source = if entry.is_thin() {
514 let member_path = archive_path.parent().unwrap().join(Path::new(&file_name));
515 ArchiveEntrySource::File(member_path)
516 } else {
517 ArchiveEntrySource::Archive { archive_index, file_range: entry.file_range() }
518 };
519 self.entries.push((file_name.into_bytes(), ArchiveEntry { source, kind }));
520 }
521 }
522
523 self.src_archives.push((archive_path, archive_map));
524 Ok(())
525 }
526
527 fn add_file(&mut self, file: &Path, kind: ArchiveEntryKind) {
529 self.entries.push((
530 file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
531 ArchiveEntry { source: ArchiveEntrySource::File(file.to_owned()), kind },
532 ));
533 }
534
535 fn build(self: Box<Self>, output: &Path, exported_symbols: Option<FxHashSet<String>>) -> bool {
538 let sess = self.sess;
539 match self.build_inner(output, exported_symbols) {
540 Ok(any_members) => any_members,
541 Err(error) => {
542 sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
543 }
544 }
545 }
546}
547
548impl<'a> ArArchiveBuilder<'a> {
549 fn build_inner(
550 self,
551 output: &Path,
552 exported_symbols: Option<FxHashSet<String>>,
553 ) -> io::Result<bool> {
554 let archive_kind = match &*self.sess.target.archive_format {
555 "gnu" => ArchiveKind::Gnu,
556 "bsd" => ArchiveKind::Bsd,
557 "darwin" => ArchiveKind::Darwin,
558 "coff" => ArchiveKind::Coff,
559 "aix_big" => ArchiveKind::AixBig,
560 kind => {
561 self.sess.dcx().emit_fatal(UnknownArchiveKind { kind });
562 }
563 };
564
565 let mut entries = Vec::new();
566
567 for (entry_name, entry) in self.entries {
568 let data: Box<dyn AsRef<[u8]>> = match entry.source {
569 ArchiveEntrySource::Archive { archive_index, file_range } => {
570 let src_archive = &self.src_archives[archive_index];
571 let archive_data = &src_archive.1;
572 let start = file_range.0 as usize;
573 let end = start + file_range.1 as usize;
574 let Some(data) = archive_data.get(start..end) else {
575 return Err(io_error_context(
576 "invalid archive member",
577 io::Error::new(
578 io::ErrorKind::InvalidData,
579 ::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!(
580 "archive member at offset {start} with size {} \
581 exceeds archive size {} in `{}`",
582 file_range.1,
583 archive_data.len(),
584 src_archive.0.display(),
585 ),
586 ),
587 ));
588 };
589
590 if entry.kind == ArchiveEntryKind::RustObj
591 && let Some(exported) = &exported_symbols
592 {
593 Box::new(apply_hide(data, exported))
594 } else {
595 Box::new(data)
596 }
597 }
598 ArchiveEntrySource::File(file) => unsafe {
599 let mmap = Mmap::map(
600 File::open(file)
601 .map_err(|err| io_error_context("failed to open object file", err))?,
602 )
603 .map_err(|err| io_error_context("failed to map object file", err))?;
604 if entry.kind == ArchiveEntryKind::RustObj
605 && let Some(exported) = &exported_symbols
606 {
607 Box::new(apply_hide(&mmap, exported))
608 } else {
609 Box::new(mmap) as Box<dyn AsRef<[u8]>>
610 }
611 },
612 };
613
614 entries.push(NewArchiveMember {
615 buf: data,
616 object_reader: self.object_reader,
617 member_name: String::from_utf8(entry_name).unwrap(),
618 mtime: 0,
619 uid: 0,
620 gid: 0,
621 perms: 0o644,
622 })
623 }
624
625 let archive_tmpdir = TempDirBuilder::new()
634 .suffix(".temp-archive")
635 .tempdir_in(output.parent().unwrap_or_else(|| Path::new("")))
636 .map_err(|err| {
637 io_error_context("couldn't create a directory for the temp file", err)
638 })?;
639 let archive_tmpfile_path = archive_tmpdir.path().join("tmp.a");
640 let archive_tmpfile = File::create_new(&archive_tmpfile_path)
641 .map_err(|err| io_error_context("couldn't create the temp file", err))?;
642
643 let mut archive_tmpfile = BufWriter::new(archive_tmpfile);
644 write_archive_to_stream(
645 &mut archive_tmpfile,
646 &entries,
647 archive_kind,
648 false,
649 Some(self.sess.target.arch == Arch::Arm64EC),
650 )?;
651 archive_tmpfile.flush()?;
652 drop(archive_tmpfile);
653
654 let any_entries = !entries.is_empty();
655 drop(entries);
656 drop(self.src_archives);
659
660 fs::rename(archive_tmpfile_path, output)
661 .map_err(|err| io_error_context("failed to rename archive file", err))?;
662 archive_tmpdir
663 .close()
664 .map_err(|err| io_error_context("failed to remove temporary directory", err))?;
665
666 Ok(any_entries)
667 }
668}
669
670fn io_error_context(context: &str, err: io::Error) -> io::Error {
671 io::Error::new(io::ErrorKind::Other, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: {1}", context, err))
})format!("{context}: {err}"))
672}