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