Skip to content

Commit

Permalink
rustc_target: Refactor internal linker flavors
Browse files Browse the repository at this point in the history
In accordance with the design from rust-lang#96827 (comment)
  • Loading branch information
petrochenkov committed Oct 6, 2022
1 parent 4bd3078 commit 572b6a9
Show file tree
Hide file tree
Showing 104 changed files with 569 additions and 438 deletions.
75 changes: 45 additions & 30 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_span::DebuggerVisualizerFile;
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, Target};

use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
use super::command::Command;
Expand Down Expand Up @@ -748,8 +748,7 @@ fn link_natively<'a>(
// then it should not default to linking executables as pie. Different
// versions of gcc seem to use different quotes in the error message so
// don't check for them.
if sess.target.linker_is_gnu
&& flavor != LinkerFlavor::Ld
if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
&& unknown_arg_regex.is_match(&out)
&& out.contains("-no-pie")
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie")
Expand All @@ -767,8 +766,7 @@ fn link_natively<'a>(

// Detect '-static-pie' used with an older version of gcc or clang not supporting it.
// Fallback from '-static-pie' to '-static' in that case.
if sess.target.linker_is_gnu
&& flavor != LinkerFlavor::Ld
if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
&& unknown_arg_regex.is_match(&out)
&& (out.contains("-static-pie") || out.contains("--no-dynamic-linker"))
&& cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie")
Expand Down Expand Up @@ -903,7 +901,7 @@ fn link_natively<'a>(
// install the Visual Studio build tools.
if let Some(code) = prog.status.code() {
if sess.target.is_like_msvc
&& flavor == LinkerFlavor::Msvc
&& flavor == LinkerFlavor::Msvc(Lld::No)
// Respect the command line override
&& sess.opts.cg.linker.is_none()
// Match exactly "link.exe"
Expand Down Expand Up @@ -1187,7 +1185,10 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
// only the linker flavor is known; use the default linker for the selected flavor
(None, Some(flavor)) => Some((
PathBuf::from(match flavor {
LinkerFlavor::Gcc => {
LinkerFlavor::Gnu(Cc::Yes, _)
| LinkerFlavor::Darwin(Cc::Yes, _)
| LinkerFlavor::WasmLld(Cc::Yes)
| LinkerFlavor::Unix(Cc::Yes) => {
if cfg!(any(target_os = "solaris", target_os = "illumos")) {
// On historical Solaris systems, "cc" may have
// been Sun Studio, which is not flag-compatible
Expand All @@ -1200,9 +1201,14 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
"cc"
}
}
LinkerFlavor::Ld => "ld",
LinkerFlavor::Lld(_) => "lld",
LinkerFlavor::Msvc => "link.exe",
LinkerFlavor::Gnu(_, Lld::Yes)
| LinkerFlavor::Darwin(_, Lld::Yes)
| LinkerFlavor::WasmLld(..)
| LinkerFlavor::Msvc(Lld::Yes) => "lld",
LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => {
"ld"
}
LinkerFlavor::Msvc(..) => "link.exe",
LinkerFlavor::EmCc => {
if cfg!(windows) {
"emcc.bat"
Expand All @@ -1227,15 +1233,20 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
|| stem == "clang"
|| stem.ends_with("-clang")
{
LinkerFlavor::Gcc
LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target)
} else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") {
LinkerFlavor::Lld(LldFlavor::Wasm)
} else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
LinkerFlavor::Ld
} else if stem == "link" || stem == "lld-link" {
LinkerFlavor::Msvc
LinkerFlavor::WasmLld(Cc::No)
} else if stem == "ld" || stem.ends_with("-ld") {
LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target)
} else if stem == "ld.lld" {
LinkerFlavor::Gnu(Cc::No, Lld::Yes)
} else if stem == "link" {
LinkerFlavor::Msvc(Lld::No)
} else if stem == "lld-link" {
LinkerFlavor::Msvc(Lld::Yes)
} else if stem == "lld" || stem == "rust-lld" {
LinkerFlavor::Lld(sess.target.lld_flavor)
let lld_flavor = sess.target.linker_flavor.lld_flavor();
LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target)
} else {
// fall back to the value in the target spec
sess.target.linker_flavor
Expand All @@ -1249,7 +1260,8 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {

// linker and linker flavor specified via command line have precedence over what the target
// specification specifies
let linker_flavor = sess.opts.cg.linker_flavor.map(LinkerFlavor::from_cli);
let linker_flavor =
sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target));
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) {
return ret;
}
Expand Down Expand Up @@ -1320,7 +1332,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
let verbatim = lib.verbatim.unwrap_or(false);
if sess.target.is_like_msvc {
Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
} else if sess.target.linker_is_gnu {
} else if sess.target.linker_flavor.is_gnu() {
Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))
} else {
Some(format!("-l{}", name))
Expand Down Expand Up @@ -1607,7 +1619,7 @@ fn add_pre_link_objects(
let empty = Default::default();
let objects = if self_contained {
&opts.pre_link_objects_self_contained
} else if !(sess.target.os == "fuchsia" && flavor == LinkerFlavor::Gcc) {
} else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) {
&opts.pre_link_objects
} else {
&empty
Expand Down Expand Up @@ -1647,7 +1659,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor)
fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) {
match (crate_type, &sess.target.link_script) {
(CrateType::Cdylib | CrateType::Executable, Some(script)) => {
if !sess.target.linker_is_gnu {
if !sess.target.linker_flavor.is_gnu() {
sess.fatal("can only use link script when linking with GNU-like linker");
}

Expand Down Expand Up @@ -1890,7 +1902,7 @@ fn add_rpath_args(
out_filename: out_filename.to_path_buf(),
has_rpath: sess.target.has_rpath,
is_like_osx: sess.target.is_like_osx,
linker_is_gnu: sess.target.linker_is_gnu,
linker_is_gnu: sess.target.linker_flavor.is_gnu(),
};
cmd.args(&rpath::get_rpath_flags(&mut rpath_config));
}
Expand Down Expand Up @@ -2104,7 +2116,7 @@ fn add_order_independent_options(

if sess.target.os == "fuchsia"
&& crate_type == CrateType::Executable
&& flavor != LinkerFlavor::Gcc
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
{
let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
"asan/"
Expand Down Expand Up @@ -2717,12 +2729,12 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
let llvm_target = &sess.target.llvm_target;
if sess.target.vendor != "apple"
|| !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "macos")
|| (flavor != LinkerFlavor::Gcc && flavor != LinkerFlavor::Lld(LldFlavor::Ld64))
|| !matches!(flavor, LinkerFlavor::Darwin(..))
{
return;
}

if os == "macos" && flavor != LinkerFlavor::Lld(LldFlavor::Ld64) {
if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) {
return;
}

Expand Down Expand Up @@ -2756,10 +2768,10 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
};

match flavor {
LinkerFlavor::Gcc => {
LinkerFlavor::Darwin(Cc::Yes, _) => {
cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]);
}
LinkerFlavor::Lld(LldFlavor::Ld64) => {
LinkerFlavor::Darwin(Cc::No, _) => {
cmd.args(&["-syslibroot", &sdk_root]);
}
_ => unreachable!(),
Expand Down Expand Up @@ -2822,7 +2834,10 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {

fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
if let LinkerFlavor::Gcc = flavor {
if let LinkerFlavor::Gnu(Cc::Yes, _)
| LinkerFlavor::Darwin(Cc::Yes, _)
| LinkerFlavor::WasmLld(Cc::Yes) = flavor
{
match ld_impl {
LdImpl::Lld => {
// Implement the "self-contained" part of -Zgcc-ld
Expand All @@ -2837,7 +2852,7 @@ fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
// Implement the "linker flavor" part of -Zgcc-ld
// by asking cc to use some kind of lld.
cmd.arg("-fuse-ld=lld");
if sess.target.lld_flavor != LldFlavor::Ld {
if !flavor.is_gnu() {
// Tell clang to use a non-default LLD flavor.
// Gcc doesn't understand the target option, but we currently assume
// that gcc is not used for Apple and Wasm targets (#97402).
Expand Down
82 changes: 37 additions & 45 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, S
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
use rustc_session::Session;
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};

use cc::windows_registry;

Expand Down Expand Up @@ -56,8 +56,13 @@ pub fn get_linker<'a>(
let mut cmd = match linker.to_str() {
Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
_ => match flavor {
LinkerFlavor::Lld(f) => Command::lld(linker, f),
LinkerFlavor::Msvc if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() => {
LinkerFlavor::Gnu(Cc::No, Lld::Yes)
| LinkerFlavor::Darwin(Cc::No, Lld::Yes)
| LinkerFlavor::WasmLld(Cc::No)
| LinkerFlavor::Msvc(Lld::Yes) => Command::lld(linker, flavor.lld_flavor()),
LinkerFlavor::Msvc(Lld::No)
if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() =>
{
Command::new(msvc_tool.as_ref().map_or(linker, |t| t.path()))
}
_ => Command::new(linker),
Expand All @@ -68,9 +73,7 @@ pub fn get_linker<'a>(
// To comply with the Windows App Certification Kit,
// MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc).
let t = &sess.target;
if (flavor == LinkerFlavor::Msvc || flavor == LinkerFlavor::Lld(LldFlavor::Link))
&& t.vendor == "uwp"
{
if matches!(flavor, LinkerFlavor::Msvc(..)) && t.vendor == "uwp" {
if let Some(ref tool) = msvc_tool {
let original_path = tool.path();
if let Some(ref root_lib_path) = original_path.ancestors().nth(4) {
Expand Down Expand Up @@ -126,23 +129,22 @@ pub fn get_linker<'a>(
// to the linker args construction.
assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp");
match flavor {
LinkerFlavor::Gcc => {
Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: false })
as Box<dyn Linker>
}
LinkerFlavor::Ld if sess.target.os == "l4re" => {
LinkerFlavor::Unix(Cc::No) if sess.target.os == "l4re" => {
Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker>
}
LinkerFlavor::Lld(LldFlavor::Ld)
| LinkerFlavor::Lld(LldFlavor::Ld64)
| LinkerFlavor::Ld => {
Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: true })
as Box<dyn Linker>
}
LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => {
Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>
}
LinkerFlavor::Lld(LldFlavor::Wasm) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>,
LinkerFlavor::WasmLld(Cc::No) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>,
LinkerFlavor::Gnu(cc, _)
| LinkerFlavor::Darwin(cc, _)
| LinkerFlavor::WasmLld(cc)
| LinkerFlavor::Unix(cc) => Box::new(GccLinker {
cmd,
sess,
target_cpu,
hinted_static: false,
is_ld: cc == Cc::No,
is_gnu: flavor.is_gnu(),
}) as Box<dyn Linker>,
LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>,
LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>,
LinkerFlavor::Bpf => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>,
LinkerFlavor::Ptx => Box::new(PtxLinker { cmd, sess }) as Box<dyn Linker>,
Expand Down Expand Up @@ -211,6 +213,7 @@ pub struct GccLinker<'a> {
hinted_static: bool, // Keeps track of the current hinting mode.
// Link as ld
is_ld: bool,
is_gnu: bool,
}

impl<'a> GccLinker<'a> {
Expand Down Expand Up @@ -359,7 +362,7 @@ impl<'a> Linker for GccLinker<'a> {
fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) {
match output_kind {
LinkOutputKind::DynamicNoPicExe => {
if !self.is_ld && self.sess.target.linker_is_gnu {
if !self.is_ld && self.is_gnu {
self.cmd.arg("-no-pie");
}
}
Expand All @@ -373,7 +376,7 @@ impl<'a> Linker for GccLinker<'a> {
LinkOutputKind::StaticNoPicExe => {
// `-static` works for both gcc wrapper and ld.
self.cmd.arg("-static");
if !self.is_ld && self.sess.target.linker_is_gnu {
if !self.is_ld && self.is_gnu {
self.cmd.arg("-no-pie");
}
}
Expand Down Expand Up @@ -432,31 +435,25 @@ impl<'a> Linker for GccLinker<'a> {
// has -needed-l{} / -needed_library {}
// but we have no way to detect that here.
self.sess.warn("`as-needed` modifier not implemented yet for ld64");
} else if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows {
} else if self.is_gnu && !self.sess.target.is_like_windows {
self.linker_arg("--no-as-needed");
} else {
self.sess.warn("`as-needed` modifier not supported for current linker");
}
}
self.hint_dynamic();
self.cmd.arg(format!(
"-l{}{lib}",
if verbatim && self.sess.target.linker_is_gnu { ":" } else { "" },
));
self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },));
if !as_needed {
if self.sess.target.is_like_osx {
// See above FIXME comment
} else if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows {
} else if self.is_gnu && !self.sess.target.is_like_windows {
self.linker_arg("--as-needed");
}
}
}
fn link_staticlib(&mut self, lib: &str, verbatim: bool) {
self.hint_static();
self.cmd.arg(format!(
"-l{}{lib}",
if verbatim && self.sess.target.linker_is_gnu { ":" } else { "" },
));
self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },));
}
fn link_rlib(&mut self, lib: &Path) {
self.hint_static();
Expand Down Expand Up @@ -511,10 +508,7 @@ impl<'a> Linker for GccLinker<'a> {
let target = &self.sess.target;
if !target.is_like_osx {
self.linker_arg("--whole-archive");
self.cmd.arg(format!(
"-l{}{lib}",
if verbatim && self.sess.target.linker_is_gnu { ":" } else { "" },
));
self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },));
self.linker_arg("--no-whole-archive");
} else {
// -force_load is the macOS equivalent of --whole-archive, but it
Expand Down Expand Up @@ -559,21 +553,19 @@ impl<'a> Linker for GccLinker<'a> {
// 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.sess.target.linker_is_gnu || self.sess.target.is_like_wasm)
&& !keep_metadata
{
} else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata {
self.linker_arg("--gc-sections");
}
}

fn no_gc_sections(&mut self) {
if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm {
if self.is_gnu || self.sess.target.is_like_wasm {
self.linker_arg("--no-gc-sections");
}
}

fn optimize(&mut self) {
if !self.sess.target.linker_is_gnu && !self.sess.target.is_like_wasm {
if !self.is_gnu && !self.sess.target.is_like_wasm {
return;
}

Expand All @@ -587,7 +579,7 @@ impl<'a> Linker for GccLinker<'a> {
}

fn pgo_gen(&mut self) {
if !self.sess.target.linker_is_gnu {
if !self.is_gnu {
return;
}

Expand Down Expand Up @@ -758,13 +750,13 @@ impl<'a> Linker for GccLinker<'a> {
fn add_no_exec(&mut self) {
if self.sess.target.is_like_windows {
self.linker_arg("--nxcompat");
} else if self.sess.target.linker_is_gnu {
} else if self.is_gnu {
self.linker_arg("-znoexecstack");
}
}

fn add_as_needed(&mut self) {
if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows {
if self.is_gnu && !self.sess.target.is_like_windows {
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
Expand Down
Loading

0 comments on commit 572b6a9

Please sign in to comment.