compiler/rustc_codegen_ssa/src/back/link.rs RUST 3,737 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,737.
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.