summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_ssa/src/back
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/back')
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs265
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs882
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs82
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs42
6 files changed, 728 insertions, 565 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index bb76ca5d2..58558fb8c 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -4,13 +4,22 @@ use rustc_session::cstore::DllImport;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
+use super::metadata::search_for_section;
+
+pub use ar_archive_writer::get_native_object_symbols;
+use ar_archive_writer::{write_archive_to_stream, ArchiveKind, NewArchiveMember};
use object::read::archive::ArchiveFile;
+use object::read::macho::FatArch;
+use tempfile::Builder as TempFileBuilder;
-use std::fmt::Display;
+use std::error::Error;
use std::fs::File;
-use std::io;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
+// Re-exporting for rustc_codegen_llvm::back::archive
+pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
+
pub trait ArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>;
@@ -28,32 +37,38 @@ pub trait ArchiveBuilderBuilder {
is_direct_dependency: bool,
) -> PathBuf;
- fn extract_bundled_libs(
- &self,
- rlib: &Path,
+ fn extract_bundled_libs<'a>(
+ &'a self,
+ rlib: &'a Path,
outdir: &Path,
bundled_lib_file_names: &FxHashSet<Symbol>,
- ) -> Result<(), String> {
- let message = |msg: &str, e: &dyn Display| format!("{} '{}': {}", msg, &rlib.display(), e);
+ ) -> Result<(), ExtractBundledLibsError<'_>> {
let archive_map = unsafe {
- Mmap::map(File::open(rlib).map_err(|e| message("failed to open file", &e))?)
- .map_err(|e| message("failed to mmap file", &e))?
+ Mmap::map(
+ File::open(rlib)
+ .map_err(|e| ExtractBundledLibsError::OpenFile { rlib, error: Box::new(e) })?,
+ )
+ .map_err(|e| ExtractBundledLibsError::MmapFile { rlib, error: Box::new(e) })?
};
let archive = ArchiveFile::parse(&*archive_map)
- .map_err(|e| message("failed to parse archive", &e))?;
+ .map_err(|e| ExtractBundledLibsError::ParseArchive { rlib, error: Box::new(e) })?;
for entry in archive.members() {
- let entry = entry.map_err(|e| message("failed to read entry", &e))?;
+ let entry = entry
+ .map_err(|e| ExtractBundledLibsError::ReadEntry { rlib, error: Box::new(e) })?;
let data = entry
.data(&*archive_map)
- .map_err(|e| message("failed to get data from archive member", &e))?;
+ .map_err(|e| ExtractBundledLibsError::ArchiveMember { rlib, error: Box::new(e) })?;
let name = std::str::from_utf8(entry.name())
- .map_err(|e| message("failed to convert name", &e))?;
+ .map_err(|e| ExtractBundledLibsError::ConvertName { rlib, error: Box::new(e) })?;
if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
continue; // We need to extract only native libraries.
}
+ let data = search_for_section(rlib, data, ".bundled_lib").map_err(|e| {
+ ExtractBundledLibsError::ExtractSection { rlib, error: Box::<dyn Error>::from(e) }
+ })?;
std::fs::write(&outdir.join(&name), data)
- .map_err(|e| message("failed to write file", &e))?;
+ .map_err(|e| ExtractBundledLibsError::WriteFile { rlib, error: Box::new(e) })?;
}
Ok(())
}
@@ -70,3 +85,225 @@ pub trait ArchiveBuilder<'a> {
fn build(self: Box<Self>, output: &Path) -> bool;
}
+
+#[must_use = "must call build() to finish building the archive"]
+pub struct ArArchiveBuilder<'a> {
+ sess: &'a Session,
+ get_object_symbols:
+ fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
+
+ src_archives: Vec<(PathBuf, Mmap)>,
+ // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs
+ // to be at the end of an archive in some cases for linkers to not get confused.
+ entries: Vec<(Vec<u8>, ArchiveEntry)>,
+}
+
+#[derive(Debug)]
+enum ArchiveEntry {
+ FromArchive { archive_index: usize, file_range: (u64, u64) },
+ File(PathBuf),
+}
+
+impl<'a> ArArchiveBuilder<'a> {
+ pub fn new(
+ sess: &'a Session,
+ get_object_symbols: fn(
+ buf: &[u8],
+ f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
+ ) -> io::Result<bool>,
+ ) -> ArArchiveBuilder<'a> {
+ ArArchiveBuilder { sess, get_object_symbols, src_archives: vec![], entries: vec![] }
+ }
+}
+
+fn try_filter_fat_archs(
+ archs: object::read::Result<&[impl FatArch]>,
+ target_arch: object::Architecture,
+ archive_path: &Path,
+ archive_map_data: &[u8],
+) -> io::Result<Option<PathBuf>> {
+ let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+ let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
+ Some(a) => a,
+ None => return Ok(None),
+ };
+
+ let (mut new_f, extracted_path) = tempfile::Builder::new()
+ .suffix(archive_path.file_name().unwrap())
+ .tempfile()?
+ .keep()
+ .unwrap();
+
+ new_f.write_all(
+ desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
+ )?;
+
+ Ok(Some(extracted_path))
+}
+
+pub fn try_extract_macho_fat_archive(
+ sess: &Session,
+ archive_path: &Path,
+) -> io::Result<Option<PathBuf>> {
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let target_arch = match sess.target.arch.as_ref() {
+ "aarch64" => object::Architecture::Aarch64,
+ "x86_64" => object::Architecture::X86_64,
+ _ => return Ok(None),
+ };
+
+ match object::macho::FatHeader::parse(&*archive_map) {
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
+ let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
+ let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ // Not a FatHeader at all, just return None.
+ _ => Ok(None),
+ }
+}
+
+impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
+ fn add_archive(
+ &mut self,
+ archive_path: &Path,
+ mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
+ ) -> io::Result<()> {
+ let mut archive_path = archive_path.to_path_buf();
+ if self.sess.target.llvm_target.contains("-apple-macosx") {
+ if let Some(new_archive_path) =
+ try_extract_macho_fat_archive(&self.sess, &archive_path)?
+ {
+ archive_path = new_archive_path
+ }
+ }
+
+ if self.src_archives.iter().any(|archive| archive.0 == archive_path) {
+ return Ok(());
+ }
+
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let archive = ArchiveFile::parse(&*archive_map)
+ .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ let archive_index = self.src_archives.len();
+
+ for entry in archive.members() {
+ let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ let file_name = String::from_utf8(entry.name().to_vec())
+ .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ if !skip(&file_name) {
+ self.entries.push((
+ file_name.into_bytes(),
+ ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
+ ));
+ }
+ }
+
+ self.src_archives.push((archive_path.to_owned(), archive_map));
+ Ok(())
+ }
+
+ /// Adds an arbitrary file to this archive
+ fn add_file(&mut self, file: &Path) {
+ self.entries.push((
+ file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
+ ArchiveEntry::File(file.to_owned()),
+ ));
+ }
+
+ /// Combine the provided files, rlibs, and native libraries into a single
+ /// `Archive`.
+ fn build(self: Box<Self>, output: &Path) -> bool {
+ let sess = self.sess;
+ match self.build_inner(output) {
+ Ok(any_members) => any_members,
+ Err(e) => sess.emit_fatal(ArchiveBuildFailure { error: e }),
+ }
+ }
+}
+
+impl<'a> ArArchiveBuilder<'a> {
+ fn build_inner(self, output: &Path) -> io::Result<bool> {
+ let archive_kind = match &*self.sess.target.archive_format {
+ "gnu" => ArchiveKind::Gnu,
+ "bsd" => ArchiveKind::Bsd,
+ "darwin" => ArchiveKind::Darwin,
+ "coff" => ArchiveKind::Coff,
+ kind => {
+ self.sess.emit_fatal(UnknownArchiveKind { kind });
+ }
+ };
+
+ let mut entries = Vec::new();
+
+ for (entry_name, entry) in self.entries {
+ let data =
+ match entry {
+ ArchiveEntry::FromArchive { archive_index, file_range } => {
+ let src_archive = &self.src_archives[archive_index];
+
+ let data = &src_archive.1
+ [file_range.0 as usize..file_range.0 as usize + file_range.1 as usize];
+
+ Box::new(data) as Box<dyn AsRef<[u8]>>
+ }
+ ArchiveEntry::File(file) => unsafe {
+ Box::new(
+ Mmap::map(File::open(file).map_err(|err| {
+ io_error_context("failed to open object file", err)
+ })?)
+ .map_err(|err| io_error_context("failed to map object file", err))?,
+ ) as Box<dyn AsRef<[u8]>>
+ },
+ };
+
+ entries.push(NewArchiveMember {
+ buf: data,
+ get_symbols: self.get_object_symbols,
+ member_name: String::from_utf8(entry_name).unwrap(),
+ mtime: 0,
+ uid: 0,
+ gid: 0,
+ perms: 0o644,
+ })
+ }
+
+ // Write to a temporary file first before atomically renaming to the final name.
+ // This prevents programs (including rustc) from attempting to read a partial archive.
+ // It also enables writing an archive with the same filename as a dependency on Windows as
+ // required by a test.
+ let mut archive_tmpfile = TempFileBuilder::new()
+ .suffix(".temp-archive")
+ .tempfile_in(output.parent().unwrap_or_else(|| Path::new("")))
+ .map_err(|err| io_error_context("couldn't create a temp file", err))?;
+
+ write_archive_to_stream(
+ archive_tmpfile.as_file_mut(),
+ &entries,
+ true,
+ archive_kind,
+ true,
+ false,
+ )?;
+
+ let any_entries = !entries.is_empty();
+ drop(entries);
+ // Drop src_archives to unmap all input archives, which is necessary if we want to write the
+ // output archive to the same location as an input archive on Windows.
+ drop(self.src_archives);
+
+ archive_tmpfile
+ .persist(output)
+ .map_err(|err| io_error_context("failed to rename archive file", err.error))?;
+
+ Ok(any_entries)
+ }
+}
+
+fn io_error_context(context: &str, err: io::Error) -> io::Error {
+ io::Error::new(io::ErrorKind::Other, format!("{context}: {err}"))
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 0dc0dee86..fe2e4b36c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -6,9 +6,9 @@ use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, Handler};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use rustc_hir::def_id::CrateNum;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
-use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
+use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Lto, Strip};
@@ -24,12 +24,12 @@ use rustc_span::symbol::Symbol;
use rustc_span::DebuggerVisualizerFile;
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy};
-use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, Target};
+use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
use super::command::Command;
use super::linker::{self, Linker};
-use super::metadata::{create_rmeta_file, MetadataPosition};
+use super::metadata::{create_wrapper_file, MetadataPosition};
use super::rpath::{self, RPathConfig};
use crate::{
errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
@@ -44,7 +44,7 @@ use std::borrow::Borrow;
use std::cell::OnceCell;
use std::collections::BTreeSet;
use std::ffi::OsString;
-use std::fs::{File, OpenOptions};
+use std::fs::{read, File, OpenOptions};
use std::io::{BufWriter, Write};
use std::ops::Deref;
use std::path::{Path, PathBuf};
@@ -102,7 +102,7 @@ pub fn link_binary<'a>(
sess,
crate_type,
outputs,
- codegen_results.crate_info.local_crate_name.as_str(),
+ codegen_results.crate_info.local_crate_name,
);
match crate_type {
CrateType::Rlib => {
@@ -253,7 +253,7 @@ pub fn each_linked_rlib(
};
for &cnum in crates {
match fmts.get(cnum.as_usize() - 1) {
- Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue,
+ Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue,
Some(_) => {}
None => return Err(errors::LinkRlibError::MissingFormat),
}
@@ -292,8 +292,8 @@ fn link_rlib<'a>(
let trailing_metadata = match flavor {
RlibFlavor::Normal => {
let (metadata, metadata_position) =
- create_rmeta_file(sess, codegen_results.metadata.raw_data());
- let metadata = emit_metadata(sess, &metadata, tmpdir);
+ create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data());
+ let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
match metadata_position {
MetadataPosition::First => {
// Most of the time metadata in rlib files is wrapped in a "dummy" object
@@ -376,12 +376,14 @@ fn link_rlib<'a>(
let location =
find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
- packed_bundled_libs.push(find_native_static_library(
- lib.filename.unwrap().as_str(),
- Some(true),
- &lib_search_paths,
- sess,
- ));
+ let filename = lib.filename.unwrap();
+ let lib_path =
+ find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
+ let src = read(lib_path)
+ .map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?;
+ let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
+ let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
+ packed_bundled_libs.push(wrapper_file);
continue;
}
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| {
@@ -459,7 +461,7 @@ fn collate_raw_dylibs<'a, 'b>(
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
- let ext = if matches!(lib.verbatim, Some(true)) { "" } else { ".dll" };
+ let ext = if lib.verbatim { "" } else { ".dll" };
let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext);
let imports = dylib_table.entry(name.clone()).or_default();
for import in &lib.dll_imports {
@@ -670,8 +672,7 @@ fn link_dwarf_object<'a>(
thorin::MissingReferencedObjectBehaviour::Skip,
)?;
- let output = package.finish()?.write()?;
- let mut output_stream = BufWriter::new(
+ let output_stream = BufWriter::new(
OpenOptions::new()
.read(true)
.write(true)
@@ -679,8 +680,10 @@ fn link_dwarf_object<'a>(
.truncate(true)
.open(dwp_out_filename)?,
);
- output_stream.write_all(&output)?;
- output_stream.flush()?;
+ let mut output_stream = object::write::StreamingBuffer::new(output_stream);
+ package.finish()?.emit(&mut output_stream)?;
+ output_stream.result()?;
+ output_stream.into_inner().flush()?;
Ok(())
}) {
@@ -919,29 +922,17 @@ fn link_natively<'a>(
)
.is_some();
- sess.note_without_error("`link.exe` returned an unexpected error");
+ sess.emit_note(errors::LinkExeUnexpectedError);
if is_vs_installed && has_linker {
// the linker is broken
- sess.note_without_error(
- "the Visual Studio build tools may need to be repaired \
- using the Visual Studio installer",
- );
- sess.note_without_error(
- "or a necessary component may be missing from the \
- \"C++ build tools\" workload",
- );
+ sess.emit_note(errors::RepairVSBuildTools);
+ sess.emit_note(errors::MissingCppBuildToolComponent);
} else if is_vs_installed {
// the linker is not installed
- sess.note_without_error(
- "in the Visual Studio installer, ensure the \
- \"C++ build tools\" workload is selected",
- );
+ sess.emit_note(errors::SelectCppBuildToolWorkload);
} else {
// visual studio is not installed
- sess.note_without_error(
- "you may need to install Visual Studio build tools with the \
- \"C++ build tools\" workload",
- );
+ sess.emit_note(errors::VisualStudioNotInstalled);
}
}
}
@@ -954,35 +945,20 @@ fn link_natively<'a>(
Err(e) => {
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
- let mut linker_error = {
- if linker_not_found {
- sess.struct_err(&format!("linker `{}` not found", linker_path.display()))
- } else {
- sess.struct_err(&format!(
- "could not exec the linker `{}`",
- linker_path.display()
- ))
- }
- };
-
- linker_error.note(&e.to_string());
-
- if !linker_not_found {
- linker_error.note(&format!("{:?}", &cmd));
+ if linker_not_found {
+ sess.emit_err(errors::LinkerNotFound { linker_path, error: e });
+ } else {
+ sess.emit_err(errors::UnableToExeLinker {
+ linker_path,
+ error: e,
+ command_formatted: format!("{:?}", &cmd),
+ });
}
- linker_error.emit();
-
if sess.target.is_like_msvc && linker_not_found {
- sess.note_without_error(
- "the msvc targets depend on the msvc linker \
- but `link.exe` was not found",
- );
- sess.note_without_error(
- "please ensure that Visual Studio 2017 or later, or Build Tools \
- for Visual Studio were installed with the Visual C++ option.",
- );
- sess.note_without_error("VS Code is a different product, and is not sufficient.");
+ sess.emit_note(errors::MsvcMissingLinker);
+ sess.emit_note(errors::CheckInstalledVisualStudio);
+ sess.emit_note(errors::UnsufficientVSCodeProduct);
}
sess.abort_if_errors();
}
@@ -1007,15 +983,13 @@ fn link_natively<'a>(
if !prog.status.success() {
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
- sess.struct_warn(&format!(
- "processing debug info with `dsymutil` failed: {}",
- prog.status
- ))
- .note(&escape_string(&output))
- .emit();
+ sess.emit_warning(errors::ProcessingDymutilFailed {
+ status: prog.status,
+ output: escape_string(&output),
+ });
}
}
- Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)),
+ Err(error) => sess.emit_fatal(errors::UnableToRunDsymutil { error }),
}
}
@@ -1092,15 +1066,14 @@ fn strip_symbols_with_external_utility<'a>(
if !prog.status.success() {
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
- sess.struct_warn(&format!(
- "stripping debug info with `{}` failed: {}",
- util, prog.status
- ))
- .note(&escape_string(&output))
- .emit();
+ sess.emit_warning(errors::StrippingDebugInfoFailed {
+ util,
+ status: prog.status,
+ output: escape_string(&output),
+ });
}
}
- Err(e) => sess.fatal(&format!("unable to run `{}`: {}", util, e)),
+ Err(error) => sess.emit_fatal(errors::UnableToRun { util, error }),
}
}
@@ -1153,7 +1126,8 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
if path.exists() {
return session_tlib;
} else {
- let default_sysroot = filesearch::get_or_default_sysroot();
+ let default_sysroot =
+ filesearch::get_or_default_sysroot().expect("Failed finding sysroot");
let default_tlib = filesearch::make_target_lib_path(
&default_sysroot,
sess.opts.target_triple.triple(),
@@ -1201,7 +1175,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool
&& (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
}
-// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
+/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
fn infer_from(
sess: &Session,
@@ -1251,7 +1225,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
)),
(Some(linker), None) => {
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
- sess.fatal("couldn't extract file stem from specified linker")
+ sess.emit_fatal(errors::LinkerFileStem);
});
let flavor = if stem == "emcc" {
@@ -1357,7 +1331,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Unspecified => {
- let verbatim = lib.verbatim.unwrap_or(false);
+ let verbatim = lib.verbatim;
if sess.target.is_like_msvc {
Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
} else if sess.target.linker_flavor.is_gnu() {
@@ -1378,13 +1352,9 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
})
.collect();
if !lib_args.is_empty() {
- sess.note_without_error(
- "Link against the following native artifacts when linking \
- against this static library. The order and any duplication \
- can be significant on some platforms.",
- );
+ sess.emit_note(errors::StaticLibraryNativeArtifacts);
// Prefix for greppability
- sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" ")));
+ sess.emit_note(errors::NativeStaticLibs { arguments: lib_args.join(" ") });
}
}
@@ -1615,6 +1585,9 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
/// We only provide such support for a very limited number of targets.
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
if let Some(self_contained) = sess.opts.cg.link_self_contained {
+ if sess.target.link_self_contained == LinkSelfContainedDefault::False {
+ sess.emit_err(errors::UnsupportedLinkSelfContained);
+ }
return self_contained;
}
@@ -1688,14 +1661,14 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
match (crate_type, &sess.target.link_script) {
(CrateType::Cdylib | CrateType::Executable, Some(script)) => {
if !sess.target.linker_flavor.is_gnu() {
- sess.fatal("can only use link script when linking with GNU-like linker");
+ sess.emit_fatal(errors::LinkScriptUnavailable);
}
let file_name = ["rustc", &sess.target.llvm_target, "linkfile.ld"].join("-");
let path = tmpdir.join(file_name);
- if let Err(e) = fs::write(&path, script.as_ref()) {
- sess.fatal(&format!("failed to write link script to {}: {}", path.display(), e));
+ if let Err(error) = fs::write(&path, script.as_ref()) {
+ sess.emit_fatal(errors::LinkScriptWriteFailure { path, error });
}
cmd.arg("--script");
@@ -1841,8 +1814,8 @@ fn add_linked_symbol_object(
let path = tmpdir.join("symbols.o");
let result = std::fs::write(&path, file.write().unwrap());
- if let Err(e) = result {
- sess.fatal(&format!("failed to write {}: {}", path.display(), e));
+ if let Err(error) = result {
+ sess.emit_fatal(errors::FailedToWrite { path, error });
}
cmd.add_object(&path);
}
@@ -2040,15 +2013,9 @@ fn linker_with_args<'a>(
cmd.add_as_needed();
// Local native libraries of all kinds.
- //
- // If `-Zlink-native-libraries=false` is set, then the assumption is that an
- // external build system already has the native dependencies defined, and it
- // will provide them to the linker itself.
- if sess.opts.unstable_opts.link_native_libraries {
- add_local_native_libraries(cmd, sess, codegen_results);
- }
+ add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
- // Upstream rust libraries and their (possibly bundled) static native libraries.
+ // Upstream rust crates and their non-dynamic native libraries.
add_upstream_rust_crates(
cmd,
sess,
@@ -2059,13 +2026,7 @@ fn linker_with_args<'a>(
);
// Dynamic native libraries from upstream crates.
- //
- // FIXME: Merge this to `add_upstream_rust_crates` so that all native libraries are linked
- // together with their respective upstream crates, and in their originally specified order.
- // This may be slightly breaking due to our use of `--as-needed` and needs a crater run.
- if sess.opts.unstable_opts.link_native_libraries {
- add_upstream_native_libraries(cmd, sess, codegen_results);
- }
+ add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
// Link with the import library generated for any raw-dylib functions.
for (raw_dylib_name, raw_dylib_imports) in
@@ -2299,56 +2260,56 @@ fn collect_natvis_visualizers(
visualizer_paths.push(visualizer_out_file);
}
Err(error) => {
- sess.warn(
- format!(
- "Unable to write debugger visualizer file `{}`: {} ",
- visualizer_out_file.display(),
- error
- )
- .as_str(),
- );
+ sess.emit_warning(errors::UnableToWriteDebuggerVisualizer {
+ path: visualizer_out_file,
+ error,
+ });
}
};
}
visualizer_paths
}
-/// # Native library linking
-///
-/// User-supplied library search paths (-L on the command line). These are the same paths used to
-/// find Rust crates, so some of them may have been added already by the previous crate linking
-/// code. This only allows them to be found at compile time so it is still entirely up to outside
-/// forces to make sure that library can be found at runtime.
-///
-/// Also note that the native libraries linked here are only the ones located in the current crate.
-/// Upstream crates with native library dependencies may have their native library pulled in above.
-fn add_local_native_libraries(
+fn add_native_libs_from_crate(
cmd: &mut dyn Linker,
sess: &Session,
+ archive_builder_builder: &dyn ArchiveBuilderBuilder,
codegen_results: &CodegenResults,
+ tmpdir: &Path,
+ search_paths: &OnceCell<Vec<PathBuf>>,
+ bundled_libs: &FxHashSet<Symbol>,
+ cnum: CrateNum,
+ link_static: bool,
+ link_dynamic: bool,
) {
- let filesearch = sess.target_filesearch(PathKind::All);
- for search_path in filesearch.search_paths() {
- match search_path.kind {
- PathKind::Framework => {
- cmd.framework_path(&search_path.dir);
- }
- _ => {
- cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir));
- }
- }
+ if !sess.opts.unstable_opts.link_native_libraries {
+ // If `-Zlink-native-libraries=false` is set, then the assumption is that an
+ // external build system already has the native dependencies defined, and it
+ // will provide them to the linker itself.
+ return;
}
- let relevant_libs =
- codegen_results.crate_info.used_libraries.iter().filter(|l| relevant_lib(sess, l));
+ if link_static && cnum != LOCAL_CRATE && !bundled_libs.is_empty() {
+ // If rlib contains native libs as archives, unpack them to tmpdir.
+ let rlib = &codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap().0;
+ archive_builder_builder
+ .extract_bundled_libs(rlib, tmpdir, &bundled_libs)
+ .unwrap_or_else(|e| sess.emit_fatal(e));
+ }
- let search_path = OnceCell::new();
- let mut last = (None, NativeLibKind::Unspecified, None);
- for lib in relevant_libs {
+ let native_libs = match cnum {
+ LOCAL_CRATE => &codegen_results.crate_info.used_libraries,
+ _ => &codegen_results.crate_info.native_libraries[&cnum],
+ };
+
+ let mut last = (None, NativeLibKind::Unspecified, false);
+ for lib in native_libs {
let Some(name) = lib.name else {
continue;
};
- let name = name.as_str();
+ if !relevant_lib(sess, lib) {
+ continue;
+ }
// Skip if this library is the same as the last.
last = if (lib.name, lib.kind, lib.verbatim) == last {
@@ -2357,46 +2318,110 @@ fn add_local_native_libraries(
(lib.name, lib.kind, lib.verbatim)
};
- let verbatim = lib.verbatim.unwrap_or(false);
+ let name = name.as_str();
+ let verbatim = lib.verbatim;
match lib.kind {
+ NativeLibKind::Static { bundle, whole_archive } => {
+ if link_static {
+ let bundle = bundle.unwrap_or(true);
+ let whole_archive = whole_archive == Some(true)
+ // Backward compatibility case: this can be a rlib (so `+whole-archive`
+ // cannot be added explicitly if necessary, see the error in `fn link_rlib`)
+ // compiled as an executable due to `--test`. Use whole-archive implicitly,
+ // like before the introduction of native lib modifiers.
+ || (whole_archive == None
+ && bundle
+ && cnum == LOCAL_CRATE
+ && sess.opts.test);
+
+ if bundle && cnum != LOCAL_CRATE {
+ if let Some(filename) = lib.filename {
+ // If rlib contains native libs as archives, they are unpacked to tmpdir.
+ let path = tmpdir.join(filename.as_str());
+ if whole_archive {
+ cmd.link_whole_rlib(&path);
+ } else {
+ cmd.link_rlib(&path);
+ }
+ }
+ } else {
+ if whole_archive {
+ cmd.link_whole_staticlib(
+ name,
+ verbatim,
+ &search_paths.get_or_init(|| archive_search_paths(sess)),
+ );
+ } else {
+ cmd.link_staticlib(name, verbatim)
+ }
+ }
+ }
+ }
NativeLibKind::Dylib { as_needed } => {
- cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+ if link_dynamic {
+ cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+ }
}
- NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
- NativeLibKind::Framework { as_needed } => {
- cmd.link_framework(name, as_needed.unwrap_or(true))
+ NativeLibKind::Unspecified => {
+ if link_dynamic {
+ cmd.link_dylib(name, verbatim, true);
+ }
}
- NativeLibKind::Static { whole_archive, bundle, .. } => {
- if whole_archive == Some(true)
- // Backward compatibility case: this can be a rlib (so `+whole-archive` cannot
- // be added explicitly if necessary, see the error in `fn link_rlib`) compiled
- // as an executable due to `--test`. Use whole-archive implicitly, like before
- // the introduction of native lib modifiers.
- || (whole_archive == None && bundle != Some(false) && sess.opts.test)
- {
- cmd.link_whole_staticlib(
- name,
- verbatim,
- &search_path.get_or_init(|| archive_search_paths(sess)),
- );
- } else {
- cmd.link_staticlib(name, verbatim)
+ NativeLibKind::Framework { as_needed } => {
+ if link_dynamic {
+ cmd.link_framework(name, as_needed.unwrap_or(true))
}
}
NativeLibKind::RawDylib => {
- // Ignore RawDylib here, they are handled separately in linker_with_args().
+ // Handled separately in `linker_with_args`.
}
NativeLibKind::LinkArg => {
- cmd.arg(name);
+ if link_static {
+ cmd.arg(name);
+ }
}
}
}
}
-/// # Linking Rust crates and their non-bundled static libraries
-///
-/// Rust crates are not considered at all when creating an rlib output. All dependencies will be
-/// linked when producing the final output (instead of the intermediate rlib version).
+fn add_local_native_libraries(
+ cmd: &mut dyn Linker,
+ sess: &Session,
+ archive_builder_builder: &dyn ArchiveBuilderBuilder,
+ codegen_results: &CodegenResults,
+ tmpdir: &Path,
+) {
+ if sess.opts.unstable_opts.link_native_libraries {
+ // User-supplied library search paths (-L on the command line). These are the same paths
+ // used to find Rust crates, so some of them may have been added already by the previous
+ // crate linking code. This only allows them to be found at compile time so it is still
+ // entirely up to outside forces to make sure that library can be found at runtime.
+ for search_path in sess.target_filesearch(PathKind::All).search_paths() {
+ match search_path.kind {
+ PathKind::Framework => cmd.framework_path(&search_path.dir),
+ _ => cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)),
+ }
+ }
+ }
+
+ let search_paths = OnceCell::new();
+ // All static and dynamic native library dependencies are linked to the local crate.
+ let link_static = true;
+ let link_dynamic = true;
+ add_native_libs_from_crate(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ &search_paths,
+ &Default::default(),
+ LOCAL_CRATE,
+ link_static,
+ link_dynamic,
+ );
+}
+
fn add_upstream_rust_crates<'a>(
cmd: &mut dyn Linker,
sess: &'a Session,
@@ -2412,7 +2437,6 @@ fn add_upstream_rust_crates<'a>(
// Linking to a rlib involves just passing it to the linker (the linker
// will slurp up the object files inside), and linking to a dynamic library
// involves just passing the right -l flag.
-
let (_, data) = codegen_results
.crate_info
.dependency_formats
@@ -2420,346 +2444,234 @@ fn add_upstream_rust_crates<'a>(
.find(|(ty, _)| *ty == crate_type)
.expect("failed to find crate type in dependency format list");
- // Invoke get_used_crates to ensure that we get a topological sorting of
- // crates.
- let deps = &codegen_results.crate_info.used_crates;
-
- let mut compiler_builtins = None;
- let search_path = OnceCell::new();
-
- for &cnum in deps.iter() {
- // We may not pass all crates through to the linker. Some crates may
- // appear statically in an existing dylib, meaning we'll pick up all the
- // symbols from the dylib.
- let src = &codegen_results.crate_info.used_crate_source[&cnum];
- match data[cnum.as_usize() - 1] {
- _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
- add_static_crate(
- cmd,
- sess,
- archive_builder_builder,
- codegen_results,
- tmpdir,
- cnum,
- &Default::default(),
- );
- }
- // compiler-builtins are always placed last to ensure that they're
- // linked correctly.
- _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => {
- assert!(compiler_builtins.is_none());
- compiler_builtins = Some(cnum);
- }
- Linkage::NotLinked | Linkage::IncludedFromDylib => {}
- Linkage::Static => {
- let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs {
- codegen_results.crate_info.native_libraries[&cnum]
+ let search_paths = OnceCell::new();
+ for &cnum in &codegen_results.crate_info.used_crates {
+ // We may not pass all crates through to the linker. Some crates may appear statically in
+ // an existing dylib, meaning we'll pick up all the symbols from the dylib.
+ // We must always link crates `compiler_builtins` and `profiler_builtins` statically.
+ // Even if they were already included into a dylib
+ // (e.g. `libstd` when `-C prefer-dynamic` is used).
+ // FIXME: `dependency_formats` can report `profiler_builtins` as `NotLinked` for some
+ // reason, it shouldn't do that because `profiler_builtins` should indeed be linked.
+ let linkage = data[cnum.as_usize() - 1];
+ let link_static_crate = linkage == Linkage::Static
+ || (linkage == Linkage::IncludedFromDylib || linkage == Linkage::NotLinked)
+ && (codegen_results.crate_info.compiler_builtins == Some(cnum)
+ || codegen_results.crate_info.profiler_runtime == Some(cnum));
+
+ let mut bundled_libs = Default::default();
+ match linkage {
+ Linkage::Static | Linkage::IncludedFromDylib | Linkage::NotLinked => {
+ if link_static_crate {
+ bundled_libs = codegen_results.crate_info.native_libraries[&cnum]
.iter()
.filter_map(|lib| lib.filename)
- .collect::<FxHashSet<_>>()
- } else {
- Default::default()
- };
- add_static_crate(
- cmd,
- sess,
- archive_builder_builder,
- codegen_results,
- tmpdir,
- cnum,
- &bundled_libs,
- );
-
- // Link static native libs with "-bundle" modifier only if the crate they originate from
- // is being linked statically to the current crate. If it's linked dynamically
- // or is an rlib already included via some other dylib crate, the symbols from
- // native libs will have already been included in that dylib.
- //
- // If `-Zlink-native-libraries=false` is set, then the assumption is that an
- // external build system already has the native dependencies defined, and it
- // will provide them to the linker itself.
- if sess.opts.unstable_opts.link_native_libraries {
- if sess.opts.unstable_opts.packed_bundled_libs {
- // If rlib contains native libs as archives, unpack them to tmpdir.
- let rlib = &src.rlib.as_ref().unwrap().0;
- archive_builder_builder
- .extract_bundled_libs(rlib, tmpdir, &bundled_libs)
- .unwrap_or_else(|e| sess.fatal(e));
- }
-
- let mut last = (None, NativeLibKind::Unspecified, None);
- for lib in &codegen_results.crate_info.native_libraries[&cnum] {
- let Some(name) = lib.name else {
- continue;
- };
- let name = name.as_str();
- if !relevant_lib(sess, lib) {
- continue;
- }
-
- // Skip if this library is the same as the last.
- last = if (lib.name, lib.kind, lib.verbatim) == last {
- continue;
- } else {
- (lib.name, lib.kind, lib.verbatim)
- };
-
- match lib.kind {
- NativeLibKind::Static {
- bundle: Some(false),
- whole_archive: Some(true),
- } => {
- cmd.link_whole_staticlib(
- name,
- lib.verbatim.unwrap_or(false),
- search_path.get_or_init(|| archive_search_paths(sess)),
- );
- }
- NativeLibKind::Static {
- bundle: Some(false),
- whole_archive: Some(false) | None,
- } => {
- // HACK/FIXME: Fixup a circular dependency between libgcc and libc
- // with glibc. This logic should be moved to the libc crate.
- if sess.target.os == "linux"
- && sess.target.env == "gnu"
- && name == "c"
- {
- cmd.link_staticlib("gcc", false);
- }
- cmd.link_staticlib(name, lib.verbatim.unwrap_or(false));
- }
- NativeLibKind::LinkArg => {
- cmd.arg(name);
- }
- NativeLibKind::Dylib { .. }
- | NativeLibKind::Framework { .. }
- | NativeLibKind::Unspecified
- | NativeLibKind::RawDylib => {}
- NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => {
- if sess.opts.unstable_opts.packed_bundled_libs {
- // If rlib contains native libs as archives, they are unpacked to tmpdir.
- let path = tmpdir.join(lib.filename.unwrap().as_str());
- if whole_archive == Some(true) {
- cmd.link_whole_rlib(&path);
- } else {
- cmd.link_rlib(&path);
- }
- }
- }
- }
- }
+ .collect();
+ add_static_crate(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ cnum,
+ &bundled_libs,
+ );
}
}
- Linkage::Dynamic => add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0),
+ Linkage::Dynamic => {
+ let src = &codegen_results.crate_info.used_crate_source[&cnum];
+ add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0);
+ }
}
- }
- // compiler-builtins are always placed last to ensure that they're
- // linked correctly.
- // We must always link the `compiler_builtins` crate statically. Even if it
- // was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
- // is used)
- if let Some(cnum) = compiler_builtins {
- add_static_crate(
+ // Static libraries are linked for a subset of linked upstream crates.
+ // 1. If the upstream crate is a directly linked rlib then we must link the native library
+ // because the rlib is just an archive.
+ // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we do not link
+ // the native library because it is already linked into the dylib, and even if
+ // inline/const/generic functions from the dylib can refer to symbols from the native
+ // library, those symbols should be exported and available from the dylib anyway.
+ // 3. Libraries bundled into `(compiler,profiler)_builtins` are special, see above.
+ let link_static = link_static_crate;
+ // Dynamic libraries are not linked here, see the FIXME in `add_upstream_native_libraries`.
+ let link_dynamic = false;
+ add_native_libs_from_crate(
cmd,
sess,
archive_builder_builder,
codegen_results,
tmpdir,
+ &search_paths,
+ &bundled_libs,
cnum,
- &Default::default(),
+ link_static,
+ link_dynamic,
);
}
+}
- // Converts a library file-stem into a cc -l argument
- fn unlib<'a>(target: &Target, stem: &'a str) -> &'a str {
- if stem.starts_with("lib") && !target.is_like_windows { &stem[3..] } else { stem }
+fn add_upstream_native_libraries(
+ cmd: &mut dyn Linker,
+ sess: &Session,
+ archive_builder_builder: &dyn ArchiveBuilderBuilder,
+ codegen_results: &CodegenResults,
+ tmpdir: &Path,
+) {
+ let search_path = OnceCell::new();
+ for &cnum in &codegen_results.crate_info.used_crates {
+ // Static libraries are not linked here, they are linked in `add_upstream_rust_crates`.
+ // FIXME: Merge this function to `add_upstream_rust_crates` so that all native libraries
+ // are linked together with their respective upstream crates, and in their originally
+ // specified order. This is slightly breaking due to our use of `--as-needed` (see crater
+ // results in https://github.com/rust-lang/rust/pull/102832#issuecomment-1279772306).
+ let link_static = false;
+ // Dynamic libraries are linked for all linked upstream crates.
+ // 1. If the upstream crate is a directly linked rlib then we must link the native library
+ // because the rlib is just an archive.
+ // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we have to link
+ // the native library too because inline/const/generic functions from the dylib can refer
+ // to symbols from the native library, so the native library providing those symbols should
+ // be available when linking our final binary.
+ let link_dynamic = true;
+ add_native_libs_from_crate(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ &search_path,
+ &Default::default(),
+ cnum,
+ link_static,
+ link_dynamic,
+ );
}
+}
- // Adds the static "rlib" versions of all crates to the command line.
- // There's a bit of magic which happens here specifically related to LTO,
- // namely that we remove upstream object files.
- //
- // When performing LTO, almost(*) all of the bytecode from the upstream
- // libraries has already been included in our object file output. As a
- // result we need to remove the object files in the upstream libraries so
- // the linker doesn't try to include them twice (or whine about duplicate
- // symbols). We must continue to include the rest of the rlib, however, as
- // it may contain static native libraries which must be linked in.
- //
- // (*) Crates marked with `#![no_builtins]` don't participate in LTO and
- // their bytecode wasn't included. The object files in those libraries must
- // still be passed to the linker.
- //
- // Note, however, that if we're not doing LTO we can just pass the rlib
- // blindly to the linker (fast) because it's fine if it's not actually
- // included as we're at the end of the dependency chain.
- fn add_static_crate<'a>(
- cmd: &mut dyn Linker,
- sess: &'a Session,
- archive_builder_builder: &dyn ArchiveBuilderBuilder,
- codegen_results: &CodegenResults,
- tmpdir: &Path,
- cnum: CrateNum,
- bundled_lib_file_names: &FxHashSet<Symbol>,
- ) {
- let src = &codegen_results.crate_info.used_crate_source[&cnum];
- let cratepath = &src.rlib.as_ref().unwrap().0;
-
- let mut link_upstream = |path: &Path| {
- cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
- };
-
- // See the comment above in `link_staticlib` and `link_rlib` for why if
- // there's a static library that's not relevant we skip all object
- // files.
- let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
- let skip_native = native_libs.iter().any(|lib| {
- matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
- && !relevant_lib(sess, lib)
- });
-
- if (!are_upstream_rust_objects_already_included(sess)
- || ignored_for_lto(sess, &codegen_results.crate_info, cnum))
- && !skip_native
- {
- link_upstream(cratepath);
- return;
- }
-
- let dst = tmpdir.join(cratepath.file_name().unwrap());
- let name = cratepath.file_name().unwrap().to_str().unwrap();
- let name = &name[3..name.len() - 5]; // chop off lib/.rlib
- let bundled_lib_file_names = bundled_lib_file_names.clone();
-
- sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
- let canonical_name = name.replace('-', "_");
- let upstream_rust_objects_already_included =
- are_upstream_rust_objects_already_included(sess);
- let is_builtins = sess.target.no_builtins
- || !codegen_results.crate_info.is_no_builtins.contains(&cnum);
-
- let mut archive = archive_builder_builder.new_archive_builder(sess);
- if let Err(e) = archive.add_archive(
- cratepath,
- Box::new(move |f| {
- if f == METADATA_FILENAME {
- return true;
- }
+// Adds the static "rlib" versions of all crates to the command line.
+// There's a bit of magic which happens here specifically related to LTO,
+// namely that we remove upstream object files.
+//
+// When performing LTO, almost(*) all of the bytecode from the upstream
+// libraries has already been included in our object file output. As a
+// result we need to remove the object files in the upstream libraries so
+// the linker doesn't try to include them twice (or whine about duplicate
+// symbols). We must continue to include the rest of the rlib, however, as
+// it may contain static native libraries which must be linked in.
+//
+// (*) Crates marked with `#![no_builtins]` don't participate in LTO and
+// their bytecode wasn't included. The object files in those libraries must
+// still be passed to the linker.
+//
+// Note, however, that if we're not doing LTO we can just pass the rlib
+// blindly to the linker (fast) because it's fine if it's not actually
+// included as we're at the end of the dependency chain.
+fn add_static_crate<'a>(
+ cmd: &mut dyn Linker,
+ sess: &'a Session,
+ archive_builder_builder: &dyn ArchiveBuilderBuilder,
+ codegen_results: &CodegenResults,
+ tmpdir: &Path,
+ cnum: CrateNum,
+ bundled_lib_file_names: &FxHashSet<Symbol>,
+) {
+ let src = &codegen_results.crate_info.used_crate_source[&cnum];
+ let cratepath = &src.rlib.as_ref().unwrap().0;
- let canonical = f.replace('-', "_");
-
- let is_rust_object =
- canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f);
-
- // If we've been requested to skip all native object files
- // (those not generated by the rust compiler) then we can skip
- // this file. See above for why we may want to do this.
- let skip_because_cfg_say_so = skip_native && !is_rust_object;
-
- // If we're performing LTO and this is a rust-generated object
- // file, then we don't need the object file as it's part of the
- // LTO module. Note that `#![no_builtins]` is excluded from LTO,
- // though, so we let that object file slide.
- let skip_because_lto =
- upstream_rust_objects_already_included && is_rust_object && is_builtins;
-
- // We skip native libraries because:
- // 1. This native libraries won't be used from the generated rlib,
- // so we can throw them away to avoid the copying work.
- // 2. We can't allow it to be a single remaining entry in archive
- // as some linkers may complain on that.
- if bundled_lib_file_names.contains(&Symbol::intern(f)) {
- return true;
- }
+ let mut link_upstream = |path: &Path| {
+ cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
+ };
- if skip_because_cfg_say_so || skip_because_lto {
- return true;
- }
+ // See the comment above in `link_staticlib` and `link_rlib` for why if
+ // there's a static library that's not relevant we skip all object
+ // files.
+ let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
+ let skip_native = native_libs.iter().any(|lib| {
+ matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
+ && !relevant_lib(sess, lib)
+ });
- false
- }),
- ) {
- sess.fatal(&format!("failed to build archive from rlib: {}", e));
- }
- if archive.build(&dst) {
- link_upstream(&dst);
- }
- });
+ if (!are_upstream_rust_objects_already_included(sess)
+ || ignored_for_lto(sess, &codegen_results.crate_info, cnum))
+ && !skip_native
+ {
+ link_upstream(cratepath);
+ return;
}
- // Same thing as above, but for dynamic crates instead of static crates.
- fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
- // Just need to tell the linker about where the library lives and
- // what its name is
- let parent = cratepath.parent();
- if let Some(dir) = parent {
- cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
- }
- let filestem = cratepath.file_stem().unwrap().to_str().unwrap();
- cmd.link_rust_dylib(
- &unlib(&sess.target, filestem),
- parent.unwrap_or_else(|| Path::new("")),
- );
- }
-}
+ let dst = tmpdir.join(cratepath.file_name().unwrap());
+ let name = cratepath.file_name().unwrap().to_str().unwrap();
+ let name = &name[3..name.len() - 5]; // chop off lib/.rlib
+ let bundled_lib_file_names = bundled_lib_file_names.clone();
-/// Link in all of our upstream crates' native dependencies. Remember that all of these upstream
-/// native dependencies are all non-static dependencies. We've got two cases then:
-///
-/// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because
-/// the rlib is just an archive.
-///
-/// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency
-/// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the
-/// dynamic dependency to this crate as well.
-///
-/// The use case for this is a little subtle. In theory the native dependencies of a crate are
-/// purely an implementation detail of the crate itself, but the problem arises with generic and
-/// inlined functions. If a generic function calls a native function, then the generic function
-/// must be instantiated in the target crate, meaning that the native symbol must also be resolved
-/// in the target crate.
-fn add_upstream_native_libraries(
- cmd: &mut dyn Linker,
- sess: &Session,
- codegen_results: &CodegenResults,
-) {
- let mut last = (None, NativeLibKind::Unspecified, None);
- for &cnum in &codegen_results.crate_info.used_crates {
- for lib in codegen_results.crate_info.native_libraries[&cnum].iter() {
- let Some(name) = lib.name else {
- continue;
- };
- let name = name.as_str();
- if !relevant_lib(sess, &lib) {
- continue;
- }
+ sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
+ let canonical_name = name.replace('-', "_");
+ let upstream_rust_objects_already_included =
+ are_upstream_rust_objects_already_included(sess);
+ let is_builtins =
+ sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum);
- // Skip if this library is the same as the last.
- last = if (lib.name, lib.kind, lib.verbatim) == last {
- continue;
- } else {
- (lib.name, lib.kind, lib.verbatim)
- };
+ let mut archive = archive_builder_builder.new_archive_builder(sess);
+ if let Err(e) = archive.add_archive(
+ cratepath,
+ Box::new(move |f| {
+ if f == METADATA_FILENAME {
+ return true;
+ }
- let verbatim = lib.verbatim.unwrap_or(false);
- match lib.kind {
- NativeLibKind::Dylib { as_needed } => {
- cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+ let canonical = f.replace('-', "_");
+
+ let is_rust_object =
+ canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f);
+
+ // If we've been requested to skip all native object files
+ // (those not generated by the rust compiler) then we can skip
+ // this file. See above for why we may want to do this.
+ let skip_because_cfg_say_so = skip_native && !is_rust_object;
+
+ // If we're performing LTO and this is a rust-generated object
+ // file, then we don't need the object file as it's part of the
+ // LTO module. Note that `#![no_builtins]` is excluded from LTO,
+ // though, so we let that object file slide.
+ let skip_because_lto =
+ upstream_rust_objects_already_included && is_rust_object && is_builtins;
+
+ // We skip native libraries because:
+ // 1. This native libraries won't be used from the generated rlib,
+ // so we can throw them away to avoid the copying work.
+ // 2. We can't allow it to be a single remaining entry in archive
+ // as some linkers may complain on that.
+ if bundled_lib_file_names.contains(&Symbol::intern(f)) {
+ return true;
}
- NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
- NativeLibKind::Framework { as_needed } => {
- cmd.link_framework(name, as_needed.unwrap_or(true))
+
+ if skip_because_cfg_say_so || skip_because_lto {
+ return true;
}
- // ignore static native libraries here as we've
- // already included them in add_local_native_libraries and
- // add_upstream_rust_crates
- NativeLibKind::Static { .. } => {}
- NativeLibKind::RawDylib | NativeLibKind::LinkArg => {}
- }
+
+ false
+ }),
+ ) {
+ sess.fatal(&format!("failed to build archive from rlib: {}", e));
}
- }
+ if archive.build(&dst) {
+ link_upstream(&dst);
+ }
+ });
+}
+
+// Same thing as above, but for dynamic crates instead of static crates.
+fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
+ // Just need to tell the linker about where the library lives and
+ // what its name is
+ let parent = cratepath.parent();
+ if let Some(dir) = parent {
+ cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
+ }
+ let stem = cratepath.file_stem().unwrap().to_str().unwrap();
+ // Convert library file-stem into a cc -l argument.
+ let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 };
+ cmd.link_rust_dylib(&stem[prefix..], parent.unwrap_or_else(|| Path::new("")));
}
fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
@@ -2813,14 +2725,14 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
("arm", "watchos") => "watchos",
(_, "macos") => "macosx",
_ => {
- sess.err(&format!("unsupported arch `{}` for os `{}`", arch, os));
+ sess.emit_err(errors::UnsupportedArch { arch, os });
return;
}
};
let sdk_root = match get_apple_sdk_root(sdk_name) {
Ok(s) => s,
Err(e) => {
- sess.err(&e);
+ sess.emit_err(e);
return;
}
};
@@ -2836,7 +2748,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
}
}
-fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {
+fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootError<'_>> {
// Following what clang does
// (https://github.com/llvm/llvm-project/blob/
// 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678)
@@ -2886,7 +2798,7 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {
match res {
Ok(output) => Ok(output.trim().to_string()),
- Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)),
+ Err(error) => Err(errors::AppleSdkRootError::SdkPath { sdk_name, error }),
}
}
@@ -2919,7 +2831,7 @@ fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
}
}
} else {
- sess.fatal("option `-Z gcc-ld` is used even though linker flavor is not gcc");
+ sess.emit_fatal(errors::OptionGccOnly);
}
}
}
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index c49b19bdf..f087d903e 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -34,9 +34,9 @@ pub fn disable_localization(linker: &mut Command) {
linker.env("VSLANG", "1033");
}
-// The third parameter is for env vars, used on windows to set up the
-// path for MSVC to find its DLLs, and gcc to find its bundled
-// toolchain
+/// The third parameter is for env vars, used on windows to set up the
+/// path for MSVC to find its DLLs, and gcc to find its bundled
+/// toolchain
pub fn get_linker<'a>(
sess: &'a Session,
linker: &Path,
@@ -515,7 +515,7 @@ impl<'a> Linker for GccLinker<'a> {
// -force_load is the macOS equivalent of --whole-archive, but it
// involves passing the full path to the library to link.
self.linker_arg("-force_load");
- let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess);
+ let lib = find_native_static_library(lib, verbatim, search_path, &self.sess);
self.linker_arg(&lib);
}
}
@@ -1260,11 +1260,11 @@ impl<'a> Linker for WasmLd<'a> {
}
fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) {
- self.cmd.arg("-l").arg(lib);
+ self.cmd.arg("--whole-archive").arg("-l").arg(lib).arg("--no-whole-archive");
}
fn link_whole_rlib(&mut self, lib: &Path) {
- self.cmd.arg(lib);
+ self.cmd.arg("--whole-archive").arg(lib).arg("--no-whole-archive");
}
fn gc_sections(&mut self, _keep_metadata: bool) {
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 99ddd1764..51c5c375d 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -60,7 +60,7 @@ impl MetadataLoader for DefaultMetadataLoader {
let data = entry
.data(data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
- return search_for_metadata(path, data, ".rmeta");
+ return search_for_section(path, data, ".rmeta");
}
}
@@ -69,11 +69,11 @@ impl MetadataLoader for DefaultMetadataLoader {
}
fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
- load_metadata_with(path, |data| search_for_metadata(path, data, ".rustc"))
+ load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
}
}
-fn search_for_metadata<'a>(
+pub(super) fn search_for_section<'a>(
path: &Path,
bytes: &'a [u8],
section: &str,
@@ -191,39 +191,43 @@ pub enum MetadataPosition {
Last,
}
-// For rlibs we "pack" rustc metadata into a dummy object file.
-//
-// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
-// In that case linkers try to include all files located in an archive, so if metadata is stored
-// in an archive then it needs to be of a form that the linker is able to process.
-// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
-//
-// Note, though, that we don't actually want this metadata to show up in any
-// final output of the compiler. Instead this is purely for rustc's own
-// metadata tracking purposes.
-//
-// With the above in mind, each "flavor" of object format gets special
-// handling here depending on the target:
-//
-// * MachO - macos-like targets will insert the metadata into a section that
-// is sort of fake dwarf debug info. Inspecting the source of the macos
-// linker this causes these sections to be skipped automatically because
-// it's not in an allowlist of otherwise well known dwarf section names to
-// go into the final artifact.
-//
-// * WebAssembly - we actually don't have any container format for this
-// target. WebAssembly doesn't support the `dylib` crate type anyway so
-// there's no need for us to support this at this time. Consequently the
-// metadata bytes are simply stored as-is into an rlib.
-//
-// * COFF - Windows-like targets create an object with a section that has
-// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
-// ever sees the section it doesn't process it and it's removed.
-//
-// * ELF - All other targets are similar to Windows in that there's a
-// `SHF_EXCLUDE` flag we can set on sections in an object file to get
-// automatically removed from the final output.
-pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataPosition) {
+/// For rlibs we "pack" rustc metadata into a dummy object file.
+///
+/// Historically it was needed because rustc linked rlibs as whole-archive in some cases.
+/// In that case linkers try to include all files located in an archive, so if metadata is stored
+/// in an archive then it needs to be of a form that the linker is able to process.
+/// Now it's not clear whether metadata still needs to be wrapped into an object file or not.
+///
+/// Note, though, that we don't actually want this metadata to show up in any
+/// final output of the compiler. Instead this is purely for rustc's own
+/// metadata tracking purposes.
+///
+/// With the above in mind, each "flavor" of object format gets special
+/// handling here depending on the target:
+///
+/// * MachO - macos-like targets will insert the metadata into a section that
+/// is sort of fake dwarf debug info. Inspecting the source of the macos
+/// linker this causes these sections to be skipped automatically because
+/// it's not in an allowlist of otherwise well known dwarf section names to
+/// go into the final artifact.
+///
+/// * WebAssembly - we actually don't have any container format for this
+/// target. WebAssembly doesn't support the `dylib` crate type anyway so
+/// there's no need for us to support this at this time. Consequently the
+/// metadata bytes are simply stored as-is into an rlib.
+///
+/// * COFF - Windows-like targets create an object with a section that has
+/// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
+/// ever sees the section it doesn't process it and it's removed.
+///
+/// * ELF - All other targets are similar to Windows in that there's a
+/// `SHF_EXCLUDE` flag we can set on sections in an object file to get
+/// automatically removed from the final output.
+pub fn create_wrapper_file(
+ sess: &Session,
+ section_name: Vec<u8>,
+ data: &[u8],
+) -> (Vec<u8>, MetadataPosition) {
let Some(mut file) = create_object_file(sess) else {
// This is used to handle all "other" targets. This includes targets
// in two categories:
@@ -241,11 +245,11 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataP
// WebAssembly and for targets not supported by the `object` crate
// yet it means that work will need to be done in the `object` crate
// to add a case above.
- return (metadata.to_vec(), MetadataPosition::Last);
+ return (data.to_vec(), MetadataPosition::Last);
};
let section = file.add_section(
file.segment_name(StandardSegment::Debug).to_vec(),
- b".rmeta".to_vec(),
+ section_name,
SectionKind::Debug,
);
match file.format() {
@@ -259,7 +263,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataP
}
_ => {}
};
- file.append_section_data(section, metadata, 1);
+ file.append_section_data(section, data, 1);
(file.write().unwrap(), MetadataPosition::First)
}
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index c2ecc4160..22f534d90 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -180,7 +180,8 @@ fn exported_symbols_provider_local<'tcx>(
.collect();
if tcx.entry_fn(()).is_some() {
- let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, "main"));
+ let exported_symbol =
+ ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
symbols.push((
exported_symbol,
@@ -193,8 +194,11 @@ fn exported_symbols_provider_local<'tcx>(
}
if tcx.allocator_kind(()).is_some() {
- for method in ALLOCATOR_METHODS {
- let symbol_name = format!("__rust_{}", method.name);
+ for symbol_name in ALLOCATOR_METHODS
+ .iter()
+ .map(|method| format!("__rust_{}", method.name))
+ .chain(["__rust_alloc_error_handler".to_string(), OomStrategy::SYMBOL.to_string()])
+ {
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
symbols.push((
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index d0ac016b0..12fca6496 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -15,10 +15,8 @@ use rustc_data_structures::profiling::TimingGuard;
use rustc_data_structures::profiling::VerboseTimingGuard;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter;
-use rustc_errors::{
- translation::{to_fluent_args, Translate},
- DiagnosticId, FatalError, Handler, Level,
-};
+use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
+use rustc_errors::{DiagnosticMessage, Style};
use rustc_fs_util::link_or_copy;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental::{
@@ -38,6 +36,7 @@ use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span};
use rustc_target::spec::{MergeFunctions, SanitizerSet};
use std::any::Any;
+use std::borrow::Cow;
use std::fs;
use std::io;
use std::marker::PhantomData;
@@ -341,20 +340,20 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
- // Number of cgus excluding the allocator/metadata modules
+ /// Number of cgus excluding the allocator/metadata modules
pub total_cgus: usize,
- // Handler to use for diagnostics produced during codegen.
+ /// Handler to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
- // LLVM optimizations for which we want to print remarks.
+ /// LLVM optimizations for which we want to print remarks.
pub remark: Passes,
- // Worker thread number
+ /// Worker thread number
pub worker: usize,
- // The incremental compilation session directory, or None if we are not
- // compiling incrementally
+ /// The incremental compilation session directory, or None if we are not
+ /// compiling incrementally
pub incr_comp_session_dir: Option<PathBuf>,
- // Used to update CGU re-use information during the thinlto phase.
+ /// Used to update CGU re-use information during the thinlto phase.
pub cgu_reuse_tracker: CguReuseTracker,
- // Channel back to the main control thread to send messages to
+ /// Channel back to the main control thread to send messages to
pub coordinator_send: Sender<Box<dyn Any + Send>>,
}
@@ -757,7 +756,7 @@ fn execute_work_item<B: ExtraBackendMethods>(
}
}
-// Actual LTO type we end up choosing based on multiple factors.
+/// Actual LTO type we end up choosing based on multiple factors.
pub enum ComputedLtoType {
No,
Thin,
@@ -969,8 +968,11 @@ pub enum Message<B: WriteBackendMethods> {
CodegenAborted,
}
+type DiagnosticArgName<'source> = Cow<'source, str>;
+
struct Diagnostic {
- msg: String,
+ msg: Vec<(DiagnosticMessage, Style)>,
+ args: FxHashMap<DiagnosticArgName<'static>, rustc_errors::DiagnosticArgValue<'static>>,
code: Option<DiagnosticId>,
lvl: Level,
}
@@ -1743,15 +1745,18 @@ impl Translate for SharedEmitter {
impl Emitter for SharedEmitter {
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
- let fluent_args = to_fluent_args(diag.args());
+ let args: FxHashMap<Cow<'_, str>, rustc_errors::DiagnosticArgValue<'_>> =
+ diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
- msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
+ msg: diag.message.clone(),
+ args: args.clone(),
code: diag.code.clone(),
lvl: diag.level(),
})));
for child in &diag.children {
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
- msg: self.translate_messages(&child.message, &fluent_args).to_string(),
+ msg: child.message.clone(),
+ args: args.clone(),
code: None,
lvl: child.level,
})));
@@ -1782,10 +1787,11 @@ impl SharedEmitterMain {
match message {
Ok(SharedEmitterMessage::Diagnostic(diag)) => {
let handler = sess.diagnostic();
- let mut d = rustc_errors::Diagnostic::new(diag.lvl, &diag.msg);
+ let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msg);
if let Some(code) = diag.code {
d.code(code);
}
+ d.replace_args(diag.args);
handler.emit_diagnostic(&mut d);
}
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {