diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ac13d61229e05..f9b985f0624e7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2527,7 +2527,7 @@ fn add_native_libs_from_crate( NativeLibKind::WasmImportModule => {} NativeLibKind::LinkArg => { if link_static { - cmd.arg(name); + cmd.linker_arg(&OsString::from(name), verbatim); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 4dd688c22345d..20a444e13dc88 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -196,6 +196,14 @@ pub trait Linker { fn add_no_exec(&mut self) {} fn add_as_needed(&mut self) {} fn reset_per_library_state(&mut self) {} + fn linker_arg(&mut self, arg: &OsStr, verbatim: bool) { + self.linker_args(&[arg], verbatim); + } + fn linker_args(&mut self, args: &[&OsStr], _verbatim: bool) { + args.into_iter().for_each(|a| { + self.cmd().arg(a); + }); + } } impl dyn Linker + '_ { @@ -223,38 +231,12 @@ pub struct GccLinker<'a> { } impl<'a> GccLinker<'a> { - /// Passes an argument directly to the linker. - /// - /// When the linker is not ld-like such as when using a compiler as a linker, the argument is - /// prepended by `-Wl,`. - fn linker_arg(&mut self, arg: impl AsRef) -> &mut Self { - self.linker_args(&[arg]); - self + fn linker_arg_(&mut self, arg: impl AsRef) { + self.linker_arg(arg.as_ref(), false); } - - /// Passes a series of arguments directly to the linker. - /// - /// When the linker is ld-like, the arguments are simply appended to the command. When the - /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by - /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a - /// single argument is appended to the command to ensure that the order of the arguments is - /// preserved by the compiler. - fn linker_args(&mut self, args: &[impl AsRef]) -> &mut Self { - if self.is_ld { - args.into_iter().for_each(|a| { - self.cmd.arg(a); - }); - } else { - if !args.is_empty() { - let mut s = OsString::from("-Wl"); - for a in args { - s.push(","); - s.push(a); - } - self.cmd.arg(s); - } - } - self + fn linker_args_(&mut self, args: &[impl AsRef]) { + let args_vec: Vec<&OsStr> = args.iter().map(|x| x.as_ref()).collect(); + self.linker_args(&args_vec, false); } fn takes_hints(&self) -> bool { @@ -278,7 +260,7 @@ impl<'a> GccLinker<'a> { return; } if self.hinted_static != Some(true) { - self.linker_arg("-Bstatic"); + self.linker_arg_("-Bstatic"); self.hinted_static = Some(true); } } @@ -288,7 +270,7 @@ impl<'a> GccLinker<'a> { return; } if self.hinted_static != Some(false) { - self.linker_arg("-Bdynamic"); + self.linker_arg_("-Bdynamic"); self.hinted_static = Some(false); } } @@ -297,7 +279,7 @@ impl<'a> GccLinker<'a> { if let Some(plugin_path) = plugin_path { let mut arg = OsString::from("-plugin="); arg.push(plugin_path); - self.linker_arg(&arg); + self.linker_arg_(&arg); } let opt_level = match self.sess.opts.optimize { @@ -308,9 +290,9 @@ impl<'a> GccLinker<'a> { }; if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use { - self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display())); + self.linker_arg_(&format!("-plugin-opt=sample-profile={}", path.display())); }; - self.linker_args(&[ + self.linker_args_(&[ &format!("-plugin-opt={opt_level}"), &format!("-plugin-opt=mcpu={}", self.target_cpu), ]); @@ -323,7 +305,7 @@ impl<'a> GccLinker<'a> { self.cmd.arg("-dynamiclib"); } - self.linker_arg("-dylib"); + self.linker_arg_("-dylib"); // Note that the `osx_rpath_install_name` option here is a hack // purely to support rustbuild right now, we should get a more @@ -332,7 +314,7 @@ impl<'a> GccLinker<'a> { if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name { let mut rpath = OsString::from("@rpath/"); rpath.push(out_filename.file_name().unwrap()); - self.linker_args(&[OsString::from("-install_name"), rpath]); + self.linker_args_(&[&OsString::from("-install_name"), &rpath]); } } else { self.cmd.arg("-shared"); @@ -352,7 +334,7 @@ impl<'a> GccLinker<'a> { if let Some(implib_name) = implib_name { let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap())); + self.linker_arg_(&format!("--out-implib={}", (*implib).to_str().unwrap())); } } } @@ -361,6 +343,38 @@ impl<'a> GccLinker<'a> { } impl<'a> Linker for GccLinker<'a> { + /// Passes an argument directly to the linker. + /// + /// When the linker is not ld-like such as when using a compiler as a linker, the argument is + /// prepended by `-Wl,`. + fn linker_arg(&mut self, arg: &OsStr, verbatim: bool) { + self.linker_args(&[arg], verbatim); + } + + /// Passes a series of arguments directly to the linker. + /// + /// When the linker is ld-like, the arguments are simply appended to the command. When the + /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by + /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a + /// single argument is appended to the command to ensure that the order of the arguments is + /// preserved by the compiler. + fn linker_args(&mut self, args: &[&OsStr], verbatim: bool) { + if self.is_ld || verbatim { + args.into_iter().for_each(|a| { + self.cmd.arg(a); + }); + } else { + if !args.is_empty() { + let mut s = OsString::from("-Wl"); + for a in args { + s.push(","); + s.push(a); + } + self.cmd.arg(s); + } + } + } + fn cmd(&mut self) -> &mut Command { &mut self.cmd } @@ -406,7 +420,7 @@ impl<'a> Linker for GccLinker<'a> { self.build_dylib(out_filename); } LinkOutputKind::WasiReactorExe => { - self.linker_args(&["--entry", "_initialize"]); + self.linker_args_(&["--entry", "_initialize"]); } } // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, @@ -442,7 +456,7 @@ impl<'a> Linker for GccLinker<'a> { // but we have no way to detect that here. self.sess.emit_warning(errors::Ld64UnimplementedModifier); } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--no-as-needed"); + self.linker_arg_("--no-as-needed"); } else { self.sess.emit_warning(errors::LinkerUnsupportedModifier); } @@ -453,7 +467,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.target.is_like_osx { // See above FIXME comment } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--as-needed"); + self.linker_arg_("--as-needed"); } } } @@ -478,13 +492,13 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg(path); } fn full_relro(&mut self) { - self.linker_args(&["-z", "relro", "-z", "now"]); + self.linker_args_(&["-z", "relro", "-z", "now"]); } fn partial_relro(&mut self) { - self.linker_args(&["-z", "relro"]); + self.linker_args_(&["-z", "relro"]); } fn no_relro(&mut self) { - self.linker_args(&["-z", "norelro"]); + self.linker_args_(&["-z", "norelro"]); } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { @@ -513,26 +527,26 @@ impl<'a> Linker for GccLinker<'a> { self.hint_static(); let target = &self.sess.target; if !target.is_like_osx { - self.linker_arg("--whole-archive"); + self.linker_arg_("--whole-archive"); self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },)); - self.linker_arg("--no-whole-archive"); + self.linker_arg_("--no-whole-archive"); } else { // -force_load is the macOS equivalent of --whole-archive, but it // involves passing the full path to the library to link. - self.linker_arg("-force_load"); + self.linker_arg_("-force_load"); let lib = find_native_static_library(lib, verbatim, search_path, self.sess); - self.linker_arg(&lib); + self.linker_arg_(&lib); } } fn link_whole_rlib(&mut self, lib: &Path) { self.hint_static(); if self.sess.target.is_like_osx { - self.linker_arg("-force_load"); - self.linker_arg(&lib); + self.linker_arg_("-force_load"); + self.linker_arg_(&lib); } else { - self.linker_arg("--whole-archive").cmd.arg(lib); - self.linker_arg("--no-whole-archive"); + self.linker_args_(&[OsString::from("--whole-archive"), lib.into()]); + self.linker_arg_("--no-whole-archive"); } } @@ -552,7 +566,7 @@ impl<'a> Linker for GccLinker<'a> { // for partial linking when using multiple codegen units (-r). So we // insert it here. if self.sess.target.is_like_osx { - self.linker_arg("-dead_strip"); + self.linker_arg_("-dead_strip"); // If we're building a dylib, we don't use --gc-sections because LLVM // has already done the best it can do, and we also don't want to @@ -560,13 +574,13 @@ impl<'a> Linker for GccLinker<'a> { // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% // reduction. } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata { - self.linker_arg("--gc-sections"); + self.linker_arg_("--gc-sections"); } } fn no_gc_sections(&mut self) { if self.is_gnu || self.sess.target.is_like_wasm { - self.linker_arg("--no-gc-sections"); + self.linker_arg_("--no-gc-sections"); } } @@ -580,7 +594,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.opts.optimize == config::OptLevel::Default || self.sess.opts.optimize == config::OptLevel::Aggressive { - self.linker_arg("-O1"); + self.linker_arg_("-O1"); } } @@ -622,20 +636,20 @@ impl<'a> Linker for GccLinker<'a> { // The --strip-debug case is handled by running an external // `strip` utility as a separate step after linking. if self.sess.target.os != "illumos" { - self.linker_arg("--strip-debug"); + self.linker_arg_("--strip-debug"); } } Strip::Symbols => { - self.linker_arg("--strip-all"); + self.linker_arg_("--strip-all"); } } match self.sess.opts.unstable_opts.debuginfo_compression { config::DebugInfoCompression::None => {} config::DebugInfoCompression::Zlib => { - self.linker_arg("--compress-debug-sections=zlib"); + self.linker_arg_("--compress-debug-sections=zlib"); } config::DebugInfoCompression::Zstd => { - self.linker_arg("--compress-debug-sections=zstd"); + self.linker_arg_("--compress-debug-sections=zstd"); } } } @@ -724,24 +738,24 @@ impl<'a> Linker for GccLinker<'a> { } if self.sess.target.is_like_osx { - self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]); + self.linker_args_(&[OsString::from("-exported_symbols_list"), path.into()]); } else if self.sess.target.is_like_solaris { - self.linker_args(&[OsString::from("-M"), path.into()]); + self.linker_args_(&[OsString::from("-M"), path.into()]); } else { if is_windows { - self.linker_arg(path); + self.linker_arg_(path); } else { let mut arg = OsString::from("--version-script="); arg.push(path); - self.linker_arg(arg); - self.linker_arg("--no-undefined-version"); + self.linker_arg_(arg); + self.linker_arg_("--no-undefined-version"); } } } fn subsystem(&mut self, subsystem: &str) { - self.linker_arg("--subsystem"); - self.linker_arg(&subsystem); + self.linker_arg_("--subsystem"); + self.linker_arg_(&subsystem); } fn reset_per_library_state(&mut self) { @@ -766,23 +780,23 @@ impl<'a> Linker for GccLinker<'a> { // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't, // so we just always add it. fn add_eh_frame_header(&mut self) { - self.linker_arg("--eh-frame-hdr"); + self.linker_arg_("--eh-frame-hdr"); } fn add_no_exec(&mut self) { if self.sess.target.is_like_windows { - self.linker_arg("--nxcompat"); + self.linker_arg_("--nxcompat"); } else if self.is_gnu { - self.linker_args(&["-z", "noexecstack"]); + self.linker_args_(&["-z", "noexecstack"]); } } fn add_as_needed(&mut self) { if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--as-needed"); + self.linker_arg_("--as-needed"); } else if self.sess.target.is_like_solaris { // -z ignore is the Solaris equivalent to the GNU ld --as-needed option - self.linker_args(&["-z", "ignore"]); + self.linker_args_(&["-z", "ignore"]); } } } diff --git a/tests/run-make/pass-linker-flags-flavor/Makefile b/tests/run-make/pass-linker-flags-flavor/Makefile new file mode 100644 index 0000000000000..bd3d3ed882fbc --- /dev/null +++ b/tests/run-make/pass-linker-flags-flavor/Makefile @@ -0,0 +1,8 @@ +# only-linux + +include ../tools.mk + +all: + $(RUSTC) rs.rs -Z unstable-options -C linker-flavor=gnu-cc -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*-Wl,a1.*l2.*-Wl,a2.*d1.*-Wl,a3' + $(RUSTC) rs.rs -Z unstable-options -C linker-flavor=gnu-cc -l static=l1 -l link-arg:+verbatim=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*-Wl,a2.*d1.*-Wl,a3' + $(RUSTC) rs.rs -Z unstable-options -C linker-flavor=ld -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*"a2".*d1.*"a3"' diff --git a/tests/run-make/pass-linker-flags-flavor/rs.rs b/tests/run-make/pass-linker-flags-flavor/rs.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/pass-linker-flags-flavor/rs.rs @@ -0,0 +1 @@ +fn main() {}