Skip to content

Commit

Permalink
Added linker_arg(s) Linker trait methods for link-arg to be prefixed …
Browse files Browse the repository at this point in the history
…"-Wl," for cc-like linker args and not verbatim
  • Loading branch information
azhogin committed Nov 24, 2023
1 parent 41fe75e commit 6bb2def
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 74 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
160 changes: 87 additions & 73 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 + '_ {
Expand Down Expand Up @@ -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<OsStr>) -> &mut Self {
self.linker_args(&[arg]);
self
fn linker_arg_(&mut self, arg: impl AsRef<OsStr>) {
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<OsStr>]) -> &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<OsStr>]) {
let args_vec: Vec<&OsStr> = args.iter().map(|x| x.as_ref()).collect();
self.linker_args(&args_vec, false);
}

fn takes_hints(&self) -> bool {
Expand All @@ -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);
}
}
Expand All @@ -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);
}
}
Expand All @@ -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 {
Expand All @@ -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),
]);
Expand All @@ -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
Expand All @@ -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");
Expand All @@ -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()));
}
}
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
Expand All @@ -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");
}
}
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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");
}
}

Expand All @@ -552,21 +566,21 @@ 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
// eliminate the metadata. If we're building an executable, however,
// --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");
}
}

Expand All @@ -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");
}
}

Expand Down Expand Up @@ -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");
}
}
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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"]);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions tests/run-make/pass-linker-flags-flavor/Makefile
Original file line number Diff line number Diff line change
@@ -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"'
1 change: 1 addition & 0 deletions tests/run-make/pass-linker-flags-flavor/rs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main() {}

0 comments on commit 6bb2def

Please sign in to comment.