about summary refs log tree commit diff
path: root/src/librustc_codegen_ssa/back/link.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_codegen_ssa/back/link.rs')
-rw-r--r--src/librustc_codegen_ssa/back/link.rs196
1 files changed, 135 insertions, 61 deletions
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index ce158fb07da..aa4b51f1acb 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -11,7 +11,9 @@ use rustc_session::search_paths::PathKind;
 /// need out of the shared crate context before we get rid of it.
 use rustc_session::{filesearch, Session};
 use rustc_span::symbol::Symbol;
-use rustc_target::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel};
+use rustc_target::spec::crt_objects::CrtObjectsFallback;
+use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
+use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel};
 
 use super::archive::ArchiveBuilder;
 use super::command::Command;
@@ -609,6 +611,55 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
                 .note(&format!("{:?}", &cmd))
                 .note(&escape_string(&output))
                 .emit();
+
+                // If MSVC's `link.exe` was expected but the return code
+                // is not a Microsoft LNK error then suggest a way to fix or
+                // install the Visual Studio build tools.
+                if let Some(code) = prog.status.code() {
+                    if sess.target.target.options.is_like_msvc
+                        && flavor == LinkerFlavor::Msvc
+                        // Respect the command line override
+                        && sess.opts.cg.linker.is_none()
+                        // Match exactly "link.exe"
+                        && linker_path.to_str() == Some("link.exe")
+                        // All Microsoft `link.exe` linking error codes are
+                        // four digit numbers in the range 1000 to 9999 inclusive
+                        && (code < 1000 || code > 9999)
+                    {
+                        let is_vs_installed = windows_registry::find_vs_version().is_ok();
+                        let has_linker = windows_registry::find_tool(
+                            &sess.opts.target_triple.triple(),
+                            "link.exe",
+                        )
+                        .is_some();
+
+                        sess.note_without_error("`link.exe` returned an unexpected error");
+                        if is_vs_installed && has_linker {
+                            // the linker is broken
+                            sess.note_without_error(
+                                "the Visual Studio build tools may need to be repaired \
+                                using the Visual Studio installer",
+                            );
+                            sess.note_without_error(
+                                "or a necessary component may be missing from the \
+                                \"C++ build tools\" workload",
+                            );
+                        } else if is_vs_installed {
+                            // the linker is not installed
+                            sess.note_without_error(
+                                "in the Visual Studio installer, ensure the \
+                                \"C++ build tools\" workload is selected",
+                            );
+                        } else {
+                            // visual studio is not installed
+                            sess.note_without_error(
+                                "you may need to install Visual Studio build tools with the \
+                                \"C++ build tools\" workload",
+                            );
+                        }
+                    }
+                }
+
                 sess.abort_if_errors();
             }
             info!("linker stderr:\n{}", escape_string(&prog.stderr));
@@ -1130,33 +1181,70 @@ fn exec_linker(
     }
 }
 
-/// Add begin object files defined by the target spec.
-fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) {
-    let pre_link_objects = if crate_type == CrateType::Executable {
-        &sess.target.target.options.pre_link_objects_exe
-    } else {
-        &sess.target.target.options.pre_link_objects_dll
+fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
+    let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) {
+        (CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe,
+        (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe,
+        (CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe,
+        (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe,
+        (_, true, _) => LinkOutputKind::StaticDylib,
+        (_, false, _) => LinkOutputKind::DynamicDylib,
     };
-    for obj in pre_link_objects {
-        cmd.add_object(&get_object_file_path(sess, obj));
+
+    // Adjust the output kind to target capabilities.
+    let pic_exe_supported = sess.target.target.options.position_independent_executables;
+    let static_pic_exe_supported = false; // FIXME: Add this option to target specs.
+    let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs;
+    match kind {
+        LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe,
+        LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe,
+        LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib,
+        _ => kind,
     }
+}
 
-    if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) {
-        for obj in &sess.target.target.options.pre_link_objects_exe_crt {
-            cmd.add_object(&get_object_file_path(sess, obj));
-        }
+/// Whether we link to our own CRT objects instead of relying on gcc to pull them.
+/// We only provide such support for a very limited number of targets.
+fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
+    match sess.target.target.options.crt_objects_fallback {
+        // FIXME: Find a better heuristic for "native musl toolchain is available",
+        // based on host and linker path, for example.
+        // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
+        Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)),
+        // FIXME: Find some heuristic for "native mingw toolchain is available",
+        // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429).
+        Some(CrtObjectsFallback::Mingw) => sess.target.target.target_vendor != "uwp",
+        // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
+        Some(CrtObjectsFallback::Wasm) => true,
+        None => false,
     }
 }
 
-/// Add end object files defined by the target spec.
-fn add_post_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) {
-    for obj in &sess.target.target.options.post_link_objects {
+/// Add pre-link object files defined by the target spec.
+fn add_pre_link_objects(
+    cmd: &mut dyn Linker,
+    sess: &Session,
+    link_output_kind: LinkOutputKind,
+    fallback: bool,
+) {
+    let opts = &sess.target.target.options;
+    let objects = if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects };
+    for obj in objects.get(&link_output_kind).iter().copied().flatten() {
         cmd.add_object(&get_object_file_path(sess, obj));
     }
-    if sess.crt_static(Some(crate_type)) {
-        for obj in &sess.target.target.options.post_link_objects_crt {
-            cmd.add_object(&get_object_file_path(sess, obj));
-        }
+}
+
+/// Add post-link object files defined by the target spec.
+fn add_post_link_objects(
+    cmd: &mut dyn Linker,
+    sess: &Session,
+    link_output_kind: LinkOutputKind,
+    fallback: bool,
+) {
+    let opts = &sess.target.target.options;
+    let objects = if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects };
+    for obj in objects.get(&link_output_kind).iter().copied().flatten() {
+        cmd.add_object(&get_object_file_path(sess, obj));
     }
 }
 
@@ -1342,38 +1430,6 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) {
     cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
 }
 
-/// Add options requesting executables to be position-independent or not position-independent.
-fn add_position_independent_executable_args(
-    cmd: &mut dyn Linker,
-    sess: &Session,
-    flavor: LinkerFlavor,
-    crate_type: CrateType,
-    codegen_results: &CodegenResults,
-) {
-    if crate_type != CrateType::Executable {
-        return;
-    }
-
-    if sess.target.target.options.position_independent_executables {
-        let attr_link_args = &*codegen_results.crate_info.link_args;
-        let mut user_defined_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args);
-        if sess.relocation_model() == RelocModel::Pic
-            && !sess.crt_static(Some(crate_type))
-            && !user_defined_link_args.any(|x| x == "-static")
-        {
-            cmd.position_independent_executable();
-            return;
-        }
-    }
-
-    // Recent versions of gcc can be configured to generate position
-    // independent executables by default. We have to pass -no-pie to
-    // explicitly turn that off. Not applicable to ld.
-    if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld {
-        cmd.no_position_independent_executable();
-    }
-}
-
 /// Add options making relocation sections in the produced ELF files read-only
 /// and suppressing lazy binding.
 fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) {
@@ -1439,6 +1495,8 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // to the linker args construction.
     assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp");
     let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu);
+    let link_output_kind = link_output_kind(sess, crate_type);
+    let crt_objects_fallback = crt_objects_fallback(sess, crate_type);
 
     // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
     add_pre_link_args(cmd, sess, flavor, crate_type);
@@ -1455,8 +1513,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
         cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
     }
 
+    // NO-OPT-OUT, OBJECT-FILES-NO
+    if crt_objects_fallback {
+        cmd.no_crt_objects();
+    }
+
     // NO-OPT-OUT, OBJECT-FILES-YES
-    add_pre_link_objects(cmd, sess, crate_type);
+    add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
 
     // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
     if sess.target.target.options.is_like_emscripten {
@@ -1515,7 +1578,16 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     }
 
     // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
-    add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results);
+    // FIXME: Support `StaticPicExe` correctly.
+    match link_output_kind {
+        LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => {
+            cmd.position_independent_executable()
+        }
+        LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => {
+            cmd.no_position_independent_executable()
+        }
+        _ => {}
+    }
 
     // OBJECT-FILES-NO, AUDIT-ORDER
     add_relro_args(cmd, sess);
@@ -1545,12 +1617,14 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     );
 
     // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
-    // Tell the linker what we're doing.
-    if crate_type != CrateType::Executable {
-        cmd.build_dylib(out_filename);
-    }
-    if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) {
-        cmd.build_static_executable();
+    // FIXME: Merge with the previous `link_output_kind` match,
+    // and support `StaticPicExe` and `StaticDylib` correctly.
+    match link_output_kind {
+        LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {
+            cmd.build_static_executable()
+        }
+        LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename),
+        _ => {}
     }
 
     // OBJECT-FILES-NO, AUDIT-ORDER
@@ -1576,7 +1650,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);
 
     // NO-OPT-OUT, OBJECT-FILES-YES
-    add_post_link_objects(cmd, sess, crate_type);
+    add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
 
     // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
     add_post_link_args(cmd, sess, flavor);