From ce25dabc66d4b7905dba3bf63ad766d9d6f421ab Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 5 Apr 2020 02:11:29 +0300 Subject: [PATCH 1/7] linker: Make argument building interface in `trait Linker` richer by redirecting everything to `Command` --- src/librustc_codegen_ssa/back/link.rs | 9 +++-- src/librustc_codegen_ssa/back/linker.rs | 47 +++++++++++++++---------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 49786bc3b068d..9ac318fbe94d8 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -787,7 +787,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" => { @@ -1300,8 +1300,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( } 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(); + let user_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args); if crate_type == config::CrateType::Executable { let mut position_independent_executable = false; @@ -1309,7 +1308,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( if t.options.position_independent_executables { if is_pic(sess) && !sess.crt_static(Some(crate_type)) - && !user_link_args.iter().any(|x| x == "-static") + && !user_link_args.clone().any(|x| x == "-static") { position_independent_executable = true; } @@ -1440,7 +1439,7 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // 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.args(user_link_args); } // # Native library linking diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index c0c533524b098..da3e805698f97 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -87,6 +87,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,7 +112,6 @@ 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); @@ -121,6 +121,16 @@ pub trait Linker { fn finalize(&mut self) -> Command; } +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 struct GccLinker<'a> { cmd: Command, sess: &'a Session, @@ -208,6 +218,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 +264,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(); @@ -545,15 +555,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"); @@ -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") } @@ -992,6 +1001,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 +1043,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); } @@ -1162,6 +1171,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 +1195,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 => { From 032462e06f7ef393bac06a76a62fe9ad3f4290b7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 5 Apr 2020 02:58:32 +0300 Subject: [PATCH 2/7] linker: Combine argument building into a single function --- src/librustc_codegen_ssa/back/link.rs | 205 +++++++++++++----------- src/librustc_codegen_ssa/back/linker.rs | 28 ++-- 2 files changed, 118 insertions(+), 115 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9ac318fbe94d8..0c418b7e3d708 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) { +pub 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 @@ -232,7 +232,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( @@ -487,95 +487,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 +520,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 +621,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 +637,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() + )) } }; @@ -1087,7 +1013,7 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { pub fn exec_linker( sess: &Session, - cmd: &mut Command, + cmd: &Command, out_filename: &Path, tmpdir: &Path, ) -> io::Result { @@ -1233,15 +1159,66 @@ pub fn exec_linker( } } -fn link_args<'a, B: ArchiveBuilder<'a>>( - cmd: &mut dyn Linker, +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); + + 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" + }); + } + + link_sanitizer_runtime(sess, crate_type, cmd); + // Linker plugins should be specified early in the list of arguments cmd.linker_plugin_lto(); @@ -1440,6 +1417,38 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // 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.finalize(); + + 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); + } + } + 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); + } + + cmd.take_cmd() } // # Native library linking diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index da3e805698f97..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; @@ -117,8 +118,7 @@ pub trait Linker { 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 + '_ { @@ -129,6 +129,10 @@ impl dyn Linker + '_ { 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> { @@ -515,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) { @@ -768,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) {} @@ -937,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) {} @@ -1107,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) {} @@ -1209,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) { From 927db7d3224646f7946d38de37cdbc388b925024 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 5 Apr 2020 17:03:30 +0300 Subject: [PATCH 3/7] linker: Factor out linking of pre- and post-link objects --- src/librustc_codegen_ssa/back/link.rs | 60 +++++++++++++++++---------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 0c418b7e3d708..7f2020f98024d 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -987,7 +987,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) { @@ -1159,6 +1159,36 @@ pub fn exec_linker( } } +/// Add begin object files defined by the target spec. +fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &'a 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: &'a 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)); + } + } +} + fn linker_with_args<'a, B: ArchiveBuilder<'a>>( path: &Path, flavor: LinkerFlavor, @@ -1193,20 +1223,8 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( 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)); - } - } + // NO-OPT-OUT + add_pre_link_objects(cmd, sess, crate_type); if sess.target.target.options.is_like_emscripten { cmd.arg("-s"); @@ -1436,14 +1454,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( 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)); - } - } + + // NO-OPT-OUT + add_post_link_objects(cmd, sess, crate_type); + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { cmd.args(args); } From 7f42d81ea489c53a93f4d2b6e59b8ddeb4ee749a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 5 Apr 2020 17:31:29 +0300 Subject: [PATCH 4/7] linker: Factor out addition of pre-, post- and late link args --- src/librustc_codegen_ssa/back/link.rs | 117 ++++++++++++++++++-------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 7f2020f98024d..39355baebee7e 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1189,6 +1189,74 @@ fn add_post_link_objects(cmd: &mut dyn Linker, sess: &'a Session, crate_type: co } } +/// 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: &'a Session, + flavor: LinkerFlavor, + crate_type: config::CrateType, +) { + 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); +} + +/// 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: &'a Session, + codegen_results: &CodegenResults, +) { + cmd.args(&sess.opts.cg.link_args); + cmd.args(&*codegen_results.crate_info.link_args); +} + +/// 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: &'a 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); + } + } +} + +/// 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: &'a Session, flavor: LinkerFlavor) { + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + cmd.args(args); + } +} + +/// 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. fn linker_with_args<'a, B: ArchiveBuilder<'a>>( path: &Path, flavor: LinkerFlavor, @@ -1205,15 +1273,8 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( 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); - 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); + // NO-OPT-OUT + add_pre_link_args(cmd, sess, flavor, crate_type); if sess.target.target.options.is_like_fuchsia { let prefix = match sess.opts.debugging_opts.sanitizer { @@ -1294,16 +1355,19 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.gc_sections(keep_metadata); } - let attr_link_args = codegen_results.crate_info.link_args.iter(); - let user_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args); - 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.clone().any(|x| x == "-static") + && !sess + .opts + .cg + .link_args + .iter() + .chain(&*codegen_results.crate_info.link_args) + .any(|x| x == "-static") { position_independent_executable = true; } @@ -1432,35 +1496,18 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } - // 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); + add_user_defined_link_args(cmd, sess, codegen_results); cmd.finalize(); - 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); - } - } + // NO-OPT-OUT + add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); // NO-OPT-OUT add_post_link_objects(cmd, sess, crate_type); - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { - cmd.args(args); - } + // NO-OPT-OUT + add_post_link_args(cmd, sess, flavor); cmd.take_cmd() } From fd6fa686df4ec661048e39c14c9295a61a19e447 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 5 Apr 2020 18:10:55 +0300 Subject: [PATCH 5/7] linker: Add more markup and comments to code producing linker arguments --- src/librustc_codegen_ssa/back/link.rs | 84 +++++++++++++++++++-------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 39355baebee7e..556a7b85cca70 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -193,6 +193,7 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> Comman _ => 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); @@ -1254,9 +1255,29 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &'a Session, flavor: LinkerFla } } +/// Add sysroot and other globally set directories to the directory search list. +fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a Session) { + // 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(); + + // 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); + } + } + + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); +} + /// 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, @@ -1273,9 +1294,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( 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 + // 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/", @@ -1284,9 +1306,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); } - // NO-OPT-OUT + // 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 { @@ -1296,43 +1319,40 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( }); } + // 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(); - // 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(); - - // target descriptor - let t = &sess.target.target; - - // 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); - } - } - - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + // 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 for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { cmd.add_object(obj); } + + // 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 // 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. @@ -1343,11 +1363,14 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } } + // OBJECT-FILES-YES let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); if let Some(obj) = obj { cmd.add_object(obj); } + // 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 { @@ -1355,10 +1378,11 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.gc_sections(keep_metadata); } + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER if crate_type == config::CrateType::Executable { let mut position_independent_executable = false; - if t.options.position_independent_executables { + if sess.target.target.options.position_independent_executables { if is_pic(sess) && !sess.crt_static(Some(crate_type)) && !sess @@ -1385,9 +1409,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } } + // OBJECT-FILES-NO, AUDIT-ORDER let relro_level = match sess.opts.debugging_opts.relro_level { Some(level) => level, - None => t.options.relro_level, + None => sess.target.target.options.relro_level, }; match relro_level { RelroLevel::Full => { @@ -1402,12 +1427,15 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( RelroLevel::None => {} } + // 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 @@ -1415,10 +1443,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // 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 { + if !sess.opts.cg.default_linker_libraries.unwrap_or(false) + && sess.target.target.options.no_default_libraries + { cmd.no_default_libraries(); } + // OBJECT-FILES-YES // 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 @@ -1456,6 +1487,8 @@ fn linker_with_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); } + + // 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); @@ -1464,14 +1497,17 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( 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 // 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 @@ -1496,17 +1532,19 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } + // 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 + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); - // NO-OPT-OUT + // NO-OPT-OUT, OBJECT-FILES-YES add_post_link_objects(cmd, sess, crate_type); - // NO-OPT-OUT + // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_post_link_args(cmd, sess, flavor); cmd.take_cmd() From 379c255eb926e4bb577121c84437ab3934aa4475 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 6 Apr 2020 01:33:03 +0300 Subject: [PATCH 6/7] linker: Factor out more parts of `linker_with_args` and add some docs --- src/librustc_codegen_ssa/back/link.rs | 304 ++++++++++++++++---------- 1 file changed, 183 insertions(+), 121 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 556a7b85cca70..9a434b39b9a14 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1255,6 +1255,87 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &'a Session, flavor: LinkerFla } } +/// 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); + } +} + +/// 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) { + let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + cmd.add_object(obj); + } +} + +/// 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 { + cmd.add_object(obj); + } + } +} + +/// 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 + // all formally implemented in terms of resolving symbols (libs on the right + // resolve unknown symbols of libs on the left, but not vice versa). + // + // For this reason, we have organized the arguments we pass to the linker as + // such: + // + // 1. The local object that LLVM just generated + // 2. Local native libraries + // 3. Upstream rust libraries + // 4. Upstream native libraries + // + // The rationale behind this ordering is that those items lower down in the + // list can't depend on items higher up in the list. For example nothing can + // depend on what we just generated (e.g., that'd be a circular dependency). + // Upstream rust libraries are not allowed to depend on our local native + // libraries as that would violate the structure of the DAG, in that + // scenario they are required to link to them as well in a shared fashion. + // + // Note that upstream rust libraries may contain native dependencies as + // well, but they also can't depend on what we just started to add to the + // link line. And finally upstream native libraries can't depend on anything + // in this DAG so far because they're only dylibs and dylibs can only depend + // on other dylibs (e.g., other native deps). + // + // If -Zlink-native-libraries=false is set, then the assumption is that an + // external build system already has the native dependencies defined, and it + // will provide them to the linker itself. + if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) { + add_local_native_libraries(cmd, sess, codegen_results); + } + add_upstream_rust_crates::(cmd, sess, codegen_results, crate_type, tmpdir); + if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) { + add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); + } +} + /// Add sysroot and other globally set directories to the directory search list. fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a Session) { // The default library location, we need this to find the runtime. @@ -1271,6 +1352,95 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a 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: &'a Session, + flavor: LinkerFlavor, + crate_type: config::CrateType, + codegen_results: &CodegenResults, +) { + if crate_type != config::CrateType::Executable { + return; + } + + let mut position_independent_executable = false; + 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") + { + 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(); + } + } +} + +/// 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: &'a Session) { + let relro_level = match sess.opts.debugging_opts.relro_level { + Some(level) => level, + None => sess.target.target.options.relro_level, + }; + match 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: &'a 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 + if sess.opts.cg.rpath { + let target_triple = sess.opts.target_triple.triple(); + let mut get_install_prefix_lib_path = || { + let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); + let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple); + let mut path = PathBuf::from(install_prefix); + path.push(&tlib); + + path + }; + let mut rpath_config = RPathConfig { + used_crates: &codegen_results.crate_info.used_crates_dynamic, + out_filename: out_filename.to_path_buf(), + has_rpath: sess.target.target.options.has_rpath, + is_like_osx: sess.target.target.options.is_like_osx, + linker_is_gnu: sess.target.target.options.linker_is_gnu, + get_install_prefix_lib_path: &mut get_install_prefix_lib_path, + }; + 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. @@ -1332,9 +1502,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_library_search_dirs(cmd, sess); // OBJECT-FILES-YES - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); - } + add_local_crate_regular_objects(cmd, codegen_results); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER cmd.output_filename(out_filename); @@ -1353,21 +1521,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.export_symbols(tmpdir, crate_type); // OBJECT-FILES-YES - // 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 { - cmd.add_object(obj); - } - } + add_local_crate_metadata_objects(cmd, crate_type, codegen_results); // OBJECT-FILES-YES - let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); - } + 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? @@ -1379,53 +1536,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - if crate_type == config::CrateType::Executable { - let mut position_independent_executable = false; - - if sess.target.target.options.position_independent_executables { - if is_pic(sess) - && !sess.crt_static(Some(crate_type)) - && !sess - .opts - .cg - .link_args - .iter() - .chain(&*codegen_results.crate_info.link_args) - .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(); - } - } - } + add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results); // OBJECT-FILES-NO, AUDIT-ORDER - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => sess.target.target.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - } - RelroLevel::Partial => { - cmd.partial_relro(); - } - RelroLevel::Off => { - cmd.no_relro(); - } - RelroLevel::None => {} - } + add_relro_args(cmd, sess); // OBJECT-FILES-NO, AUDIT-ORDER // Pass optimization flags down to the linker. @@ -1450,43 +1564,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // OBJECT-FILES-YES - // 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 - // all formally implemented in terms of resolving symbols (libs on the right - // resolve unknown symbols of libs on the left, but not vice versa). - // - // For this reason, we have organized the arguments we pass to the linker as - // such: - // - // 1. The local object that LLVM just generated - // 2. Local native libraries - // 3. Upstream rust libraries - // 4. Upstream native libraries - // - // The rationale behind this ordering is that those items lower down in the - // list can't depend on items higher up in the list. For example nothing can - // depend on what we just generated (e.g., that'd be a circular dependency). - // Upstream rust libraries are not allowed to depend on our local native - // libraries as that would violate the structure of the DAG, in that - // scenario they are required to link to them as well in a shared fashion. - // - // Note that upstream rust libraries may contain native dependencies as - // well, but they also can't depend on what we just started to add to the - // link line. And finally upstream native libraries can't depend on anything - // in this DAG so far because they're only dylibs and dylibs can only depend - // on other dylibs (e.g., other native deps). - // - // If -Zlink-native-libraries=false is set, then the assumption is that an - // external build system already has the native dependencies defined, and it - // will provide them to the linker itself. - if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) { - add_local_native_libraries(cmd, sess, codegen_results); - } - add_upstream_rust_crates::(cmd, sess, codegen_results, crate_type, tmpdir); - if sess.opts.debugging_opts.link_native_libraries.unwrap_or(true) { - add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); - } + 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. @@ -1508,29 +1592,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // OBJECT-FILES-NO, AUDIT-ORDER - // 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 - if sess.opts.cg.rpath { - let target_triple = sess.opts.target_triple.triple(); - let mut get_install_prefix_lib_path = || { - let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); - let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple); - let mut path = PathBuf::from(install_prefix); - path.push(&tlib); - - path - }; - let mut rpath_config = RPathConfig { - used_crates: &codegen_results.crate_info.used_crates_dynamic, - out_filename: out_filename.to_path_buf(), - has_rpath: sess.target.target.options.has_rpath, - is_like_osx: sess.target.target.options.is_like_osx, - linker_is_gnu: sess.target.target.options.linker_is_gnu, - get_install_prefix_lib_path: &mut get_install_prefix_lib_path, - }; - cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); - } + add_rpath_args(cmd, sess, codegen_results, out_filename); // OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_user_defined_link_args(cmd, sess, codegen_results); From 5a4fa4554f26b1d34c4b336f4609edc65704643e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 7 Apr 2020 01:48:30 +0300 Subject: [PATCH 7/7] linker: Some minor code cleanup --- src/librustc_codegen_ssa/back/link.rs | 96 +++++++++++---------------- 1 file changed, 38 insertions(+), 58 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9a434b39b9a14..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) -> 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 @@ -285,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); @@ -744,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, @@ -832,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; @@ -886,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)) @@ -1012,7 +1008,7 @@ fn get_object_file_path(sess: &Session, name: &str) -> PathBuf { PathBuf::from(name) } -pub fn exec_linker( +fn exec_linker( sess: &Session, cmd: &Command, out_filename: &Path, @@ -1161,7 +1157,7 @@ pub fn exec_linker( } /// Add begin object files defined by the target spec. -fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &'a Session, crate_type: config::CrateType) { +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 { @@ -1179,7 +1175,7 @@ fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &'a Session, crate_type: con } /// Add end object files defined by the target spec. -fn add_post_link_objects(cmd: &mut dyn Linker, sess: &'a Session, crate_type: config::CrateType) { +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)); } @@ -1194,7 +1190,7 @@ fn add_post_link_objects(cmd: &mut dyn Linker, sess: &'a Session, crate_type: co /// FIXME: Determine where exactly these args need to be inserted. fn add_pre_link_args( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, flavor: LinkerFlavor, crate_type: config::CrateType, ) { @@ -1213,7 +1209,7 @@ fn add_pre_link_args( /// FIXME: Determine where exactly these args need to be inserted. fn add_user_defined_link_args( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, codegen_results: &CodegenResults, ) { cmd.args(&sess.opts.cg.link_args); @@ -1224,7 +1220,7 @@ fn add_user_defined_link_args( /// FIXME: Determine where exactly these args need to be inserted. fn add_late_link_args( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, flavor: LinkerFlavor, crate_type: config::CrateType, codegen_results: &CodegenResults, @@ -1249,7 +1245,7 @@ fn add_late_link_args( /// 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: &'a Session, flavor: LinkerFlavor) { +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); } @@ -1264,8 +1260,7 @@ fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &Codeg /// 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) { - let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { + if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { cmd.add_object(obj); } } @@ -1280,8 +1275,8 @@ fn add_local_crate_metadata_objects( // 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); } } @@ -1337,25 +1332,24 @@ fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<' } /// Add sysroot and other globally set directories to the directory search list. -fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &'a Session) { - // 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(); - - // prefer system mingw-w64 libs, see get_crt_libs_path comment for more details +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: &'a Session, + sess: &Session, flavor: LinkerFlavor, crate_type: config::CrateType, codegen_results: &CodegenResults, @@ -1364,7 +1358,6 @@ fn add_position_independent_executable_args( return; } - let mut position_independent_executable = false; 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); @@ -1372,39 +1365,26 @@ fn add_position_independent_executable_args( && !sess.crt_static(Some(crate_type)) && !user_defined_link_args.any(|x| x == "-static") { - position_independent_executable = true; + cmd.position_independent_executable(); + return; } } - 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(); - } + // 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: &'a Session) { - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => sess.target.target.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - } - RelroLevel::Partial => { - cmd.partial_relro(); - } - RelroLevel::Off => { - cmd.no_relro(); - } +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 => {} } } @@ -1412,7 +1392,7 @@ fn add_relro_args(cmd: &mut dyn Linker, sess: &'a Session) { /// Add library search paths used at runtime by dynamic linkers. fn add_rpath_args( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, codegen_results: &CodegenResults, out_filename: &Path, ) { @@ -1623,7 +1603,7 @@ fn linker_with_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, @@ -1953,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, @@ -2010,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 => {