rustc_codegen_ssa/back/
link.rs

1mod raw_dylib;
2
3use 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};
11
12use find_msvc_tools;
13use itertools::Itertools;
14use regex::Regex;
15use rustc_arena::TypedArena;
16use rustc_ast::CRATE_NODE_ID;
17use rustc_attr_parsing::{ShouldEmit, eval_config_entry};
18use rustc_data_structures::fx::FxIndexSet;
19use rustc_data_structures::memmap::Mmap;
20use rustc_data_structures::temp_dir::MaybeTempDir;
21use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
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_macros::LintDiagnostic;
26use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
27use rustc_metadata::{
28    EncodedMetadata, NativeLibSearchFallback, find_native_static_library,
29    walk_native_lib_search_dirs,
30};
31use rustc_middle::bug;
32use rustc_middle::lint::lint_level;
33use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
34use rustc_middle::middle::dependency_format::Linkage;
35use rustc_middle::middle::exported_symbols::SymbolExportKind;
36use rustc_session::config::{
37    self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames,
38    OutputType, PrintKind, SplitDwarfKind, Strip,
39};
40use rustc_session::lint::builtin::LINKER_MESSAGES;
41use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
42use rustc_session::search_paths::PathKind;
43/// For all the linkers we support, and information they might
44/// need out of the shared crate context before we get rid of it.
45use rustc_session::{Session, filesearch};
46use rustc_span::Symbol;
47use rustc_target::spec::crt_objects::CrtObjects;
48use rustc_target::spec::{
49    BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault,
50    LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet,
51    SplitDebuginfo,
52};
53use tracing::{debug, info, warn};
54
55use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
56use super::command::Command;
57use super::linker::{self, Linker};
58use super::metadata::{MetadataPosition, create_wrapper_file};
59use super::rpath::{self, RPathConfig};
60use super::{apple, versioned_llvm_target};
61use crate::base::needs_allocator_shim_for_linking;
62use crate::{
63    CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
64};
65
66pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
67    if let Err(e) = fs::remove_file(path) {
68        if e.kind() != io::ErrorKind::NotFound {
69            dcx.err(format!("failed to remove {}: {}", path.display(), e));
70        }
71    }
72}
73
74/// Performs the linkage portion of the compilation phase. This will generate all
75/// of the requested outputs for this compilation session.
76pub fn link_binary(
77    sess: &Session,
78    archive_builder_builder: &dyn ArchiveBuilderBuilder,
79    codegen_results: CodegenResults,
80    metadata: EncodedMetadata,
81    outputs: &OutputFilenames,
82) {
83    let _timer = sess.timer("link_binary");
84    let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
85    let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
86    for &crate_type in &codegen_results.crate_info.crate_types {
87        // Ignore executable crates if we have -Z no-codegen, as they will error.
88        if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
89            && !output_metadata
90            && crate_type == CrateType::Executable
91        {
92            continue;
93        }
94
95        if invalid_output_for_target(sess, crate_type) {
96            bug!("invalid output type `{:?}` for target `{}`", crate_type, sess.opts.target_triple);
97        }
98
99        sess.time("link_binary_check_files_are_writeable", || {
100            for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
101                check_file_is_writeable(obj, sess);
102            }
103        });
104
105        if outputs.outputs.should_link() {
106            let tmpdir = TempDirBuilder::new()
107                .prefix("rustc")
108                .tempdir()
109                .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error }));
110            let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
111            let output = out_filename(
112                sess,
113                crate_type,
114                outputs,
115                codegen_results.crate_info.local_crate_name,
116            );
117            let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
118            let out_filename = output.file_for_writing(
119                outputs,
120                OutputType::Exe,
121                &crate_name,
122                sess.invocation_temp.as_deref(),
123            );
124            match crate_type {
125                CrateType::Rlib => {
126                    let _timer = sess.timer("link_rlib");
127                    info!("preparing rlib to {:?}", out_filename);
128                    link_rlib(
129                        sess,
130                        archive_builder_builder,
131                        &codegen_results,
132                        &metadata,
133                        RlibFlavor::Normal,
134                        &path,
135                    )
136                    .build(&out_filename);
137                }
138                CrateType::Staticlib => {
139                    link_staticlib(
140                        sess,
141                        archive_builder_builder,
142                        &codegen_results,
143                        &metadata,
144                        &out_filename,
145                        &path,
146                    );
147                }
148                _ => {
149                    link_natively(
150                        sess,
151                        archive_builder_builder,
152                        crate_type,
153                        &out_filename,
154                        &codegen_results,
155                        &metadata,
156                        path.as_ref(),
157                    );
158                }
159            }
160            if sess.opts.json_artifact_notifications {
161                sess.dcx().emit_artifact_notification(&out_filename, "link");
162            }
163
164            if sess.prof.enabled()
165                && let Some(artifact_name) = out_filename.file_name()
166            {
167                // Record size for self-profiling
168                let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0);
169
170                sess.prof.artifact_size(
171                    "linked_artifact",
172                    artifact_name.to_string_lossy(),
173                    file_size,
174                );
175            }
176
177            if sess.target.binary_format == BinaryFormat::Elf {
178                if let Err(err) = warn_if_linked_with_gold(sess, &out_filename) {
179                    info!(?err, "Error while checking if gold was the linker");
180                }
181            }
182
183            if output.is_stdout() {
184                if output.is_tty() {
185                    sess.dcx().emit_err(errors::BinaryOutputToTty {
186                        shorthand: OutputType::Exe.shorthand(),
187                    });
188                } else if let Err(e) = copy_to_stdout(&out_filename) {
189                    sess.dcx().emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e));
190                }
191                tempfiles_for_stdout_output.push(out_filename);
192            }
193        }
194    }
195
196    // Remove the temporary object file and metadata if we aren't saving temps.
197    sess.time("link_binary_remove_temps", || {
198        // If the user requests that temporaries are saved, don't delete any.
199        if sess.opts.cg.save_temps {
200            return;
201        }
202
203        let maybe_remove_temps_from_module =
204            |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| {
205                if !preserve_objects && let Some(ref obj) = module.object {
206                    ensure_removed(sess.dcx(), obj);
207                }
208
209                if !preserve_dwarf_objects && let Some(ref dwo_obj) = module.dwarf_object {
210                    ensure_removed(sess.dcx(), dwo_obj);
211                }
212            };
213
214        let remove_temps_from_module =
215            |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module);
216
217        // Otherwise, always remove the allocator module temporaries.
218        if let Some(ref allocator_module) = codegen_results.allocator_module {
219            remove_temps_from_module(allocator_module);
220        }
221
222        // Remove the temporary files if output goes to stdout
223        for temp in tempfiles_for_stdout_output {
224            ensure_removed(sess.dcx(), &temp);
225        }
226
227        // If no requested outputs require linking, then the object temporaries should
228        // be kept.
229        if !sess.opts.output_types.should_link() {
230            return;
231        }
232
233        // Potentially keep objects for their debuginfo.
234        let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess);
235        debug!(?preserve_objects, ?preserve_dwarf_objects);
236
237        for module in &codegen_results.modules {
238            maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module);
239        }
240    });
241}
242
243// Crate type is not passed when calculating the dylibs to include for LTO. In that case all
244// crate types must use the same dependency formats.
245pub fn each_linked_rlib(
246    info: &CrateInfo,
247    crate_type: Option<CrateType>,
248    f: &mut dyn FnMut(CrateNum, &Path),
249) -> Result<(), errors::LinkRlibError> {
250    let fmts = if let Some(crate_type) = crate_type {
251        let Some(fmts) = info.dependency_formats.get(&crate_type) else {
252            return Err(errors::LinkRlibError::MissingFormat);
253        };
254
255        fmts
256    } else {
257        let mut dep_formats = info.dependency_formats.iter();
258        let (ty1, list1) = dep_formats.next().ok_or(errors::LinkRlibError::MissingFormat)?;
259        if let Some((ty2, list2)) = dep_formats.find(|(_, list2)| list1 != *list2) {
260            return Err(errors::LinkRlibError::IncompatibleDependencyFormats {
261                ty1: format!("{ty1:?}"),
262                ty2: format!("{ty2:?}"),
263                list1: format!("{list1:?}"),
264                list2: format!("{list2:?}"),
265            });
266        }
267        list1
268    };
269
270    let used_dep_crates = info.used_crates.iter();
271    for &cnum in used_dep_crates {
272        match fmts.get(cnum) {
273            Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue,
274            Some(_) => {}
275            None => return Err(errors::LinkRlibError::MissingFormat),
276        }
277        let crate_name = info.crate_name[&cnum];
278        let used_crate_source = &info.used_crate_source[&cnum];
279        if let Some((path, _)) = &used_crate_source.rlib {
280            f(cnum, path);
281        } else if used_crate_source.rmeta.is_some() {
282            return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name });
283        } else {
284            return Err(errors::LinkRlibError::NotFound { crate_name });
285        }
286    }
287    Ok(())
288}
289
290/// Create an 'rlib'.
291///
292/// An rlib in its current incarnation is essentially a renamed .a file (with "dummy" object files).
293/// The rlib primarily contains the object file of the crate, but it also some of the object files
294/// from native libraries.
295fn link_rlib<'a>(
296    sess: &'a Session,
297    archive_builder_builder: &dyn ArchiveBuilderBuilder,
298    codegen_results: &CodegenResults,
299    metadata: &EncodedMetadata,
300    flavor: RlibFlavor,
301    tmpdir: &MaybeTempDir,
302) -> Box<dyn ArchiveBuilder + 'a> {
303    let mut ab = archive_builder_builder.new_archive_builder(sess);
304
305    let trailing_metadata = match flavor {
306        RlibFlavor::Normal => {
307            let (metadata, metadata_position) =
308                create_wrapper_file(sess, ".rmeta".to_string(), metadata.stub_or_full());
309            let metadata = emit_wrapper_file(sess, &metadata, tmpdir.as_ref(), METADATA_FILENAME);
310            match metadata_position {
311                MetadataPosition::First => {
312                    // Most of the time metadata in rlib files is wrapped in a "dummy" object
313                    // file for the target platform so the rlib can be processed entirely by
314                    // normal linkers for the platform. Sometimes this is not possible however.
315                    // If it is possible however, placing the metadata object first improves
316                    // performance of getting metadata from rlibs.
317                    ab.add_file(&metadata);
318                    None
319                }
320                MetadataPosition::Last => Some(metadata),
321            }
322        }
323
324        RlibFlavor::StaticlibBase => None,
325    };
326
327    for m in &codegen_results.modules {
328        if let Some(obj) = m.object.as_ref() {
329            ab.add_file(obj);
330        }
331
332        if let Some(dwarf_obj) = m.dwarf_object.as_ref() {
333            ab.add_file(dwarf_obj);
334        }
335    }
336
337    match flavor {
338        RlibFlavor::Normal => {}
339        RlibFlavor::StaticlibBase => {
340            let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref());
341            if let Some(obj) = obj {
342                ab.add_file(obj);
343            }
344        }
345    }
346
347    // Used if packed_bundled_libs flag enabled.
348    let mut packed_bundled_libs = Vec::new();
349
350    // Note that in this loop we are ignoring the value of `lib.cfg`. That is,
351    // we may not be configured to actually include a static library if we're
352    // adding it here. That's because later when we consume this rlib we'll
353    // decide whether we actually needed the static library or not.
354    //
355    // To do this "correctly" we'd need to keep track of which libraries added
356    // which object files to the archive. We don't do that here, however. The
357    // #[link(cfg(..))] feature is unstable, though, and only intended to get
358    // liblibc working. In that sense the check below just indicates that if
359    // there are any libraries we want to omit object files for at link time we
360    // just exclude all custom object files.
361    //
362    // Eventually if we want to stabilize or flesh out the #[link(cfg(..))]
363    // feature then we'll need to figure out how to record what objects were
364    // loaded from the libraries found here and then encode that into the
365    // metadata of the rlib we're generating somehow.
366    for lib in codegen_results.crate_info.used_libraries.iter() {
367        let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {
368            continue;
369        };
370        if flavor == RlibFlavor::Normal
371            && let Some(filename) = lib.filename
372        {
373            let path = find_native_static_library(filename.as_str(), true, sess);
374            let src = read(path)
375                .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }));
376            let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);
377            let wrapper_file = emit_wrapper_file(sess, &data, tmpdir.as_ref(), filename.as_str());
378            packed_bundled_libs.push(wrapper_file);
379        } else {
380            let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);
381            ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
382                sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })
383            });
384        }
385    }
386
387    // On Windows, we add the raw-dylib import libraries to the rlibs already.
388    // But on ELF, this is not possible, as a shared object cannot be a member of a static library.
389    // Instead, we add all raw-dylibs to the final link on ELF.
390    if sess.target.is_like_windows {
391        for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
392            sess,
393            archive_builder_builder,
394            codegen_results.crate_info.used_libraries.iter(),
395            tmpdir.as_ref(),
396            true,
397        ) {
398            ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
399                sess.dcx()
400                    .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
401            });
402        }
403    }
404
405    if let Some(trailing_metadata) = trailing_metadata {
406        // Note that it is important that we add all of our non-object "magical
407        // files" *after* all of the object files in the archive. The reason for
408        // this is as follows:
409        //
410        // * When performing LTO, this archive will be modified to remove
411        //   objects from above. The reason for this is described below.
412        //
413        // * When the system linker looks at an archive, it will attempt to
414        //   determine the architecture of the archive in order to see whether its
415        //   linkable.
416        //
417        //   The algorithm for this detection is: iterate over the files in the
418        //   archive. Skip magical SYMDEF names. Interpret the first file as an
419        //   object file. Read architecture from the object file.
420        //
421        // * As one can probably see, if "metadata" and "foo.bc" were placed
422        //   before all of the objects, then the architecture of this archive would
423        //   not be correctly inferred once 'foo.o' is removed.
424        //
425        // * Most of the time metadata in rlib files is wrapped in a "dummy" object
426        //   file for the target platform so the rlib can be processed entirely by
427        //   normal linkers for the platform. Sometimes this is not possible however.
428        //
429        // Basically, all this means is that this code should not move above the
430        // code above.
431        ab.add_file(&trailing_metadata);
432    }
433
434    // Add all bundled static native library dependencies.
435    // Archives added to the end of .rlib archive, see comment above for the reason.
436    for lib in packed_bundled_libs {
437        ab.add_file(&lib)
438    }
439
440    ab
441}
442
443/// Create a static archive.
444///
445/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
446/// crates' objects into the archive. This will slurp in all of the native libraries of upstream
447/// dependencies as well.
448///
449/// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic
450/// library dependencies that they're not linked in.
451///
452/// There's no need to include metadata in a static archive, so ensure to not link in the metadata
453/// object file (and also don't prepare the archive with a metadata file).
454fn link_staticlib(
455    sess: &Session,
456    archive_builder_builder: &dyn ArchiveBuilderBuilder,
457    codegen_results: &CodegenResults,
458    metadata: &EncodedMetadata,
459    out_filename: &Path,
460    tempdir: &MaybeTempDir,
461) {
462    info!("preparing staticlib to {:?}", out_filename);
463    let mut ab = link_rlib(
464        sess,
465        archive_builder_builder,
466        codegen_results,
467        metadata,
468        RlibFlavor::StaticlibBase,
469        tempdir,
470    );
471    let mut all_native_libs = vec![];
472
473    let res = each_linked_rlib(
474        &codegen_results.crate_info,
475        Some(CrateType::Staticlib),
476        &mut |cnum, path| {
477            let lto = are_upstream_rust_objects_already_included(sess)
478                && !ignored_for_lto(sess, &codegen_results.crate_info, cnum);
479
480            let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter();
481            let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib));
482            let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect();
483
484            let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
485            ab.add_archive(
486                path,
487                Box::new(move |fname: &str| {
488                    // Ignore metadata files, no matter the name.
489                    if fname == METADATA_FILENAME {
490                        return true;
491                    }
492
493                    // Don't include Rust objects if LTO is enabled
494                    if lto && looks_like_rust_object_file(fname) {
495                        return true;
496                    }
497
498                    // Skip objects for bundled libs.
499                    if bundled_libs.contains(&Symbol::intern(fname)) {
500                        return true;
501                    }
502
503                    false
504                }),
505            )
506            .unwrap();
507
508            archive_builder_builder
509                .extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs)
510                .unwrap_or_else(|e| sess.dcx().emit_fatal(e));
511
512            for filename in relevant_libs.iter() {
513                let joined = tempdir.as_ref().join(filename.as_str());
514                let path = joined.as_path();
515                ab.add_archive(path, Box::new(|_| false)).unwrap();
516            }
517
518            all_native_libs
519                .extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned());
520        },
521    );
522    if let Err(e) = res {
523        sess.dcx().emit_fatal(e);
524    }
525
526    ab.build(out_filename);
527
528    let crates = codegen_results.crate_info.used_crates.iter();
529
530    let fmts = codegen_results
531        .crate_info
532        .dependency_formats
533        .get(&CrateType::Staticlib)
534        .expect("no dependency formats for staticlib");
535
536    let mut all_rust_dylibs = vec![];
537    for &cnum in crates {
538        let Some(Linkage::Dynamic) = fmts.get(cnum) else {
539            continue;
540        };
541        let crate_name = codegen_results.crate_info.crate_name[&cnum];
542        let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum];
543        if let Some((path, _)) = &used_crate_source.dylib {
544            all_rust_dylibs.push(&**path);
545        } else if used_crate_source.rmeta.is_some() {
546            sess.dcx().emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name });
547        } else {
548            sess.dcx().emit_fatal(errors::LinkRlibError::NotFound { crate_name });
549        }
550    }
551
552    all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries);
553
554    for print in &sess.opts.prints {
555        if print.kind == PrintKind::NativeStaticLibs {
556            print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs);
557        }
558    }
559}
560
561/// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a
562/// DWARF package.
563fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out_filename: &Path) {
564    let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string();
565    dwp_out_filename.push(".dwp");
566    debug!(?dwp_out_filename, ?executable_out_filename);
567
568    #[derive(Default)]
569    struct ThorinSession<Relocations> {
570        arena_data: TypedArena<Vec<u8>>,
571        arena_mmap: TypedArena<Mmap>,
572        arena_relocations: TypedArena<Relocations>,
573    }
574
575    impl<Relocations> ThorinSession<Relocations> {
576        fn alloc_mmap(&self, data: Mmap) -> &Mmap {
577            &*self.arena_mmap.alloc(data)
578        }
579    }
580
581    impl<Relocations> thorin::Session<Relocations> for ThorinSession<Relocations> {
582        fn alloc_data(&self, data: Vec<u8>) -> &[u8] {
583            &*self.arena_data.alloc(data)
584        }
585
586        fn alloc_relocation(&self, data: Relocations) -> &Relocations {
587            &*self.arena_relocations.alloc(data)
588        }
589
590        fn read_input(&self, path: &Path) -> std::io::Result<&[u8]> {
591            let file = File::open(&path)?;
592            let mmap = (unsafe { Mmap::map(file) })?;
593            Ok(self.alloc_mmap(mmap))
594        }
595    }
596
597    match sess.time("run_thorin", || -> Result<(), thorin::Error> {
598        let thorin_sess = ThorinSession::default();
599        let mut package = thorin::DwarfPackage::new(&thorin_sess);
600
601        // Input objs contain .o/.dwo files from the current crate.
602        match sess.opts.unstable_opts.split_dwarf_kind {
603            SplitDwarfKind::Single => {
604                for input_obj in cg_results.modules.iter().filter_map(|m| m.object.as_ref()) {
605                    package.add_input_object(input_obj)?;
606                }
607            }
608            SplitDwarfKind::Split => {
609                for input_obj in cg_results.modules.iter().filter_map(|m| m.dwarf_object.as_ref()) {
610                    package.add_input_object(input_obj)?;
611                }
612            }
613        }
614
615        // Input rlibs contain .o/.dwo files from dependencies.
616        let input_rlibs = cg_results
617            .crate_info
618            .used_crate_source
619            .items()
620            .filter_map(|(_, csource)| csource.rlib.as_ref())
621            .map(|(path, _)| path)
622            .into_sorted_stable_ord();
623
624        for input_rlib in input_rlibs {
625            debug!(?input_rlib);
626            package.add_input_object(input_rlib)?;
627        }
628
629        // Failing to read the referenced objects is expected for dependencies where the path in the
630        // executable will have been cleaned by Cargo, but the referenced objects will be contained
631        // within rlibs provided as inputs.
632        //
633        // If paths have been remapped, then .o/.dwo files from the current crate also won't be
634        // found, but are provided explicitly above.
635        //
636        // Adding an executable is primarily done to make `thorin` check that all the referenced
637        // dwarf objects are found in the end.
638        package.add_executable(
639            executable_out_filename,
640            thorin::MissingReferencedObjectBehaviour::Skip,
641        )?;
642
643        let output_stream = BufWriter::new(
644            OpenOptions::new()
645                .read(true)
646                .write(true)
647                .create(true)
648                .truncate(true)
649                .open(dwp_out_filename)?,
650        );
651        let mut output_stream = thorin::object::write::StreamingBuffer::new(output_stream);
652        package.finish()?.emit(&mut output_stream)?;
653        output_stream.result()?;
654        output_stream.into_inner().flush()?;
655
656        Ok(())
657    }) {
658        Ok(()) => {}
659        Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)),
660    }
661}
662
663#[derive(LintDiagnostic)]
664#[diag(codegen_ssa_linker_output)]
665/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just
666/// end up with inconsistent languages within the same diagnostic.
667struct LinkerOutput {
668    inner: String,
669}
670
671/// Create a dynamic library or executable.
672///
673/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
674/// files as well.
675fn link_natively(
676    sess: &Session,
677    archive_builder_builder: &dyn ArchiveBuilderBuilder,
678    crate_type: CrateType,
679    out_filename: &Path,
680    codegen_results: &CodegenResults,
681    metadata: &EncodedMetadata,
682    tmpdir: &Path,
683) {
684    info!("preparing {:?} to {:?}", crate_type, out_filename);
685    let (linker_path, flavor) = linker_and_flavor(sess);
686    let self_contained_components = self_contained_components(sess, crate_type, &linker_path);
687
688    // On AIX, we ship all libraries as .a big_af archive
689    // the expected format is lib<name>.a(libname.so) for the actual
690    // dynamic library. So we link to a temporary .so file to be archived
691    // at the final out_filename location
692    let should_archive = crate_type != CrateType::Executable && sess.target.is_like_aix;
693    let archive_member =
694        should_archive.then(|| tmpdir.join(out_filename.file_name().unwrap()).with_extension("so"));
695    let temp_filename = archive_member.as_deref().unwrap_or(out_filename);
696
697    let mut cmd = linker_with_args(
698        &linker_path,
699        flavor,
700        sess,
701        archive_builder_builder,
702        crate_type,
703        tmpdir,
704        temp_filename,
705        codegen_results,
706        metadata,
707        self_contained_components,
708    );
709
710    linker::disable_localization(&mut cmd);
711
712    for (k, v) in sess.target.link_env.as_ref() {
713        cmd.env(k.as_ref(), v.as_ref());
714    }
715    for k in sess.target.link_env_remove.as_ref() {
716        cmd.env_remove(k.as_ref());
717    }
718
719    for print in &sess.opts.prints {
720        if print.kind == PrintKind::LinkArgs {
721            let content = format!("{cmd:?}\n");
722            print.out.overwrite(&content, sess);
723        }
724    }
725
726    // May have not found libraries in the right formats.
727    sess.dcx().abort_if_errors();
728
729    // Invoke the system linker
730    info!("{cmd:?}");
731    let unknown_arg_regex =
732        Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap();
733    let mut prog;
734    loop {
735        prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, flavor, tmpdir));
736        let Ok(ref output) = prog else {
737            break;
738        };
739        if output.status.success() {
740            break;
741        }
742        let mut out = output.stderr.clone();
743        out.extend(&output.stdout);
744        let out = String::from_utf8_lossy(&out);
745
746        // Check to see if the link failed with an error message that indicates it
747        // doesn't recognize the -no-pie option. If so, re-perform the link step
748        // without it. This is safe because if the linker doesn't support -no-pie
749        // then it should not default to linking executables as pie. Different
750        // versions of gcc seem to use different quotes in the error message so
751        // don't check for them.
752        if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
753            && unknown_arg_regex.is_match(&out)
754            && out.contains("-no-pie")
755            && cmd.get_args().iter().any(|e| e == "-no-pie")
756        {
757            info!("linker output: {:?}", out);
758            warn!("Linker does not support -no-pie command line option. Retrying without.");
759            for arg in cmd.take_args() {
760                if arg != "-no-pie" {
761                    cmd.arg(arg);
762                }
763            }
764            info!("{cmd:?}");
765            continue;
766        }
767
768        // Check if linking failed with an error message that indicates the driver didn't recognize
769        // the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having
770        // to spawn multiple instances on the happy path to do version checking, and ensures things
771        // keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs
772        // circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9.
773        if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes))
774            && unknown_arg_regex.is_match(&out)
775            && out.contains("-fuse-ld=lld")
776            && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld")
777        {
778            info!("linker output: {:?}", out);
779            info!("The linker driver does not support `-fuse-ld=lld`. Retrying without it.");
780            for arg in cmd.take_args() {
781                if arg.to_string_lossy() != "-fuse-ld=lld" {
782                    cmd.arg(arg);
783                }
784            }
785            info!("{cmd:?}");
786            continue;
787        }
788
789        // Detect '-static-pie' used with an older version of gcc or clang not supporting it.
790        // Fallback from '-static-pie' to '-static' in that case.
791        if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
792            && unknown_arg_regex.is_match(&out)
793            && (out.contains("-static-pie") || out.contains("--no-dynamic-linker"))
794            && cmd.get_args().iter().any(|e| e == "-static-pie")
795        {
796            info!("linker output: {:?}", out);
797            warn!(
798                "Linker does not support -static-pie command line option. Retrying with -static instead."
799            );
800            // Mirror `add_(pre,post)_link_objects` to replace CRT objects.
801            let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
802            let opts = &sess.target;
803            let pre_objects = if self_contained_crt_objects {
804                &opts.pre_link_objects_self_contained
805            } else {
806                &opts.pre_link_objects
807            };
808            let post_objects = if self_contained_crt_objects {
809                &opts.post_link_objects_self_contained
810            } else {
811                &opts.post_link_objects
812            };
813            let get_objects = |objects: &CrtObjects, kind| {
814                objects
815                    .get(&kind)
816                    .iter()
817                    .copied()
818                    .flatten()
819                    .map(|obj| {
820                        get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string()
821                    })
822                    .collect::<Vec<_>>()
823            };
824            let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe);
825            let post_objects_static_pie = get_objects(post_objects, LinkOutputKind::StaticPicExe);
826            let mut pre_objects_static = get_objects(pre_objects, LinkOutputKind::StaticNoPicExe);
827            let mut post_objects_static = get_objects(post_objects, LinkOutputKind::StaticNoPicExe);
828            // Assume that we know insertion positions for the replacement arguments from replaced
829            // arguments, which is true for all supported targets.
830            assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty());
831            assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty());
832            for arg in cmd.take_args() {
833                if arg == "-static-pie" {
834                    // Replace the output kind.
835                    cmd.arg("-static");
836                } else if pre_objects_static_pie.contains(&arg) {
837                    // Replace the pre-link objects (replace the first and remove the rest).
838                    cmd.args(mem::take(&mut pre_objects_static));
839                } else if post_objects_static_pie.contains(&arg) {
840                    // Replace the post-link objects (replace the first and remove the rest).
841                    cmd.args(mem::take(&mut post_objects_static));
842                } else {
843                    cmd.arg(arg);
844                }
845            }
846            info!("{cmd:?}");
847            continue;
848        }
849
850        break;
851    }
852
853    match prog {
854        Ok(prog) => {
855            let is_msvc_link_exe = sess.target.is_like_msvc
856                && flavor == LinkerFlavor::Msvc(Lld::No)
857                // Match exactly "link.exe"
858                && linker_path.to_str() == Some("link.exe");
859
860            if !prog.status.success() {
861                let mut output = prog.stderr.clone();
862                output.extend_from_slice(&prog.stdout);
863                let escaped_output = escape_linker_output(&output, flavor);
864                let err = errors::LinkingFailed {
865                    linker_path: &linker_path,
866                    exit_status: prog.status,
867                    command: cmd,
868                    escaped_output,
869                    verbose: sess.opts.verbose,
870                    sysroot_dir: sess.opts.sysroot.path().to_owned(),
871                };
872                sess.dcx().emit_err(err);
873                // If MSVC's `link.exe` was expected but the return code
874                // is not a Microsoft LNK error then suggest a way to fix or
875                // install the Visual Studio build tools.
876                if let Some(code) = prog.status.code() {
877                    // All Microsoft `link.exe` linking ror codes are
878                    // four digit numbers in the range 1000 to 9999 inclusive
879                    if is_msvc_link_exe && (code < 1000 || code > 9999) {
880                        let is_vs_installed = find_msvc_tools::find_vs_version().is_ok();
881                        let has_linker =
882                            find_msvc_tools::find_tool(&sess.target.arch, "link.exe").is_some();
883
884                        sess.dcx().emit_note(errors::LinkExeUnexpectedError);
885
886                        // STATUS_STACK_BUFFER_OVERRUN is also used for fast abnormal program termination, e.g. abort().
887                        // Emit a special diagnostic to let people know that this most likely doesn't indicate a stack buffer overrun.
888                        const STATUS_STACK_BUFFER_OVERRUN: i32 = 0xc0000409u32 as _;
889                        if code == STATUS_STACK_BUFFER_OVERRUN {
890                            sess.dcx().emit_note(errors::LinkExeStatusStackBufferOverrun);
891                        }
892
893                        if is_vs_installed && has_linker {
894                            // the linker is broken
895                            sess.dcx().emit_note(errors::RepairVSBuildTools);
896                            sess.dcx().emit_note(errors::MissingCppBuildToolComponent);
897                        } else if is_vs_installed {
898                            // the linker is not installed
899                            sess.dcx().emit_note(errors::SelectCppBuildToolWorkload);
900                        } else {
901                            // visual studio is not installed
902                            sess.dcx().emit_note(errors::VisualStudioNotInstalled);
903                        }
904                    }
905                }
906
907                sess.dcx().abort_if_errors();
908            }
909
910            let stderr = escape_string(&prog.stderr);
911            let mut stdout = escape_string(&prog.stdout);
912            info!("linker stderr:\n{}", &stderr);
913            info!("linker stdout:\n{}", &stdout);
914
915            // Hide some progress messages from link.exe that we don't care about.
916            // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
917            if is_msvc_link_exe {
918                if let Ok(str) = str::from_utf8(&prog.stdout) {
919                    let mut output = String::with_capacity(str.len());
920                    for line in stdout.lines() {
921                        if line.starts_with("   Creating library")
922                            || line.starts_with("Generating code")
923                            || line.starts_with("Finished generating code")
924                        {
925                            continue;
926                        }
927                        output += line;
928                        output += "\r\n"
929                    }
930                    stdout = escape_string(output.trim().as_bytes())
931                }
932            }
933
934            let level = codegen_results.crate_info.lint_levels.linker_messages;
935            let lint = |msg| {
936                lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
937                    LinkerOutput { inner: msg }.decorate_lint(diag)
938                })
939            };
940
941            if !prog.stderr.is_empty() {
942                // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
943                let stderr = stderr
944                    .strip_prefix("warning: ")
945                    .unwrap_or(&stderr)
946                    .replace(": warning: ", ": ");
947                lint(format!("linker stderr: {stderr}"));
948            }
949            if !stdout.is_empty() {
950                lint(format!("linker stdout: {}", stdout))
951            }
952        }
953        Err(e) => {
954            let linker_not_found = e.kind() == io::ErrorKind::NotFound;
955
956            let err = if linker_not_found {
957                sess.dcx().emit_err(errors::LinkerNotFound { linker_path, error: e })
958            } else {
959                sess.dcx().emit_err(errors::UnableToExeLinker {
960                    linker_path,
961                    error: e,
962                    command_formatted: format!("{cmd:?}"),
963                })
964            };
965
966            if sess.target.is_like_msvc && linker_not_found {
967                sess.dcx().emit_note(errors::MsvcMissingLinker);
968                sess.dcx().emit_note(errors::CheckInstalledVisualStudio);
969                sess.dcx().emit_note(errors::InsufficientVSCodeProduct);
970            }
971            err.raise_fatal();
972        }
973    }
974
975    match sess.split_debuginfo() {
976        // If split debug information is disabled or located in individual files
977        // there's nothing to do here.
978        SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {}
979
980        // If packed split-debuginfo is requested, but the final compilation
981        // doesn't actually have any debug information, then we skip this step.
982        SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {}
983
984        // On macOS the external `dsymutil` tool is used to create the packed
985        // debug information. Note that this will read debug information from
986        // the objects on the filesystem which we'll clean up later.
987        SplitDebuginfo::Packed if sess.target.is_like_darwin => {
988            let prog = Command::new("dsymutil").arg(out_filename).output();
989            match prog {
990                Ok(prog) => {
991                    if !prog.status.success() {
992                        let mut output = prog.stderr.clone();
993                        output.extend_from_slice(&prog.stdout);
994                        sess.dcx().emit_warn(errors::ProcessingDymutilFailed {
995                            status: prog.status,
996                            output: escape_string(&output),
997                        });
998                    }
999                }
1000                Err(error) => sess.dcx().emit_fatal(errors::UnableToRunDsymutil { error }),
1001            }
1002        }
1003
1004        // On MSVC packed debug information is produced by the linker itself so
1005        // there's no need to do anything else here.
1006        SplitDebuginfo::Packed if sess.target.is_like_windows => {}
1007
1008        // ... and otherwise we're processing a `*.dwp` packed dwarf file.
1009        //
1010        // We cannot rely on the .o paths in the executable because they may have been
1011        // remapped by --remap-path-prefix and therefore invalid, so we need to provide
1012        // the .o/.dwo paths explicitly.
1013        SplitDebuginfo::Packed => link_dwarf_object(sess, codegen_results, out_filename),
1014    }
1015
1016    let strip = sess.opts.cg.strip;
1017
1018    if sess.target.is_like_darwin {
1019        let stripcmd = "rust-objcopy";
1020        match (strip, crate_type) {
1021            (Strip::Debuginfo, _) => {
1022                strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"])
1023            }
1024
1025            // Per the manpage, --discard-all is the maximum safe strip level for dynamic libraries. (#93988)
1026            (
1027                Strip::Symbols,
1028                CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib,
1029            ) => strip_with_external_utility(sess, stripcmd, out_filename, &["--discard-all"]),
1030            (Strip::Symbols, _) => {
1031                strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"])
1032            }
1033            (Strip::None, _) => {}
1034        }
1035    }
1036
1037    if sess.target.is_like_solaris {
1038        // Many illumos systems will have both the native 'strip' utility and
1039        // the GNU one. Use the native version explicitly and do not rely on
1040        // what's in the path.
1041        //
1042        // If cross-compiling and there is not a native version, then use
1043        // `llvm-strip` and hope.
1044        let stripcmd = if !sess.host.is_like_solaris { "rust-objcopy" } else { "/usr/bin/strip" };
1045        match strip {
1046            // Always preserve the symbol table (-x).
1047            Strip::Debuginfo => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]),
1048            // Strip::Symbols is handled via the --strip-all linker option.
1049            Strip::Symbols => {}
1050            Strip::None => {}
1051        }
1052    }
1053
1054    if sess.target.is_like_aix {
1055        // `llvm-strip` doesn't work for AIX - their strip must be used.
1056        if !sess.host.is_like_aix {
1057            sess.dcx().emit_warn(errors::AixStripNotUsed);
1058        }
1059        let stripcmd = "/usr/bin/strip";
1060        match strip {
1061            Strip::Debuginfo => {
1062                // FIXME: AIX's strip utility only offers option to strip line number information.
1063                strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-l"])
1064            }
1065            Strip::Symbols => {
1066                // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata.
1067                strip_with_external_utility(sess, stripcmd, temp_filename, &["-X32_64", "-r"])
1068            }
1069            Strip::None => {}
1070        }
1071    }
1072
1073    if should_archive {
1074        let mut ab = archive_builder_builder.new_archive_builder(sess);
1075        ab.add_file(temp_filename);
1076        ab.build(out_filename);
1077    }
1078}
1079
1080fn strip_with_external_utility(sess: &Session, util: &str, out_filename: &Path, options: &[&str]) {
1081    let mut cmd = Command::new(util);
1082    cmd.args(options);
1083
1084    let mut new_path = sess.get_tools_search_paths(false);
1085    if let Some(path) = env::var_os("PATH") {
1086        new_path.extend(env::split_paths(&path));
1087    }
1088    cmd.env("PATH", env::join_paths(new_path).unwrap());
1089
1090    let prog = cmd.arg(out_filename).output();
1091    match prog {
1092        Ok(prog) => {
1093            if !prog.status.success() {
1094                let mut output = prog.stderr.clone();
1095                output.extend_from_slice(&prog.stdout);
1096                sess.dcx().emit_warn(errors::StrippingDebugInfoFailed {
1097                    util,
1098                    status: prog.status,
1099                    output: escape_string(&output),
1100                });
1101            }
1102        }
1103        Err(error) => sess.dcx().emit_fatal(errors::UnableToRun { util, error }),
1104    }
1105}
1106
1107fn escape_string(s: &[u8]) -> String {
1108    match str::from_utf8(s) {
1109        Ok(s) => s.to_owned(),
1110        Err(_) => format!("Non-UTF-8 output: {}", s.escape_ascii()),
1111    }
1112}
1113
1114#[cfg(not(windows))]
1115fn escape_linker_output(s: &[u8], _flavour: LinkerFlavor) -> String {
1116    escape_string(s)
1117}
1118
1119/// If the output of the msvc linker is not UTF-8 and the host is Windows,
1120/// then try to convert the string from the OEM encoding.
1121#[cfg(windows)]
1122fn escape_linker_output(s: &[u8], flavour: LinkerFlavor) -> String {
1123    // This only applies to the actual MSVC linker.
1124    if flavour != LinkerFlavor::Msvc(Lld::No) {
1125        return escape_string(s);
1126    }
1127    match str::from_utf8(s) {
1128        Ok(s) => return s.to_owned(),
1129        Err(_) => match win::locale_byte_str_to_string(s, win::oem_code_page()) {
1130            Some(s) => s,
1131            // The string is not UTF-8 and isn't valid for the OEM code page
1132            None => format!("Non-UTF-8 output: {}", s.escape_ascii()),
1133        },
1134    }
1135}
1136
1137/// Wrappers around the Windows API.
1138#[cfg(windows)]
1139mod win {
1140    use windows::Win32::Globalization::{
1141        CP_OEMCP, GetLocaleInfoEx, LOCALE_IUSEUTF8LEGACYOEMCP, LOCALE_NAME_SYSTEM_DEFAULT,
1142        LOCALE_RETURN_NUMBER, MB_ERR_INVALID_CHARS, MultiByteToWideChar,
1143    };
1144
1145    /// Get the Windows system OEM code page. This is most notably the code page
1146    /// used for link.exe's output.
1147    pub(super) fn oem_code_page() -> u32 {
1148        unsafe {
1149            let mut cp: u32 = 0;
1150            // We're using the `LOCALE_RETURN_NUMBER` flag to return a u32.
1151            // But the API requires us to pass the data as though it's a [u16] string.
1152            let len = size_of::<u32>() / size_of::<u16>();
1153            let data = std::slice::from_raw_parts_mut(&mut cp as *mut u32 as *mut u16, len);
1154            let len_written = GetLocaleInfoEx(
1155                LOCALE_NAME_SYSTEM_DEFAULT,
1156                LOCALE_IUSEUTF8LEGACYOEMCP | LOCALE_RETURN_NUMBER,
1157                Some(data),
1158            );
1159            if len_written as usize == len { cp } else { CP_OEMCP }
1160        }
1161    }
1162    /// Try to convert a multi-byte string to a UTF-8 string using the given code page
1163    /// The string does not need to be null terminated.
1164    ///
1165    /// This is implemented as a wrapper around `MultiByteToWideChar`.
1166    /// See <https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar>
1167    ///
1168    /// It will fail if the multi-byte string is longer than `i32::MAX` or if it contains
1169    /// any invalid bytes for the expected encoding.
1170    pub(super) fn locale_byte_str_to_string(s: &[u8], code_page: u32) -> Option<String> {
1171        // `MultiByteToWideChar` requires a length to be a "positive integer".
1172        if s.len() > isize::MAX as usize {
1173            return None;
1174        }
1175        // Error if the string is not valid for the expected code page.
1176        let flags = MB_ERR_INVALID_CHARS;
1177        // Call MultiByteToWideChar twice.
1178        // First to calculate the length then to convert the string.
1179        let mut len = unsafe { MultiByteToWideChar(code_page, flags, s, None) };
1180        if len > 0 {
1181            let mut utf16 = vec![0; len as usize];
1182            len = unsafe { MultiByteToWideChar(code_page, flags, s, Some(&mut utf16)) };
1183            if len > 0 {
1184                return utf16.get(..len as usize).map(String::from_utf16_lossy);
1185            }
1186        }
1187        None
1188    }
1189}
1190
1191fn add_sanitizer_libraries(
1192    sess: &Session,
1193    flavor: LinkerFlavor,
1194    crate_type: CrateType,
1195    linker: &mut dyn Linker,
1196) {
1197    if sess.target.is_like_android {
1198        // Sanitizer runtime libraries are provided dynamically on Android
1199        // targets.
1200        return;
1201    }
1202
1203    if sess.opts.unstable_opts.external_clangrt {
1204        // Linking against in-tree sanitizer runtimes is disabled via
1205        // `-Z external-clangrt`
1206        return;
1207    }
1208
1209    if matches!(crate_type, CrateType::Rlib | CrateType::Staticlib) {
1210        return;
1211    }
1212
1213    // On macOS and Windows using MSVC the runtimes are distributed as dylibs
1214    // which should be linked to both executables and dynamic libraries.
1215    // Everywhere else the runtimes are currently distributed as static
1216    // libraries which should be linked to executables only.
1217    if matches!(
1218        crate_type,
1219        CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib
1220    ) && !(sess.target.is_like_darwin || sess.target.is_like_msvc)
1221    {
1222        return;
1223    }
1224
1225    let sanitizer = sess.opts.unstable_opts.sanitizer;
1226    if sanitizer.contains(SanitizerSet::ADDRESS) {
1227        link_sanitizer_runtime(sess, flavor, linker, "asan");
1228    }
1229    if sanitizer.contains(SanitizerSet::DATAFLOW) {
1230        link_sanitizer_runtime(sess, flavor, linker, "dfsan");
1231    }
1232    if sanitizer.contains(SanitizerSet::LEAK)
1233        && !sanitizer.contains(SanitizerSet::ADDRESS)
1234        && !sanitizer.contains(SanitizerSet::HWADDRESS)
1235    {
1236        link_sanitizer_runtime(sess, flavor, linker, "lsan");
1237    }
1238    if sanitizer.contains(SanitizerSet::MEMORY) {
1239        link_sanitizer_runtime(sess, flavor, linker, "msan");
1240    }
1241    if sanitizer.contains(SanitizerSet::THREAD) {
1242        link_sanitizer_runtime(sess, flavor, linker, "tsan");
1243    }
1244    if sanitizer.contains(SanitizerSet::HWADDRESS) {
1245        link_sanitizer_runtime(sess, flavor, linker, "hwasan");
1246    }
1247    if sanitizer.contains(SanitizerSet::SAFESTACK) {
1248        link_sanitizer_runtime(sess, flavor, linker, "safestack");
1249    }
1250}
1251
1252fn link_sanitizer_runtime(
1253    sess: &Session,
1254    flavor: LinkerFlavor,
1255    linker: &mut dyn Linker,
1256    name: &str,
1257) {
1258    fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf {
1259        let path = sess.target_tlib_path.dir.join(filename);
1260        if path.exists() {
1261            sess.target_tlib_path.dir.clone()
1262        } else {
1263            filesearch::make_target_lib_path(
1264                &sess.opts.sysroot.default,
1265                sess.opts.target_triple.tuple(),
1266            )
1267        }
1268    }
1269
1270    let channel =
1271        option_env!("CFG_RELEASE_CHANNEL").map(|channel| format!("-{channel}")).unwrap_or_default();
1272
1273    if sess.target.is_like_darwin {
1274        // On Apple platforms, the sanitizer is always built as a dylib, and
1275        // LLVM will link to `@rpath/*.dylib`, so we need to specify an
1276        // rpath to the library as well (the rpath should be absolute, see
1277        // PR #41352 for details).
1278        let filename = format!("rustc{channel}_rt.{name}");
1279        let path = find_sanitizer_runtime(sess, &filename);
1280        let rpath = path.to_str().expect("non-utf8 component in path");
1281        linker.link_args(&["-rpath", rpath]);
1282        linker.link_dylib_by_name(&filename, false, true);
1283    } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {
1284        // MSVC provides the `/INFERASANLIBS` argument to automatically find the
1285        // compatible ASAN library.
1286        linker.link_arg("/INFERASANLIBS");
1287    } else {
1288        let filename = format!("librustc{channel}_rt.{name}.a");
1289        let path = find_sanitizer_runtime(sess, &filename).join(&filename);
1290        linker.link_staticlib_by_path(&path, true);
1291    }
1292}
1293
1294/// Returns a boolean indicating whether the specified crate should be ignored
1295/// during LTO.
1296///
1297/// Crates ignored during LTO are not lumped together in the "massive object
1298/// file" that we create and are linked in their normal rlib states. See
1299/// comments below for what crates do not participate in LTO.
1300///
1301/// It's unusual for a crate to not participate in LTO. Typically only
1302/// compiler-specific and unstable crates have a reason to not participate in
1303/// LTO.
1304pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
1305    // If our target enables builtin function lowering in LLVM then the
1306    // crates providing these functions don't participate in LTO (e.g.
1307    // no_builtins or compiler builtins crates).
1308    !sess.target.no_builtins
1309        && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
1310}
1311
1312/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
1313pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
1314    fn infer_from(
1315        sess: &Session,
1316        linker: Option<PathBuf>,
1317        flavor: Option<LinkerFlavor>,
1318        features: LinkerFeaturesCli,
1319    ) -> Option<(PathBuf, LinkerFlavor)> {
1320        let flavor = flavor.map(|flavor| adjust_flavor_to_features(flavor, features));
1321        match (linker, flavor) {
1322            (Some(linker), Some(flavor)) => Some((linker, flavor)),
1323            // only the linker flavor is known; use the default linker for the selected flavor
1324            (None, Some(flavor)) => Some((
1325                PathBuf::from(match flavor {
1326                    LinkerFlavor::Gnu(Cc::Yes, _)
1327                    | LinkerFlavor::Darwin(Cc::Yes, _)
1328                    | LinkerFlavor::WasmLld(Cc::Yes)
1329                    | LinkerFlavor::Unix(Cc::Yes) => {
1330                        if cfg!(any(target_os = "solaris", target_os = "illumos")) {
1331                            // On historical Solaris systems, "cc" may have
1332                            // been Sun Studio, which is not flag-compatible
1333                            // with "gcc". This history casts a long shadow,
1334                            // and many modern illumos distributions today
1335                            // ship GCC as "gcc" without also making it
1336                            // available as "cc".
1337                            "gcc"
1338                        } else {
1339                            "cc"
1340                        }
1341                    }
1342                    LinkerFlavor::Gnu(_, Lld::Yes)
1343                    | LinkerFlavor::Darwin(_, Lld::Yes)
1344                    | LinkerFlavor::WasmLld(..)
1345                    | LinkerFlavor::Msvc(Lld::Yes) => "lld",
1346                    LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => {
1347                        "ld"
1348                    }
1349                    LinkerFlavor::Msvc(..) => "link.exe",
1350                    LinkerFlavor::EmCc => {
1351                        if cfg!(windows) {
1352                            "emcc.bat"
1353                        } else {
1354                            "emcc"
1355                        }
1356                    }
1357                    LinkerFlavor::Bpf => "bpf-linker",
1358                    LinkerFlavor::Llbc => "llvm-bitcode-linker",
1359                    LinkerFlavor::Ptx => "rust-ptx-linker",
1360                }),
1361                flavor,
1362            )),
1363            (Some(linker), None) => {
1364                let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
1365                    sess.dcx().emit_fatal(errors::LinkerFileStem);
1366                });
1367                let flavor = sess.target.linker_flavor.with_linker_hints(stem);
1368                let flavor = adjust_flavor_to_features(flavor, features);
1369                Some((linker, flavor))
1370            }
1371            (None, None) => None,
1372        }
1373    }
1374
1375    // While linker flavors and linker features are isomorphic (and thus targets don't need to
1376    // define features separately), we use the flavor as the root piece of data and have the
1377    // linker-features CLI flag influence *that*, so that downstream code does not have to check for
1378    // both yet.
1379    fn adjust_flavor_to_features(
1380        flavor: LinkerFlavor,
1381        features: LinkerFeaturesCli,
1382    ) -> LinkerFlavor {
1383        // Note: a linker feature cannot be both enabled and disabled on the CLI.
1384        if features.enabled.contains(LinkerFeatures::LLD) {
1385            flavor.with_lld_enabled()
1386        } else if features.disabled.contains(LinkerFeatures::LLD) {
1387            flavor.with_lld_disabled()
1388        } else {
1389            flavor
1390        }
1391    }
1392
1393    let features = sess.opts.cg.linker_features;
1394
1395    // linker and linker flavor specified via command line have precedence over what the target
1396    // specification specifies
1397    let linker_flavor = match sess.opts.cg.linker_flavor {
1398        // The linker flavors that are non-target specific can be directly translated to LinkerFlavor
1399        Some(LinkerFlavorCli::Llbc) => Some(LinkerFlavor::Llbc),
1400        Some(LinkerFlavorCli::Ptx) => Some(LinkerFlavor::Ptx),
1401        // The linker flavors that corresponds to targets needs logic that keeps the base LinkerFlavor
1402        _ => sess
1403            .opts
1404            .cg
1405            .linker_flavor
1406            .map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)),
1407    };
1408    if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor, features) {
1409        return ret;
1410    }
1411
1412    if let Some(ret) = infer_from(
1413        sess,
1414        sess.target.linker.as_deref().map(PathBuf::from),
1415        Some(sess.target.linker_flavor),
1416        features,
1417    ) {
1418        return ret;
1419    }
1420
1421    bug!("Not enough information provided to determine how to invoke the linker");
1422}
1423
1424/// Returns a pair of boolean indicating whether we should preserve the object and
1425/// dwarf object files on the filesystem for their debug information. This is often
1426/// useful with split-dwarf like schemes.
1427fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) {
1428    // If the objects don't have debuginfo there's nothing to preserve.
1429    if sess.opts.debuginfo == config::DebugInfo::None {
1430        return (false, false);
1431    }
1432
1433    match (sess.split_debuginfo(), sess.opts.unstable_opts.split_dwarf_kind) {
1434        // If there is no split debuginfo then do not preserve objects.
1435        (SplitDebuginfo::Off, _) => (false, false),
1436        // If there is packed split debuginfo, then the debuginfo in the objects
1437        // has been packaged and the objects can be deleted.
1438        (SplitDebuginfo::Packed, _) => (false, false),
1439        // If there is unpacked split debuginfo and the current target can not use
1440        // split dwarf, then keep objects.
1441        (SplitDebuginfo::Unpacked, _) if !sess.target_can_use_split_dwarf() => (true, false),
1442        // If there is unpacked split debuginfo and the target can use split dwarf, then
1443        // keep the object containing that debuginfo (whether that is an object file or
1444        // dwarf object file depends on the split dwarf kind).
1445        (SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => (true, false),
1446        (SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => (false, true),
1447    }
1448}
1449
1450#[derive(PartialEq)]
1451enum RlibFlavor {
1452    Normal,
1453    StaticlibBase,
1454}
1455
1456fn print_native_static_libs(
1457    sess: &Session,
1458    out: &OutFileName,
1459    all_native_libs: &[NativeLib],
1460    all_rust_dylibs: &[&Path],
1461) {
1462    let mut lib_args: Vec<_> = all_native_libs
1463        .iter()
1464        .filter(|l| relevant_lib(sess, l))
1465        .filter_map(|lib| {
1466            let name = lib.name;
1467            match lib.kind {
1468                NativeLibKind::Static { bundle: Some(false), .. }
1469                | NativeLibKind::Dylib { .. }
1470                | NativeLibKind::Unspecified => {
1471                    let verbatim = lib.verbatim;
1472                    if sess.target.is_like_msvc {
1473                        let (prefix, suffix) = sess.staticlib_components(verbatim);
1474                        Some(format!("{prefix}{name}{suffix}"))
1475                    } else if sess.target.linker_flavor.is_gnu() {
1476                        Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))
1477                    } else {
1478                        Some(format!("-l{name}"))
1479                    }
1480                }
1481                NativeLibKind::Framework { .. } => {
1482                    // ld-only syntax, since there are no frameworks in MSVC
1483                    Some(format!("-framework {name}"))
1484                }
1485                // These are included, no need to print them
1486                NativeLibKind::Static { bundle: None | Some(true), .. }
1487                | NativeLibKind::LinkArg
1488                | NativeLibKind::WasmImportModule
1489                | NativeLibKind::RawDylib => None,
1490            }
1491        })
1492        // deduplication of consecutive repeated libraries, see rust-lang/rust#113209
1493        .dedup()
1494        .collect();
1495    for path in all_rust_dylibs {
1496        // FIXME deduplicate with add_dynamic_crate
1497
1498        // Just need to tell the linker about where the library lives and
1499        // what its name is
1500        let parent = path.parent();
1501        if let Some(dir) = parent {
1502            let dir = fix_windows_verbatim_for_gcc(dir);
1503            if sess.target.is_like_msvc {
1504                let mut arg = String::from("/LIBPATH:");
1505                arg.push_str(&dir.display().to_string());
1506                lib_args.push(arg);
1507            } else {
1508                lib_args.push("-L".to_owned());
1509                lib_args.push(dir.display().to_string());
1510            }
1511        }
1512        let stem = path.file_stem().unwrap().to_str().unwrap();
1513        // Convert library file-stem into a cc -l argument.
1514        let lib = if let Some(lib) = stem.strip_prefix("lib")
1515            && !sess.target.is_like_windows
1516        {
1517            lib
1518        } else {
1519            stem
1520        };
1521        let path = parent.unwrap_or_else(|| Path::new(""));
1522        if sess.target.is_like_msvc {
1523            // When producing a dll, the MSVC linker may not actually emit a
1524            // `foo.lib` file if the dll doesn't actually export any symbols, so we
1525            // check to see if the file is there and just omit linking to it if it's
1526            // not present.
1527            let name = format!("{lib}.dll.lib");
1528            if path.join(&name).exists() {
1529                lib_args.push(name);
1530            }
1531        } else {
1532            lib_args.push(format!("-l{lib}"));
1533        }
1534    }
1535
1536    match out {
1537        OutFileName::Real(path) => {
1538            out.overwrite(&lib_args.join(" "), sess);
1539            sess.dcx().emit_note(errors::StaticLibraryNativeArtifactsToFile { path });
1540        }
1541        OutFileName::Stdout => {
1542            sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts);
1543            // Prefix for greppability
1544            // Note: This must not be translated as tools are allowed to depend on this exact string.
1545            sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" ")));
1546        }
1547    }
1548}
1549
1550fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf {
1551    let file_path = sess.target_tlib_path.dir.join(name);
1552    if file_path.exists() {
1553        return file_path;
1554    }
1555    // Special directory with objects used only in self-contained linkage mode
1556    if self_contained {
1557        let file_path = sess.target_tlib_path.dir.join("self-contained").join(name);
1558        if file_path.exists() {
1559            return file_path;
1560        }
1561    }
1562    for search_path in sess.target_filesearch().search_paths(PathKind::Native) {
1563        let file_path = search_path.dir.join(name);
1564        if file_path.exists() {
1565            return file_path;
1566        }
1567    }
1568    PathBuf::from(name)
1569}
1570
1571fn exec_linker(
1572    sess: &Session,
1573    cmd: &Command,
1574    out_filename: &Path,
1575    flavor: LinkerFlavor,
1576    tmpdir: &Path,
1577) -> io::Result<Output> {
1578    // When attempting to spawn the linker we run a risk of blowing out the
1579    // size limits for spawning a new process with respect to the arguments
1580    // we pass on the command line.
1581    //
1582    // Here we attempt to handle errors from the OS saying "your list of
1583    // arguments is too big" by reinvoking the linker again with an `@`-file
1584    // that contains all the arguments (aka 'response' files).
1585    // The theory is that this is then accepted on all linkers and the linker
1586    // will read all its options out of there instead of looking at the command line.
1587    if !cmd.very_likely_to_exceed_some_spawn_limit() {
1588        match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() {
1589            Ok(child) => {
1590                let output = child.wait_with_output();
1591                flush_linked_file(&output, out_filename)?;
1592                return output;
1593            }
1594            Err(ref e) if command_line_too_big(e) => {
1595                info!("command line to linker was too big: {}", e);
1596            }
1597            Err(e) => return Err(e),
1598        }
1599    }
1600
1601    info!("falling back to passing arguments to linker via an @-file");
1602    let mut cmd2 = cmd.clone();
1603    let mut args = String::new();
1604    for arg in cmd2.take_args() {
1605        args.push_str(
1606            &Escape {
1607                arg: arg.to_str().unwrap(),
1608                // Windows-style escaping for @-files is used by
1609                // - all linkers targeting MSVC-like targets, including LLD
1610                // - all LLD flavors running on Windows hosts
1611                // С/С++ compilers use Posix-style escaping (except clang-cl, which we do not use).
1612                is_like_msvc: sess.target.is_like_msvc
1613                    || (cfg!(windows) && flavor.uses_lld() && !flavor.uses_cc()),
1614            }
1615            .to_string(),
1616        );
1617        args.push('\n');
1618    }
1619    let file = tmpdir.join("linker-arguments");
1620    let bytes = if sess.target.is_like_msvc {
1621        let mut out = Vec::with_capacity((1 + args.len()) * 2);
1622        // start the stream with a UTF-16 BOM
1623        for c in std::iter::once(0xFEFF).chain(args.encode_utf16()) {
1624            // encode in little endian
1625            out.push(c as u8);
1626            out.push((c >> 8) as u8);
1627        }
1628        out
1629    } else {
1630        args.into_bytes()
1631    };
1632    fs::write(&file, &bytes)?;
1633    cmd2.arg(format!("@{}", file.display()));
1634    info!("invoking linker {:?}", cmd2);
1635    let output = cmd2.output();
1636    flush_linked_file(&output, out_filename)?;
1637    return output;
1638
1639    #[cfg(not(windows))]
1640    fn flush_linked_file(_: &io::Result<Output>, _: &Path) -> io::Result<()> {
1641        Ok(())
1642    }
1643
1644    #[cfg(windows)]
1645    fn flush_linked_file(
1646        command_output: &io::Result<Output>,
1647        out_filename: &Path,
1648    ) -> io::Result<()> {
1649        // On Windows, under high I/O load, output buffers are sometimes not flushed,
1650        // even long after process exit, causing nasty, non-reproducible output bugs.
1651        //
1652        // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem.
1653        //
1654        // А full writeup of the original Chrome bug can be found at
1655        // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp
1656
1657        if let &Ok(ref out) = command_output {
1658            if out.status.success() {
1659                if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) {
1660                    of.sync_all()?;
1661                }
1662            }
1663        }
1664
1665        Ok(())
1666    }
1667
1668    #[cfg(unix)]
1669    fn command_line_too_big(err: &io::Error) -> bool {
1670        err.raw_os_error() == Some(::libc::E2BIG)
1671    }
1672
1673    #[cfg(windows)]
1674    fn command_line_too_big(err: &io::Error) -> bool {
1675        const ERROR_FILENAME_EXCED_RANGE: i32 = 206;
1676        err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE)
1677    }
1678
1679    #[cfg(not(any(unix, windows)))]
1680    fn command_line_too_big(_: &io::Error) -> bool {
1681        false
1682    }
1683
1684    struct Escape<'a> {
1685        arg: &'a str,
1686        is_like_msvc: bool,
1687    }
1688
1689    impl<'a> fmt::Display for Escape<'a> {
1690        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1691            if self.is_like_msvc {
1692                // This is "documented" at
1693                // https://docs.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file
1694                //
1695                // Unfortunately there's not a great specification of the
1696                // syntax I could find online (at least) but some local
1697                // testing showed that this seemed sufficient-ish to catch
1698                // at least a few edge cases.
1699                write!(f, "\"")?;
1700                for c in self.arg.chars() {
1701                    match c {
1702                        '"' => write!(f, "\\{c}")?,
1703                        c => write!(f, "{c}")?,
1704                    }
1705                }
1706                write!(f, "\"")?;
1707            } else {
1708                // This is documented at https://linux.die.net/man/1/ld, namely:
1709                //
1710                // > Options in file are separated by whitespace. A whitespace
1711                // > character may be included in an option by surrounding the
1712                // > entire option in either single or double quotes. Any
1713                // > character (including a backslash) may be included by
1714                // > prefixing the character to be included with a backslash.
1715                //
1716                // We put an argument on each line, so all we need to do is
1717                // ensure the line is interpreted as one whole argument.
1718                for c in self.arg.chars() {
1719                    match c {
1720                        '\\' | ' ' => write!(f, "\\{c}")?,
1721                        c => write!(f, "{c}")?,
1722                    }
1723                }
1724            }
1725            Ok(())
1726        }
1727    }
1728}
1729
1730fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
1731    let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) {
1732        (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe,
1733        (CrateType::Executable, false, RelocModel::Pic | RelocModel::Pie) => {
1734            LinkOutputKind::DynamicPicExe
1735        }
1736        (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe,
1737        (CrateType::Executable, true, RelocModel::Pic | RelocModel::Pie) => {
1738            LinkOutputKind::StaticPicExe
1739        }
1740        (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe,
1741        (_, true, _) => LinkOutputKind::StaticDylib,
1742        (_, false, _) => LinkOutputKind::DynamicDylib,
1743    };
1744
1745    // Adjust the output kind to target capabilities.
1746    let opts = &sess.target;
1747    let pic_exe_supported = opts.position_independent_executables;
1748    let static_pic_exe_supported = opts.static_position_independent_executables;
1749    let static_dylib_supported = opts.crt_static_allows_dylibs;
1750    match kind {
1751        LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe,
1752        LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe,
1753        LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib,
1754        _ => kind,
1755    }
1756}
1757
1758// Returns true if linker is located within sysroot
1759fn detect_self_contained_mingw(sess: &Session, linker: &Path) -> bool {
1760    // Assume `-C linker=rust-lld` as self-contained mode
1761    if linker == Path::new("rust-lld") {
1762        return true;
1763    }
1764    let linker_with_extension = if cfg!(windows) && linker.extension().is_none() {
1765        linker.with_extension("exe")
1766    } else {
1767        linker.to_path_buf()
1768    };
1769    for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
1770        let full_path = dir.join(&linker_with_extension);
1771        // If linker comes from sysroot assume self-contained mode
1772        if full_path.is_file() && !full_path.starts_with(sess.opts.sysroot.path()) {
1773            return false;
1774        }
1775    }
1776    true
1777}
1778
1779/// Various toolchain components used during linking are used from rustc distribution
1780/// instead of being found somewhere on the host system.
1781/// We only provide such support for a very limited number of targets.
1782fn self_contained_components(
1783    sess: &Session,
1784    crate_type: CrateType,
1785    linker: &Path,
1786) -> LinkSelfContainedComponents {
1787    // Turn the backwards compatible bool values for `self_contained` into fully inferred
1788    // `LinkSelfContainedComponents`.
1789    let self_contained =
1790        if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
1791            // Emit an error if the user requested self-contained mode on the CLI but the target
1792            // explicitly refuses it.
1793            if sess.target.link_self_contained.is_disabled() {
1794                sess.dcx().emit_err(errors::UnsupportedLinkSelfContained);
1795            }
1796            self_contained
1797        } else {
1798            match sess.target.link_self_contained {
1799                LinkSelfContainedDefault::False => false,
1800                LinkSelfContainedDefault::True => true,
1801
1802                LinkSelfContainedDefault::WithComponents(components) => {
1803                    // For target specs with explicitly enabled components, we can return them
1804                    // directly.
1805                    return components;
1806                }
1807
1808                // FIXME: Find a better heuristic for "native musl toolchain is available",
1809                // based on host and linker path, for example.
1810                // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
1811                LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)),
1812                LinkSelfContainedDefault::InferredForMingw => {
1813                    sess.host == sess.target
1814                        && sess.target.vendor != "uwp"
1815                        && detect_self_contained_mingw(sess, linker)
1816                }
1817            }
1818        };
1819    if self_contained {
1820        LinkSelfContainedComponents::all()
1821    } else {
1822        LinkSelfContainedComponents::empty()
1823    }
1824}
1825
1826/// Add pre-link object files defined by the target spec.
1827fn add_pre_link_objects(
1828    cmd: &mut dyn Linker,
1829    sess: &Session,
1830    flavor: LinkerFlavor,
1831    link_output_kind: LinkOutputKind,
1832    self_contained: bool,
1833) {
1834    // FIXME: we are currently missing some infra here (per-linker-flavor CRT objects),
1835    // so Fuchsia has to be special-cased.
1836    let opts = &sess.target;
1837    let empty = Default::default();
1838    let objects = if self_contained {
1839        &opts.pre_link_objects_self_contained
1840    } else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) {
1841        &opts.pre_link_objects
1842    } else {
1843        &empty
1844    };
1845    for obj in objects.get(&link_output_kind).iter().copied().flatten() {
1846        cmd.add_object(&get_object_file_path(sess, obj, self_contained));
1847    }
1848}
1849
1850/// Add post-link object files defined by the target spec.
1851fn add_post_link_objects(
1852    cmd: &mut dyn Linker,
1853    sess: &Session,
1854    link_output_kind: LinkOutputKind,
1855    self_contained: bool,
1856) {
1857    let objects = if self_contained {
1858        &sess.target.post_link_objects_self_contained
1859    } else {
1860        &sess.target.post_link_objects
1861    };
1862    for obj in objects.get(&link_output_kind).iter().copied().flatten() {
1863        cmd.add_object(&get_object_file_path(sess, obj, self_contained));
1864    }
1865}
1866
1867/// Add arbitrary "pre-link" args defined by the target spec or from command line.
1868/// FIXME: Determine where exactly these args need to be inserted.
1869fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
1870    if let Some(args) = sess.target.pre_link_args.get(&flavor) {
1871        cmd.verbatim_args(args.iter().map(Deref::deref));
1872    }
1873
1874    cmd.verbatim_args(&sess.opts.unstable_opts.pre_link_args);
1875}
1876
1877/// Add a link script embedded in the target, if applicable.
1878fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) {
1879    match (crate_type, &sess.target.link_script) {
1880        (CrateType::Cdylib | CrateType::Executable, Some(script)) => {
1881            if !sess.target.linker_flavor.is_gnu() {
1882                sess.dcx().emit_fatal(errors::LinkScriptUnavailable);
1883            }
1884
1885            let file_name = ["rustc", &sess.target.llvm_target, "linkfile.ld"].join("-");
1886
1887            let path = tmpdir.join(file_name);
1888            if let Err(error) = fs::write(&path, script.as_ref()) {
1889                sess.dcx().emit_fatal(errors::LinkScriptWriteFailure { path, error });
1890            }
1891
1892            cmd.link_arg("--script").link_arg(path);
1893        }
1894        _ => {}
1895    }
1896}
1897
1898/// Add arbitrary "user defined" args defined from command line.
1899/// FIXME: Determine where exactly these args need to be inserted.
1900fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) {
1901    cmd.verbatim_args(&sess.opts.cg.link_args);
1902}
1903
1904/// Add arbitrary "late link" args defined by the target spec.
1905/// FIXME: Determine where exactly these args need to be inserted.
1906fn add_late_link_args(
1907    cmd: &mut dyn Linker,
1908    sess: &Session,
1909    flavor: LinkerFlavor,
1910    crate_type: CrateType,
1911    codegen_results: &CodegenResults,
1912) {
1913    let any_dynamic_crate = crate_type == CrateType::Dylib
1914        || crate_type == CrateType::Sdylib
1915        || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
1916            *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
1917        });
1918    if any_dynamic_crate {
1919        if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) {
1920            cmd.verbatim_args(args.iter().map(Deref::deref));
1921        }
1922    } else if let Some(args) = sess.target.late_link_args_static.get(&flavor) {
1923        cmd.verbatim_args(args.iter().map(Deref::deref));
1924    }
1925    if let Some(args) = sess.target.late_link_args.get(&flavor) {
1926        cmd.verbatim_args(args.iter().map(Deref::deref));
1927    }
1928}
1929
1930/// Add arbitrary "post-link" args defined by the target spec.
1931/// FIXME: Determine where exactly these args need to be inserted.
1932fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
1933    if let Some(args) = sess.target.post_link_args.get(&flavor) {
1934        cmd.verbatim_args(args.iter().map(Deref::deref));
1935    }
1936}
1937
1938/// Add a synthetic object file that contains reference to all symbols that we want to expose to
1939/// the linker.
1940///
1941/// Background: we implement rlibs as static library (archives). Linkers treat archives
1942/// differently from object files: all object files participate in linking, while archives will
1943/// only participate in linking if they can satisfy at least one undefined reference (version
1944/// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the
1945/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
1946/// can't keep them either. This causes #47384.
1947///
1948/// To keep them around, we could use `--whole-archive`, `-force_load` and equivalents to force rlib
1949/// to participate in linking like object files, but this proves to be expensive (#93791). Therefore
1950/// we instead just introduce an undefined reference to them. This could be done by `-u` command
1951/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
1952/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
1953/// from removing them, and this is especially problematic for embedded programming where every
1954/// byte counts.
1955///
1956/// This method creates a synthetic object file, which contains undefined references to all symbols
1957/// that are necessary for the linking. They are only present in symbol table but not actually
1958/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
1959/// unused `#[no_mangle]` or `#[used(compiler)]` can still be discard by GC sections.
1960///
1961/// There's a few internal crates in the standard library (aka libcore and
1962/// libstd) which actually have a circular dependence upon one another. This
1963/// currently arises through "weak lang items" where libcore requires things
1964/// like `rust_begin_unwind` but libstd ends up defining it. To get this
1965/// circular dependence to work correctly we declare some of these things
1966/// in this synthetic object.
1967fn add_linked_symbol_object(
1968    cmd: &mut dyn Linker,
1969    sess: &Session,
1970    tmpdir: &Path,
1971    symbols: &[(String, SymbolExportKind)],
1972) {
1973    if symbols.is_empty() {
1974        return;
1975    }
1976
1977    let Some(mut file) = super::metadata::create_object_file(sess) else {
1978        return;
1979    };
1980
1981    if file.format() == object::BinaryFormat::Coff {
1982        // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
1983        // so add an empty section.
1984        file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);
1985
1986        // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
1987        // default mangler in `object` crate.
1988        file.set_mangling(object::write::Mangling::None);
1989    }
1990
1991    if file.format() == object::BinaryFormat::MachO {
1992        // Divide up the sections into sub-sections via symbols for dead code stripping.
1993        // Without this flag, unused `#[no_mangle]` or `#[used(compiler)]` cannot be
1994        // discard on MachO targets.
1995        file.set_subsections_via_symbols();
1996    }
1997
1998    // ld64 requires a relocation to load undefined symbols, see below.
1999    // Not strictly needed if linking with lld, but might as well do it there too.
2000    let ld64_section_helper = if file.format() == object::BinaryFormat::MachO {
2001        Some(file.add_section(
2002            file.segment_name(object::write::StandardSegment::Data).to_vec(),
2003            "__data".into(),
2004            object::SectionKind::Data,
2005        ))
2006    } else {
2007        None
2008    };
2009
2010    for (sym, kind) in symbols.iter() {
2011        let symbol = file.add_symbol(object::write::Symbol {
2012            name: sym.clone().into(),
2013            value: 0,
2014            size: 0,
2015            kind: match kind {
2016                SymbolExportKind::Text => object::SymbolKind::Text,
2017                SymbolExportKind::Data => object::SymbolKind::Data,
2018                SymbolExportKind::Tls => object::SymbolKind::Tls,
2019            },
2020            scope: object::SymbolScope::Unknown,
2021            weak: false,
2022            section: object::write::SymbolSection::Undefined,
2023            flags: object::SymbolFlags::None,
2024        });
2025
2026        // The linker shipped with Apple's Xcode, ld64, works a bit differently from other linkers.
2027        //
2028        // Code-wise, the relevant parts of ld64 are roughly:
2029        // 1. Find the `ArchiveLoadMode` based on commandline options, default to `parseObjects`.
2030        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.cpp#L924-L932
2031        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/Options.h#L55
2032        //
2033        // 2. Read the archive table of contents (__.SYMDEF file).
2034        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L294-L325
2035        //
2036        // 3. Begin linking by loading "atoms" from input files.
2037        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/doc/design/linker.html
2038        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1349
2039        //
2040        //   a. Directly specified object files (`.o`) are parsed immediately.
2041        //      https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L4611-L4627
2042        //
2043        //     - Undefined symbols are not atoms (`n_value > 0` denotes a common symbol).
2044        //       https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/macho_relocatable_file.cpp#L2455-L2468
2045        //       https://maskray.me/blog/2022-02-06-all-about-common-symbols
2046        //
2047        //     - Relocations/fixups are atoms.
2048        //       https://github.com/apple-oss-distributions/ld64/blob/ce6341ae966b3451aa54eeb049f2be865afbd578/src/ld/parsers/macho_relocatable_file.cpp#L2088-L2114
2049        //
2050        //   b. Archives are not parsed yet.
2051        //      https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L467-L577
2052        //
2053        // 4. When a symbol is needed by an atom, parse the object file that contains the symbol.
2054        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/InputFiles.cpp#L1417-L1491
2055        //    https://github.com/apple-oss-distributions/ld64/blob/ld64-954.16/src/ld/parsers/archive_file.cpp#L579-L597
2056        //
2057        // All of the steps above are fairly similar to other linkers, except that **it completely
2058        // ignores undefined symbols**.
2059        //
2060        // So to make this trick work on ld64, we need to do something else to load the relevant
2061        // object files. We do this by inserting a relocation (fixup) for each symbol.
2062        if let Some(section) = ld64_section_helper {
2063            apple::add_data_and_relocation(&mut file, section, symbol, &sess.target, *kind)
2064                .expect("failed adding relocation");
2065        }
2066    }
2067
2068    let path = tmpdir.join("symbols.o");
2069    let result = std::fs::write(&path, file.write().unwrap());
2070    if let Err(error) = result {
2071        sess.dcx().emit_fatal(errors::FailedToWrite { path, error });
2072    }
2073    cmd.add_object(&path);
2074}
2075
2076/// Add object files containing code from the current crate.
2077fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
2078    for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
2079        cmd.add_object(obj);
2080    }
2081}
2082
2083/// Add object files for allocator code linked once for the whole crate tree.
2084fn add_local_crate_allocator_objects(
2085    cmd: &mut dyn Linker,
2086    codegen_results: &CodegenResults,
2087    crate_type: CrateType,
2088) {
2089    if needs_allocator_shim_for_linking(&codegen_results.crate_info.dependency_formats, crate_type)
2090    {
2091        if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref())
2092        {
2093            cmd.add_object(obj);
2094        }
2095    }
2096}
2097
2098/// Add object files containing metadata for the current crate.
2099fn add_local_crate_metadata_objects(
2100    cmd: &mut dyn Linker,
2101    sess: &Session,
2102    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2103    crate_type: CrateType,
2104    tmpdir: &Path,
2105    codegen_results: &CodegenResults,
2106    metadata: &EncodedMetadata,
2107) {
2108    // When linking a dynamic library, we put the metadata into a section of the
2109    // executable. This metadata is in a separate object file from the main
2110    // object file, so we create and link it in here.
2111    if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro) {
2112        let data = archive_builder_builder.create_dylib_metadata_wrapper(
2113            sess,
2114            &metadata,
2115            &codegen_results.crate_info.metadata_symbol,
2116        );
2117        let obj = emit_wrapper_file(sess, &data, tmpdir, "rmeta.o");
2118
2119        cmd.add_object(&obj);
2120    }
2121}
2122
2123/// Add sysroot and other globally set directories to the directory search list.
2124fn add_library_search_dirs(
2125    cmd: &mut dyn Linker,
2126    sess: &Session,
2127    self_contained_components: LinkSelfContainedComponents,
2128    apple_sdk_root: Option<&Path>,
2129) {
2130    if !sess.opts.unstable_opts.link_native_libraries {
2131        return;
2132    }
2133
2134    let fallback = Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root });
2135    let _ = walk_native_lib_search_dirs(sess, fallback, |dir, is_framework| {
2136        if is_framework {
2137            cmd.framework_path(dir);
2138        } else {
2139            cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
2140        }
2141        ControlFlow::<()>::Continue(())
2142    });
2143}
2144
2145/// Add options making relocation sections in the produced ELF files read-only
2146/// and suppressing lazy binding.
2147fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) {
2148    match sess.opts.cg.relro_level.unwrap_or(sess.target.relro_level) {
2149        RelroLevel::Full => cmd.full_relro(),
2150        RelroLevel::Partial => cmd.partial_relro(),
2151        RelroLevel::Off => cmd.no_relro(),
2152        RelroLevel::None => {}
2153    }
2154}
2155
2156/// Add library search paths used at runtime by dynamic linkers.
2157fn add_rpath_args(
2158    cmd: &mut dyn Linker,
2159    sess: &Session,
2160    codegen_results: &CodegenResults,
2161    out_filename: &Path,
2162) {
2163    if !sess.target.has_rpath {
2164        return;
2165    }
2166
2167    // FIXME (#2397): At some point we want to rpath our guesses as to
2168    // where extern libraries might live, based on the
2169    // add_lib_search_paths
2170    if sess.opts.cg.rpath {
2171        let libs = codegen_results
2172            .crate_info
2173            .used_crates
2174            .iter()
2175            .filter_map(|cnum| {
2176                codegen_results.crate_info.used_crate_source[cnum]
2177                    .dylib
2178                    .as_ref()
2179                    .map(|(path, _)| &**path)
2180            })
2181            .collect::<Vec<_>>();
2182        let rpath_config = RPathConfig {
2183            libs: &*libs,
2184            out_filename: out_filename.to_path_buf(),
2185            is_like_darwin: sess.target.is_like_darwin,
2186            linker_is_gnu: sess.target.linker_flavor.is_gnu(),
2187        };
2188        cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config));
2189    }
2190}
2191
2192/// Produce the linker command line containing linker path and arguments.
2193///
2194/// When comments in the function say "order-(in)dependent" they mean order-dependence between
2195/// options and libraries/object files. For example `--whole-archive` (order-dependent) applies
2196/// to specific libraries passed after it, and `-o` (output file, order-independent) applies
2197/// to the linking process as a whole.
2198/// Order-independent options may still override each other in order-dependent fashion,
2199/// e.g `--foo=yes --foo=no` may be equivalent to `--foo=no`.
2200fn linker_with_args(
2201    path: &Path,
2202    flavor: LinkerFlavor,
2203    sess: &Session,
2204    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2205    crate_type: CrateType,
2206    tmpdir: &Path,
2207    out_filename: &Path,
2208    codegen_results: &CodegenResults,
2209    metadata: &EncodedMetadata,
2210    self_contained_components: LinkSelfContainedComponents,
2211) -> Command {
2212    let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
2213    let cmd = &mut *super::linker::get_linker(
2214        sess,
2215        path,
2216        flavor,
2217        self_contained_components.are_any_components_enabled(),
2218        &codegen_results.crate_info.target_cpu,
2219    );
2220    let link_output_kind = link_output_kind(sess, crate_type);
2221
2222    // ------------ Early order-dependent options ------------
2223
2224    // If we're building something like a dynamic library then some platforms
2225    // need to make sure that all symbols are exported correctly from the
2226    // dynamic library.
2227    // Must be passed before any libraries to prevent the symbols to export from being thrown away,
2228    // at least on some platforms (e.g. windows-gnu).
2229    cmd.export_symbols(
2230        tmpdir,
2231        crate_type,
2232        &codegen_results.crate_info.exported_symbols[&crate_type],
2233    );
2234
2235    // Can be used for adding custom CRT objects or overriding order-dependent options above.
2236    // FIXME: In practice built-in target specs use this for arbitrary order-independent options,
2237    // introduce a target spec option for order-independent linker options and migrate built-in
2238    // specs to it.
2239    add_pre_link_args(cmd, sess, flavor);
2240
2241    // ------------ Object code and libraries, order-dependent ------------
2242
2243    // Pre-link CRT objects.
2244    add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects);
2245
2246    add_linked_symbol_object(
2247        cmd,
2248        sess,
2249        tmpdir,
2250        &codegen_results.crate_info.linked_symbols[&crate_type],
2251    );
2252
2253    // Sanitizer libraries.
2254    add_sanitizer_libraries(sess, flavor, crate_type, cmd);
2255
2256    // Object code from the current crate.
2257    // Take careful note of the ordering of the arguments we pass to the linker
2258    // here. Linkers will assume that things on the left depend on things to the
2259    // right. Things on the right cannot depend on things on the left. This is
2260    // all formally implemented in terms of resolving symbols (libs on the right
2261    // resolve unknown symbols of libs on the left, but not vice versa).
2262    //
2263    // For this reason, we have organized the arguments we pass to the linker as
2264    // such:
2265    //
2266    // 1. The local object that LLVM just generated
2267    // 2. Local native libraries
2268    // 3. Upstream rust libraries
2269    // 4. Upstream native libraries
2270    //
2271    // The rationale behind this ordering is that those items lower down in the
2272    // list can't depend on items higher up in the list. For example nothing can
2273    // depend on what we just generated (e.g., that'd be a circular dependency).
2274    // Upstream rust libraries are not supposed to depend on our local native
2275    // libraries as that would violate the structure of the DAG, in that
2276    // scenario they are required to link to them as well in a shared fashion.
2277    //
2278    // Note that upstream rust libraries may contain native dependencies as
2279    // well, but they also can't depend on what we just started to add to the
2280    // link line. And finally upstream native libraries can't depend on anything
2281    // in this DAG so far because they can only depend on other native libraries
2282    // and such dependencies are also required to be specified.
2283    add_local_crate_regular_objects(cmd, codegen_results);
2284    add_local_crate_metadata_objects(
2285        cmd,
2286        sess,
2287        archive_builder_builder,
2288        crate_type,
2289        tmpdir,
2290        codegen_results,
2291        metadata,
2292    );
2293    add_local_crate_allocator_objects(cmd, codegen_results, crate_type);
2294
2295    // Avoid linking to dynamic libraries unless they satisfy some undefined symbols
2296    // at the point at which they are specified on the command line.
2297    // Must be passed before any (dynamic) libraries to have effect on them.
2298    // On Solaris-like systems, `-z ignore` acts as both `--as-needed` and `--gc-sections`
2299    // so it will ignore unreferenced ELF sections from relocatable objects.
2300    // For that reason, we put this flag after metadata objects as they would otherwise be removed.
2301    // FIXME: Support more fine-grained dead code removal on Solaris/illumos
2302    // and move this option back to the top.
2303    cmd.add_as_needed();
2304
2305    // Local native libraries of all kinds.
2306    add_local_native_libraries(
2307        cmd,
2308        sess,
2309        archive_builder_builder,
2310        codegen_results,
2311        tmpdir,
2312        link_output_kind,
2313    );
2314
2315    // Upstream rust crates and their non-dynamic native libraries.
2316    add_upstream_rust_crates(
2317        cmd,
2318        sess,
2319        archive_builder_builder,
2320        codegen_results,
2321        crate_type,
2322        tmpdir,
2323        link_output_kind,
2324    );
2325
2326    // Dynamic native libraries from upstream crates.
2327    add_upstream_native_libraries(
2328        cmd,
2329        sess,
2330        archive_builder_builder,
2331        codegen_results,
2332        tmpdir,
2333        link_output_kind,
2334    );
2335
2336    // Raw-dylibs from all crates.
2337    let raw_dylib_dir = tmpdir.join("raw-dylibs");
2338    if sess.target.binary_format == BinaryFormat::Elf {
2339        // On ELF we can't pass the raw-dylibs stubs to the linker as a path,
2340        // instead we need to pass them via -l. To find the stub, we need to add
2341        // the directory of the stub to the linker search path.
2342        // We make an extra directory for this to avoid polluting the search path.
2343        if let Err(error) = fs::create_dir(&raw_dylib_dir) {
2344            sess.dcx().emit_fatal(errors::CreateTempDir { error })
2345        }
2346        cmd.include_path(&raw_dylib_dir);
2347    }
2348
2349    // Link with the import library generated for any raw-dylib functions.
2350    if sess.target.is_like_windows {
2351        for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
2352            sess,
2353            archive_builder_builder,
2354            codegen_results.crate_info.used_libraries.iter(),
2355            tmpdir,
2356            true,
2357        ) {
2358            cmd.add_object(&output_path);
2359        }
2360    } else {
2361        for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
2362            sess,
2363            codegen_results.crate_info.used_libraries.iter(),
2364            &raw_dylib_dir,
2365        ) {
2366            // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
2367            cmd.link_dylib_by_name(&link_path, true, false);
2368        }
2369    }
2370    // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
2371    // they are used within inlined functions or instantiated generic functions. We do this *after*
2372    // handling the raw-dylib symbols in the current crate to make sure that those are chosen first
2373    // by the linker.
2374    let dependency_linkage = codegen_results
2375        .crate_info
2376        .dependency_formats
2377        .get(&crate_type)
2378        .expect("failed to find crate type in dependency format list");
2379
2380    // We sort the libraries below
2381    #[allow(rustc::potential_query_instability)]
2382    let mut native_libraries_from_nonstatics = codegen_results
2383        .crate_info
2384        .native_libraries
2385        .iter()
2386        .filter_map(|(&cnum, libraries)| {
2387            if sess.target.is_like_windows {
2388                (dependency_linkage[cnum] != Linkage::Static).then_some(libraries)
2389            } else {
2390                Some(libraries)
2391            }
2392        })
2393        .flatten()
2394        .collect::<Vec<_>>();
2395    native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
2396
2397    if sess.target.is_like_windows {
2398        for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
2399            sess,
2400            archive_builder_builder,
2401            native_libraries_from_nonstatics,
2402            tmpdir,
2403            false,
2404        ) {
2405            cmd.add_object(&output_path);
2406        }
2407    } else {
2408        for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
2409            sess,
2410            native_libraries_from_nonstatics,
2411            &raw_dylib_dir,
2412        ) {
2413            // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
2414            cmd.link_dylib_by_name(&link_path, true, false);
2415        }
2416    }
2417
2418    // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
2419    // command line shorter, reset it to default here before adding more libraries.
2420    cmd.reset_per_library_state();
2421
2422    // FIXME: Built-in target specs occasionally use this for linking system libraries,
2423    // eliminate all such uses by migrating them to `#[link]` attributes in `lib(std,c,unwind)`
2424    // and remove the option.
2425    add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);
2426
2427    // ------------ Arbitrary order-independent options ------------
2428
2429    // Add order-independent options determined by rustc from its compiler options,
2430    // target properties and source code.
2431    add_order_independent_options(
2432        cmd,
2433        sess,
2434        link_output_kind,
2435        self_contained_components,
2436        flavor,
2437        crate_type,
2438        codegen_results,
2439        out_filename,
2440        tmpdir,
2441    );
2442
2443    // Can be used for arbitrary order-independent options.
2444    // In practice may also be occasionally used for linking native libraries.
2445    // Passed after compiler-generated options to support manual overriding when necessary.
2446    add_user_defined_link_args(cmd, sess);
2447
2448    // ------------ Builtin configurable linker scripts ------------
2449    // The user's link args should be able to overwrite symbols in the compiler's
2450    // linker script that were weakly defined (i.e. defined with `PROVIDE()`). For this
2451    // to work correctly, the user needs to be able to specify linker arguments like
2452    // `--defsym` and `--script` *before* any builtin linker scripts are evaluated.
2453    add_link_script(cmd, sess, tmpdir, crate_type);
2454
2455    // ------------ Object code and libraries, order-dependent ------------
2456
2457    // Post-link CRT objects.
2458    add_post_link_objects(cmd, sess, link_output_kind, self_contained_crt_objects);
2459
2460    // ------------ Late order-dependent options ------------
2461
2462    // Doesn't really make sense.
2463    // FIXME: In practice built-in target specs use this for arbitrary order-independent options.
2464    // Introduce a target spec option for order-independent linker options, migrate built-in specs
2465    // to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten.
2466    add_post_link_args(cmd, sess, flavor);
2467
2468    cmd.take_cmd()
2469}
2470
2471fn add_order_independent_options(
2472    cmd: &mut dyn Linker,
2473    sess: &Session,
2474    link_output_kind: LinkOutputKind,
2475    self_contained_components: LinkSelfContainedComponents,
2476    flavor: LinkerFlavor,
2477    crate_type: CrateType,
2478    codegen_results: &CodegenResults,
2479    out_filename: &Path,
2480    tmpdir: &Path,
2481) {
2482    // Take care of the flavors and CLI options requesting the `lld` linker.
2483    add_lld_args(cmd, sess, flavor, self_contained_components);
2484
2485    add_apple_link_args(cmd, sess, flavor);
2486
2487    let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
2488
2489    if sess.target.os == "fuchsia"
2490        && crate_type == CrateType::Executable
2491        && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
2492    {
2493        let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
2494            "asan/"
2495        } else {
2496            ""
2497        };
2498        cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1"));
2499    }
2500
2501    if sess.target.eh_frame_header {
2502        cmd.add_eh_frame_header();
2503    }
2504
2505    // Make the binary compatible with data execution prevention schemes.
2506    cmd.add_no_exec();
2507
2508    if self_contained_components.is_crt_objects_enabled() {
2509        cmd.no_crt_objects();
2510    }
2511
2512    if sess.target.os == "emscripten" {
2513        cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh {
2514            "-fwasm-exceptions"
2515        } else if sess.panic_strategy().unwinds() {
2516            "-sDISABLE_EXCEPTION_CATCHING=0"
2517        } else {
2518            "-sDISABLE_EXCEPTION_CATCHING=1"
2519        });
2520    }
2521
2522    if flavor == LinkerFlavor::Llbc {
2523        cmd.link_args(&[
2524            "--target",
2525            &versioned_llvm_target(sess),
2526            "--target-cpu",
2527            &codegen_results.crate_info.target_cpu,
2528        ]);
2529        if codegen_results.crate_info.target_features.len() > 0 {
2530            cmd.link_arg(&format!(
2531                "--target-feature={}",
2532                &codegen_results.crate_info.target_features.join(",")
2533            ));
2534        }
2535    } else if flavor == LinkerFlavor::Ptx {
2536        cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]);
2537    } else if flavor == LinkerFlavor::Bpf {
2538        cmd.link_args(&["--cpu", &codegen_results.crate_info.target_cpu]);
2539        if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features]
2540            .into_iter()
2541            .find(|feat| !feat.is_empty())
2542        {
2543            cmd.link_args(&["--cpu-features", feat]);
2544        }
2545    }
2546
2547    cmd.linker_plugin_lto();
2548
2549    add_library_search_dirs(cmd, sess, self_contained_components, apple_sdk_root.as_deref());
2550
2551    cmd.output_filename(out_filename);
2552
2553    if crate_type == CrateType::Executable
2554        && sess.target.is_like_windows
2555        && let Some(s) = &codegen_results.crate_info.windows_subsystem
2556    {
2557        cmd.subsystem(s);
2558    }
2559
2560    // Try to strip as much out of the generated object by removing unused
2561    // sections if possible. See more comments in linker.rs
2562    if !sess.link_dead_code() {
2563        // If PGO is enabled sometimes gc_sections will remove the profile data section
2564        // as it appears to be unused. This can then cause the PGO profile file to lose
2565        // some functions. If we are generating a profile we shouldn't strip those metadata
2566        // sections to ensure we have all the data for PGO.
2567        let keep_metadata =
2568            crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled();
2569        cmd.gc_sections(keep_metadata);
2570    }
2571
2572    cmd.set_output_kind(link_output_kind, crate_type, out_filename);
2573
2574    add_relro_args(cmd, sess);
2575
2576    // Pass optimization flags down to the linker.
2577    cmd.optimize();
2578
2579    // Gather the set of NatVis files, if any, and write them out to a temp directory.
2580    let natvis_visualizers = collect_natvis_visualizers(
2581        tmpdir,
2582        sess,
2583        &codegen_results.crate_info.local_crate_name,
2584        &codegen_results.crate_info.natvis_debugger_visualizers,
2585    );
2586
2587    // Pass debuginfo, NatVis debugger visualizers and strip flags down to the linker.
2588    cmd.debuginfo(sess.opts.cg.strip, &natvis_visualizers);
2589
2590    // We want to prevent the compiler from accidentally leaking in any system libraries,
2591    // so by default we tell linkers not to link to any default libraries.
2592    if !sess.opts.cg.default_linker_libraries && sess.target.no_default_libraries {
2593        cmd.no_default_libraries();
2594    }
2595
2596    if sess.opts.cg.profile_generate.enabled() || sess.instrument_coverage() {
2597        cmd.pgo_gen();
2598    }
2599
2600    if sess.opts.cg.control_flow_guard != CFGuard::Disabled {
2601        cmd.control_flow_guard();
2602    }
2603
2604    // OBJECT-FILES-NO, AUDIT-ORDER
2605    if sess.opts.unstable_opts.ehcont_guard {
2606        cmd.ehcont_guard();
2607    }
2608
2609    add_rpath_args(cmd, sess, codegen_results, out_filename);
2610}
2611
2612// Write the NatVis debugger visualizer files for each crate to the temp directory and gather the file paths.
2613fn collect_natvis_visualizers(
2614    tmpdir: &Path,
2615    sess: &Session,
2616    crate_name: &Symbol,
2617    natvis_debugger_visualizers: &BTreeSet<DebuggerVisualizerFile>,
2618) -> Vec<PathBuf> {
2619    let mut visualizer_paths = Vec::with_capacity(natvis_debugger_visualizers.len());
2620
2621    for (index, visualizer) in natvis_debugger_visualizers.iter().enumerate() {
2622        let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name.as_str(), index));
2623
2624        match fs::write(&visualizer_out_file, &visualizer.src) {
2625            Ok(()) => {
2626                visualizer_paths.push(visualizer_out_file);
2627            }
2628            Err(error) => {
2629                sess.dcx().emit_warn(errors::UnableToWriteDebuggerVisualizer {
2630                    path: visualizer_out_file,
2631                    error,
2632                });
2633            }
2634        };
2635    }
2636    visualizer_paths
2637}
2638
2639fn add_native_libs_from_crate(
2640    cmd: &mut dyn Linker,
2641    sess: &Session,
2642    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2643    codegen_results: &CodegenResults,
2644    tmpdir: &Path,
2645    bundled_libs: &FxIndexSet<Symbol>,
2646    cnum: CrateNum,
2647    link_static: bool,
2648    link_dynamic: bool,
2649    link_output_kind: LinkOutputKind,
2650) {
2651    if !sess.opts.unstable_opts.link_native_libraries {
2652        // If `-Zlink-native-libraries=false` is set, then the assumption is that an
2653        // external build system already has the native dependencies defined, and it
2654        // will provide them to the linker itself.
2655        return;
2656    }
2657
2658    if link_static && cnum != LOCAL_CRATE && !bundled_libs.is_empty() {
2659        // If rlib contains native libs as archives, unpack them to tmpdir.
2660        let rlib = &codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap().0;
2661        archive_builder_builder
2662            .extract_bundled_libs(rlib, tmpdir, bundled_libs)
2663            .unwrap_or_else(|e| sess.dcx().emit_fatal(e));
2664    }
2665
2666    let native_libs = match cnum {
2667        LOCAL_CRATE => &codegen_results.crate_info.used_libraries,
2668        _ => &codegen_results.crate_info.native_libraries[&cnum],
2669    };
2670
2671    let mut last = (None, NativeLibKind::Unspecified, false);
2672    for lib in native_libs {
2673        if !relevant_lib(sess, lib) {
2674            continue;
2675        }
2676
2677        // Skip if this library is the same as the last.
2678        last = if (Some(lib.name), lib.kind, lib.verbatim) == last {
2679            continue;
2680        } else {
2681            (Some(lib.name), lib.kind, lib.verbatim)
2682        };
2683
2684        let name = lib.name.as_str();
2685        let verbatim = lib.verbatim;
2686        match lib.kind {
2687            NativeLibKind::Static { bundle, whole_archive } => {
2688                if link_static {
2689                    let bundle = bundle.unwrap_or(true);
2690                    let whole_archive = whole_archive == Some(true);
2691                    if bundle && cnum != LOCAL_CRATE {
2692                        if let Some(filename) = lib.filename {
2693                            // If rlib contains native libs as archives, they are unpacked to tmpdir.
2694                            let path = tmpdir.join(filename.as_str());
2695                            cmd.link_staticlib_by_path(&path, whole_archive);
2696                        }
2697                    } else {
2698                        cmd.link_staticlib_by_name(name, verbatim, whole_archive);
2699                    }
2700                }
2701            }
2702            NativeLibKind::Dylib { as_needed } => {
2703                if link_dynamic {
2704                    cmd.link_dylib_by_name(name, verbatim, as_needed.unwrap_or(true))
2705                }
2706            }
2707            NativeLibKind::Unspecified => {
2708                // If we are generating a static binary, prefer static library when the
2709                // link kind is unspecified.
2710                if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs {
2711                    if link_static {
2712                        cmd.link_staticlib_by_name(name, verbatim, false);
2713                    }
2714                } else if link_dynamic {
2715                    cmd.link_dylib_by_name(name, verbatim, true);
2716                }
2717            }
2718            NativeLibKind::Framework { as_needed } => {
2719                if link_dynamic {
2720                    cmd.link_framework_by_name(name, verbatim, as_needed.unwrap_or(true))
2721                }
2722            }
2723            NativeLibKind::RawDylib => {
2724                // Handled separately in `linker_with_args`.
2725            }
2726            NativeLibKind::WasmImportModule => {}
2727            NativeLibKind::LinkArg => {
2728                if link_static {
2729                    if verbatim {
2730                        cmd.verbatim_arg(name);
2731                    } else {
2732                        cmd.link_arg(name);
2733                    }
2734                }
2735            }
2736        }
2737    }
2738}
2739
2740fn add_local_native_libraries(
2741    cmd: &mut dyn Linker,
2742    sess: &Session,
2743    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2744    codegen_results: &CodegenResults,
2745    tmpdir: &Path,
2746    link_output_kind: LinkOutputKind,
2747) {
2748    // All static and dynamic native library dependencies are linked to the local crate.
2749    let link_static = true;
2750    let link_dynamic = true;
2751    add_native_libs_from_crate(
2752        cmd,
2753        sess,
2754        archive_builder_builder,
2755        codegen_results,
2756        tmpdir,
2757        &Default::default(),
2758        LOCAL_CRATE,
2759        link_static,
2760        link_dynamic,
2761        link_output_kind,
2762    );
2763}
2764
2765fn add_upstream_rust_crates(
2766    cmd: &mut dyn Linker,
2767    sess: &Session,
2768    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2769    codegen_results: &CodegenResults,
2770    crate_type: CrateType,
2771    tmpdir: &Path,
2772    link_output_kind: LinkOutputKind,
2773) {
2774    // All of the heavy lifting has previously been accomplished by the
2775    // dependency_format module of the compiler. This is just crawling the
2776    // output of that module, adding crates as necessary.
2777    //
2778    // Linking to a rlib involves just passing it to the linker (the linker
2779    // will slurp up the object files inside), and linking to a dynamic library
2780    // involves just passing the right -l flag.
2781    let data = codegen_results
2782        .crate_info
2783        .dependency_formats
2784        .get(&crate_type)
2785        .expect("failed to find crate type in dependency format list");
2786
2787    if sess.target.is_like_aix {
2788        // Unlike ELF linkers, AIX doesn't feature `DT_SONAME` to override
2789        // the dependency name when outputting a shared library. Thus, `ld` will
2790        // use the full path to shared libraries as the dependency if passed it
2791        // by default unless `noipath` is passed.
2792        // https://www.ibm.com/docs/en/aix/7.3?topic=l-ld-command.
2793        cmd.link_or_cc_arg("-bnoipath");
2794    }
2795
2796    for &cnum in &codegen_results.crate_info.used_crates {
2797        // We may not pass all crates through to the linker. Some crates may appear statically in
2798        // an existing dylib, meaning we'll pick up all the symbols from the dylib.
2799        // We must always link crates `compiler_builtins` and `profiler_builtins` statically.
2800        // Even if they were already included into a dylib
2801        // (e.g. `libstd` when `-C prefer-dynamic` is used).
2802        // FIXME: `dependency_formats` can report `profiler_builtins` as `NotLinked` for some
2803        // reason, it shouldn't do that because `profiler_builtins` should indeed be linked.
2804        let linkage = data[cnum];
2805        let link_static_crate = linkage == Linkage::Static
2806            || (linkage == Linkage::IncludedFromDylib || linkage == Linkage::NotLinked)
2807                && (codegen_results.crate_info.compiler_builtins == Some(cnum)
2808                    || codegen_results.crate_info.profiler_runtime == Some(cnum));
2809
2810        let mut bundled_libs = Default::default();
2811        match linkage {
2812            Linkage::Static | Linkage::IncludedFromDylib | Linkage::NotLinked => {
2813                if link_static_crate {
2814                    bundled_libs = codegen_results.crate_info.native_libraries[&cnum]
2815                        .iter()
2816                        .filter_map(|lib| lib.filename)
2817                        .collect();
2818                    add_static_crate(
2819                        cmd,
2820                        sess,
2821                        archive_builder_builder,
2822                        codegen_results,
2823                        tmpdir,
2824                        cnum,
2825                        &bundled_libs,
2826                    );
2827                }
2828            }
2829            Linkage::Dynamic => {
2830                let src = &codegen_results.crate_info.used_crate_source[&cnum];
2831                add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0);
2832            }
2833        }
2834
2835        // Static libraries are linked for a subset of linked upstream crates.
2836        // 1. If the upstream crate is a directly linked rlib then we must link the native library
2837        // because the rlib is just an archive.
2838        // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we do not link
2839        // the native library because it is already linked into the dylib, and even if
2840        // inline/const/generic functions from the dylib can refer to symbols from the native
2841        // library, those symbols should be exported and available from the dylib anyway.
2842        // 3. Libraries bundled into `(compiler,profiler)_builtins` are special, see above.
2843        let link_static = link_static_crate;
2844        // Dynamic libraries are not linked here, see the FIXME in `add_upstream_native_libraries`.
2845        let link_dynamic = false;
2846        add_native_libs_from_crate(
2847            cmd,
2848            sess,
2849            archive_builder_builder,
2850            codegen_results,
2851            tmpdir,
2852            &bundled_libs,
2853            cnum,
2854            link_static,
2855            link_dynamic,
2856            link_output_kind,
2857        );
2858    }
2859}
2860
2861fn add_upstream_native_libraries(
2862    cmd: &mut dyn Linker,
2863    sess: &Session,
2864    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2865    codegen_results: &CodegenResults,
2866    tmpdir: &Path,
2867    link_output_kind: LinkOutputKind,
2868) {
2869    for &cnum in &codegen_results.crate_info.used_crates {
2870        // Static libraries are not linked here, they are linked in `add_upstream_rust_crates`.
2871        // FIXME: Merge this function to `add_upstream_rust_crates` so that all native libraries
2872        // are linked together with their respective upstream crates, and in their originally
2873        // specified order. This is slightly breaking due to our use of `--as-needed` (see crater
2874        // results in https://github.com/rust-lang/rust/pull/102832#issuecomment-1279772306).
2875        let link_static = false;
2876        // Dynamic libraries are linked for all linked upstream crates.
2877        // 1. If the upstream crate is a directly linked rlib then we must link the native library
2878        // because the rlib is just an archive.
2879        // 2. If the upstream crate is a dylib or a rlib linked through dylib, then we have to link
2880        // the native library too because inline/const/generic functions from the dylib can refer
2881        // to symbols from the native library, so the native library providing those symbols should
2882        // be available when linking our final binary.
2883        let link_dynamic = true;
2884        add_native_libs_from_crate(
2885            cmd,
2886            sess,
2887            archive_builder_builder,
2888            codegen_results,
2889            tmpdir,
2890            &Default::default(),
2891            cnum,
2892            link_static,
2893            link_dynamic,
2894            link_output_kind,
2895        );
2896    }
2897}
2898
2899// Rehome lib paths (which exclude the library file name) that point into the sysroot lib directory
2900// to be relative to the sysroot directory, which may be a relative path specified by the user.
2901//
2902// If the sysroot is a relative path, and the sysroot libs are specified as an absolute path, the
2903// linker command line can be non-deterministic due to the paths including the current working
2904// directory. The linker command line needs to be deterministic since it appears inside the PDB
2905// file generated by the MSVC linker. See https://github.com/rust-lang/rust/issues/112586.
2906//
2907// The returned path will always have `fix_windows_verbatim_for_gcc()` applied to it.
2908fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf {
2909    let sysroot_lib_path = &sess.target_tlib_path.dir;
2910    let canonical_sysroot_lib_path =
2911        { try_canonicalize(sysroot_lib_path).unwrap_or_else(|_| sysroot_lib_path.clone()) };
2912
2913    let canonical_lib_dir = try_canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf());
2914    if canonical_lib_dir == canonical_sysroot_lib_path {
2915        // This path already had `fix_windows_verbatim_for_gcc()` applied if needed.
2916        sysroot_lib_path.clone()
2917    } else {
2918        fix_windows_verbatim_for_gcc(lib_dir)
2919    }
2920}
2921
2922fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf {
2923    if let Some(dir) = path.parent() {
2924        let file_name = path.file_name().expect("library path has no file name component");
2925        rehome_sysroot_lib_dir(sess, dir).join(file_name)
2926    } else {
2927        fix_windows_verbatim_for_gcc(path)
2928    }
2929}
2930
2931// Adds the static "rlib" versions of all crates to the command line.
2932// There's a bit of magic which happens here specifically related to LTO,
2933// namely that we remove upstream object files.
2934//
2935// When performing LTO, almost(*) all of the bytecode from the upstream
2936// libraries has already been included in our object file output. As a
2937// result we need to remove the object files in the upstream libraries so
2938// the linker doesn't try to include them twice (or whine about duplicate
2939// symbols). We must continue to include the rest of the rlib, however, as
2940// it may contain static native libraries which must be linked in.
2941//
2942// (*) Crates marked with `#![no_builtins]` don't participate in LTO and
2943// their bytecode wasn't included. The object files in those libraries must
2944// still be passed to the linker.
2945//
2946// Note, however, that if we're not doing LTO we can just pass the rlib
2947// blindly to the linker (fast) because it's fine if it's not actually
2948// included as we're at the end of the dependency chain.
2949fn add_static_crate(
2950    cmd: &mut dyn Linker,
2951    sess: &Session,
2952    archive_builder_builder: &dyn ArchiveBuilderBuilder,
2953    codegen_results: &CodegenResults,
2954    tmpdir: &Path,
2955    cnum: CrateNum,
2956    bundled_lib_file_names: &FxIndexSet<Symbol>,
2957) {
2958    let src = &codegen_results.crate_info.used_crate_source[&cnum];
2959    let cratepath = &src.rlib.as_ref().unwrap().0;
2960
2961    let mut link_upstream =
2962        |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);
2963
2964    if !are_upstream_rust_objects_already_included(sess)
2965        || ignored_for_lto(sess, &codegen_results.crate_info, cnum)
2966    {
2967        link_upstream(cratepath);
2968        return;
2969    }
2970
2971    let dst = tmpdir.join(cratepath.file_name().unwrap());
2972    let name = cratepath.file_name().unwrap().to_str().unwrap();
2973    let name = &name[3..name.len() - 5]; // chop off lib/.rlib
2974    let bundled_lib_file_names = bundled_lib_file_names.clone();
2975
2976    sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
2977        let canonical_name = name.replace('-', "_");
2978        let upstream_rust_objects_already_included =
2979            are_upstream_rust_objects_already_included(sess);
2980        let is_builtins =
2981            sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum);
2982
2983        let mut archive = archive_builder_builder.new_archive_builder(sess);
2984        if let Err(error) = archive.add_archive(
2985            cratepath,
2986            Box::new(move |f| {
2987                if f == METADATA_FILENAME {
2988                    return true;
2989                }
2990
2991                let canonical = f.replace('-', "_");
2992
2993                let is_rust_object =
2994                    canonical.starts_with(&canonical_name) && looks_like_rust_object_file(f);
2995
2996                // If we're performing LTO and this is a rust-generated object
2997                // file, then we don't need the object file as it's part of the
2998                // LTO module. Note that `#![no_builtins]` is excluded from LTO,
2999                // though, so we let that object file slide.
3000                if upstream_rust_objects_already_included && is_rust_object && is_builtins {
3001                    return true;
3002                }
3003
3004                // We skip native libraries because:
3005                // 1. This native libraries won't be used from the generated rlib,
3006                //    so we can throw them away to avoid the copying work.
3007                // 2. We can't allow it to be a single remaining entry in archive
3008                //    as some linkers may complain on that.
3009                if bundled_lib_file_names.contains(&Symbol::intern(f)) {
3010                    return true;
3011                }
3012
3013                false
3014            }),
3015        ) {
3016            sess.dcx()
3017                .emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error });
3018        }
3019        if archive.build(&dst) {
3020            link_upstream(&dst);
3021        }
3022    });
3023}
3024
3025// Same thing as above, but for dynamic crates instead of static crates.
3026fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
3027    cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
3028}
3029
3030fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
3031    match lib.cfg {
3032        Some(ref cfg) => {
3033            eval_config_entry(sess, cfg, CRATE_NODE_ID, None, ShouldEmit::ErrorsAndLints).as_bool()
3034        }
3035        None => true,
3036    }
3037}
3038
3039pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool {
3040    match sess.lto() {
3041        config::Lto::Fat => true,
3042        config::Lto::Thin => {
3043            // If we defer LTO to the linker, we haven't run LTO ourselves, so
3044            // any upstream object files have not been copied yet.
3045            !sess.opts.cg.linker_plugin_lto.enabled()
3046        }
3047        config::Lto::No | config::Lto::ThinLocal => false,
3048    }
3049}
3050
3051/// We need to communicate five things to the linker on Apple/Darwin targets:
3052/// - The architecture.
3053/// - The operating system (and that it's an Apple platform).
3054/// - The environment.
3055/// - The deployment target.
3056/// - The SDK version.
3057fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
3058    if !sess.target.is_like_darwin {
3059        return;
3060    }
3061    let LinkerFlavor::Darwin(cc, _) = flavor else {
3062        return;
3063    };
3064
3065    // `sess.target.arch` (`target_arch`) is not detailed enough.
3066    let llvm_arch = sess.target.llvm_target.split_once('-').expect("LLVM target must have arch").0;
3067    let target_os = &*sess.target.os;
3068    let target_env = &*sess.target.env;
3069
3070    // The architecture name to forward to the linker.
3071    //
3072    // Supported architecture names can be found in the source:
3073    // https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/src/abstraction/MachOFileAbstraction.hpp#L578-L648
3074    //
3075    // Intentionally verbose to ensure that the list always matches correctly
3076    // with the list in the source above.
3077    let ld64_arch = match llvm_arch {
3078        "armv7k" => "armv7k",
3079        "armv7s" => "armv7s",
3080        "arm64" => "arm64",
3081        "arm64e" => "arm64e",
3082        "arm64_32" => "arm64_32",
3083        // ld64 doesn't understand i686, so fall back to i386 instead.
3084        //
3085        // Same story when linking with cc, since that ends up invoking ld64.
3086        "i386" | "i686" => "i386",
3087        "x86_64" => "x86_64",
3088        "x86_64h" => "x86_64h",
3089        _ => bug!("unsupported architecture in Apple target: {}", sess.target.llvm_target),
3090    };
3091
3092    if cc == Cc::No {
3093        // From the man page for ld64 (`man ld`):
3094        // > The linker accepts universal (multiple-architecture) input files,
3095        // > but always creates a "thin" (single-architecture), standard
3096        // > Mach-O output file. The architecture for the output file is
3097        // > specified using the -arch option.
3098        //
3099        // The linker has heuristics to determine the desired architecture,
3100        // but to be safe, and to avoid a warning, we set the architecture
3101        // explicitly.
3102        cmd.link_args(&["-arch", ld64_arch]);
3103
3104        // Man page says that ld64 supports the following platform names:
3105        // > - macos
3106        // > - ios
3107        // > - tvos
3108        // > - watchos
3109        // > - bridgeos
3110        // > - visionos
3111        // > - xros
3112        // > - mac-catalyst
3113        // > - ios-simulator
3114        // > - tvos-simulator
3115        // > - watchos-simulator
3116        // > - visionos-simulator
3117        // > - xros-simulator
3118        // > - driverkit
3119        let platform_name = match (target_os, target_env) {
3120            (os, "") => os,
3121            ("ios", "macabi") => "mac-catalyst",
3122            ("ios", "sim") => "ios-simulator",
3123            ("tvos", "sim") => "tvos-simulator",
3124            ("watchos", "sim") => "watchos-simulator",
3125            ("visionos", "sim") => "visionos-simulator",
3126            _ => bug!("invalid OS/env combination for Apple target: {target_os}, {target_env}"),
3127        };
3128
3129        let min_version = sess.apple_deployment_target().fmt_full().to_string();
3130
3131        // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
3132        // - By dyld to give extra warnings and errors, see e.g.:
3133        //   <https://github.com/apple-oss-distributions/dyld/blob/dyld-1165.3/common/MachOFile.cpp#L3029>
3134        //   <https://github.com/apple-oss-distributions/dyld/blob/dyld-1165.3/common/MachOFile.cpp#L3738-L3857>
3135        // - By system frameworks to change certain behaviour. For example, the default value of
3136        //   `-[NSView wantsBestResolutionOpenGLSurface]` is `YES` when the SDK version is >= 10.15.
3137        //   <https://developer.apple.com/documentation/appkit/nsview/1414938-wantsbestresolutionopenglsurface?language=objc>
3138        //
3139        // We do not currently know the actual SDK version though, so we have a few options:
3140        // 1. Use the minimum version supported by rustc.
3141        // 2. Use the same as the deployment target.
3142        // 3. Use an arbitrary recent version.
3143        // 4. Omit the version.
3144        //
3145        // The first option is too low / too conservative, and means that users will not get the
3146        // same behaviour from a binary compiled with rustc as with one compiled by clang.
3147        //
3148        // The second option is similarly conservative, and also wrong since if the user specified a
3149        // higher deployment target than the SDK they're compiling/linking with, the runtime might
3150        // make invalid assumptions about the capabilities of the binary.
3151        //
3152        // The third option requires that `rustc` is periodically kept up to date with Apple's SDK
3153        // version, and is also wrong for similar reasons as above.
3154        //
3155        // The fourth option is bad because while `ld`, `otool`, `vtool` and such understand it to
3156        // mean "absent" or `n/a`, dyld doesn't actually understand it, and will end up interpreting
3157        // it as 0.0, which is again too low/conservative.
3158        //
3159        // Currently, we lie about the SDK version, and choose the second option.
3160        //
3161        // FIXME(madsmtm): Parse the SDK version from the SDK root instead.
3162        // <https://github.com/rust-lang/rust/issues/129432>
3163        let sdk_version = &*min_version;
3164
3165        // From the man page for ld64 (`man ld`):
3166        // > This is set to indicate the platform, oldest supported version of
3167        // > that platform that output is to be used on, and the SDK that the
3168        // > output was built against.
3169        //
3170        // Like with `-arch`, the linker can figure out the platform versions
3171        // itself from the binaries being linked, but to be safe, we specify
3172        // the desired versions here explicitly.
3173        cmd.link_args(&["-platform_version", platform_name, &*min_version, sdk_version]);
3174    } else {
3175        // cc == Cc::Yes
3176        //
3177        // We'd _like_ to use `-target` everywhere, since that can uniquely
3178        // communicate all the required details except for the SDK version
3179        // (which is read by Clang itself from the SDKROOT), but that doesn't
3180        // work on GCC, and since we don't know whether the `cc` compiler is
3181        // Clang, GCC, or something else, we fall back to other options that
3182        // also work on GCC when compiling for macOS.
3183        //
3184        // Targets other than macOS are ill-supported by GCC (it doesn't even
3185        // support e.g. `-miphoneos-version-min`), so in those cases we can
3186        // fairly safely use `-target`. See also the following, where it is
3187        // made explicit that the recommendation by LLVM developers is to use
3188        // `-target`: <https://github.com/llvm/llvm-project/issues/88271>
3189        if target_os == "macos" {
3190            // `-arch` communicates the architecture.
3191            //
3192            // CC forwards the `-arch` to the linker, so we use the same value
3193            // here intentionally.
3194            cmd.cc_args(&["-arch", ld64_arch]);
3195
3196            // The presence of `-mmacosx-version-min` makes CC default to
3197            // macOS, and it sets the deployment target.
3198            let version = sess.apple_deployment_target().fmt_full();
3199            // Intentionally pass this as a single argument, Clang doesn't
3200            // seem to like it otherwise.
3201            cmd.cc_arg(&format!("-mmacosx-version-min={version}"));
3202
3203            // macOS has no environment, so with these two, we've told CC the
3204            // four desired parameters.
3205            //
3206            // We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
3207        } else {
3208            cmd.cc_args(&["-target", &versioned_llvm_target(sess)]);
3209        }
3210    }
3211}
3212
3213fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> {
3214    if !sess.target.is_like_darwin {
3215        return None;
3216    }
3217    let LinkerFlavor::Darwin(cc, _) = flavor else {
3218        return None;
3219    };
3220
3221    // The default compiler driver on macOS is at `/usr/bin/cc`. This is a trampoline binary that
3222    // effectively invokes `xcrun cc` internally to look up both the compiler binary and the SDK
3223    // root from the current Xcode installation. When cross-compiling, when `rustc` is invoked
3224    // inside Xcode, or when invoking the linker directly, this default logic is unsuitable, so
3225    // instead we invoke `xcrun` manually.
3226    //
3227    // (Note that this doesn't mean we get a duplicate lookup here - passing `SDKROOT` below will
3228    // cause the trampoline binary to skip looking up the SDK itself).
3229    let sdkroot = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?;
3230
3231    if cc == Cc::Yes {
3232        // There are a few options to pass the SDK root when linking with a C/C++ compiler:
3233        // - The `--sysroot` flag.
3234        // - The `-isysroot` flag.
3235        // - The `SDKROOT` environment variable.
3236        //
3237        // `--sysroot` isn't actually enough to get Clang to treat it as a platform SDK, you need
3238        // to specify `-isysroot`. This is admittedly a bit strange, as on most targets `-isysroot`
3239        // only applies to include header files, but on Apple targets it also applies to libraries
3240        // and frameworks.
3241        //
3242        // This leaves the choice between `-isysroot` and `SDKROOT`. Both are supported by Clang and
3243        // GCC, though they may not be supported by all compiler drivers. We choose `SDKROOT`,
3244        // primarily because that is the same interface that is used when invoking the tool under
3245        // `xcrun -sdk macosx $tool`.
3246        //
3247        // In that sense, if a given compiler driver does not support `SDKROOT`, the blame is fairly
3248        // clearly in the tool in question, since they also don't support being run under `xcrun`.
3249        //
3250        // Additionally, `SDKROOT` is an environment variable and thus optional. It also has lower
3251        // precedence than `-isysroot`, so a custom compiler driver that does not support it and
3252        // instead figures out the SDK on their own can easily do so by using `-isysroot`.
3253        //
3254        // (This in particular affects Clang built with the `DEFAULT_SYSROOT` CMake flag, such as
3255        // the one provided by some versions of Homebrew's `llvm` package. Those will end up
3256        // ignoring the value we set here, and instead use their built-in sysroot).
3257        cmd.cmd().env("SDKROOT", &sdkroot);
3258    } else {
3259        // When invoking the linker directly, we use the `-syslibroot` parameter. `SDKROOT` is not
3260        // read by the linker, so it's really the only option.
3261        //
3262        // This is also what Clang does.
3263        cmd.link_arg("-syslibroot");
3264        cmd.link_arg(&sdkroot);
3265    }
3266
3267    Some(sdkroot)
3268}
3269
3270fn get_apple_sdk_root(sess: &Session) -> Option<PathBuf> {
3271    if let Ok(sdkroot) = env::var("SDKROOT") {
3272        let p = PathBuf::from(&sdkroot);
3273
3274        // Ignore invalid SDKs, similar to what clang does:
3275        // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.6/clang/lib/Driver/ToolChains/Darwin.cpp#L2212-L2229
3276        //
3277        // NOTE: Things are complicated here by the fact that `rustc` can be run by Cargo to compile
3278        // build scripts and proc-macros for the host, and thus we need to ignore SDKROOT if it's
3279        // clearly set for the wrong platform.
3280        //
3281        // FIXME(madsmtm): Make this more robust (maybe read `SDKSettings.json` like Clang does?).
3282        match &*apple::sdk_name(&sess.target).to_lowercase() {
3283            "appletvos"
3284                if sdkroot.contains("TVSimulator.platform")
3285                    || sdkroot.contains("MacOSX.platform") => {}
3286            "appletvsimulator"
3287                if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {}
3288            "iphoneos"
3289                if sdkroot.contains("iPhoneSimulator.platform")
3290                    || sdkroot.contains("MacOSX.platform") => {}
3291            "iphonesimulator"
3292                if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => {
3293            }
3294            "macosx"
3295                if sdkroot.contains("iPhoneOS.platform")
3296                    || sdkroot.contains("iPhoneSimulator.platform")
3297                    || sdkroot.contains("AppleTVOS.platform")
3298                    || sdkroot.contains("AppleTVSimulator.platform")
3299                    || sdkroot.contains("WatchOS.platform")
3300                    || sdkroot.contains("WatchSimulator.platform")
3301                    || sdkroot.contains("XROS.platform")
3302                    || sdkroot.contains("XRSimulator.platform") => {}
3303            "watchos"
3304                if sdkroot.contains("WatchSimulator.platform")
3305                    || sdkroot.contains("MacOSX.platform") => {}
3306            "watchsimulator"
3307                if sdkroot.contains("WatchOS.platform") || sdkroot.contains("MacOSX.platform") => {}
3308            "xros"
3309                if sdkroot.contains("XRSimulator.platform")
3310                    || sdkroot.contains("MacOSX.platform") => {}
3311            "xrsimulator"
3312                if sdkroot.contains("XROS.platform") || sdkroot.contains("MacOSX.platform") => {}
3313            // Ignore `SDKROOT` if it's not a valid path.
3314            _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {}
3315            _ => return Some(p),
3316        }
3317    }
3318
3319    apple::get_sdk_root(sess)
3320}
3321
3322/// When using the linker flavors opting in to `lld`, add the necessary paths and arguments to
3323/// invoke it:
3324/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
3325/// - or any `lld` available to `cc`.
3326fn add_lld_args(
3327    cmd: &mut dyn Linker,
3328    sess: &Session,
3329    flavor: LinkerFlavor,
3330    self_contained_components: LinkSelfContainedComponents,
3331) {
3332    debug!(
3333        "add_lld_args requested, flavor: '{:?}', target self-contained components: {:?}",
3334        flavor, self_contained_components,
3335    );
3336
3337    // If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
3338    // we don't need to do anything.
3339    if !(flavor.uses_cc() && flavor.uses_lld()) {
3340        return;
3341    }
3342
3343    // 1. Implement the "self-contained" part of this feature by adding rustc distribution
3344    // directories to the tool's search path, depending on a mix between what users can specify on
3345    // the CLI, and what the target spec enables (as it can't disable components):
3346    // - if the self-contained linker is enabled on the CLI or by the target spec,
3347    // - and if the self-contained linker is not disabled on the CLI.
3348    let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled();
3349    let self_contained_target = self_contained_components.is_linker_enabled();
3350
3351    let self_contained_linker = self_contained_cli || self_contained_target;
3352    if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
3353        let mut linker_path_exists = false;
3354        for path in sess.get_tools_search_paths(false) {
3355            let linker_path = path.join("gcc-ld");
3356            linker_path_exists |= linker_path.exists();
3357            cmd.cc_arg({
3358                let mut arg = OsString::from("-B");
3359                arg.push(linker_path);
3360                arg
3361            });
3362        }
3363        if !linker_path_exists {
3364            // As a sanity check, we emit an error if none of these paths exist: we want
3365            // self-contained linking and have no linker.
3366            sess.dcx().emit_fatal(errors::SelfContainedLinkerMissing);
3367        }
3368    }
3369
3370    // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
3371    // `lld` as the linker.
3372    //
3373    // Note that wasm targets skip this step since the only option there anyway
3374    // is to use LLD but the `wasm32-wasip2` target relies on a wrapper around
3375    // this, `wasm-component-ld`, which is overridden if this option is passed.
3376    if !sess.target.is_like_wasm {
3377        cmd.cc_arg("-fuse-ld=lld");
3378    }
3379
3380    if !flavor.is_gnu() {
3381        // Tell clang to use a non-default LLD flavor.
3382        // Gcc doesn't understand the target option, but we currently assume
3383        // that gcc is not used for Apple and Wasm targets (#97402).
3384        //
3385        // Note that we don't want to do that by default on macOS: e.g. passing a
3386        // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
3387        // shown in issue #101653 and the discussion in PR #101792.
3388        //
3389        // It could be required in some cases of cross-compiling with
3390        // LLD, but this is generally unspecified, and we don't know
3391        // which specific versions of clang, macOS SDK, host and target OS
3392        // combinations impact us here.
3393        //
3394        // So we do a simple first-approximation until we know more of what the
3395        // Apple targets require (and which would be handled prior to hitting this
3396        // LLD codepath anyway), but the expectation is that until then
3397        // this should be manually passed if needed. We specify the target when
3398        // targeting a different linker flavor on macOS, and that's also always
3399        // the case when targeting WASM.
3400        if sess.target.linker_flavor != sess.host.linker_flavor {
3401            cmd.cc_arg(format!("--target={}", versioned_llvm_target(sess)));
3402        }
3403    }
3404}
3405
3406// gold has been deprecated with binutils 2.44
3407// and is known to behave incorrectly around Rust programs.
3408// There have been reports of being unable to bootstrap with gold:
3409// https://github.com/rust-lang/rust/issues/139425
3410// Additionally, gold miscompiles SHF_GNU_RETAIN sections, which are
3411// emitted with `#[used(linker)]`.
3412fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
3413    use object::read::elf::{FileHeader, SectionHeader};
3414    use object::read::{ReadCache, ReadRef, Result};
3415    use object::{Endianness, elf};
3416
3417    fn elf_has_gold_version_note<'a>(
3418        elf: &impl FileHeader,
3419        data: impl ReadRef<'a>,
3420    ) -> Result<bool> {
3421        let endian = elf.endian()?;
3422
3423        let section =
3424            elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version");
3425        if let Some((_, section)) = section
3426            && let Some(mut notes) = section.notes(endian, data)?
3427        {
3428            return Ok(notes.any(|note| {
3429                note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
3430            }));
3431        }
3432
3433        Ok(false)
3434    }
3435
3436    let data = ReadCache::new(BufReader::new(File::open(path)?));
3437
3438    let was_linked_with_gold = if sess.target.pointer_width == 64 {
3439        let elf = elf::FileHeader64::<Endianness>::parse(&data)?;
3440        elf_has_gold_version_note(elf, &data)?
3441    } else if sess.target.pointer_width == 32 {
3442        let elf = elf::FileHeader32::<Endianness>::parse(&data)?;
3443        elf_has_gold_version_note(elf, &data)?
3444    } else {
3445        return Ok(());
3446    };
3447
3448    if was_linked_with_gold {
3449        let mut warn =
3450            sess.dcx().struct_warn("the gold linker is deprecated and has known bugs with Rust");
3451        warn.help("consider using LLD or ld from GNU binutils instead");
3452        warn.emit();
3453    }
3454    Ok(())
3455}