compiler/rustc_codegen_ssa/src/back/linker.rs RUST 1,955 lines View on github.com → Search inside
1use std::ffi::{OsStr, OsString};2use std::fs::{self, File};3use std::io::prelude::*;4use std::path::{Path, PathBuf};5use std::{env, iter, mem, str};67use find_msvc_tools;8use rustc_hir::attrs::WindowsSubsystemKind;9use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};10use rustc_middle::bug;11use rustc_middle::middle::dependency_format::Linkage;12use rustc_middle::middle::exported_symbols::{13    self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,14};15use rustc_middle::ty::{SymbolName, TyCtxt};16use rustc_session::Session;17use rustc_session::config::{self, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};18use rustc_structures::CrateType;19use rustc_target::spec::{Arch, Cc, CfgAbi, LinkOutputKind, LinkerFlavor, Lld, Os};20use tracing::{debug, warn};2122use super::command::Command;23use super::symbol_export;24use crate::back::link::{25    find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,26};27use crate::back::symbol_export::allocator_shim_symbols;28use crate::base::needs_allocator_shim_for_linking;29use crate::{SymbolExport, diagnostics};3031#[cfg(test)]32mod tests;3334/// Disables non-English messages from localized linkers.35/// Such messages may cause issues with text encoding on Windows (#35785)36/// and prevent inspection of linker output in case of errors, which we occasionally do.37/// This should be acceptable because other messages from rustc are in English anyway,38/// and may also be desirable to improve searchability of the linker diagnostics.39pub(crate) fn disable_localization(linker: &mut Command) {40    // No harm in setting both env vars simultaneously.41    // Unix-style linkers.42    linker.env("LC_ALL", "C");43    // MSVC's `link.exe`.44    linker.env("VSLANG", "1033");45}4647/// The third parameter is for env vars, used on windows to set up the48/// path for MSVC to find its DLLs, and gcc to find its bundled49/// toolchain50pub(crate) fn get_linker<'a>(51    sess: &'a Session,52    linker: &Path,53    flavor: LinkerFlavor,54    self_contained: bool,55    target_cpu: &'a str,56    codegen_backend: &'static str,57) -> Box<dyn Linker + 'a> {58    let msvc_tool = find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe");5960    // If our linker looks like a batch script on Windows then to execute this61    // we'll need to spawn `cmd` explicitly. This is primarily done to handle62    // emscripten where the linker is `emcc.bat` and needs to be spawned as63    // `cmd /c emcc.bat ...`.64    //65    // This worked historically but is needed manually since #42436 (regression66    // was tagged as #42791) and some more info can be found on #44443 for67    // emscripten itself.68    let mut cmd = match linker.to_str() {69        Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),70        _ => match flavor {71            LinkerFlavor::Gnu(Cc::No, Lld::Yes)72            | LinkerFlavor::Darwin(Cc::No, Lld::Yes)73            | LinkerFlavor::WasmLld(Cc::No)74            | LinkerFlavor::Msvc(Lld::Yes) => Command::lld(linker, flavor.lld_flavor()),75            LinkerFlavor::Msvc(Lld::No)76                if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() =>77            {78                Command::new(msvc_tool.as_ref().map_or(linker, |t| t.path()))79            }80            _ => Command::new(linker),81        },82    };8384    // UWP apps have API restrictions enforced during Store submissions.85    // To comply with the Windows App Certification Kit,86    // MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc).87    let t = &sess.target;88    if matches!(flavor, LinkerFlavor::Msvc(..)) && t.cfg_abi == CfgAbi::Uwp {89        if let Some(ref tool) = msvc_tool {90            let original_path = tool.path();91            if let Some(root_lib_path) = original_path.ancestors().nth(4) {92                let arch = match t.arch {93                    Arch::X86_64 => Some("x64"),94                    Arch::X86 => Some("x86"),95                    Arch::AArch64 => Some("arm64"),96                    Arch::Arm => Some("arm"),97                    _ => None,98                };99                if let Some(ref a) = arch {100                    // FIXME: Move this to `fn linker_with_args`.101                    let mut arg = OsString::from("/LIBPATH:");102                    arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a));103                    cmd.arg(&arg);104                } else {105                    warn!("arch is not supported");106                }107            } else {108                warn!("MSVC root path lib location not found");109            }110        } else {111            warn!("link.exe not found");112        }113    }114115    // The compiler's sysroot often has some bundled tools, so add it to the116    // PATH for the child.117    let mut new_path = sess.get_tools_search_paths(self_contained);118    let mut msvc_changed_path = false;119    if sess.target.is_like_msvc120        && let Some(ref tool) = msvc_tool121    {122        for (k, v) in tool.env() {123            if k == "PATH" {124                new_path.extend(env::split_paths(v));125                msvc_changed_path = true;126            } else {127                cmd.env(k, v);128            }129        }130    }131132    if !msvc_changed_path && let Some(path) = env::var_os("PATH") {133        new_path.extend(env::split_paths(&path));134    }135    cmd.env("PATH", env::join_paths(new_path).unwrap());136137    // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction138    // to the linker args construction.139    assert!(cmd.get_args().is_empty() || sess.target.cfg_abi == CfgAbi::Uwp);140    match flavor {141        LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::Aix => {142            Box::new(AixLinker::new(cmd, sess)) as Box<dyn Linker>143        }144        LinkerFlavor::WasmLld(Cc::No) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>,145        LinkerFlavor::Gnu(cc, _)146        | LinkerFlavor::Darwin(cc, _)147        | LinkerFlavor::WasmLld(cc)148        | LinkerFlavor::Unix(cc) => Box::new(GccLinker {149            cmd,150            sess,151            target_cpu,152            hinted_static: None,153            is_ld: cc == Cc::No,154            is_gnu: flavor.is_gnu(),155            uses_lld: flavor.uses_lld(),156            codegen_backend,157        }) as Box<dyn Linker>,158        LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>,159        LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>,160        LinkerFlavor::Bpf => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>,161        LinkerFlavor::Llbc => Box::new(LlbcLinker { cmd, sess }) as Box<dyn Linker>,162    }163}164165// Note: Ideally neither these helper function, nor the macro-generated inherent methods below166// would exist, and these functions would live in `trait Linker`.167// Unfortunately, adding these functions to `trait Linker` make it `dyn`-incompatible.168// If the methods are added to the trait with `where Self: Sized` bounds, then even a separate169// implementation of them for `dyn Linker {}` wouldn't work due to a conflict with those170// uncallable methods in the trait.171172/// Just pass the arguments to the linker as is.173/// It is assumed that they are correctly prepared in advance.174fn verbatim_args<L: Linker + ?Sized>(175    l: &mut L,176    args: impl IntoIterator<Item: AsRef<OsStr>>,177) -> &mut L {178    for arg in args {179        l.cmd().arg(arg);180    }181    l182}183/// Add underlying linker arguments to C compiler command, by wrapping them in184/// `-Wl` or `-Xlinker`.185fn convert_link_args_to_cc_args(cmd: &mut Command, args: impl IntoIterator<Item: AsRef<OsStr>>) {186    let mut combined_arg = OsString::from("-Wl");187    for arg in args {188        // If the argument itself contains a comma, we need to emit it189        // as `-Xlinker`, otherwise we can use `-Wl`.190        if arg.as_ref().as_encoded_bytes().contains(&b',') {191            // Emit current `-Wl` argument, if any has been built.192            if combined_arg != OsStr::new("-Wl") {193                cmd.arg(combined_arg);194                // Begin next `-Wl` argument.195                combined_arg = OsString::from("-Wl");196            }197198            // Emit `-Xlinker` argument.199            cmd.arg("-Xlinker");200            cmd.arg(arg);201        } else {202            // Append to `-Wl` argument.203            combined_arg.push(",");204            combined_arg.push(arg);205        }206    }207    // Emit final `-Wl` argument.208    if combined_arg != OsStr::new("-Wl") {209        cmd.arg(combined_arg);210    }211}212/// Arguments for the underlying linker.213/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper.214fn link_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L {215    if !l.is_cc() {216        verbatim_args(l, args);217    } else {218        convert_link_args_to_cc_args(l.cmd(), args);219    }220    l221}222/// Arguments for the cc wrapper specifically.223/// Check that it's indeed a cc wrapper and pass verbatim.224fn cc_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L {225    assert!(l.is_cc());226    verbatim_args(l, args)227}228/// Arguments supported by both underlying linker and cc wrapper, pass verbatim.229fn link_or_cc_args<L: Linker + ?Sized>(230    l: &mut L,231    args: impl IntoIterator<Item: AsRef<OsStr>>,232) -> &mut L {233    verbatim_args(l, args)234}235236macro_rules! generate_arg_methods {237    ($($ty:ty)*) => { $(238        impl $ty {239            #[allow(unused)]240            pub(crate) fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {241                verbatim_args(self, args)242            }243            #[allow(unused)]244            pub(crate) fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {245                verbatim_args(self, iter::once(arg))246            }247            #[allow(unused)]248            pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {249                link_args(self, args)250            }251            #[allow(unused)]252            pub(crate) fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {253                link_args(self, iter::once(arg))254            }255            #[allow(unused)]256            pub(crate) fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {257                cc_args(self, args)258            }259            #[allow(unused)]260            pub(crate) fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {261                cc_args(self, iter::once(arg))262            }263            #[allow(unused)]264            pub(crate) fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {265                link_or_cc_args(self, args)266            }267            #[allow(unused)]268            pub(crate) fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {269                link_or_cc_args(self, iter::once(arg))270            }271        }272    )* }273}274275generate_arg_methods! {276    GccLinker<'_>277    MsvcLinker<'_>278    EmLinker<'_>279    WasmLd<'_>280    AixLinker<'_>281    LlbcLinker<'_>282    BpfLinker<'_>283    dyn Linker + '_284}285286/// Linker abstraction used by `back::link` to build up the command to invoke a287/// linker.288///289/// This trait is the total list of requirements needed by `back::link` and290/// represents the meaning of each option being passed down. This trait is then291/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an292/// MSVC linker (e.g., `link.exe`) is being used.293pub(crate) trait Linker {294    fn cmd(&mut self) -> &mut Command;295    fn is_cc(&self) -> bool {296        false297    }298    fn set_output_kind(299        &mut self,300        output_kind: LinkOutputKind,301        crate_type: CrateType,302        out_filename: &Path,303    );304    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {305        bug!("dylib linked with unsupported linker")306    }307    fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {308        bug!("dylib linked with unsupported linker")309    }310    fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {311        bug!("framework linked with unsupported linker")312    }313    fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool);314    fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool);315    fn include_path(&mut self, path: &Path) {316        link_or_cc_args(link_or_cc_args(self, &["-L"]), &[path]);317    }318    fn framework_path(&mut self, _path: &Path) {319        bug!("framework path set with unsupported linker")320    }321    fn output_filename(&mut self, path: &Path) {322        link_or_cc_args(link_or_cc_args(self, &["-o"]), &[path]);323    }324    fn add_object(&mut self, path: &Path) {325        link_or_cc_args(self, &[path]);326    }327    fn gc_sections(&mut self, keep_metadata: bool);328    fn full_relro(&mut self);329    fn partial_relro(&mut self);330    fn no_relro(&mut self);331    fn optimize(&mut self);332    fn pgo_gen(&mut self);333    fn control_flow_guard(&mut self);334    fn ehcont_guard(&mut self);335    fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);336    fn no_crt_objects(&mut self);337    fn no_default_libraries(&mut self);338    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]);339    fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind);340    fn linker_plugin_lto(&mut self);341    fn add_eh_frame_header(&mut self) {}342    fn add_no_exec(&mut self) {}343    fn add_as_needed(&mut self) {}344    fn reset_per_library_state(&mut self) {}345    fn enable_profiling(&mut self) {}346}347348impl dyn Linker + '_ {349    pub(crate) fn take_cmd(&mut self) -> Command {350        mem::replace(self.cmd(), Command::new(""))351    }352}353354struct GccLinker<'a> {355    cmd: Command,356    sess: &'a Session,357    target_cpu: &'a str,358    hinted_static: Option<bool>, // Keeps track of the current hinting mode.359    // Link as ld360    is_ld: bool,361    is_gnu: bool,362    uses_lld: bool,363    codegen_backend: &'static str,364}365366impl<'a> GccLinker<'a> {367    fn takes_hints(&self) -> bool {368        // Really this function only returns true if the underlying linker369        // configured for a compiler is binutils `ld.bfd` and `ld.gold`. We370        // don't really have a foolproof way to detect that, so rule out some371        // platforms where currently this is guaranteed to *not* be the case:372        //373        // * On OSX they have their own linker, not binutils'374        // * For WebAssembly the only functional linker is LLD, which doesn't375        //   support hint flags376        !self.sess.target.is_like_darwin && !self.sess.target.is_like_wasm377    }378379    // Some platforms take hints about whether a library is static or dynamic.380    // For those that support this, we ensure we pass the option if the library381    // was flagged "static" (most defaults are dynamic) to ensure that if382    // libfoo.a and libfoo.so both exist that the right one is chosen.383    fn hint_static(&mut self) {384        if !self.takes_hints() {385            return;386        }387        if self.hinted_static != Some(true) {388            self.link_arg("-Bstatic");389            self.hinted_static = Some(true);390        }391    }392393    fn hint_dynamic(&mut self) {394        if !self.takes_hints() {395            return;396        }397        if self.hinted_static != Some(false) {398            self.link_arg("-Bdynamic");399            self.hinted_static = Some(false);400        }401    }402403    fn push_linker_plugin_lto_args(&mut self, plugin_path: Option<&OsStr>) {404        if let Some(plugin_path) = plugin_path {405            let mut arg = OsString::from("-plugin=");406            arg.push(plugin_path);407            self.link_arg(&arg);408        }409410        let opt_level = match self.sess.opts.optimize {411            config::OptLevel::No => "O0",412            config::OptLevel::Less => "O1",413            config::OptLevel::More | config::OptLevel::Size | config::OptLevel::SizeMin => "O2",414            config::OptLevel::Aggressive => "O3",415        };416417        if let Some(path) = &self.sess.opts.cg.profile_sample_use {418            self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display()));419        };420        let prefix = if self.codegen_backend == "gcc" {421            // The GCC linker plugin requires a leading dash.422            "-"423        } else {424            ""425        };426        self.link_args(&[427            &format!("-plugin-opt={prefix}{opt_level}"),428            &format!("-plugin-opt={prefix}mcpu={}", self.target_cpu),429        ]);430    }431432    fn build_dylib(&mut self, crate_type: CrateType, out_filename: &Path) {433        // On mac we need to tell the linker to let this library be rpathed434        if self.sess.target.is_like_darwin {435            if self.is_cc() {436                // `-dynamiclib` makes `cc` pass `-dylib` to the linker.437                self.cc_arg("-dynamiclib");438            } else {439                self.link_arg("-dylib");440                // Clang also sets `-dynamic`, but that's implied by `-dylib`, so unnecessary.441            }442443            // Note that the `osx_rpath_install_name` option here is a hack444            // purely to support bootstrap right now, we should get a more445            // principled solution at some point to force the compiler to pass446            // the right `-Wl,-install_name` with an `@rpath` in it.447            if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name {448                let mut rpath = OsString::from("@rpath/");449                rpath.push(out_filename.file_name().unwrap());450                self.link_arg("-install_name").link_arg(rpath);451            }452        } else {453            self.link_or_cc_arg("-shared");454            if let Some(name) = out_filename.file_name() {455                if self.sess.target.is_like_windows {456                    // The output filename already contains `dll_suffix` so457                    // the resulting import library will have a name in the458                    // form of libfoo.dll.a459                    let (prefix, suffix) = self.sess.staticlib_components(false);460                    let mut implib_name = OsString::from(prefix);461                    implib_name.push(name);462                    implib_name.push(suffix);463                    let mut out_implib = OsString::from("--out-implib=");464                    out_implib.push(out_filename.with_file_name(implib_name));465                    self.link_arg(out_implib);466                } else if crate_type == CrateType::Dylib {467                    // When dylibs are linked by a full path this value will get into `DT_NEEDED`468                    // instead of the full path, so the library can be later found in some other469                    // location than that specific path.470                    let mut soname = OsString::from("-soname=");471                    soname.push(name);472                    self.link_arg(soname);473                }474            }475        }476    }477478    fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) {479        if !as_needed {480            if self.sess.target.is_like_darwin {481                // FIXME(81490): ld64 doesn't support these flags but macOS 11482                // has -needed-l{} / -needed_library {}483                // but we have no way to detect that here.484                self.sess.dcx().emit_warn(diagnostics::Ld64UnimplementedModifier);485            } else if self.is_gnu && !self.sess.target.is_like_windows {486                self.link_arg("--no-as-needed");487            } else {488                self.sess.dcx().emit_warn(diagnostics::LinkerUnsupportedModifier);489            }490        }491492        f(self);493494        if !as_needed {495            if self.sess.target.is_like_darwin {496                // See above FIXME comment497            } else if self.is_gnu && !self.sess.target.is_like_windows {498                self.link_arg("--as-needed");499            }500        }501    }502}503504impl<'a> Linker for GccLinker<'a> {505    fn cmd(&mut self) -> &mut Command {506        &mut self.cmd507    }508509    fn is_cc(&self) -> bool {510        !self.is_ld511    }512513    fn set_output_kind(514        &mut self,515        output_kind: LinkOutputKind,516        crate_type: CrateType,517        out_filename: &Path,518    ) {519        match output_kind {520            LinkOutputKind::DynamicNoPicExe => {521                // noop on windows w/ gcc, warning w/ clang522                if !self.is_ld && self.is_gnu && !self.sess.target.is_like_windows {523                    self.cc_arg("-no-pie");524                }525            }526            LinkOutputKind::DynamicPicExe => {527                // noop on windows w/ gcc & ld, error w/ lld528                if !self.sess.target.is_like_windows {529                    // `-pie` works for both gcc wrapper and ld.530                    self.link_or_cc_arg("-pie");531                }532            }533            LinkOutputKind::StaticNoPicExe => {534                // `-static` works for both gcc wrapper and ld.535                self.link_or_cc_arg("-static");536                // noop on windows w/ gcc, warning w/ clang537                if !self.is_ld && self.is_gnu && !self.sess.target.is_like_windows {538                    self.cc_arg("-no-pie");539                }540            }541            LinkOutputKind::StaticPicExe => {542                if !self.is_ld {543                    // Note that combination `-static -pie` doesn't work as expected544                    // for the gcc wrapper, `-static` in that case suppresses `-pie`.545                    self.cc_arg("-static-pie");546                } else {547                    // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing548                    // a static pie, but currently passed because gcc and clang pass them.549                    // The former suppresses the `INTERP` ELF header specifying dynamic linker,550                    // which is otherwise implicitly injected by ld (but not lld).551                    // The latter doesn't change anything, only ensures that everything is pic.552                    self.link_args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);553                }554            }555            LinkOutputKind::DynamicDylib => self.build_dylib(crate_type, out_filename),556            LinkOutputKind::StaticDylib => {557                self.link_or_cc_arg("-static");558                self.build_dylib(crate_type, out_filename);559            }560            LinkOutputKind::WasiReactorExe => {561                self.link_args(&["--entry", "_initialize"]);562            }563        }564565        // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,566        // it switches linking for libc and similar system libraries to static without using567        // any `#[link]` attributes in the `libc` crate, see #72782 for details.568        // FIXME: Switch to using `#[link]` attributes in the `libc` crate569        // similarly to other targets.570        if self.sess.target.os == Os::VxWorks571            && matches!(572                output_kind,573                LinkOutputKind::StaticNoPicExe574                    | LinkOutputKind::StaticPicExe575                    | LinkOutputKind::StaticDylib576            )577        {578            self.cc_arg("--static-crt");579        }580581        // avr-none doesn't have default ISA, users must specify which specific582        // CPU (well, microcontroller) they are targetting using `-Ctarget-cpu`.583        //584        // Currently this makes sense only when using avr-gcc as a linker, since585        // it brings a couple of hand-written important intrinsics from libgcc.586        if self.sess.target.arch == Arch::Avr && !self.uses_lld {587            self.verbatim_arg(format!("-mmcu={}", self.target_cpu));588        }589    }590591    fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool) {592        if self.sess.target.os == Os::Illumos && name == "c" {593            // libc will be added via late_link_args on illumos so that it will594            // appear last in the library search order.595            // FIXME: This should be replaced by a more complete and generic596            // mechanism for controlling the order of library arguments passed597            // to the linker.598            return;599        }600        self.hint_dynamic();601        self.with_as_needed(as_needed, |this| {602            let colon = if verbatim && this.is_gnu { ":" } else { "" };603            this.link_or_cc_arg(format!("-l{colon}{name}"));604        });605    }606607    fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) {608        self.hint_dynamic();609        self.with_as_needed(as_needed, |this| {610            this.link_or_cc_arg(path);611        })612    }613614    fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) {615        self.hint_dynamic();616        if !as_needed {617            // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework618            // flag but we have no way to detect that here.619            // self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name);620            self.sess.dcx().emit_warn(diagnostics::Ld64UnimplementedModifier);621        }622        self.link_or_cc_args(&["-framework", name]);623    }624625    fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {626        self.hint_static();627        let colon = if verbatim && self.is_gnu { ":" } else { "" };628        if !whole_archive {629            self.link_or_cc_arg(format!("-l{colon}{name}"));630        } else if self.sess.target.is_like_darwin {631            // -force_load is the macOS equivalent of --whole-archive, but it632            // involves passing the full path to the library to link.633            self.link_arg("-force_load");634            self.link_arg(find_native_static_library(name, verbatim, self.sess));635        } else {636            self.link_arg("--whole-archive")637                .link_or_cc_arg(format!("-l{colon}{name}"))638                .link_arg("--no-whole-archive");639        }640    }641642    fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {643        self.hint_static();644        if !whole_archive {645            self.link_or_cc_arg(path);646        } else if self.sess.target.is_like_darwin {647            self.link_arg("-force_load").link_arg(path);648        } else {649            self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive");650        }651    }652653    fn framework_path(&mut self, path: &Path) {654        self.link_or_cc_arg("-F").link_or_cc_arg(path);655    }656    fn full_relro(&mut self) {657        self.link_args(&["-z", "relro", "-z", "now"]);658    }659    fn partial_relro(&mut self) {660        self.link_args(&["-z", "relro"]);661    }662    fn no_relro(&mut self) {663        self.link_args(&["-z", "norelro"]);664    }665666    fn gc_sections(&mut self, keep_metadata: bool) {667        // The dead_strip option to the linker specifies that functions and data668        // unreachable by the entry point will be removed. This is quite useful669        // with Rust's compilation model of compiling libraries at a time into670        // one object file. For example, this brings hello world from 1.7MB to671        // 458K.672        //673        // Note that this is done for both executables and dynamic libraries. We674        // won't get much benefit from dylibs because LLVM will have already675        // stripped away as much as it could. This has not been seen to impact676        // link times negatively.677        //678        // -dead_strip can't be part of the pre_link_args because it's also used679        // for partial linking when using multiple codegen units (-r). So we680        // insert it here.681        if self.sess.target.is_like_darwin {682            self.link_arg("-dead_strip");683684        // If we're building a dylib, we don't use --gc-sections because LLVM685        // has already done the best it can do, and we also don't want to686        // eliminate the metadata. If we're building an executable, however,687        // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%688        // reduction.689        } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata {690            self.link_arg("--gc-sections");691        }692    }693694    fn optimize(&mut self) {695        if !self.is_gnu && !self.sess.target.is_like_wasm {696            return;697        }698699        // GNU-style linkers support optimization with -O. GNU ld doesn't700        // need a numeric argument, but other linkers do.701        if self.sess.opts.optimize == config::OptLevel::More702            || self.sess.opts.optimize == config::OptLevel::Aggressive703        {704            self.link_arg("-O1");705        }706    }707708    fn pgo_gen(&mut self) {709        if !self.is_gnu {710            return;711        }712713        // If we're doing PGO generation stuff and on a GNU-like linker, use the714        // "-u" flag to properly pull in the profiler runtime bits.715        //716        // This is because LLVM otherwise won't add the needed initialization717        // for us on Linux (though the extra flag should be harmless if it718        // does).719        //720        // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030.721        //722        // Though it may be worth to try to revert those changes upstream, since723        // the overhead of the initialization should be minor.724        self.link_or_cc_args(&["-u", "__llvm_profile_runtime"]);725    }726727    fn enable_profiling(&mut self) {728        // This flag is also used when linking to choose target specific729        // libraries needed to enable profiling.730        if !self.is_ld {731            self.cc_arg("-pg");732            // On windows-gnu targets, libgmon also needs to be linked, and this733            // requires readding libraries to satisfy its dependencies.734            if self.sess.target.is_like_windows {735                self.cc_arg("-lgmon");736                self.cc_arg("-lkernel32");737                self.cc_arg("-lmsvcrt");738            }739        }740    }741742    fn control_flow_guard(&mut self) {}743744    fn ehcont_guard(&mut self) {}745746    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {747        // MacOS linker doesn't support stripping symbols directly anymore.748        if self.sess.target.is_like_darwin {749            return;750        }751752        match strip {753            Strip::None => {}754            Strip::Debuginfo => {755                // The illumos linker does not support --strip-debug although756                // it does support --strip-all as a compatibility alias for -s.757                // The --strip-debug case is handled by running an external758                // `strip` utility as a separate step after linking.759                if !self.sess.target.is_like_solaris {760                    self.link_arg("--strip-debug");761                }762            }763            Strip::Symbols => {764                self.link_arg("--strip-all");765            }766        }767        match self.sess.opts.unstable_opts.debuginfo_compression {768            config::DebugInfoCompression::None => {}769            config::DebugInfoCompression::Zlib => {770                self.link_arg("--compress-debug-sections=zlib");771            }772            config::DebugInfoCompression::Zstd => {773                self.link_arg("--compress-debug-sections=zstd");774            }775        }776    }777778    fn no_crt_objects(&mut self) {779        if !self.is_ld {780            self.cc_arg("-nostartfiles");781        }782    }783784    fn no_default_libraries(&mut self) {785        if !self.is_ld {786            self.cc_arg("-nodefaultlibs");787        }788    }789790    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]) {791        // Symbol visibility in object files typically takes care of this.792        if crate_type == CrateType::Executable {793            let should_export_executable_symbols =794                self.sess.opts.unstable_opts.export_executable_symbols;795            if self.sess.target.override_export_symbols.is_none()796                && !should_export_executable_symbols797            {798                return;799            }800        }801802        // We manually create a list of exported symbols to ensure we don't expose any more.803        // The object files have far more public symbols than we actually want to export,804        // so we hide them all here.805806        if !self.sess.target.limit_rdylib_exports {807            return;808        }809810        let path = tmpdir.join(if self.sess.target.is_like_windows { "list.def" } else { "list" });811        debug!("EXPORTED SYMBOLS:");812813        if self.sess.target.is_like_darwin {814            // Write a plain, newline-separated list of symbols815            let res = try {816                let mut f = File::create_buffered(&path)?;817                for sym in symbols {818                    debug!("  _{}", sym.name);819                    writeln!(f, "_{}", sym.name)?;820                }821            };822            if let Err(error) = res {823                self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error });824            }825            self.link_arg("-exported_symbols_list").link_arg(path);826        } else if self.sess.target.is_like_windows {827            let res = try {828                let mut f = File::create_buffered(&path)?;829830                // .def file similar to MSVC one but without LIBRARY section831                // because LD doesn't like when it's empty832                writeln!(f, "EXPORTS")?;833                for symbol in symbols {834                    let kind_marker =835                        if symbol.kind == SymbolExportKind::Data { " DATA" } else { "" };836                    debug!("  _{}", symbol.name);837                    // Quote the name in case it's reserved by linker in some way838                    // (this accounts for names with dots in particular).839                    writeln!(f, "  \"{}\"{kind_marker}", symbol.name)?;840                }841            };842            if let Err(error) = res {843                self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error });844            }845            self.link_arg(path);846        } else if self.sess.target.is_like_wasm {847            self.link_arg("--no-export-dynamic");848            for sym in symbols {849                self.link_arg("--export").link_arg(&sym.name);850            }851        } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris {852            let res = try {853                let mut f = File::create_buffered(&path)?;854                writeln!(f, "{{")?;855                for sym in symbols {856                    debug!("{}", sym.name);857                    writeln!(f, "  {};", sym.name)?;858                }859                writeln!(f, "}};")?;860            };861            if let Err(error) = res {862                self.sess.dcx().emit_fatal(diagnostics::VersionScriptWriteFailure { error });863            }864            self.link_arg("--dynamic-list").link_arg(path);865        } else {866            // Write an LD version script867            let res = try {868                let mut f = File::create_buffered(&path)?;869                writeln!(f, "{{")?;870                if !symbols.is_empty() {871                    writeln!(f, "  global:")?;872                    for sym in symbols {873                        debug!("    {};", sym.name);874                        writeln!(f, "    {};", sym.name)?;875                    }876                }877                writeln!(f, "\n  local:\n    *;\n}};")?;878            };879            if let Err(error) = res {880                self.sess.dcx().emit_fatal(diagnostics::VersionScriptWriteFailure { error });881            }882            if self.sess.target.is_like_solaris {883                self.link_arg("-M").link_arg(path);884            } else {885                let mut arg = OsString::from("--version-script=");886                arg.push(path);887                self.link_arg(arg).link_arg("--no-undefined-version");888            }889        }890    }891892    fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind) {893        self.link_args(&["--subsystem", subsystem.as_str()]);894    }895896    fn reset_per_library_state(&mut self) {897        self.hint_dynamic(); // Reset to default before returning the composed command line.898    }899900    fn linker_plugin_lto(&mut self) {901        match self.sess.opts.cg.linker_plugin_lto {902            LinkerPluginLto::Disabled => {903                // Nothing to do904            }905            LinkerPluginLto::LinkerPluginAuto => {906                self.push_linker_plugin_lto_args(None);907            }908            LinkerPluginLto::LinkerPlugin(ref path) => {909                self.push_linker_plugin_lto_args(Some(path.as_os_str()));910            }911        }912    }913914    // Add the `GNU_EH_FRAME` program header which is required to locate unwinding information.915    // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't,916    // so we just always add it.917    fn add_eh_frame_header(&mut self) {918        self.link_arg("--eh-frame-hdr");919    }920921    fn add_no_exec(&mut self) {922        if self.sess.target.is_like_windows {923            self.link_arg("--nxcompat");924        } else if self.is_gnu {925            self.link_args(&["-z", "noexecstack"]);926        }927    }928929    fn add_as_needed(&mut self) {930        if self.is_gnu && !self.sess.target.is_like_windows {931            self.link_arg("--as-needed");932        } else if self.sess.target.is_like_solaris {933            // -z ignore is the Solaris equivalent to the GNU ld --as-needed option934            self.link_args(&["-z", "ignore"]);935        }936    }937}938939struct MsvcLinker<'a> {940    cmd: Command,941    sess: &'a Session,942}943944impl<'a> Linker for MsvcLinker<'a> {945    fn cmd(&mut self) -> &mut Command {946        &mut self.cmd947    }948949    fn set_output_kind(950        &mut self,951        output_kind: LinkOutputKind,952        _crate_type: CrateType,953        out_filename: &Path,954    ) {955        match output_kind {956            LinkOutputKind::DynamicNoPicExe957            | LinkOutputKind::DynamicPicExe958            | LinkOutputKind::StaticNoPicExe959            | LinkOutputKind::StaticPicExe => {}960            LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {961                self.link_arg("/DLL");962                let mut arg: OsString = "/IMPLIB:".into();963                arg.push(out_filename.with_extension("dll.lib"));964                self.link_arg(arg);965            }966            LinkOutputKind::WasiReactorExe => {967                panic!("can't link as reactor on non-wasi target");968            }969        }970    }971972    fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) {973        // On MSVC-like targets rustc supports import libraries using alternative naming974        // scheme (`libfoo.a`) unsupported by linker, search for such libraries manually.975        if let Some(path) = try_find_native_dynamic_library(self.sess, name, verbatim) {976            self.link_arg(path);977        } else {978            self.link_arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));979        }980    }981982    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {983        // When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export984        // any symbols, so we skip linking if the implib file is not present.985        let implib_path = path.with_extension("dll.lib");986        if implib_path.exists() {987            self.link_or_cc_arg(implib_path);988        }989    }990991    fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {992        // On MSVC-like targets rustc supports static libraries using alternative naming993        // scheme (`libfoo.a`) unsupported by linker, search for such libraries manually.994        if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) {995            self.link_staticlib_by_path(&path, whole_archive);996        } else {997            let opts = if whole_archive { "/WHOLEARCHIVE:" } else { "" };998            let (prefix, suffix) = self.sess.staticlib_components(verbatim);999            self.link_arg(format!("{opts}{prefix}{name}{suffix}"));1000        }1001    }10021003    fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {1004        if !whole_archive {1005            self.link_arg(path);1006        } else {1007            let mut arg = OsString::from("/WHOLEARCHIVE:");1008            arg.push(path);1009            self.link_arg(arg);1010        }1011    }10121013    fn gc_sections(&mut self, _keep_metadata: bool) {1014        // MSVC's ICF (Identical COMDAT Folding) link optimization is1015        // slow for Rust and thus we disable it by default when not in1016        // optimization build.1017        if self.sess.opts.optimize != config::OptLevel::No {1018            self.link_arg("/OPT:REF,ICF");1019        } else {1020            // It is necessary to specify NOICF here, because /OPT:REF1021            // implies ICF by default.1022            self.link_arg("/OPT:REF,NOICF");1023        }1024    }10251026    fn full_relro(&mut self) {1027        // noop1028    }10291030    fn partial_relro(&mut self) {1031        // noop1032    }10331034    fn no_relro(&mut self) {1035        // noop1036    }10371038    fn no_crt_objects(&mut self) {1039        // noop1040    }10411042    fn no_default_libraries(&mut self) {1043        self.link_arg("/NODEFAULTLIB");1044    }10451046    fn include_path(&mut self, path: &Path) {1047        let mut arg = OsString::from("/LIBPATH:");1048        arg.push(path);1049        self.link_arg(&arg);1050    }10511052    fn output_filename(&mut self, path: &Path) {1053        let mut arg = OsString::from("/OUT:");1054        arg.push(path);1055        self.link_arg(&arg);1056    }10571058    fn optimize(&mut self) {1059        // Needs more investigation of `/OPT` arguments1060    }10611062    fn pgo_gen(&mut self) {1063        // Nothing needed here.1064    }10651066    fn control_flow_guard(&mut self) {1067        self.link_arg("/guard:cf");1068    }10691070    fn ehcont_guard(&mut self) {1071        if self.sess.target.pointer_width == 64 {1072            self.link_arg("/guard:ehcont");1073        }1074    }10751076    fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {1077        // This will cause the Microsoft linker to generate a PDB file1078        // from the CodeView line tables in the object files.1079        self.link_arg("/DEBUG");10801081        // Default to emitting only the file name of the PDB file into1082        // the binary instead of the full path. Emitting the full path1083        // may leak private information (such as user names).1084        // See https://github.com/rust-lang/rust/issues/87825.1085        //1086        // This default behavior can be overridden by explicitly passing1087        // `-Clink-arg=/PDBALTPATH:...` to rustc.1088        self.link_arg("/PDBALTPATH:%_PDB%");10891090        // This will cause the Microsoft linker to embed .natvis info into the PDB file1091        let natvis_dir_path = self.sess.opts.sysroot.path().join("lib\\rustlib\\etc");1092        if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {1093            for entry in natvis_dir {1094                match entry {1095                    Ok(entry) => {1096                        let path = entry.path();1097                        if path.extension() == Some("natvis".as_ref()) {1098                            let mut arg = OsString::from("/NATVIS:");1099                            arg.push(path);1100                            self.link_arg(arg);1101                        }1102                    }1103                    Err(error) => {1104                        self.sess.dcx().emit_warn(diagnostics::NoNatvisDirectory { error });1105                    }1106                }1107            }1108        }11091110        // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file1111        for path in natvis_debugger_visualizers {1112            let mut arg = OsString::from("/NATVIS:");1113            arg.push(path);1114            self.link_arg(arg);1115        }1116    }11171118    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, _symbols: &[SymbolExport]) {1119        // We already add /EXPORT arguments to the .drectve section of symbols.o.1120        // Keep passing an empty .def file: link.exe otherwise skips the import1121        // library for DLLs with no exports.1122        if crate_type == CrateType::Executable {1123            let should_export_executable_symbols =1124                self.sess.opts.unstable_opts.export_executable_symbols;1125            if !should_export_executable_symbols {1126                return;1127            }1128        }11291130        let path = tmpdir.join("lib.def");1131        let res = try {1132            let mut f = File::create_buffered(&path)?;1133            writeln!(f, "LIBRARY")?;1134            writeln!(f, "EXPORTS")?;1135        };1136        if let Err(error) = res {1137            self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error });1138        }1139        let mut arg = OsString::from("/DEF:");1140        arg.push(path);1141        self.link_arg(&arg);1142    }11431144    fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind) {1145        let subsystem = subsystem.as_str();1146        self.link_arg(&format!("/SUBSYSTEM:{subsystem}"));11471148        // Windows has two subsystems we're interested in right now, the console1149        // and windows subsystems. These both implicitly have different entry1150        // points (starting symbols). The console entry point starts with1151        // `mainCRTStartup` and the windows entry point starts with1152        // `WinMainCRTStartup`. These entry points, defined in system libraries,1153        // will then later probe for either `main` or `WinMain`, respectively to1154        // start the application.1155        //1156        // In Rust we just always generate a `main` function so we want control1157        // to always start there, so we force the entry point on the windows1158        // subsystem to be `mainCRTStartup` to get everything booted up1159        // correctly.1160        //1161        // For more information see RFC #16651162        if subsystem == "windows" {1163            self.link_arg("/ENTRY:mainCRTStartup");1164        }1165    }11661167    fn linker_plugin_lto(&mut self) {1168        // Do nothing1169    }11701171    fn add_no_exec(&mut self) {1172        self.link_arg("/NXCOMPAT");1173    }1174}11751176struct EmLinker<'a> {1177    cmd: Command,1178    sess: &'a Session,1179}11801181impl<'a> Linker for EmLinker<'a> {1182    fn cmd(&mut self) -> &mut Command {1183        &mut self.cmd1184    }11851186    fn is_cc(&self) -> bool {1187        true1188    }11891190    fn set_output_kind(1191        &mut self,1192        output_kind: LinkOutputKind,1193        _crate_type: CrateType,1194        _out_filename: &Path,1195    ) {1196        match output_kind {1197            LinkOutputKind::DynamicNoPicExe | LinkOutputKind::DynamicPicExe => {1198                self.cmd.arg("-sMAIN_MODULE=2");1199            }1200            LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {1201                self.cmd.arg("-sSIDE_MODULE=2");1202            }1203            // -fno-pie is the default on Emscripten.1204            LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {}1205            LinkOutputKind::WasiReactorExe => {1206                unreachable!();1207            }1208        }1209    }12101211    fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {1212        // Emscripten always links statically1213        self.link_or_cc_args(&["-l", name]);1214    }12151216    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {1217        self.link_or_cc_arg(path);1218    }12191220    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {1221        self.link_or_cc_args(&["-l", name]);1222    }12231224    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {1225        self.link_or_cc_arg(path);1226    }12271228    fn full_relro(&mut self) {1229        // noop1230    }12311232    fn partial_relro(&mut self) {1233        // noop1234    }12351236    fn no_relro(&mut self) {1237        // noop1238    }12391240    fn gc_sections(&mut self, _keep_metadata: bool) {1241        // noop1242    }12431244    fn optimize(&mut self) {1245        // Emscripten performs own optimizations1246        self.cc_arg(match self.sess.opts.optimize {1247            OptLevel::No => "-O0",1248            OptLevel::Less => "-O1",1249            OptLevel::More => "-O2",1250            OptLevel::Aggressive => "-O3",1251            OptLevel::Size => "-Os",1252            OptLevel::SizeMin => "-Oz",1253        });1254    }12551256    fn pgo_gen(&mut self) {1257        // noop, but maybe we need something like the gnu linker?1258    }12591260    fn control_flow_guard(&mut self) {}12611262    fn ehcont_guard(&mut self) {}12631264    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {1265        // Preserve names or generate source maps depending on debug info1266        // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g1267        self.cc_arg(match self.sess.opts.debuginfo {1268            DebugInfo::None => "-g0",1269            DebugInfo::Limited | DebugInfo::LineTablesOnly | DebugInfo::LineDirectivesOnly => {1270                "--profiling-funcs"1271            }1272            DebugInfo::Full => "-g",1273        });1274    }12751276    fn no_crt_objects(&mut self) {}12771278    fn no_default_libraries(&mut self) {1279        self.cc_arg("-nodefaultlibs");1280    }12811282    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) {1283        debug!("EXPORTED SYMBOLS:");12841285        self.cc_arg("-s");12861287        // Emscripten exposes the program entry point under the JS name `_main`1288        // regardless of the underlying wasm symbol (which is `__main_argc_argv`1289        // per the wasm C ABI in the tool-conventions BasicCABI spec), bridging1290        // the two internally. So the entry symbol must be requested as `_main`1291        // here rather than as a `_`-prefixed form of its wasm name.1292        let entry_name = self.sess.target.entry_name.as_ref();1293        let mut arg = OsString::from("EXPORTED_FUNCTIONS=");1294        let encoded = serde_json::to_string(1295            &symbols1296                .iter()1297                .map(|sym| {1298                    if sym.name == entry_name {1299                        "_main".to_owned()1300                    } else {1301                        "_".to_owned() + &sym.name1302                    }1303                })1304                .collect::<Vec<_>>(),1305        )1306        .unwrap();1307        debug!("{encoded}");13081309        arg.push(encoded);13101311        self.cc_arg(arg);1312    }13131314    fn windows_subsystem(&mut self, _subsystem: WindowsSubsystemKind) {1315        // noop1316    }13171318    fn linker_plugin_lto(&mut self) {1319        // Do nothing1320    }1321}13221323struct WasmLd<'a> {1324    cmd: Command,1325    sess: &'a Session,1326}13271328impl<'a> WasmLd<'a> {1329    fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {1330        WasmLd { cmd, sess }1331    }1332}13331334impl<'a> Linker for WasmLd<'a> {1335    fn cmd(&mut self) -> &mut Command {1336        &mut self.cmd1337    }13381339    fn set_output_kind(1340        &mut self,1341        output_kind: LinkOutputKind,1342        _crate_type: CrateType,1343        _out_filename: &Path,1344    ) {1345        match output_kind {1346            LinkOutputKind::DynamicNoPicExe1347            | LinkOutputKind::DynamicPicExe1348            | LinkOutputKind::StaticNoPicExe1349            | LinkOutputKind::StaticPicExe => {}1350            LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {1351                self.link_arg("--no-entry");1352            }1353            LinkOutputKind::WasiReactorExe => {1354                self.link_args(&["--entry", "_initialize"]);1355            }1356        }1357    }13581359    fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {1360        self.link_or_cc_args(&["-l", name]);1361    }13621363    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {1364        self.link_or_cc_arg(path);1365    }13661367    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {1368        if !whole_archive {1369            self.link_or_cc_args(&["-l", name]);1370        } else {1371            self.link_arg("--whole-archive")1372                .link_or_cc_args(&["-l", name])1373                .link_arg("--no-whole-archive");1374        }1375    }13761377    fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {1378        if !whole_archive {1379            self.link_or_cc_arg(path);1380        } else {1381            self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive");1382        }1383    }13841385    fn full_relro(&mut self) {}13861387    fn partial_relro(&mut self) {}13881389    fn no_relro(&mut self) {}13901391    fn gc_sections(&mut self, _keep_metadata: bool) {1392        self.link_arg("--gc-sections");1393    }13941395    fn optimize(&mut self) {1396        // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and1397        // only differentiates -O0 and -O1. It does not apply to LTO.1398        self.link_arg(match self.sess.opts.optimize {1399            OptLevel::No => "-O0",1400            OptLevel::Less => "-O1",1401            OptLevel::More => "-O2",1402            OptLevel::Aggressive => "-O3",1403            // Currently LLD doesn't support `Os` and `Oz`, so pass through `O2`1404            // instead.1405            OptLevel::Size => "-O2",1406            OptLevel::SizeMin => "-O2",1407        });1408    }14091410    fn pgo_gen(&mut self) {}14111412    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {1413        match strip {1414            Strip::None => {}1415            Strip::Debuginfo => {1416                self.link_arg("--strip-debug");1417            }1418            Strip::Symbols => {1419                self.link_arg("--strip-all");1420            }1421        }1422    }14231424    fn control_flow_guard(&mut self) {}14251426    fn ehcont_guard(&mut self) {}14271428    fn no_crt_objects(&mut self) {}14291430    fn no_default_libraries(&mut self) {}14311432    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) {1433        for sym in symbols {1434            self.link_args(&["--export", &sym.name]);1435        }1436    }14371438    fn windows_subsystem(&mut self, _subsystem: WindowsSubsystemKind) {}14391440    fn linker_plugin_lto(&mut self) {1441        match self.sess.opts.cg.linker_plugin_lto {1442            LinkerPluginLto::Disabled => {1443                // Nothing to do1444            }1445            LinkerPluginLto::LinkerPluginAuto => {1446                self.push_linker_plugin_lto_args();1447            }1448            LinkerPluginLto::LinkerPlugin(_) => {1449                self.push_linker_plugin_lto_args();1450            }1451        }1452    }1453}14541455impl<'a> WasmLd<'a> {1456    fn push_linker_plugin_lto_args(&mut self) {1457        let opt_level = match self.sess.opts.optimize {1458            config::OptLevel::No => "O0",1459            config::OptLevel::Less => "O1",1460            config::OptLevel::More => "O2",1461            config::OptLevel::Aggressive => "O3",1462            // wasm-ld only handles integer LTO opt levels. Use O21463            config::OptLevel::Size | config::OptLevel::SizeMin => "O2",1464        };1465        self.link_arg(&format!("--lto-{opt_level}"));1466    }1467}14681469/// Linker for AIX.1470struct AixLinker<'a> {1471    cmd: Command,1472    sess: &'a Session,1473    hinted_static: Option<bool>,1474}14751476impl<'a> AixLinker<'a> {1477    fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> {1478        AixLinker { cmd, sess, hinted_static: None }1479    }14801481    fn hint_static(&mut self) {1482        if self.hinted_static != Some(true) {1483            self.link_arg("-bstatic");1484            self.hinted_static = Some(true);1485        }1486    }14871488    fn hint_dynamic(&mut self) {1489        if self.hinted_static != Some(false) {1490            self.link_arg("-bdynamic");1491            self.hinted_static = Some(false);1492        }1493    }14941495    fn build_dylib(&mut self, _out_filename: &Path) {1496        self.link_args(&["-bM:SRE", "-bnoentry"]);1497        // FIXME: Use CreateExportList utility to create export list1498        // and remove -bexpfull.1499        self.link_arg("-bexpfull");1500    }1501}15021503impl<'a> Linker for AixLinker<'a> {1504    fn cmd(&mut self) -> &mut Command {1505        &mut self.cmd1506    }15071508    fn set_output_kind(1509        &mut self,1510        output_kind: LinkOutputKind,1511        _crate_type: CrateType,1512        out_filename: &Path,1513    ) {1514        match output_kind {1515            LinkOutputKind::DynamicDylib => {1516                self.hint_dynamic();1517                self.build_dylib(out_filename);1518            }1519            LinkOutputKind::StaticDylib => {1520                self.hint_static();1521                self.build_dylib(out_filename);1522            }1523            _ => {}1524        }1525    }15261527    fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) {1528        self.hint_dynamic();1529        self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") });1530    }15311532    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {1533        self.hint_dynamic();1534        self.link_or_cc_arg(path);1535    }15361537    fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {1538        self.hint_static();1539        if !whole_archive {1540            self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") });1541        } else {1542            let mut arg = OsString::from("-bkeepfile:");1543            arg.push(find_native_static_library(name, verbatim, self.sess));1544            self.link_or_cc_arg(arg);1545        }1546    }15471548    fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {1549        self.hint_static();1550        if !whole_archive {1551            self.link_or_cc_arg(path);1552        } else {1553            let mut arg = OsString::from("-bkeepfile:");1554            arg.push(path);1555            self.link_arg(arg);1556        }1557    }15581559    fn full_relro(&mut self) {}15601561    fn partial_relro(&mut self) {}15621563    fn no_relro(&mut self) {}15641565    fn gc_sections(&mut self, _keep_metadata: bool) {1566        self.link_arg("-bgc");1567    }15681569    fn optimize(&mut self) {}15701571    fn pgo_gen(&mut self) {1572        self.link_arg("-bdbg:namedsects:ss");1573        self.link_arg("-u");1574        self.link_arg("__llvm_profile_runtime");1575    }15761577    fn control_flow_guard(&mut self) {}15781579    fn ehcont_guard(&mut self) {}15801581    fn debuginfo(&mut self, _: Strip, _: &[PathBuf]) {}15821583    fn no_crt_objects(&mut self) {}15841585    fn no_default_libraries(&mut self) {}15861587    fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) {1588        let path = tmpdir.join("list.exp");1589        let res = try {1590            let mut f = File::create_buffered(&path)?;1591            // FIXME: use llvm-nm to generate export list.1592            for symbol in symbols {1593                debug!("  _{}", symbol.name);1594                writeln!(f, "  {}", symbol.name)?;1595            }1596        };1597        if let Err(e) = res {1598            self.sess.dcx().fatal(format!("failed to write export file: {e}"));1599        }1600        self.link_arg(format!("-bE:{}", path.to_str().unwrap()));1601    }16021603    fn windows_subsystem(&mut self, _subsystem: WindowsSubsystemKind) {}16041605    fn reset_per_library_state(&mut self) {1606        self.hint_dynamic();1607    }16081609    fn linker_plugin_lto(&mut self) {}16101611    fn add_eh_frame_header(&mut self) {}16121613    fn add_no_exec(&mut self) {}16141615    fn add_as_needed(&mut self) {}1616}16171618fn for_each_exported_symbols_include_dep<'tcx>(1619    tcx: TyCtxt<'tcx>,1620    crate_type: CrateType,1621    mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum),1622) {1623    let formats = tcx.dependency_formats(());1624    let deps = &formats[&crate_type];16251626    for (cnum, dep_format) in deps.iter_enumerated() {1627        // For each dependency that we are linking to statically ...1628        if *dep_format == Linkage::Static {1629            for &(symbol, info) in tcx.exported_non_generic_symbols(cnum).iter() {1630                callback(symbol, info, cnum);1631            }1632            for &(symbol, info) in tcx.exported_generic_symbols(cnum).iter() {1633                callback(symbol, info, cnum);1634            }1635        }1636    }1637}16381639fn symbol_export_from_exported_symbol<'tcx>(1640    tcx: TyCtxt<'tcx>,1641    symbol: ExportedSymbol<'tcx>,1642    kind: SymbolExportKind,1643    cnum: CrateNum,1644) -> SymbolExport {1645    let name = symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum);1646    let link_name =1647        symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, cnum);1648    SymbolExport::with_link_name(name, kind, link_name)1649}16501651fn symbol_export_from_raw_name(1652    tcx: TyCtxt<'_>,1653    name: String,1654    kind: SymbolExportKind,1655) -> SymbolExport {1656    let symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &name));1657    let link_name =1658        symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, LOCAL_CRATE);1659    SymbolExport::with_link_name(name, kind, link_name)1660}16611662pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<SymbolExport> {1663    if let Some(ref exports) = tcx.sess.target.override_export_symbols {1664        return exports1665            .iter()1666            .map(|name| {1667                symbol_export_from_raw_name(1668                    tcx,1669                    name.to_string(),1670                    // FIXME use the correct export kind for this symbol. override_export_symbols1671                    // can't directly specify the SymbolExportKind as it is defined in rustc_middle1672                    // which rustc_target can't depend on.1673                    SymbolExportKind::Text,1674                )1675            })1676            .collect();1677    }16781679    let mut symbols = if let CrateType::ProcMacro = crate_type {1680        exported_symbols_for_proc_macro_crate(tcx)1681    } else {1682        exported_symbols_for_non_proc_macro(tcx, crate_type)1683    };16841685    // Preserve the metadata symbol to ensure the metadata section doesn't get removed by the1686    // linker. On wasm however the metadata is put in a custom section, to which symbols can't1687    // refer, so there is no metadata symbol there. Luckily custom sections are always preserved by1688    // the linker.1689    if (crate_type == CrateType::Dylib || crate_type == CrateType::ProcMacro)1690        && !tcx.sess.target.is_like_wasm1691    {1692        let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);1693        symbols.push(symbol_export_from_raw_name(1694            tcx,1695            metadata_symbol_name,1696            SymbolExportKind::Data,1697        ));1698    }16991700    symbols1701}17021703fn exported_symbols_for_non_proc_macro(1704    tcx: TyCtxt<'_>,1705    crate_type: CrateType,1706) -> Vec<SymbolExport> {1707    let mut symbols = Vec::new();1708    let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);1709    for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {1710        // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins1711        // from any dylib. The latter doesn't work anyway as we use hidden visibility for1712        // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.1713        if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {1714            symbols.push(symbol_export_from_exported_symbol(tcx, symbol, info.kind, cnum));1715            symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);1716        }1717    });17181719    // Mark allocator shim symbols as exported only if they were generated.1720    if export_threshold == SymbolExportLevel::Rust1721        && needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type)1722        && let Some(kind) = tcx.allocator_kind(())1723    {1724        symbols.extend(1725            allocator_shim_symbols(tcx, kind)1726                .map(|(name, kind)| symbol_export_from_raw_name(tcx, name, kind)),1727        );1728    }17291730    symbols1731}17321733fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<SymbolExport> {1734    // `exported_symbols` will be empty when !should_codegen.1735    if !tcx.sess.opts.output_types.should_codegen() {1736        return Vec::new();1737    }17381739    let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);1740    let proc_macro_decls_name = rustc_session::generate_proc_macro_decls_symbol(stable_crate_id);17411742    vec![symbol_export_from_raw_name(tcx, proc_macro_decls_name, SymbolExportKind::Data)]1743}17441745pub(crate) fn linked_symbols(1746    tcx: TyCtxt<'_>,1747    crate_type: CrateType,1748) -> Vec<(String, SymbolExportKind)> {1749    match crate_type {1750        CrateType::Executable1751        | CrateType::ProcMacro1752        | CrateType::Cdylib1753        | CrateType::Dylib1754        | CrateType::Sdylib => (),1755        CrateType::StaticLib | CrateType::Rlib => {1756            // These are not linked, so no need to generate symbols.o for them.1757            return Vec::new();1758        }1759    }17601761    match tcx.sess.lto() {1762        Lto::No | Lto::ThinLocal => {}1763        Lto::Thin | Lto::Fat => {1764            // We really only need symbols from upstream rlibs to end up in the linked symbols list.1765            // The rest are in separate object files which the linker will always link in and1766            // doesn't have rules around the order in which they need to appear.1767            // When doing LTO, some of the symbols in the linked symbols list happen to be1768            // internalized by LTO, which then prevents referencing them from symbols.o. When doing1769            // LTO, all object files that get linked in will be local object files rather than1770            // pulled in from rlibs, so an empty linked symbols list works fine to avoid referencing1771            // all those internalized symbols from symbols.o.1772            return Vec::new();1773        }1774    }17751776    let mut symbols = Vec::new();17771778    let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);1779    for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {1780        if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum)1781            || info.used1782            || info.rustc_std_internal_symbol1783        {1784            symbols.push((1785                symbol_export::linking_symbol_name_for_instance_in_crate(1786                    tcx, symbol, info.kind, cnum,1787                ),1788                info.kind,1789            ));1790        }1791    });17921793    symbols1794}17951796/// The `self-contained` LLVM bitcode linker1797struct LlbcLinker<'a> {1798    cmd: Command,1799    sess: &'a Session,1800}18011802impl<'a> Linker for LlbcLinker<'a> {1803    fn cmd(&mut self) -> &mut Command {1804        &mut self.cmd1805    }18061807    fn set_output_kind(1808        &mut self,1809        _output_kind: LinkOutputKind,1810        _crate_type: CrateType,1811        _out_filename: &Path,1812    ) {1813    }18141815    fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {1816        panic!("staticlibs not supported")1817    }18181819    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {1820        self.link_or_cc_arg(path);1821    }18221823    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {1824        match strip {1825            Strip::None => {1826                self.link_arg("--debug");1827            }1828            Strip::Debuginfo | Strip::Symbols => {}1829        }1830    }18311832    fn optimize(&mut self) {1833        self.link_arg(match self.sess.opts.optimize {1834            OptLevel::No => "-O0",1835            OptLevel::Less => "-O1",1836            OptLevel::More => "-O2",1837            OptLevel::Aggressive => "-O3",1838            OptLevel::Size => "-Os",1839            OptLevel::SizeMin => "-Oz",1840        });1841    }18421843    fn full_relro(&mut self) {}18441845    fn partial_relro(&mut self) {}18461847    fn no_relro(&mut self) {}18481849    fn gc_sections(&mut self, _keep_metadata: bool) {}18501851    fn pgo_gen(&mut self) {}18521853    fn no_crt_objects(&mut self) {}18541855    fn no_default_libraries(&mut self) {}18561857    fn control_flow_guard(&mut self) {}18581859    fn ehcont_guard(&mut self) {}18601861    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) {1862        match _crate_type {1863            CrateType::Cdylib => {1864                for sym in symbols {1865                    self.link_args(&["--export-symbol", &sym.name]);1866                }1867            }1868            _ => (),1869        }1870    }18711872    fn windows_subsystem(&mut self, _subsystem: WindowsSubsystemKind) {}18731874    fn linker_plugin_lto(&mut self) {}1875}18761877struct BpfLinker<'a> {1878    cmd: Command,1879    sess: &'a Session,1880}18811882impl<'a> Linker for BpfLinker<'a> {1883    fn cmd(&mut self) -> &mut Command {1884        &mut self.cmd1885    }18861887    fn set_output_kind(1888        &mut self,1889        _output_kind: LinkOutputKind,1890        _crate_type: CrateType,1891        _out_filename: &Path,1892    ) {1893    }18941895    fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {1896        self.sess.dcx().emit_fatal(diagnostics::BpfStaticlibNotSupported)1897    }18981899    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {1900        self.link_or_cc_arg(path);1901    }19021903    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {1904        self.link_arg("--debug");1905    }19061907    fn optimize(&mut self) {1908        self.link_arg(match self.sess.opts.optimize {1909            OptLevel::No => "-O0",1910            OptLevel::Less => "-O1",1911            OptLevel::More => "-O2",1912            OptLevel::Aggressive => "-O3",1913            OptLevel::Size => "-Os",1914            OptLevel::SizeMin => "-Oz",1915        });1916    }19171918    fn full_relro(&mut self) {}19191920    fn partial_relro(&mut self) {}19211922    fn no_relro(&mut self) {}19231924    fn gc_sections(&mut self, _keep_metadata: bool) {}19251926    fn pgo_gen(&mut self) {}19271928    fn no_crt_objects(&mut self) {}19291930    fn no_default_libraries(&mut self) {}19311932    fn control_flow_guard(&mut self) {}19331934    fn ehcont_guard(&mut self) {}19351936    fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) {1937        let path = tmpdir.join("symbols");1938        let res = try {1939            let mut f = File::create_buffered(&path)?;1940            for sym in symbols {1941                writeln!(f, "{}", sym.name)?;1942            }1943        };1944        if let Err(error) = res {1945            self.sess.dcx().emit_fatal(diagnostics::SymbolFileWriteFailure { error });1946        } else {1947            self.link_arg("--export-symbols").link_arg(&path);1948        }1949    }19501951    fn windows_subsystem(&mut self, _subsystem: WindowsSubsystemKind) {}19521953    fn linker_plugin_lto(&mut self) {}1954}

Code quality findings 21

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
cmd.env("PATH", env::join_paths(new_path).unwrap());
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
rpath.push(out_filename.file_name().unwrap());
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.link_arg(format!("-bE:{}", path.to_str().unwrap()));
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let deps = &formats[&crate_type];
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use std::io::prelude::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let mut cmd = match linker.to_str() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
_ => match flavor {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let arch = match t.arch {
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
arg.push(path);
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
arg.push(path);
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match output_kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match _crate_type {

Get this view in your editor

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