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}