1mod raw_dylib;23use std::collections::BTreeSet;4use std::ffi::OsString;5use std::fs::{File, OpenOptions, read};6use std::io::{BufReader, BufWriter, Write};7use std::ops::{ControlFlow, Deref};8use std::path::{Path, PathBuf};9use std::process::{Output, Stdio};10use std::{env, fmt, fs, io, mem, str};1112use find_msvc_tools;13use itertools::Itertools;14use object::{Object, ObjectSection, ObjectSymbol};15use regex::Regex;16use rustc_arena::TypedArena;17use rustc_attr_parsing::eval_config_entry;18use rustc_data_structures::fx::{FxHashSet, FxIndexSet};19use rustc_data_structures::memmap::Mmap;20use rustc_data_structures::temp_dir::MaybeTempDir;21use rustc_errors::DiagCtxtHandle;22use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};23use rustc_hir::attrs::NativeLibKind;24use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};25use rustc_lint_defs::builtin::LINKER_INFO;26use rustc_macros::Diagnostic;27use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};28use rustc_metadata::{29 EncodedMetadata, NativeLibSearchFallback, find_native_static_library,30 walk_native_lib_search_dirs,31};32use rustc_middle::bug;33use rustc_middle::lint::emit_lint_base;34use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;35use rustc_middle::middle::dependency_format::Linkage;36use rustc_middle::middle::exported_symbols::SymbolExportKind;37use rustc_session::config::{38 self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames,39 OutputType, PrintKind, SplitDwarfKind, Strip,40};41use rustc_session::lint::builtin::LINKER_MESSAGES;42use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};43use rustc_session::search_paths::PathKind;44/// For all the linkers we support, and information they might45/// need out of the shared crate context before we get rid of it.46use rustc_session::{Session, filesearch};47use rustc_span::Symbol;48use rustc_target::spec::crt_objects::CrtObjects;49use rustc_target::spec::{50 BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents,51 LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel,52 RelroLevel, SanitizerSet, SplitDebuginfo,53};54use tracing::{debug, info, warn};5556use super::archive::{57 AddArchiveKind, ArchiveBuilder, ArchiveBuilderBuilder, ArchiveEntryKind, ArchiveSymbols,58};59use super::command::Command;60use super::linker::{self, Linker};61use super::metadata::{MetadataPosition, create_wrapper_file};62use super::rpath::{self, RPathConfig};63use super::{apple, rmeta_link, versioned_llvm_target};64use crate::base::needs_allocator_shim_for_linking;65use crate::{CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors};6667pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {68 if let Err(e) = fs::remove_file(path) {69 if e.kind() != io::ErrorKind::NotFound {70 dcx.err(format!("failed to remove {}: {}", path.display(), e));71 }72 }73}7475/// Performs the linkage portion of the compilation phase. This will generate all76/// of the requested outputs for this compilation session.77pub fn link_binary(78 sess: &Session,79 archive_builder_builder: &dyn ArchiveBuilderBuilder,80 compiled_modules: CompiledModules,81 crate_info: CrateInfo,82 metadata: EncodedMetadata,83 outputs: &OutputFilenames,84 codegen_backend: &'static str,85) {86 let _timer = sess.timer("link_binary");87 let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);88 let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();89 for &crate_type in &crate_info.crate_types {90 // Ignore executable crates if we have -Z no-codegen, as they will error.91 if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())92 && !output_metadata93 && crate_type == CrateType::Executable94 {95 continue;96 }9798 if invalid_output_for_target(sess, crate_type) {99 bug!("invalid output type `{:?}` for target `{}`", crate_type, sess.opts.target_triple);100 }101102 sess.time("link_binary_check_files_are_writeable", || {103 for m in &compiled_modules.modules {104 if let Some(obj) = &m.object {105 check_file_is_writeable(obj, sess);106 }107 if let Some(obj) = &m.global_asm_object {108 check_file_is_writeable(obj, sess);109 }110 }111 });112113 if outputs.outputs.should_link() {114 let output = out_filename(sess, crate_type, outputs, crate_info.local_crate_name);115 let tmpdir = TempDirBuilder::new()116 .prefix("rustc")117 .tempdir_in(output.parent().unwrap_or_else(|| Path::new(".")))118 .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error }));119 let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);120121 let crate_name = format!("{}", crate_info.local_crate_name);122 let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name);123 match crate_type {124 CrateType::Rlib => {125 let _timer = sess.timer("link_rlib");126 info!("preparing rlib to {:?}", out_filename);127 link_rlib(128 sess,129 archive_builder_builder,130 &compiled_modules,131 &crate_info,132 &metadata,133 RlibFlavor::Normal,134 &path,135 )136 .build(&out_filename, None);137 }138 CrateType::StaticLib => {139 link_staticlib(140 sess,141 archive_builder_builder,142 &compiled_modules,143 &crate_info,144 &metadata,145 &out_filename,146 &path,147 );148 }149 _ => {150 link_natively(151 sess,152 archive_builder_builder,153 crate_type,154 &out_filename,155 &compiled_modules,156 &crate_info,157 &metadata,158 path.as_ref(),159 codegen_backend,160 );161 }162 }163 if sess.opts.json_artifact_notifications {164 sess.dcx().emit_artifact_notification(&out_filename, "link");165 }166167 if sess.prof.enabled()168 && let Some(artifact_name) = out_filename.file_name()169 {170 // Record size for self-profiling171 let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0);172173 sess.prof.artifact_size(174 "linked_artifact",175 artifact_name.to_string_lossy(),176 file_size,177 );178 }179180 if sess.target.binary_format == BinaryFormat::Elf {181 if let Err(err) = warn_if_linked_with_gold(sess, &out_filename) {182 info!(?err, "Error while checking if gold was the linker");183 }184 }185186 if output.is_stdout() {187 if output.is_tty() {188 sess.dcx().emit_err(errors::BinaryOutputToTty {189 shorthand: OutputType::Exe.shorthand(),190 });191 } else if let Err(e) = copy_to_stdout(&out_filename) {192 sess.dcx().emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e));193 }194 tempfiles_for_stdout_output.push(out_filename);195 }196 }197 }198199 // Remove the temporary object file and metadata if we aren't saving temps.200 sess.time("link_binary_remove_temps", || {201 // If the user requests that temporaries are saved, don't delete any.202 if sess.opts.cg.save_temps {203 return;204 }205206 let maybe_remove_temps_from_module =207 |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| {208 if !preserve_objects && let Some(ref obj) = module.object {209 ensure_removed(sess.dcx(), obj);210 }211212 if !preserve_objects && let Some(ref obj) = module.global_asm_object {213 ensure_removed(sess.dcx(), obj);214 }215216 if !preserve_dwarf_objects && let Some(ref dwo_obj) = module.dwarf_object {217 ensure_removed(sess.dcx(), dwo_obj);218 }219 };220221 let remove_temps_from_module =222 |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module);223224 // Otherwise, always remove the allocator module temporaries.225 if let Some(ref allocator_module) = compiled_modules.allocator_module {226 remove_temps_from_module(allocator_module);227 }228229 // Remove the temporary files if output goes to stdout230 for temp in tempfiles_for_stdout_output {231 ensure_removed(sess.dcx(), &temp);232 }233234 // If no requested outputs require linking, then the object temporaries should235 // be kept.236 if !sess.opts.output_types.should_link() {237 return;238 }239240 // Potentially keep objects for their debuginfo.241 let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess);242 debug!(?preserve_objects, ?preserve_dwarf_objects);243244 for module in &compiled_modules.modules {245 maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module);246 }247 });248}249250// Crate type is not passed when calculating the dylibs to include for LTO. In that case all251// crate types must use the same dependency formats.252pub fn each_linked_rlib(253 info: &CrateInfo,254 crate_type: Option<CrateType>,255 f: &mut dyn FnMut(CrateNum, &Path),256) -> Result<(), errors::LinkRlibError> {257 let fmts = if let Some(crate_type) = crate_type {258 let Some(fmts) = info.dependency_formats.get(&crate_type) else {259 return Err(errors::LinkRlibError::MissingFormat);260 };261262 fmts263 } else {264 let mut dep_formats = info.dependency_formats.iter();265 let (ty1, list1) = dep_formats.next().ok_or(errors::LinkRlibError::MissingFormat)?;266 if let Some((ty2, list2)) = dep_formats.find(|(_, list2)| list1 != *list2) {267 return Err(errors::LinkRlibError::IncompatibleDependencyFormats {268 ty1: format!("{ty1:?}"),269 ty2: format!("{ty2:?}"),270 list1: format!("{list1:?}"),271 list2: format!("{list2:?}"),272 });273 }274 list1275 };276277 let used_dep_crates = info.used_crates.iter();278 for &cnum in used_dep_crates {279 match fmts.get(cnum) {280 Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue,281 Some(_) => {}282 None => return Err(errors::LinkRlibError::MissingFormat),283 }284 let crate_name = info.crate_name[&cnum];285 let used_crate_source = &info.used_crate_source[&cnum];286 if let Some(path) = &used_crate_source.rlib {287 f(cnum, path);288 } else if used_crate_source.rmeta.is_some() {289 return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name });290 } else {291 return Err(errors::LinkRlibError::NotFound { crate_name });292 }293 }294 Ok(())295}296297/// Create an 'rlib'.298///299/// An rlib in its current incarnation is essentially a renamed .a file (with "dummy" object files).300/// The rlib primarily contains the object file of the crate, but it also some of the object files301/// from native libraries.302fn link_rlib<'a>(303 sess: &'a Session,304 archive_builder_builder: &dyn ArchiveBuilderBuilder,305 compiled_modules: &CompiledModules,306 crate_info: &CrateInfo,307 metadata: &EncodedMetadata,308 flavor: RlibFlavor,309 tmpdir: &MaybeTempDir,310) -> Box<dyn ArchiveBuilder + 'a> {311 let mut ab = archive_builder_builder.new_archive_builder(sess);312313 // Pre-compute the list of Rust object filenames and materialize the rmeta-link314 // wrapper file before any `add_file` calls. This lets the rmeta-link member be315 // placed immediately after metadata in the archive, so consumers can find316 // it without iterating every archive member.317 let rust_object_files: Vec<String> = compiled_modules318 .modules319 .iter()320 .filter_map(|m| m.object.as_ref())321 .chain(compiled_modules.modules.iter().filter_map(|m| m.global_asm_object.as_ref()))322 .map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string())323 .collect();324325 let metadata_link_file = if matches!(flavor, RlibFlavor::Normal) {326 let metadata_link = rmeta_link::RmetaLink { rust_object_files };327 let metadata_link_data = metadata_link.encode();328 let (wrapper, _) =329 create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &metadata_link_data);330 Some(emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME))331 } else {332 None333 };334335 let trailing_metadata = match flavor {336 RlibFlavor::Normal => {337 let (metadata, metadata_position) =338 create_wrapper_file(sess, ".rmeta".to_string(), metadata.stub_or_full());339 let metadata = emit_wrapper_file(sess, &metadata, tmpdir.as_ref(), METADATA_FILENAME);340 match metadata_position {341 MetadataPosition::First => {342 // Most of the time metadata in rlib files is wrapped in a "dummy" object343 // file for the target platform so the rlib can be processed entirely by344 // normal linkers for the platform. Sometimes this is not possible however.345 // If it is possible however, placing the metadata object first improves346 // performance of getting metadata from rlibs.347 ab.add_file(&metadata, ArchiveEntryKind::Other);348 // Place the rmeta-link member immediately after metadata so consumers349 // can find it without iterating the whole archive.350 if let Some(file) = &metadata_link_file {351 ab.add_file(file, ArchiveEntryKind::Other);352 }353 None354 }355 MetadataPosition::Last => Some(metadata),356 }357 }358359 RlibFlavor::StaticlibBase => None,360 };361362 for m in &compiled_modules.modules {363 if let Some(obj) = m.object.as_ref() {364 ab.add_file(obj, ArchiveEntryKind::RustObj);365 }366367 if let Some(obj) = m.global_asm_object.as_ref() {368 ab.add_file(obj, ArchiveEntryKind::RustObj);369 }370371 if let Some(dwarf_obj) = m.dwarf_object.as_ref() {372 ab.add_file(dwarf_obj, ArchiveEntryKind::Other);373 }374 }375376 match flavor {377 RlibFlavor::Normal => {}378 RlibFlavor::StaticlibBase => {379 if let Some(m) = &compiled_modules.allocator_module {380 if let Some(obj) = &m.object {381 ab.add_file(obj, ArchiveEntryKind::RustObj);382 }383 if let Some(obj) = &m.global_asm_object {384 ab.add_file(obj, ArchiveEntryKind::RustObj);385 }386 }387 }388 }389390 // Used if packed_bundled_libs flag enabled.391 let mut packed_bundled_libs = Vec::new();392393 // Note that in this loop we are ignoring the value of `lib.cfg`. That is,394 // we may not be configured to actually include a static library if we're395 // adding it here. That's because later when we consume this rlib we'll396 // decide whether we actually needed the static library or not.397 //398 // To do this "correctly" we'd need to keep track of which libraries added399 // which object files to the archive. We don't do that here, however. The400 // #[link(cfg(..))] feature is unstable, though, and only intended to get401 // liblibc working. In that sense the check below just indicates that if402 // there are any libraries we want to omit object files for at link time we403 // just exclude all custom object files.404 //405 // Eventually if we want to stabilize or flesh out the #[link(cfg(..))]406 // feature then we'll need to figure out how to record what objects were407 // loaded from the libraries found here and then encode that into the408 // metadata of the rlib we're generating somehow.409 for lib in crate_info.used_libraries.iter() {410 let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {411 continue;412 };413 if flavor == RlibFlavor::Normal414 && let Some(filename) = lib.filename415 {416 let path = find_native_static_library(filename.as_str(), true, sess);417 let src = read(path)418 .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }));419 let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);420 let wrapper_file = emit_wrapper_file(sess, &data, tmpdir.as_ref(), filename.as_str());421 packed_bundled_libs.push(wrapper_file);422 } else {423 let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);424 ab.add_archive(&path, AddArchiveKind::Other).unwrap_or_else(|error| {425 sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })426 });427 }428 }429430 // On Windows, we add the raw-dylib import libraries to the rlibs already.431 // But on ELF, this is not possible, as a shared object cannot be a member of a static library.432 // Instead, we add all raw-dylibs to the final link on ELF.433 if sess.target.is_like_windows {434 for output_path in raw_dylib::create_raw_dylib_dll_import_libs(435 sess,436 archive_builder_builder,437 crate_info.used_libraries.iter(),438 tmpdir.as_ref(),439 true,440 ) {441 ab.add_archive(&output_path, AddArchiveKind::Other).unwrap_or_else(|error| {442 sess.dcx()443 .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });444 });445 }446 }447448 if let Some(trailing_metadata) = trailing_metadata {449 // Note that it is important that we add all of our non-object "magical450 // files" *after* all of the object files in the archive. The reason for451 // this is as follows:452 //453 // * When performing LTO, this archive will be modified to remove454 // objects from above. The reason for this is described below.455 //456 // * When the system linker looks at an archive, it will attempt to457 // determine the architecture of the archive in order to see whether its458 // linkable.459 //460 // The algorithm for this detection is: iterate over the files in the461 // archive. Skip magical SYMDEF names. Interpret the first file as an462 // object file. Read architecture from the object file.463 //464 // * As one can probably see, if "metadata" and "foo.bc" were placed465 // before all of the objects, then the architecture of this archive would466 // not be correctly inferred once 'foo.o' is removed.467 //468 // * Most of the time metadata in rlib files is wrapped in a "dummy" object469 // file for the target platform so the rlib can be processed entirely by470 // normal linkers for the platform. Sometimes this is not possible however.471 //472 // Basically, all this means is that this code should not move above the473 // code above.474 ab.add_file(&trailing_metadata, ArchiveEntryKind::Other);475 // Place the rmeta-link member immediately after metadata so consumers can476 // find it without iterating the whole archive.477 if let Some(file) = &metadata_link_file {478 ab.add_file(file, ArchiveEntryKind::Other);479 }480 }481482 // Add all bundled static native library dependencies.483 // Archives added to the end of .rlib archive, see comment above for the reason.484 for lib in packed_bundled_libs {485 ab.add_file(&lib, ArchiveEntryKind::Other)486 }487488 ab489}490491/// Create a static archive.492///493/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream494/// crates' objects into the archive. This will slurp in all of the native libraries of upstream495/// dependencies as well.496///497/// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic498/// library dependencies that they're not linked in.499///500/// There's no need to include metadata in a static archive, so ensure to not link in the metadata501/// object file (and also don't prepare the archive with a metadata file).502fn link_staticlib(503 sess: &Session,504 archive_builder_builder: &dyn ArchiveBuilderBuilder,505 compiled_modules: &CompiledModules,506 crate_info: &CrateInfo,507 metadata: &EncodedMetadata,508 out_filename: &Path,509 tempdir: &MaybeTempDir,510) {511 info!("preparing staticlib to {:?}", out_filename);512 let mut ab = link_rlib(513 sess,514 archive_builder_builder,515 compiled_modules,516 crate_info,517 metadata,518 RlibFlavor::StaticlibBase,519 tempdir,520 );521 let mut all_native_libs = vec![];522523 let res = each_linked_rlib(crate_info, Some(CrateType::StaticLib), &mut |cnum, path| {524 let lto = are_upstream_rust_objects_already_included(sess)525 && !ignored_for_lto(sess, crate_info, cnum);526527 let native_libs = crate_info.native_libraries[&cnum].iter();528 let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib));529 let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect();530531 let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();532 ab.add_archive(533 path,534 AddArchiveKind::Rlib(&|fname: &str, entry_kind| {535 // Ignore metadata and rmeta-link files.536 if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME {537 return true;538 }539540 // Don't include Rust objects if LTO is enabled.541 if lto && entry_kind == ArchiveEntryKind::RustObj {542 return true;543 }544545 // Skip objects for bundled libs.546 if bundled_libs.contains(&Symbol::intern(fname)) {547 return true;548 }549550 false551 }),552 )553 .unwrap();554555 archive_builder_builder556 .extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs)557 .unwrap_or_else(|e| sess.dcx().emit_fatal(e));558559 for filename in relevant_libs.iter() {560 let joined = tempdir.as_ref().join(filename.as_str());561 let path = joined.as_path();562 ab.add_archive(path, AddArchiveKind::Other).unwrap();563 }564565 all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned());566 });567 if let Err(e) = res {568 sess.dcx().emit_fatal(e);569 }570571 let hide = sess.opts.unstable_opts.staticlib_hide_internal_symbols;572 let rename = sess.opts.unstable_opts.staticlib_rename_internal_symbols;573574 let exported_symbols = if hide || rename {575 if !matches!(sess.target.binary_format, BinaryFormat::Elf | BinaryFormat::MachO) {576 if hide {577 sess.dcx().emit_warn(errors::StaticlibHideInternalSymbolsUnsupported {578 binary_format: sess.target.archive_format.to_string(),579 });580 }581 if rename {582 sess.dcx().emit_warn(errors::StaticlibRenameInternalSymbolsUnsupported {583 binary_format: sess.target.archive_format.to_string(),584 });585 }586 None587 } else {588 crate_info589 .exported_symbols590 .get(&CrateType::StaticLib)591 .map(|symbols| symbols.iter().map(|(s, _)| s.clone()).collect())592 }593 } else {594 None595 };596597 let symbols = exported_symbols.map(|exported| ArchiveSymbols {598 exported,599 rename_suffix: rename.then(|| crate_info.symbol_rename_suffix.clone()),600 hide,601 });602603 ab.build(out_filename, symbols);604605 let crates = crate_info.used_crates.iter();606607 let fmts = crate_info608 .dependency_formats609 .get(&CrateType::StaticLib)610 .expect("no dependency formats for staticlib");611612 let mut all_rust_dylibs = vec![];613 for &cnum in crates {614 let Some(Linkage::Dynamic) = fmts.get(cnum) else {615 continue;616 };617 let crate_name = crate_info.crate_name[&cnum];618 let used_crate_source = &crate_info.used_crate_source[&cnum];619 if let Some(path) = &used_crate_source.dylib {620 all_rust_dylibs.push(&**path);621 } else if used_crate_source.rmeta.is_some() {622 sess.dcx().emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name });623 } else {624 sess.dcx().emit_fatal(errors::LinkRlibError::NotFound { crate_name });625 }626 }627628 all_native_libs.extend_from_slice(&crate_info.used_libraries);629630 for print in &sess.opts.prints {631 if print.kind == PrintKind::NativeStaticLibs {632 print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs);633 }634 }635}636637/// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a638/// DWARF package.639fn link_dwarf_object(640 sess: &Session,641 compiled_modules: &CompiledModules,642 crate_info: &CrateInfo,643 executable_out_filename: &Path,644) {645 let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string();646 dwp_out_filename.push(".dwp");647 debug!(?dwp_out_filename, ?executable_out_filename);648649 #[derive(Default)]650 struct ThorinSession<Relocations> {651 arena_data: TypedArena<Vec<u8>>,652 arena_mmap: TypedArena<Mmap>,653 arena_relocations: TypedArena<Relocations>,654 }655656 impl<Relocations> ThorinSession<Relocations> {657 fn alloc_mmap(&self, data: Mmap) -> &Mmap {658 &*self.arena_mmap.alloc(data)659 }660 }661662 impl<Relocations> thorin::Session<Relocations> for ThorinSession<Relocations> {663 fn alloc_data(&self, data: Vec<u8>) -> &[u8] {664 &*self.arena_data.alloc(data)665 }666667 fn alloc_relocation(&self, data: Relocations) -> &Relocations {668 &*self.arena_relocations.alloc(data)669 }670671 fn read_input(&self, path: &Path) -> std::io::Result<&[u8]> {672 let file = File::open(&path)?;673 let mmap = (unsafe { Mmap::map(file) })?;674 Ok(self.alloc_mmap(mmap))675 }676 }677678 match sess.time("run_thorin", || -> Result<(), thorin::Error> {679 let thorin_sess = ThorinSession::default();680 let mut package = thorin::DwarfPackage::new(&thorin_sess);681682 // Input objs contain .o/.dwo files from the current crate.683 match sess.opts.unstable_opts.split_dwarf_kind {684 SplitDwarfKind::Single => {685 for m in &compiled_modules.modules {686 if let Some(input_obj) = &m.object {687 package.add_input_object(input_obj)?;688 }689 if let Some(input_obj) = &m.global_asm_object {690 package.add_input_object(input_obj)?;691 }692 }693 }694 SplitDwarfKind::Split => {695 for input_obj in696 compiled_modules.modules.iter().filter_map(|m| m.dwarf_object.as_ref())697 {698 package.add_input_object(input_obj)?;699 }700 }701 }702703 // Input rlibs contain .o/.dwo files from dependencies.704 let input_rlibs = crate_info705 .used_crate_source706 .items()707 .filter_map(|(_, csource)| csource.rlib.as_ref())708 .into_sorted_stable_ord();709710 for input_rlib in input_rlibs {711 debug!(?input_rlib);712 package.add_input_object(input_rlib)?;713 }714715 // Failing to read the referenced objects is expected for dependencies where the path in the716 // executable will have been cleaned by Cargo, but the referenced objects will be contained717 // within rlibs provided as inputs.718 //719 // If paths have been remapped, then .o/.dwo files from the current crate also won't be720 // found, but are provided explicitly above.721 //722 // Adding an executable is primarily done to make `thorin` check that all the referenced723 // dwarf objects are found in the end.724 package.add_executable(725 executable_out_filename,726 thorin::MissingReferencedObjectBehaviour::Skip,727 )?;728729 let output_stream = BufWriter::new(730 OpenOptions::new()731 .read(true)732 .write(true)733 .create(true)734 .truncate(true)735 .open(dwp_out_filename)?,736 );737 let mut output_stream = thorin::object::write::StreamingBuffer::new(output_stream);738 package.finish()?.emit(&mut output_stream)?;739 output_stream.result()?;740 output_stream.into_inner().flush()?;741742 Ok(())743 }) {744 Ok(()) => {}745 Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)),746 }747}748749#[derive(Diagnostic)]750#[diag("{$inner}")]751/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just752/// end up with inconsistent languages within the same diagnostic.753struct LinkerOutput {754 inner: String,755}756757fn is_msvc_link_exe(sess: &Session) -> bool {758 let (linker_path, flavor) = linker_and_flavor(sess);759 sess.target.is_like_msvc760 && flavor == LinkerFlavor::Msvc(Lld::No)761 // Match exactly "link.exe"762 && linker_path.to_str() == Some("link.exe")763}764765fn is_macos_ld(sess: &Session) -> bool {766 let (_, flavor) = linker_and_flavor(sess);767 sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No))768}769770fn is_windows_gnu_ld(sess: &Session) -> bool {771 let (_, flavor) = linker_and_flavor(sess);772 sess.target.is_like_windows773 && !sess.target.is_like_msvc774 && matches!(flavor, LinkerFlavor::Gnu(_, Lld::No))775 && sess.target.options.cfg_abi != CfgAbi::Llvm776}777778fn is_windows_gnu_clang(sess: &Session) -> bool {779 let (_, flavor) = linker_and_flavor(sess);780 sess.target.is_like_windows781 && !sess.target.is_like_msvc782 && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::No))783 && sess.target.options.cfg_abi == CfgAbi::Llvm784}785786fn report_linker_output(787 sess: &Session,788 levels: CodegenLintLevelSpecs,789 stdout: &[u8],790 stderr: &[u8],791) {792 let mut escaped_stderr = escape_string(&stderr);793 let mut escaped_stdout = escape_string(&stdout);794 let mut linker_info = String::new();795796 info!("linker stderr:\n{}", &escaped_stderr);797 info!("linker stdout:\n{}", &escaped_stdout);798799 fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String {800 let mut output = String::new();801 if let Ok(str) = str::from_utf8(bytes) {802 info!("line: {str}");803 output = String::with_capacity(str.len());804 for line in str.lines() {805 f(line.trim(), &mut output);806 }807 }808 escape_string(output.trim().as_bytes())809 }810811 if is_msvc_link_exe(sess) {812 info!("inferred MSVC link.exe");813814 escaped_stdout = for_each(&stdout, |line, output| {815 // Hide some progress messages from link.exe that we don't care about.816 // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146817 // When incremental linking is enabled and an .ilk exists, but its associated .exe is818 // missing, link.exe prints the path of the missing .exe followed by:819 let ilk_but_no_exe =820 "not found or not built by the last incremental link; performing full link";821 let trimmed = line.trim_start();822 if trimmed.starts_with("Creating library")823 || trimmed.starts_with("Generating code")824 || trimmed.starts_with("Finished generating code")825 || trimmed.ends_with(ilk_but_no_exe)826 {827 linker_info += line;828 linker_info += "\r\n";829 } else {830 *output += line;831 *output += "\r\n"832 }833 });834 } else if is_macos_ld(sess) {835 info!("inferred macOS LD");836837 // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113838 let deployment_mismatch = |line: &str| {839 // ld64 (object files + dylibs) and ld_prime (object files only):840 (line.starts_with("ld: ")841 && line.contains("was built for newer")842 && line.contains("than being linked"))843 // ld_prime (Xcode 15+, dylibs only):844 || (line.starts_with("ld: ")845 && line.contains("building for")846 && line.contains("but linking with")847 && line.contains("which was built for newer version"))848 };849 // FIXME: This is a real warning we would like to show, but it hits too many crates850 // to want to turn it on immediately.851 let search_path = |line: &str| {852 line.starts_with("ld: warning: search path '") && line.ends_with("' not found")853 };854 escaped_stderr = for_each(&stderr, |line, output| {855 // This duplicate library warning is just not helpful at all.856 if line.starts_with("ld: warning: ignoring duplicate libraries: ")857 || deployment_mismatch(line)858 || search_path(line)859 {860 linker_info += line;861 linker_info += "\n";862 } else {863 *output += line;864 *output += "\n"865 }866 });867 } else if is_windows_gnu_ld(sess) {868 info!("inferred Windows GNU LD");869870 let mut saw_exclude_symbol = false;871 // See https://github.com/rust-lang/rust/issues/112368.872 // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?873 let exclude_symbols = |line: &str| {874 line.starts_with("Warning: .drectve `-exclude-symbols:")875 && line.ends_with("' unrecognized")876 };877 escaped_stderr = for_each(&stderr, |line, output| {878 if exclude_symbols(line) {879 saw_exclude_symbol = true;880 linker_info += line;881 linker_info += "\n";882 } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {883 linker_info += line;884 linker_info += "\n";885 } else {886 *output += line;887 *output += "\n"888 }889 });890 } else if is_windows_gnu_clang(sess) {891 info!("inferred Windows Clang (GNU ABI)");892 escaped_stderr = for_each(&stderr, |line, output| {893 if line.contains("argument unused during compilation: '-nolibc'") {894 linker_info += line;895 linker_info += "\n";896 } else {897 *output += line;898 *output += "\n"899 }900 });901 };902903 let lint_msg = |msg| {904 emit_lint_base(905 sess,906 LINKER_MESSAGES,907 levels.linker_messages,908 None,909 LinkerOutput { inner: msg },910 );911 };912 let lint_info = |msg| {913 emit_lint_base(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg });914 };915916 if !escaped_stderr.is_empty() {917 // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.918 escaped_stderr =919 escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned();920 // Windows GNU LD prints uppercase Warning921 escaped_stderr = escaped_stderr922 .strip_prefix("Warning: ")923 .unwrap_or(&escaped_stderr)924 .replace(": warning: ", ": ");925 lint_msg(format!("linker stderr: {}", escaped_stderr.trim_end()));926 }927 if !escaped_stdout.is_empty() {928 lint_msg(format!("linker stdout: {}", escaped_stdout.trim_end()))929 }930 if !linker_info.is_empty() {931 lint_info(linker_info);932 }933}934935/// Create a dynamic library or executable.936///937/// This will invoke the system linker/cc to create the resulting file. This links to all upstream938/// files as well.939fn link_natively(940 sess: &Session,941 archive_builder_builder: &dyn ArchiveBuilderBuilder,942 crate_type: CrateType,943 out_filename: &Path,944 compiled_modules: &CompiledModules,945 crate_info: &CrateInfo,946 metadata: &EncodedMetadata,947 tmpdir: &Path,948 codegen_backend: &'static str,949) {950 info!("preparing {:?} to {:?}", crate_type, out_filename);951 let (linker_path, flavor) = linker_and_flavor(sess);952 let self_contained_components = self_contained_components(sess, crate_type, &linker_path);953954 // On AIX, we ship all libraries as .a big_af archive955 // the expected format is lib<name>.a(libname.so) for the actual956 // dynamic library. So we link to a temporary .so file to be archived957 // at the final out_filename location958 let should_archive = crate_type != CrateType::Executable && sess.target.is_like_aix;959 let archive_member =960 should_archive.then(|| tmpdir.join(out_filename.file_name().unwrap()).with_extension("so"));961 let temp_filename = archive_member.as_deref().unwrap_or(out_filename);962963 let mut cmd = linker_with_args(964 &linker_path,965 flavor,966 sess,967 archive_builder_builder,968 crate_type,969 tmpdir,970 temp_filename,971 compiled_modules,972 crate_info,973 metadata,974 self_contained_components,975 codegen_backend,976 );977978 linker::disable_localization(&mut cmd);979980 for (k, v) in sess.target.link_env.as_ref() {981 cmd.env(k.as_ref(), v.as_ref());982 }983 for k in sess.target.link_env_remove.as_ref() {984 cmd.env_remove(k.as_ref());985 }986987 for print in &sess.opts.prints {988 if print.kind == PrintKind::LinkArgs {989 let content = format!("{cmd:?}\n");990 print.out.overwrite(&content, sess);991 }992 }993994 // May have not found libraries in the right formats.995 sess.dcx().abort_if_errors();996997 // Invoke the system linker998 info!("{cmd:?}");999 let unknown_arg_regex =1000 Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap();1001 let mut prog;1002 loop {1003 prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, flavor, tmpdir));1004 let Ok(ref output) = prog else {1005 break;1006 };1007 if output.status.success() {1008 break;1009 }1010 let mut out = output.stderr.clone();1011 out.extend(&output.stdout);1012 let out = String::from_utf8_lossy(&out);10131014 // Check to see if the link failed with an error message that indicates it1015 // doesn't recognize the -no-pie option. If so, re-perform the link step1016 // without it. This is safe because if the linker doesn't support -no-pie1017 // then it should not default to linking executables as pie. Different1018 // versions of gcc seem to use different quotes in the error message so1019 // don't check for them.1020 if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))1021 && unknown_arg_regex.is_match(&out)1022 && out.contains("-no-pie")1023 && cmd.get_args().iter().any(|e| e == "-no-pie")1024 {1025 info!("linker output: {:?}", out);1026 warn!("Linker does not support -no-pie command line option. Retrying without.");1027 for arg in cmd.take_args() {1028 if arg != "-no-pie" {1029 cmd.arg(arg);1030 }1031 }1032 info!("{cmd:?}");1033 continue;1034 }10351036 // Check if linking failed with an error message that indicates the driver didn't recognize1037 // the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having1038 // to spawn multiple instances on the happy path to do version checking, and ensures things1039 // keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs1040 // circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9.1041 if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes))1042 && unknown_arg_regex.is_match(&out)1043 && out.contains("-fuse-ld=lld")1044 && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld")1045 {1046 info!("linker output: {:?}", out);1047 info!("The linker driver does not support `-fuse-ld=lld`. Retrying without it.");1048 for arg in cmd.take_args() {1049 if arg.to_string_lossy() != "-fuse-ld=lld" {1050 cmd.arg(arg);1051 }1052 }1053 info!("{cmd:?}");1054 continue;1055 }10561057 // Detect '-static-pie' used with an older version of gcc or clang not supporting it.1058 // Fallback from '-static-pie' to '-static' in that case.1059 if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))1060 && unknown_arg_regex.is_match(&out)1061 && (out.contains("-static-pie") || out.contains("--no-dynamic-linker"))1062 && cmd.get_args().iter().any(|e| e == "-static-pie")1063 {1064 info!("linker output: {:?}", out);1065 warn!(1066 "Linker does not support -static-pie command line option. Retrying with -static instead."1067 );1068 // Mirror `add_(pre,post)_link_objects` to replace CRT objects.1069 let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();1070 let opts = &sess.target;1071 let pre_objects = if self_contained_crt_objects {1072 &opts.pre_link_objects_self_contained1073 } else {1074 &opts.pre_link_objects1075 };1076 let post_objects = if self_contained_crt_objects {1077 &opts.post_link_objects_self_contained1078 } else {1079 &opts.post_link_objects1080 };1081 let get_objects = |objects: &CrtObjects, kind| {1082 objects1083 .get(&kind)1084 .iter()1085 .copied()1086 .flatten()1087 .map(|obj| {1088 get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string()1089 })1090 .collect::<Vec<_>>()1091 };1092 let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe);1093 let post_objects_static_pie = get_objects(post_objects, LinkOutputKind::StaticPicExe);1094 let mut pre_objects_static = get_objects(pre_objects, LinkOutputKind::StaticNoPicExe);1095 let mut post_objects_static = get_objects(post_objects, LinkOutputKind::StaticNoPicExe);1096 // Assume that we know insertion positions for the replacement arguments from replaced1097 // arguments, which is true for all supported targets.1098 assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty());1099 assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty());1100 for arg in cmd.take_args() {1101 if arg == "-static-pie" {1102 // Replace the output kind.1103 cmd.arg("-static");1104 } else if pre_objects_static_pie.contains(&arg) {1105 // Replace the pre-link objects (replace the first and remove the rest).1106 cmd.args(mem::take(&mut pre_objects_static));1107 } else if post_objects_static_pie.contains(&arg) {1108 // Replace the post-link objects (replace the first and remove the rest).1109 cmd.args(mem::take(&mut post_objects_static));1110 } else {1111 cmd.arg(arg);1112 }1113 }1114 info!("{cmd:?}");1115 continue;1116 }11171118 break;1119 }11201121 match prog {1122 Ok(prog) => {1123 if !prog.status.success() {1124 let mut output = prog.stderr.clone();1125 output.extend_from_slice(&prog.stdout);1126 let escaped_output = escape_linker_output(&output, flavor);1127 let err = errors::LinkingFailed {1128 linker_path: &linker_path,1129 exit_status: prog.status,1130 command: cmd,1131 escaped_output,1132 verbose: sess.opts.verbose,1133 sysroot_dir: sess.opts.sysroot.path().to_owned(),1134 };1135 sess.dcx().emit_err(err);1136 // If MSVC's `link.exe` was expected but the return code1137 // is not a Microsoft LNK error then suggest a way to fix or1138 // install the Visual Studio build tools.1139 if let Some(code) = prog.status.code() {1140 // All Microsoft `link.exe` linking ror codes are1141 // four digit numbers in the range 1000 to 9999 inclusive1142 if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) {1143 let is_vs_installed = find_msvc_tools::find_vs_version().is_ok();1144 let has_linker =1145 find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe")1146 .is_some();11471148 sess.dcx().emit_note(errors::LinkExeUnexpectedError);11491150 // STATUS_STACK_BUFFER_OVERRUN is also used for fast abnormal program termination, e.g. abort().1151 // Emit a special diagnostic to let people know that this most likely doesn't indicate a stack buffer overrun.1152 const STATUS_STACK_BUFFER_OVERRUN: i32 = 0xc0000409u32 as _;1153 if code == STATUS_STACK_BUFFER_OVERRUN {1154 sess.dcx().emit_note(errors::LinkExeStatusStackBufferOverrun);1155 }11561157 if is_vs_installed && has_linker {1158 // the linker is broken1159 sess.dcx().emit_note(errors::RepairVSBuildTools);1160 sess.dcx().emit_note(errors::MissingCppBuildToolComponent);1161 } else if is_vs_installed {1162 // the linker is not installed1163 sess.dcx().emit_note(errors::SelectCppBuildToolWorkload);1164 } else {1165 // visual studio is not installed1166 sess.dcx().emit_note(errors::VisualStudioNotInstalled);1167 }1168 }1169 }11701171 sess.dcx().abort_if_errors();1172 }11731174 info!("reporting linker output: flavor={flavor:?}");1175 report_linker_output(sess, crate_info.lint_level_specs, &prog.stdout, &prog.stderr);1176 }1177 Err(e) => {1178 let linker_not_found = e.kind() == io::ErrorKind::NotFound;11791180 let err = if linker_not_found {1181 sess.dcx().emit_err(errors::LinkerNotFound { linker_path, error: e })1182 } else {1183 sess.dcx().emit_err(errors::UnableToExeLinker {1184 linker_path,1185 error: e,1186 command_formatted: format!("{cmd:?}"),1187 })1188 };11891190 if sess.target.is_like_msvc && linker_not_found {1191 sess.dcx().emit_note(errors::MsvcMissingLinker);1192 sess.dcx().emit_note(errors::CheckInstalledVisualStudio);1193 sess.dcx().emit_note(errors::InsufficientVSCodeProduct);1194 }1195 err.raise_fatal();1196 }1197 }11981199 match sess.split_debuginfo() {1200 // If split debug information is disabled or located in individual files1201 // there's nothing to do here.1202 SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {}12031204 // If packed split-debuginfo is requested, but the final compilation1205 // doesn't actually have any debug information, then we skip this step.1206 SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {}12071208 // On macOS the external `dsymutil` tool is used to create the packed1209 // debug information. Note that this will read debug information from1210 // the objects on the filesystem which we'll clean up later.1211 SplitDebuginfo::Packed if sess.target.is_like_darwin => {1212 let prog = Command::new("dsymutil").arg(out_filename).output();1213 match prog {1214 Ok(prog) => {1215 if !prog.status.success() {1216 let mut output = prog.stderr.clone();1217 output.extend_from_slice(&prog.stdout);1218 sess.dcx().emit_warn(errors::ProcessingDymutilFailed {1219 status: prog.status,1220 output: escape_string(&output),1221 });1222 }1223 }1224 Err(error) => sess.dcx().emit_fatal(errors::UnableToRunDsymutil { error }),1225 }1226 }12271228 // On MSVC packed debug information is produced by the linker itself so1229 // there's no need to do anything else here.1230 SplitDebuginfo::Packed if sess.target.is_like_windows => {}12311232 // ... and otherwise we're processing a `*.dwp` packed dwarf file.1233 //1234 // We cannot rely on the .o paths in the executable because they may have been1235 // remapped by --remap-path-prefix and therefore invalid, so we need to provide1236 // the .o/.dwo paths explicitly.1237 SplitDebuginfo::Packed => {1238 link_dwarf_object(sess, compiled_modules, crate_info, out_filename)1239 }1240 }12411242 let strip = sess.opts.cg.strip;12431244 if sess.target.is_like_darwin {1245 let stripcmd = "rust-objcopy";1246 match (strip, crate_type) {1247 (Strip::Debuginfo, _) => {1248 strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"])1249 }12501251 // Per the manpage, --discard-all is the maximum safe strip level for dynamic libraries. (#93988)1252 (1253 Strip::Symbols,1254 CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib,1255 ) => strip_with_external_utility(sess, stripcmd, out_filename, &["--discard-all"]),1256 (Strip::Symbols, _) => {1257 strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"])1258 }1259 (Strip::None, _) => {}1260 }1261 }12621263 if sess.target.is_like_solaris {1264 // Many illumos systems will have both the native 'strip' utility and1265 // the GNU one. Use the native version explicitly and do not rely on1266 // what's in the path.1267 //1268 // If cross-compiling and there is not a native version, then use1269 // `llvm-strip` and hope.1270 let stripcmd = if !sess.host.is_like_solaris { "rust-objcopy" } else { "/usr/bin/strip" };1271 match strip {1272 // Always preserve the symbol table (-x).1273 Strip::Debuginfo => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]),1274 // Strip::Symbols is handled via the --strip-all linker option.1275 Strip::Symbols => {}1276 Strip::None => {}1277 }1278 }12791280 if sess.target.is_like_aix {1281 // `llvm-strip` doesn't work for AIX - their strip must be used.1282 if !sess.host.is_like_aix {1283 sess.dcx().emit_warn(errors::AixStripNotUsed);1284 }1285 let stripcmd = "/usr/bin/strip";1286 match strip {1287 Strip::Debuginfo => {1288 // FIXME: AIX's strip utility only offers option to strip line number information.1289 strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-l"])1290 }1291 Strip::Symbols => {1292 // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata.1293 strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-r"])1294 }1295 Strip::None => {}1296 }1297 }12981299 if should_archive {1300 let mut ab = archive_builder_builder.new_archive_builder(sess);1301 ab.add_file(temp_filename, ArchiveEntryKind::Other);1302 ab.build(out_filename, None);1303 }1304}13051306fn strip_with_external_utility(sess: &Session, util: &str, out_filename: &Path, options: &[&str]) {1307 let mut cmd = Command::new(util);1308 cmd.args(options);13091310 let mut new_path = sess.get_tools_search_paths(false);1311 if let Some(path) = env::var_os("PATH") {1312 new_path.extend(env::split_paths(&path));1313 }1314 cmd.env("PATH", env::join_paths(new_path).unwrap());13151316 let prog = cmd.arg(out_filename).output();1317 match prog {1318 Ok(prog) => {1319 if !prog.status.success() {1320 let mut output = prog.stderr.clone();1321 output.extend_from_slice(&prog.stdout);1322 sess.dcx().emit_warn(errors::StrippingDebugInfoFailed {1323 util,1324 status: prog.status,1325 output: escape_string(&output),1326 });1327 }1328 }1329 Err(error) => sess.dcx().emit_fatal(errors::UnableToRun { util, error }),1330 }1331}13321333fn escape_string(s: &[u8]) -> String {1334 match str::from_utf8(s) {1335 Ok(s) => s.to_owned(),1336 Err(_) => format!("Non-UTF-8 output: {}", s.escape_ascii()),1337 }1338}13391340#[cfg(not(windows))]1341fn escape_linker_output(s: &[u8], _flavour: LinkerFlavor) -> String {1342 escape_string(s)1343}13441345/// If the output of the msvc linker is not UTF-8 and the host is Windows,1346/// then try to convert the string from the OEM encoding.1347#[cfg(windows)]1348fn escape_linker_output(s: &[u8], flavour: LinkerFlavor) -> String {1349 // This only applies to the actual MSVC linker.1350 if flavour != LinkerFlavor::Msvc(Lld::No) {1351 return escape_string(s);1352 }1353 match str::from_utf8(s) {1354 Ok(s) => return s.to_owned(),1355 Err(_) => match win::locale_byte_str_to_string(s, win::oem_code_page()) {1356 Some(s) => s,1357 // The string is not UTF-8 and isn't valid for the OEM code page1358 None => format!("Non-UTF-8 output: {}", s.escape_ascii()),1359 },1360 }1361}13621363/// Wrappers around the Windows API.1364#[cfg(windows)]1365mod win {1366 use windows::Win32::Globalization::{1367 CP_OEMCP, GetLocaleInfoEx, LOCALE_IUSEUTF8LEGACYOEMCP, LOCALE_NAME_SYSTEM_DEFAULT,1368 LOCALE_RETURN_NUMBER, MB_ERR_INVALID_CHARS, MultiByteToWideChar,1369 };13701371 /// Get the Windows system OEM code page. This is most notably the code page1372 /// used for link.exe's output.1373 pub(super) fn oem_code_page() -> u32 {1374 unsafe {1375 let mut cp: u32 = 0;1376 // We're using the `LOCALE_RETURN_NUMBER` flag to return a u32.1377 // But the API requires us to pass the data as though it's a [u16] string.1378 let len = size_of::<u32>() / size_of::<u16>();1379 let data = std::slice::from_raw_parts_mut(&mut cp as *mut u32 as *mut u16, len);1380 let len_written = GetLocaleInfoEx(1381 LOCALE_NAME_SYSTEM_DEFAULT,1382 LOCALE_IUSEUTF8LEGACYOEMCP | LOCALE_RETURN_NUMBER,1383 Some(data),1384 );1385 if len_written as usize == len { cp } else { CP_OEMCP }1386 }1387 }1388 /// Try to convert a multi-byte string to a UTF-8 string using the given code page1389 /// The string does not need to be null terminated.1390 ///1391 /// This is implemented as a wrapper around `MultiByteToWideChar`.1392 /// See <https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar>1393 ///1394 /// It will fail if the multi-byte string is longer than `i32::MAX` or if it contains1395 /// any invalid bytes for the expected encoding.1396 pub(super) fn locale_byte_str_to_string(s: &[u8], code_page: u32) -> Option<String> {1397 // `MultiByteToWideChar` requires a length to be a "positive integer".1398 if s.len() > isize::MAX as usize {1399 return None;1400 }1401 // Error if the string is not valid for the expected code page.1402 let flags = MB_ERR_INVALID_CHARS;1403 // Call MultiByteToWideChar twice.1404 // First to calculate the length then to convert the string.1405 let mut len = unsafe { MultiByteToWideChar(code_page, flags, s, None) };1406 if len > 0 {1407 let mut utf16 = vec![0; len as usize];1408 len = unsafe { MultiByteToWideChar(code_page, flags, s, Some(&mut utf16)) };1409 if len > 0 {1410 return utf16.get(..len as usize).map(String::from_utf16_lossy);1411 }1412 }1413 None1414 }1415}14161417fn add_sanitizer_libraries(1418 sess: &Session,1419 flavor: LinkerFlavor,1420 crate_type: CrateType,1421 linker: &mut dyn Linker,1422) {1423 if sess.target.is_like_android {1424 // Sanitizer runtime libraries are provided dynamically on Android1425 // targets.1426 return;1427 }14281429 if sess.opts.unstable_opts.external_clangrt {1430 // Linking against in-tree sanitizer runtimes is disabled via1431 // `-Z external-clangrt`1432 return;1433 }14341435 if matches!(crate_type, CrateType::Rlib | CrateType::StaticLib) {1436 return;1437 }14381439 // On macOS and Windows using MSVC the runtimes are distributed as dylibs1440 // which should be linked to both executables and dynamic libraries.1441 // Everywhere else the runtimes are currently distributed as static1442 // libraries which should be linked to executables only.1443 if matches!(1444 crate_type,1445 CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib1446 ) && !(sess.target.is_like_darwin || sess.target.is_like_msvc)1447 {1448 return;1449 }14501451 let sanitizer = sess.sanitizers();1452 if sanitizer.contains(SanitizerSet::ADDRESS) {1453 link_sanitizer_runtime(sess, flavor, linker, "asan");1454 }1455 if sanitizer.contains(SanitizerSet::DATAFLOW) {1456 link_sanitizer_runtime(sess, flavor, linker, "dfsan");1457 }1458 if sanitizer.contains(SanitizerSet::LEAK)1459 && !sanitizer.contains(SanitizerSet::ADDRESS)1460 && !sanitizer.contains(SanitizerSet::HWADDRESS)1461 {1462 link_sanitizer_runtime(sess, flavor, linker, "lsan");1463 }1464 if sanitizer.contains(SanitizerSet::MEMORY) {1465 link_sanitizer_runtime(sess, flavor, linker, "msan");1466 }1467 if sanitizer.contains(SanitizerSet::THREAD) {1468 link_sanitizer_runtime(sess, flavor, linker, "tsan");1469 }1470 if sanitizer.contains(SanitizerSet::HWADDRESS) {1471 link_sanitizer_runtime(sess, flavor, linker, "hwasan");1472 }1473 if sanitizer.contains(SanitizerSet::SAFESTACK) {1474 link_sanitizer_runtime(sess, flavor, linker, "safestack");1475 }1476 if sanitizer.contains(SanitizerSet::REALTIME) {1477 link_sanitizer_runtime(sess, flavor, linker, "rtsan");1478 }1479}14801481fn link_sanitizer_runtime(1482 sess: &Session,1483 flavor: LinkerFlavor,1484 linker: &mut dyn Linker,1485 name: &str,1486) {1487 fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf {1488 let path = sess.target_tlib_path.dir.join(filename);1489 if path.exists() {1490 sess.target_tlib_path.dir.clone()1491 } else {1492 filesearch::make_target_lib_path(1493 &sess.opts.sysroot.default,1494 sess.opts.target_triple.tuple(),1495 )1496 }1497 }14981499 let channel =1500 option_env!("CFG_RELEASE_CHANNEL").map(|channel| format!("-{channel}")).unwrap_or_default();15011502 if sess.target.is_like_darwin {1503 // On Apple platforms, the sanitizer is always built as a dylib, and1504 // LLVM will link to `@rpath/*.dylib`, so we need to specify an1505 // rpath to the library as well (the rpath should be absolute, see1506 // PR #41352 for details).1507 let filename = format!("rustc{channel}_rt.{name}");1508 let path = find_sanitizer_runtime(sess, &filename);1509 let rpath = path.to_str().expect("non-utf8 component in path");1510 linker.link_args(&["-rpath", rpath]);1511 linker.link_dylib_by_name(&filename, false, true);1512 } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {1513 // MSVC provides the `/INFERASANLIBS` argument to automatically find the1514 // compatible ASAN library.1515 linker.link_arg("/INFERASANLIBS");1516 } else {1517 let filename = format!("librustc{channel}_rt.{name}.a");1518 let path = find_sanitizer_runtime(sess, &filename).join(&filename);1519 linker.link_staticlib_by_path(&path, true);1520 }1521}15221523/// Returns a boolean indicating whether the specified crate should be ignored1524/// during LTO.1525///1526/// Crates ignored during LTO are not lumped together in the "massive object1527/// file" that we create and are linked in their normal rlib states. See1528/// comments below for what crates do not participate in LTO.1529///1530/// It's unusual for a crate to not participate in LTO. Typically only1531/// compiler-specific and unstable crates have a reason to not participate in1532/// LTO.1533pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {1534 // If our target enables builtin function lowering in LLVM then the1535 // crates providing these functions don't participate in LTO (e.g.1536 // no_builtins or compiler builtins crates).1537 !sess.target.no_builtins1538 && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))1539}15401541/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use1542pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {1543 fn infer_from(1544 sess: &Session,1545 linker: Option<PathBuf>,1546 flavor: Option<LinkerFlavor>,1547 features: LinkerFeaturesCli,1548 ) -> Option<(PathBuf, LinkerFlavor)> {1549 let flavor = flavor.map(|flavor| adjust_flavor_to_features(flavor, features));1550 match (linker, flavor) {1551 (Some(linker), Some(flavor)) => Some((linker, flavor)),1552 // only the linker flavor is known; use the default linker for the selected flavor1553 (None, Some(flavor)) => Some((1554 PathBuf::from(match flavor {1555 LinkerFlavor::Gnu(Cc::Yes, _)1556 | LinkerFlavor::Darwin(Cc::Yes, _)1557 | LinkerFlavor::WasmLld(Cc::Yes)1558 | LinkerFlavor::Unix(Cc::Yes) => {1559 if cfg!(any(target_os = "solaris", target_os = "illumos")) {1560 // On historical Solaris systems, "cc" may have1561 // been Sun Studio, which is not flag-compatible1562 // with "gcc". This history casts a long shadow,1563 // and many modern illumos distributions today1564 // ship GCC as "gcc" without also making it1565 // available as "cc".1566 "gcc"1567 } else {1568 "cc"1569 }1570 }1571 LinkerFlavor::Gnu(_, Lld::Yes)1572 | LinkerFlavor::Darwin(_, Lld::Yes)1573 | LinkerFlavor::WasmLld(..)1574 | LinkerFlavor::Msvc(Lld::Yes) => "lld",1575 LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => {1576 "ld"1577 }1578 LinkerFlavor::Msvc(..) => "link.exe",1579 LinkerFlavor::EmCc => {1580 if cfg!(windows) {1581 "emcc.bat"1582 } else {1583 "emcc"1584 }1585 }1586 LinkerFlavor::Bpf => "bpf-linker",1587 LinkerFlavor::Llbc => "llvm-bitcode-linker",1588 }),1589 flavor,1590 )),1591 (Some(linker), None) => {1592 let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {1593 sess.dcx().emit_fatal(errors::LinkerFileStem);1594 });1595 let flavor = sess.target.linker_flavor.with_linker_hints(stem);1596 let flavor = adjust_flavor_to_features(flavor, features);1597 Some((linker, flavor))1598 }1599 (None, None) => None,1600 }1601 }16021603 // While linker flavors and linker features are isomorphic (and thus targets don't need to1604 // define features separately), we use the flavor as the root piece of data and have the1605 // linker-features CLI flag influence *that*, so that downstream code does not have to check for1606 // both yet.1607 fn adjust_flavor_to_features(1608 flavor: LinkerFlavor,1609 features: LinkerFeaturesCli,1610 ) -> LinkerFlavor {1611 // Note: a linker feature cannot be both enabled and disabled on the CLI.1612 if features.enabled.contains(LinkerFeatures::LLD) {1613 flavor.with_lld_enabled()1614 } else if features.disabled.contains(LinkerFeatures::LLD) {1615 flavor.with_lld_disabled()1616 } else {1617 flavor1618 }1619 }16201621 let features = sess.opts.cg.linker_features;16221623 // linker and linker flavor specified via command line have precedence over what the target1624 // specification specifies1625 let linker_flavor = match sess.opts.cg.linker_flavor {1626 // The linker flavors that are non-target specific can be directly translated to LinkerFlavor1627 Some(LinkerFlavorCli::Llbc) => Some(LinkerFlavor::Llbc),1628 // The linker flavors that corresponds to targets needs logic that keeps the base LinkerFlavor1629 linker_flavor => {1630 linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor))1631 }1632 };1633 if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor, features) {1634 return ret;1635 }16361637 if let Some(ret) = infer_from(1638 sess,1639 sess.target.linker.as_deref().map(PathBuf::from),1640 Some(sess.target.linker_flavor),1641 features,1642 ) {1643 return ret;1644 }16451646 bug!("Not enough information provided to determine how to invoke the linker");1647}16481649/// Returns a pair of boolean indicating whether we should preserve the object and1650/// dwarf object files on the filesystem for their debug information. This is often1651/// useful with split-dwarf like schemes.1652fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) {1653 // If the objects don't have debuginfo there's nothing to preserve.1654 if sess.opts.debuginfo == config::DebugInfo::None {1655 return (false, false);1656 }16571658 match (sess.split_debuginfo(), sess.opts.unstable_opts.split_dwarf_kind) {1659 // If there is no split debuginfo then do not preserve objects.1660 (SplitDebuginfo::Off, _) => (false, false),1661 // If there is packed split debuginfo, then the debuginfo in the objects1662 // has been packaged and the objects can be deleted.1663 (SplitDebuginfo::Packed, _) => (false, false),1664 // If there is unpacked split debuginfo and the current target can not use1665 // split dwarf, then keep objects.1666 (SplitDebuginfo::Unpacked, _) if !sess.target_can_use_split_dwarf() => (true, false),1667 // If there is unpacked split debuginfo and the target can use split dwarf, then1668 // keep the object containing that debuginfo (whether that is an object file or1669 // dwarf object file depends on the split dwarf kind).1670 (SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => (true, false),1671 (SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => (false, true),1672 }1673}16741675#[derive(PartialEq)]1676enum RlibFlavor {1677 Normal,1678 StaticlibBase,1679}16801681fn print_native_static_libs(1682 sess: &Session,1683 out: &OutFileName,1684 all_native_libs: &[NativeLib],1685 all_rust_dylibs: &[&Path],1686) {1687 let mut lib_args: Vec<_> = all_native_libs1688 .iter()1689 .filter(|l| relevant_lib(sess, l))1690 .filter_map(|lib| {1691 let name = lib.name;1692 match lib.kind {1693 NativeLibKind::Static { bundle: Some(false), .. }1694 | NativeLibKind::Dylib { .. }1695 | NativeLibKind::Unspecified => {1696 let verbatim = lib.verbatim;1697 if sess.target.is_like_msvc {1698 let (prefix, suffix) = sess.staticlib_components(verbatim);1699 Some(format!("{prefix}{name}{suffix}"))1700 } else if sess.target.linker_flavor.is_gnu() {1701 Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))1702 } else {1703 Some(format!("-l{name}"))1704 }1705 }1706 NativeLibKind::Framework { .. } => {1707 // ld-only syntax, since there are no frameworks in MSVC1708 Some(format!("-framework {name}"))1709 }1710 // These are included, no need to print them1711 NativeLibKind::Static { bundle: None | Some(true), .. }1712 | NativeLibKind::LinkArg1713 | NativeLibKind::WasmImportModule1714 | NativeLibKind::RawDylib { .. } => None,1715 }1716 })1717 // deduplication of consecutive repeated libraries, see rust-lang/rust#1132091718 .dedup()1719 .collect();1720 for path in all_rust_dylibs {1721 // FIXME deduplicate with add_dynamic_crate17221723 // Just need to tell the linker about where the library lives and1724 // what its name is1725 let parent = path.parent();1726 if let Some(dir) = parent {1727 let dir = fix_windows_verbatim_for_gcc(dir);1728 if sess.target.is_like_msvc {1729 let mut arg = String::from("/LIBPATH:");1730 arg.push_str(&dir.display().to_string());1731 lib_args.push(arg);1732 } else {1733 lib_args.push("-L".to_owned());1734 lib_args.push(dir.display().to_string());1735 }1736 }1737 let stem = path.file_stem().unwrap().to_str().unwrap();1738 // Convert library file-stem into a cc -l argument.1739 let lib = if let Some(lib) = stem.strip_prefix("lib")1740 && !sess.target.is_like_windows1741 {1742 lib1743 } else {1744 stem1745 };1746 let path = parent.unwrap_or_else(|| Path::new(""));1747 if sess.target.is_like_msvc {1748 // When producing a dll, the MSVC linker may not actually emit a1749 // `foo.lib` file if the dll doesn't actually export any symbols, so we1750 // check to see if the file is there and just omit linking to it if it's1751 // not present.1752 let name = format!("{lib}.dll.lib");1753 if path.join(&name).exists() {1754 lib_args.push(name);1755 }1756 } else {1757 lib_args.push(format!("-l{lib}"));1758 }1759 }17601761 match out {1762 OutFileName::Real(path) => {1763 out.overwrite(&lib_args.join(" "), sess);1764 sess.dcx().emit_note(errors::StaticLibraryNativeArtifactsToFile { path });1765 }1766 OutFileName::Stdout => {1767 sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts);1768 // Prefix for greppability1769 // Note: This must not be translated as tools are allowed to depend on this exact string.1770 sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" ")));1771 }1772 }1773}17741775fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf {1776 let file_path = sess.target_tlib_path.dir.join(name);1777 if file_path.exists() {1778 return file_path;1779 }1780 // Special directory with objects used only in self-contained linkage mode1781 if self_contained {1782 let file_path = sess.target_tlib_path.dir.join("self-contained").join(name);1783 if file_path.exists() {1784 return file_path;1785 }1786 }1787 for search_path in sess.target_filesearch().search_paths(PathKind::Native) {1788 let file_path = search_path.dir.join(name);1789 if file_path.exists() {1790 return file_path;1791 }1792 }1793 PathBuf::from(name)1794}17951796fn exec_linker(1797 sess: &Session,1798 cmd: &Command,1799 out_filename: &Path,1800 flavor: LinkerFlavor,1801 tmpdir: &Path,1802) -> io::Result<Output> {1803 // When attempting to spawn the linker we run a risk of blowing out the1804 // size limits for spawning a new process with respect to the arguments1805 // we pass on the command line.1806 //1807 // Here we attempt to handle errors from the OS saying "your list of1808 // arguments is too big" by reinvoking the linker again with an `@`-file1809 // that contains all the arguments (aka 'response' files).1810 // The theory is that this is then accepted on all linkers and the linker1811 // will read all its options out of there instead of looking at the command line.1812 if !cmd.very_likely_to_exceed_some_spawn_limit() {1813 match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() {1814 Ok(child) => {1815 let output = child.wait_with_output();1816 flush_linked_file(&output, out_filename)?;1817 return output;1818 }1819 Err(ref e) if command_line_too_big(e) => {1820 info!("command line to linker was too big: {}", e);1821 }1822 Err(e) => return Err(e),1823 }1824 }18251826 info!("falling back to passing arguments to linker via an @-file");1827 let mut cmd2 = cmd.clone();1828 let mut args = String::new();1829 for arg in cmd2.take_args() {1830 args.push_str(1831 &Escape {1832 arg: arg.to_str().unwrap(),1833 // Windows-style escaping for @-files is used by1834 // - all linkers targeting MSVC-like targets, including LLD1835 // - all LLD flavors running on Windows hosts1836 // С/С++ compilers use Posix-style escaping (except clang-cl, which we do not use).1837 is_like_msvc: sess.target.is_like_msvc1838 || (cfg!(windows) && flavor.uses_lld() && !flavor.uses_cc()),1839 }1840 .to_string(),1841 );1842 args.push('\n');1843 }1844 let file = tmpdir.join("linker-arguments");1845 let bytes = if sess.target.is_like_msvc {1846 let mut out = Vec::with_capacity((1 + args.len()) * 2);1847 // start the stream with a UTF-16 BOM1848 for c in std::iter::once(0xFEFF).chain(args.encode_utf16()) {1849 // encode in little endian1850 out.push(c as u8);1851 out.push((c >> 8) as u8);1852 }1853 out1854 } else {1855 args.into_bytes()1856 };1857 fs::write(&file, &bytes)?;1858 cmd2.arg(format!("@{}", file.display()));1859 info!("invoking linker {:?}", cmd2);1860 let output = cmd2.output();1861 flush_linked_file(&output, out_filename)?;1862 return output;18631864 #[cfg(not(windows))]1865 fn flush_linked_file(_: &io::Result<Output>, _: &Path) -> io::Result<()> {1866 Ok(())1867 }18681869 #[cfg(windows)]1870 fn flush_linked_file(1871 command_output: &io::Result<Output>,1872 out_filename: &Path,1873 ) -> io::Result<()> {1874 // On Windows, under high I/O load, output buffers are sometimes not flushed,1875 // even long after process exit, causing nasty, non-reproducible output bugs.1876 //1877 // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem.1878 //1879 // А full writeup of the original Chrome bug can be found at1880 // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp18811882 if let &Ok(ref out) = command_output {1883 if out.status.success() {1884 if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) {1885 of.sync_all()?;1886 }1887 }1888 }18891890 Ok(())1891 }18921893 #[cfg(unix)]1894 fn command_line_too_big(err: &io::Error) -> bool {1895 err.raw_os_error() == Some(::libc::E2BIG)1896 }18971898 #[cfg(windows)]1899 fn command_line_too_big(err: &io::Error) -> bool {1900 const ERROR_FILENAME_EXCED_RANGE: i32 = 206;1901 err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE)1902 }19031904 #[cfg(not(any(unix, windows)))]1905 fn command_line_too_big(_: &io::Error) -> bool {1906 false1907 }19081909 struct Escape<'a> {1910 arg: &'a str,1911 is_like_msvc: bool,1912 }19131914 impl<'a> fmt::Display for Escape<'a> {1915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {1916 if self.is_like_msvc {1917 // This is "documented" at1918 // https://docs.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file1919 //1920 // Unfortunately there's not a great specification of the1921 // syntax I could find online (at least) but some local1922 // testing showed that this seemed sufficient-ish to catch1923 // at least a few edge cases.1924 write!(f, "\"")?;1925 for c in self.arg.chars() {1926 match c {1927 '"' => write!(f, "\\{c}")?,1928 c => write!(f, "{c}")?,1929 }1930 }1931 write!(f, "\"")?;1932 } else {1933 // This is documented at https://linux.die.net/man/1/ld, namely:1934 //1935 // > Options in file are separated by whitespace. A whitespace1936 // > character may be included in an option by surrounding the1937 // > entire option in either single or double quotes. Any1938 // > character (including a backslash) may be included by1939 // > prefixing the character to be included with a backslash.1940 //1941 // We put an argument on each line, so all we need to do is1942 // ensure the line is interpreted as one whole argument.1943 for c in self.arg.chars() {1944 match c {1945 '\\' | ' ' => write!(f, "\\{c}")?,1946 c => write!(f, "{c}")?,1947 }1948 }1949 }1950 Ok(())1951 }1952 }1953}19541955fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {1956 let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) {1957 (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe,1958 (CrateType::Executable, false, RelocModel::Pic | RelocModel::Pie) => {1959 LinkOutputKind::DynamicPicExe1960 }1961 (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe,1962 (CrateType::Executable, true, RelocModel::Pic | RelocModel::Pie) => {1963 LinkOutputKind::StaticPicExe1964 }1965 (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe,1966 (_, true, _) => LinkOutputKind::StaticDylib,1967 (_, false, _) => LinkOutputKind::DynamicDylib,1968 };19691970 // Adjust the output kind to target capabilities.1971 let opts = &sess.target;1972 let pic_exe_supported = opts.position_independent_executables;1973 let static_pic_exe_supported = opts.static_position_independent_executables;1974 let static_dylib_supported = opts.crt_static_allows_dylibs;1975 match kind {1976 LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe,1977 LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe,1978 LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib,1979 _ => kind,1980 }1981}19821983// Returns true if linker is located within sysroot1984fn detect_self_contained_mingw(sess: &Session, linker: &Path) -> bool {1985 let linker_with_extension = if cfg!(windows) && linker.extension().is_none() {1986 linker.with_extension("exe")1987 } else {1988 linker.to_path_buf()1989 };1990 for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {1991 let full_path = dir.join(&linker_with_extension);1992 // If linker comes from sysroot assume self-contained mode1993 if full_path.is_file() && !full_path.starts_with(sess.opts.sysroot.path()) {1994 return false;1995 }1996 }1997 true1998}19992000/// Various toolchain components used during linking are used from rustc distribution
Findings
✓ No findings reported for this file.