rustc_codegen_ssa/back/
link.rs

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