diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 49786bc3b068d..20e64f0c48851 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -154,7 +154,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( // The third parameter is for env vars, used on windows to set up the // path for MSVC to find its DLLs, and gcc to find its bundled // toolchain -pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) { +fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Command { let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); // If our linker looks like a batch script on Windows then to execute this @@ -193,6 +193,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB _ => None, }; if let Some(ref a) = arch { + // FIXME: Move this to `fn linker_with_args`. let mut arg = OsString::from("/LIBPATH:"); arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a.to_string())); cmd.arg(&arg); @@ -232,7 +233,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB } cmd.env("PATH", env::join_paths(new_path).unwrap()); - (linker.to_path_buf(), cmd) + cmd } pub fn each_linked_rlib( @@ -284,11 +285,7 @@ pub fn each_linked_rlib( /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a /// directory being searched for `extern crate` (observing an incomplete file). /// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata<'a>( - sess: &'a Session, - metadata: &EncodedMetadata, - tmpdir: &TempDir, -) -> PathBuf { +pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &TempDir) -> PathBuf { let out_filename = tmpdir.path().join(METADATA_FILENAME); let result = fs::write(&out_filename, &metadata.raw_data); @@ -487,95 +484,18 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( target_cpu: &str, ) { info!("preparing {:?} to {:?}", crate_type, out_filename); - let (linker, flavor) = linker_and_flavor(sess); - - let any_dynamic_crate = crate_type == config::CrateType::Dylib - || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { - *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) - }); - - // The invocations of cc share some flags across platforms - let (pname, mut cmd) = get_linker(sess, &linker, flavor); - - if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { - cmd.args(args); - } - if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { - if sess.crt_static(Some(crate_type)) { - cmd.args(args); - } - } - cmd.args(&sess.opts.debugging_opts.pre_link_args); - - if sess.target.target.options.is_like_fuchsia { - let prefix = match sess.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => "asan/", - _ => "", - }; - cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); - } - - let pre_link_objects = if crate_type == config::CrateType::Executable { - &sess.target.target.options.pre_link_objects_exe - } else { - &sess.target.target.options.pre_link_objects_dll - }; - for obj in pre_link_objects { - cmd.arg(get_file_path(sess, obj)); - } - - if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.arg(get_file_path(sess, obj)); - } - } - - if sess.target.target.options.is_like_emscripten { - cmd.arg("-s"); - cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" - } else { - "DISABLE_EXCEPTION_CATCHING=0" - }); - } + let (linker_path, flavor) = linker_and_flavor(sess); + let mut cmd = linker_with_args::( + &linker_path, + flavor, + sess, + crate_type, + tmpdir, + out_filename, + codegen_results, + target_cpu, + ); - { - let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu); - link_sanitizer_runtime(sess, crate_type, &mut *linker); - link_args::( - &mut *linker, - flavor, - sess, - crate_type, - tmpdir, - out_filename, - codegen_results, - ); - cmd = linker.finalize(); - } - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { - cmd.args(args); - } - if any_dynamic_crate { - if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) { - cmd.args(args); - } - } else { - if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) { - cmd.args(args); - } - } - for obj in &sess.target.target.options.post_link_objects { - cmd.arg(get_file_path(sess, obj)); - } - if sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.arg(get_file_path(sess, obj)); - } - } - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { - cmd.args(args); - } for &(ref k, ref v) in &sess.target.target.options.link_env { cmd.env(k, v); } @@ -597,7 +517,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( let mut i = 0; loop { i += 1; - prog = sess.time("run_linker", || exec_linker(sess, &mut cmd, out_filename, tmpdir)); + prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir)); let output = match prog { Ok(ref output) => output, Err(_) => break, @@ -698,7 +618,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( output.extend_from_slice(&prog.stdout); sess.struct_err(&format!( "linking with `{}` failed: {}", - pname.display(), + linker_path.display(), prog.status )) .note(&format!("{:?}", &cmd)) @@ -714,9 +634,12 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( let mut linker_error = { if linker_not_found { - sess.struct_err(&format!("linker `{}` not found", pname.display())) + sess.struct_err(&format!("linker `{}` not found", linker_path.display())) } else { - sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) + sess.struct_err(&format!( + "could not exec the linker `{}`", + linker_path.display() + )) } }; @@ -787,7 +710,7 @@ fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: // PR #41352 for details). let libname = format!("rustc{}_rt.{}", channel, name); let rpath = default_tlib.to_str().expect("non-utf8 component in path"); - linker.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); + linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); linker.link_dylib(Symbol::intern(&libname)); } "x86_64-unknown-linux-gnu" | "x86_64-fuchsia" | "aarch64-fuchsia" => { @@ -817,7 +740,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) } -pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { +fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { fn infer_from( sess: &Session, linker: Option, @@ -905,7 +828,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { /// Returns a boolean indicating whether we should preserve the object files on /// the filesystem for their debug information. This is often useful with /// split-dwarf like schemes. -pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { +fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // If the objects don't have debuginfo there's nothing to preserve. if sess.opts.debuginfo == config::DebugInfo::None { return false; @@ -959,7 +882,7 @@ enum RlibFlavor { StaticlibBase, } -pub fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { +fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { let lib_args: Vec<_> = all_native_libs .iter() .filter(|l| relevant_lib(sess, l)) @@ -1061,7 +984,7 @@ fn get_crt_libs_path(sess: &Session) -> Option { } } -pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { +fn get_object_file_path(sess: &Session, name: &str) -> PathBuf { // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details if sess.target.target.llvm_target.contains("windows-gnu") { if let Some(compiler_libs_path) = get_crt_libs_path(sess) { @@ -1085,9 +1008,9 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { PathBuf::from(name) } -pub fn exec_linker( +fn exec_linker( sess: &Session, - cmd: &mut Command, + cmd: &Command, out_filename: &Path, tmpdir: &Path, ) -> io::Result { @@ -1233,134 +1156,142 @@ pub fn exec_linker( } } -fn link_args<'a, B: ArchiveBuilder<'a>>( +/// Add begin object files defined by the target spec. +fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: config::CrateType) { + let pre_link_objects = if crate_type == config::CrateType::Executable { + &sess.target.target.options.pre_link_objects_exe + } else { + &sess.target.target.options.pre_link_objects_dll + }; + for obj in pre_link_objects { + cmd.add_object(&get_object_file_path(sess, obj)); + } + + if crate_type == config::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)); + } + } +} + +/// Add end object files defined by the target spec. +fn add_post_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: config::CrateType) { + for obj in &sess.target.target.options.post_link_objects { + 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 arbitrary "pre-link" args defined by the target spec or from command line. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_pre_link_args( cmd: &mut dyn Linker, + sess: &Session, flavor: LinkerFlavor, - sess: &'a Session, crate_type: config::CrateType, - tmpdir: &Path, - out_filename: &Path, - codegen_results: &CodegenResults, ) { - // Linker plugins should be specified early in the list of arguments - cmd.linker_plugin_lto(); - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + cmd.args(args); + } + if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { + if sess.crt_static(Some(crate_type)) { + cmd.args(args); + } + } + cmd.args(&sess.opts.debugging_opts.pre_link_args); +} - // target descriptor - let t = &sess.target.target; +/// Add arbitrary "user defined" args defined from command line and by `#[link_args]` attributes. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_user_defined_link_args( + cmd: &mut dyn Linker, + sess: &Session, + codegen_results: &CodegenResults, +) { + cmd.args(&sess.opts.cg.link_args); + cmd.args(&*codegen_results.crate_info.link_args); +} - // prefer system mingw-w64 libs, see get_crt_libs_path comment for more details - if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - cmd.include_path(&compiler_libs_path); +/// Add arbitrary "late link" args defined by the target spec. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_late_link_args( + cmd: &mut dyn Linker, + sess: &Session, + flavor: LinkerFlavor, + crate_type: config::CrateType, + codegen_results: &CodegenResults, +) { + if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + cmd.args(args); + } + let any_dynamic_crate = crate_type == config::CrateType::Dylib + || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { + *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) + }); + if any_dynamic_crate { + if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) { + cmd.args(args); + } + } else { + if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) { + cmd.args(args); } } +} - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); +/// Add arbitrary "post-link" args defined by the target spec. +/// FIXME: Determine where exactly these args need to be inserted. +fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + cmd.args(args); + } +} +/// Add object files containing code from the current crate. +fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { cmd.add_object(obj); } - cmd.output_filename(out_filename); +} - if crate_type == config::CrateType::Executable && sess.target.target.options.is_like_windows { - if let Some(ref s) = codegen_results.windows_subsystem { - cmd.subsystem(s); - } +/// Add object files for allocator code linked once for the whole crate tree. +fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { + if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { + cmd.add_object(obj); } +} - // If we're building something like a dynamic library then some platforms - // need to make sure that all symbols are exported correctly from the - // dynamic library. - cmd.export_symbols(tmpdir, crate_type); - +/// Add object files containing metadata for the current crate. +fn add_local_crate_metadata_objects( + cmd: &mut dyn Linker, + crate_type: config::CrateType, + codegen_results: &CodegenResults, +) { // When linking a dynamic library, we put the metadata into a section of the // executable. This metadata is in a separate object file from the main // object file, so we link that in here. if crate_type == config::CrateType::Dylib || crate_type == config::CrateType::ProcMacro { - let obj = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { + if let Some(obj) = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref()) + { cmd.add_object(obj); } } +} - let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); - } - - // Try to strip as much out of the generated object by removing unused - // sections if possible. See more comments in linker.rs - if !sess.opts.cg.link_dead_code { - let keep_metadata = crate_type == config::CrateType::Dylib; - cmd.gc_sections(keep_metadata); - } - - let attr_link_args = codegen_results.crate_info.link_args.iter(); - let user_link_args: Vec<_> = - sess.opts.cg.link_args.iter().chain(attr_link_args).cloned().collect(); - - if crate_type == config::CrateType::Executable { - let mut position_independent_executable = false; - - if t.options.position_independent_executables { - if is_pic(sess) - && !sess.crt_static(Some(crate_type)) - && !user_link_args.iter().any(|x| x == "-static") - { - position_independent_executable = true; - } - } - - if position_independent_executable { - cmd.position_independent_executable(); - } else { - // 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(); - } - } - } - - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => t.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - } - RelroLevel::Partial => { - cmd.partial_relro(); - } - RelroLevel::Off => { - cmd.no_relro(); - } - RelroLevel::None => {} - } - - // Pass optimization flags down to the linker. - cmd.optimize(); - - // Pass debuginfo flags down to the linker. - cmd.debuginfo(); - - // We want to, by default, prevent the compiler from accidentally leaking in - // any system libraries, so we may explicitly ask linkers to not link to any - // libraries by default. Note that this does not happen for windows because - // windows pulls in some large number of libraries and I couldn't quite - // figure out which subset we wanted. - // - // This is all naturally configurable via the standard methods as well. - if !sess.opts.cg.default_linker_libraries.unwrap_or(false) && t.options.no_default_libraries { - cmd.no_default_libraries(); - } - +/// Link native libraries corresponding to the current crate and all libraries corresponding to +/// all its dependency crates. +/// FIXME: Consider combining this with the functions above adding object files for the local crate. +fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'a>>( + cmd: &mut dyn Linker, + sess: &'a Session, + crate_type: config::CrateType, + codegen_results: &CodegenResults, + tmpdir: &Path, +) { // Take careful note of the ordering of the arguments we pass to the linker // here. Linkers will assume that things on the left depend on things to the // right. Things on the right cannot depend on things on the left. This is @@ -1398,22 +1329,73 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) { add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); } - // Tell the linker what we're doing. +} + +/// Add sysroot and other globally set directories to the directory search list. +fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) { + // Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details. + if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { + if let Some(compiler_libs_path) = get_crt_libs_path(sess) { + cmd.include_path(&compiler_libs_path); + } + } + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + 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: config::CrateType, + codegen_results: &CodegenResults, +) { if crate_type != config::CrateType::Executable { - cmd.build_dylib(out_filename); + return; } - if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) { - cmd.build_static_executable(); + + 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 is_pic(sess) + && !sess.crt_static(Some(crate_type)) + && !user_defined_link_args.any(|x| x == "-static") + { + cmd.position_independent_executable(); + return; + } } - if sess.opts.cg.profile_generate.enabled() { - cmd.pgo_gen(); + // 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(); } +} - if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { - cmd.control_flow_guard(); +/// 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) { + match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.target.options.relro_level) { + RelroLevel::Full => cmd.full_relro(), + RelroLevel::Partial => cmd.partial_relro(), + RelroLevel::Off => cmd.no_relro(), + RelroLevel::None => {} } +} +/// Add library search paths used at runtime by dynamic linkers. +fn add_rpath_args( + cmd: &mut dyn Linker, + sess: &Session, + codegen_results: &CodegenResults, + out_filename: &Path, +) { // FIXME (#2397): At some point we want to rpath our guesses as to // where extern libraries might live, based on the // addl_lib_search_paths @@ -1437,10 +1419,177 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( }; cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } +} + +/// Produce the linker command line containing linker path and arguments. +/// `NO-OPT-OUT` marks the arguments that cannot be removed from the command line +/// by the user without creating a custom target specification. +/// `OBJECT-FILES` specify whether the arguments can add object files. +/// `CUSTOMIZATION-POINT` means that arbitrary arguments defined by the user +/// or by the target spec can be inserted here. +/// `AUDIT-ORDER` - need to figure out whether the option is order-dependent or not. +fn linker_with_args<'a, B: ArchiveBuilder<'a>>( + path: &Path, + flavor: LinkerFlavor, + sess: &'a Session, + crate_type: config::CrateType, + tmpdir: &Path, + out_filename: &Path, + codegen_results: &CodegenResults, + target_cpu: &str, +) -> Command { + let base_cmd = get_linker(sess, path, flavor); + // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction + // 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); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_pre_link_args(cmd, sess, flavor, crate_type); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + if sess.target.target.options.is_like_fuchsia { + let prefix = match sess.opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => "asan/", + _ => "", + }; + cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); + } + + // NO-OPT-OUT, OBJECT-FILES-YES + add_pre_link_objects(cmd, sess, crate_type); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + if sess.target.target.options.is_like_emscripten { + cmd.arg("-s"); + cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + "DISABLE_EXCEPTION_CATCHING=1" + } else { + "DISABLE_EXCEPTION_CATCHING=0" + }); + } + + // OBJECT-FILES-YES, AUDIT-ORDER + link_sanitizer_runtime(sess, crate_type, cmd); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Linker plugins should be specified early in the list of arguments + // FIXME: How "early" exactly? + cmd.linker_plugin_lto(); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Order-dependent, at least relatively to other args adding searh directories. + add_library_search_dirs(cmd, sess); + + // OBJECT-FILES-YES + add_local_crate_regular_objects(cmd, codegen_results); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + cmd.output_filename(out_filename); + + // OBJECT-FILES-NO, AUDIT-ORDER + if crate_type == config::CrateType::Executable && sess.target.target.options.is_like_windows { + if let Some(ref s) = codegen_results.windows_subsystem { + cmd.subsystem(s); + } + } + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // If we're building something like a dynamic library then some platforms + // need to make sure that all symbols are exported correctly from the + // dynamic library. + cmd.export_symbols(tmpdir, crate_type); + + // OBJECT-FILES-YES + add_local_crate_metadata_objects(cmd, crate_type, codegen_results); + + // OBJECT-FILES-YES + add_local_crate_allocator_objects(cmd, codegen_results); + + // OBJECT-FILES-NO, AUDIT-ORDER + // FIXME: Order dependent, applies to the following objects. Where should it be placed? + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + if !sess.opts.cg.link_dead_code { + let keep_metadata = crate_type == config::CrateType::Dylib; + cmd.gc_sections(keep_metadata); + } + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results); + + // OBJECT-FILES-NO, AUDIT-ORDER + add_relro_args(cmd, sess); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Pass optimization flags down to the linker. + cmd.optimize(); + + // OBJECT-FILES-NO, AUDIT-ORDER + // Pass debuginfo flags down to the linker. + cmd.debuginfo(); + + // OBJECT-FILES-NO, AUDIT-ORDER + // We want to, by default, prevent the compiler from accidentally leaking in + // any system libraries, so we may explicitly ask linkers to not link to any + // libraries by default. Note that this does not happen for windows because + // windows pulls in some large number of libraries and I couldn't quite + // figure out which subset we wanted. + // + // This is all naturally configurable via the standard methods as well. + if !sess.opts.cg.default_linker_libraries.unwrap_or(false) + && sess.target.target.options.no_default_libraries + { + cmd.no_default_libraries(); + } + + // OBJECT-FILES-YES + link_local_crate_native_libs_and_dependent_crate_libs::( + cmd, + sess, + crate_type, + codegen_results, + tmpdir, + ); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // Tell the linker what we're doing. + if crate_type != config::CrateType::Executable { + cmd.build_dylib(out_filename); + } + if crate_type == config::CrateType::Executable && sess.crt_static(Some(crate_type)) { + cmd.build_static_executable(); + } + + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.cg.profile_generate.enabled() { + cmd.pgo_gen(); + } + + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { + cmd.control_flow_guard(); + } + + // OBJECT-FILES-NO, AUDIT-ORDER + add_rpath_args(cmd, sess, codegen_results, out_filename); + + // OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_user_defined_link_args(cmd, sess, codegen_results); + + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + cmd.finalize(); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + 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); + + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT + add_post_link_args(cmd, sess, flavor); - // Finally add all the linker arguments provided on the command line along - // with any #[link_args] attributes found inside the crate - cmd.args(&user_link_args); + cmd.take_cmd() } // # Native library linking @@ -1454,7 +1603,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // Also note that the native libraries linked here are only the ones located // in the current crate. Upstream crates with native library dependencies // may have their native library pulled in above. -pub fn add_local_native_libraries( +fn add_local_native_libraries( cmd: &mut dyn Linker, sess: &Session, codegen_results: &CodegenResults, @@ -1784,7 +1933,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // generic function calls a native function, then the generic function must // be instantiated in the target crate, meaning that the native symbol must // also be resolved in the target crate. -pub fn add_upstream_native_libraries( +fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, codegen_results: &CodegenResults, @@ -1841,14 +1990,14 @@ pub fn add_upstream_native_libraries( } } -pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { +fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { match lib.cfg { Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None), None => true, } } -pub fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { +fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { match sess.lto() { config::Lto::Fat => true, config::Lto::Thin => { diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index c0c533524b098..0baa37ae9f1ab 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -6,6 +6,7 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufWriter}; +use std::mem; use std::path::{Path, PathBuf}; use rustc_data_structures::fx::FxHashMap; @@ -87,6 +88,7 @@ impl LinkerInfo { /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an /// MSVC linker (e.g., `link.exe`) is being used. pub trait Linker { + fn cmd(&mut self) -> &mut Command; fn link_dylib(&mut self, lib: Symbol); fn link_rust_dylib(&mut self, lib: Symbol, path: &Path); fn link_framework(&mut self, framework: Symbol); @@ -111,14 +113,26 @@ pub trait Linker { fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); fn build_static_executable(&mut self); - fn args(&mut self, args: &[String]); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); fn subsystem(&mut self, subsystem: &str); fn group_start(&mut self); fn group_end(&mut self); fn linker_plugin_lto(&mut self); - // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). - fn finalize(&mut self) -> Command; + fn finalize(&mut self); +} + +impl dyn Linker + '_ { + pub fn arg(&mut self, arg: impl AsRef) { + self.cmd().arg(arg); + } + + pub fn args(&mut self, args: impl IntoIterator>) { + self.cmd().args(args); + } + + pub fn take_cmd(&mut self) -> Command { + mem::replace(self.cmd(), Command::new("")) + } } pub struct GccLinker<'a> { @@ -208,6 +222,9 @@ impl<'a> GccLinker<'a> { } impl<'a> Linker for GccLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn link_dylib(&mut self, lib: Symbol) { self.hint_dynamic(); self.cmd.arg(format!("-l{}", lib)); @@ -251,9 +268,6 @@ impl<'a> Linker for GccLinker<'a> { fn build_static_executable(&mut self) { self.cmd.arg("-static"); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.hint_dynamic(); @@ -505,10 +519,8 @@ impl<'a> Linker for GccLinker<'a> { self.linker_arg(&subsystem); } - fn finalize(&mut self) -> Command { + fn finalize(&mut self) { self.hint_dynamic(); // Reset to default before returning the composed command line. - - ::std::mem::replace(&mut self.cmd, Command::new("")) } fn group_start(&mut self) { @@ -545,15 +557,15 @@ pub struct MsvcLinker<'a> { } impl<'a> Linker for MsvcLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } fn build_dylib(&mut self, out_filename: &Path) { self.cmd.arg("/DLL"); @@ -758,9 +770,7 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // MSVC doesn't need group indicators fn group_start(&mut self) {} @@ -778,6 +788,9 @@ pub struct EmLinker<'a> { } impl<'a> Linker for EmLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } @@ -837,10 +850,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn framework_path(&mut self, _path: &Path) { bug!("frameworks are not supported on Emscripten") } @@ -928,9 +937,7 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // Appears not necessary on Emscripten fn group_start(&mut self) {} @@ -992,6 +999,10 @@ impl<'a> WasmLd<'a> { } impl<'a> Linker for WasmLd<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + fn link_dylib(&mut self, lib: Symbol) { self.cmd.arg("-l").sym_arg(lib); } @@ -1030,10 +1041,6 @@ impl<'a> Linker for WasmLd<'a> { fn build_static_executable(&mut self) {} - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.cmd.arg("-l").sym_arg(lib); } @@ -1098,9 +1105,7 @@ impl<'a> Linker for WasmLd<'a> { fn no_position_independent_executable(&mut self) {} - fn finalize(&mut self) -> Command { - ::std::mem::replace(&mut self.cmd, Command::new("")) - } + fn finalize(&mut self) {} // Not needed for now with LLD fn group_start(&mut self) {} @@ -1162,6 +1167,10 @@ pub struct PtxLinker<'a> { } impl<'a> Linker for PtxLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + fn link_rlib(&mut self, path: &Path) { self.cmd.arg("--rlib").arg(path); } @@ -1182,10 +1191,6 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("--bitcode").arg(path); } - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - fn optimize(&mut self) { match self.sess.lto() { Lto::Thin | Lto::Fat | Lto::ThinLocal => { @@ -1200,14 +1205,12 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("-o").arg(path); } - fn finalize(&mut self) -> Command { + fn finalize(&mut self) { // Provide the linker with fallback to internal `target-cpu`. self.cmd.arg("--fallback-arch").arg(match self.sess.opts.cg.target_cpu { Some(ref s) => s, None => &self.sess.target.target.options.cpu, }); - - ::std::mem::replace(&mut self.cmd, Command::new("")) } fn link_dylib(&mut self, _lib: Symbol) {