Skip to content

Commit d2048b6

Browse files
committed
Auto merge of #52101 - japaric:linker-flavor, r=alexcrichton
try to infer linker flavor from linker name and vice versa This is a second take on PR #50359 that implements the logic proposed in #50359 (review) With this change it would become possible to link `thumb*` binaries using GNU's LD on stable as `-C linker=arm-none-eabi-ld` would be enough to change both the linker and the linker flavor from their default values of `arm-none-eabi-gcc` and `gcc`. To link `thumb*` binaries using rustc's LLD on stable `-Z linker-flavor` would need to be stabilized as `-C linker=rust-lld -Z linker-flavor=ld.lld` are both required to change the linker and the linker flavor, but this PR doesn't propose that. We would probably need some sort of stability guarantee around `rust-lld`'s name and availability to make linking with rustc's LLD truly stable. With this change it would also be possible to link `thumb*` binaries using a system installed LLD on stable using the `-C linker=ld.lld` flag (provided that `ld.lld` is a symlink to the system installed LLD). r? @alexcrichton
2 parents 6bf6d50 + a6f4ae8 commit d2048b6

File tree

6 files changed

+133
-44
lines changed

6 files changed

+133
-44
lines changed

Diff for: src/librustc/session/mod.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use syntax::feature_gate::AttributeType;
4040
use syntax_pos::{MultiSpan, Span};
4141
use util::profiling::SelfProfiler;
4242

43-
use rustc_target::spec::{LinkerFlavor, PanicStrategy};
43+
use rustc_target::spec::PanicStrategy;
4444
use rustc_target::spec::{Target, TargetTriple};
4545
use rustc_data_structures::flock;
4646
use jobserver::Client;
@@ -602,13 +602,6 @@ impl Session {
602602
.panic
603603
.unwrap_or(self.target.target.options.panic_strategy)
604604
}
605-
pub fn linker_flavor(&self) -> LinkerFlavor {
606-
self.opts
607-
.debugging_opts
608-
.linker_flavor
609-
.unwrap_or(self.target.target.linker_flavor)
610-
}
611-
612605
pub fn fewer_names(&self) -> bool {
613606
let more_names = self.opts
614607
.output_types

Diff for: src/librustc_codegen_llvm/back/link.rs

+82-33
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default
5353
// The third parameter is for env vars, used on windows to set up the
5454
// path for MSVC to find its DLLs, and gcc to find its bundled
5555
// toolchain
56-
pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
56+
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
57+
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
58+
5759
// If our linker looks like a batch script on Windows then to execute this
5860
// we'll need to spawn `cmd` explicitly. This is primarily done to handle
5961
// emscripten where the linker is `emcc.bat` and needs to be spawned as
@@ -62,36 +64,19 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
6264
// This worked historically but is needed manually since #42436 (regression
6365
// was tagged as #42791) and some more info can be found on #44443 for
6466
// emscripten itself.
65-
let cmd = |linker: &Path| {
66-
if let Some(linker) = linker.to_str() {
67-
if cfg!(windows) && linker.ends_with(".bat") {
68-
return Command::bat_script(linker)
69-
}
70-
}
71-
match sess.linker_flavor() {
67+
let mut cmd = match linker.to_str() {
68+
Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
69+
_ => match flavor {
7270
LinkerFlavor::Lld(f) => Command::lld(linker, f),
71+
LinkerFlavor::Msvc
72+
if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
73+
{
74+
Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
75+
},
7376
_ => Command::new(linker),
74-
7577
}
7678
};
7779

78-
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
79-
80-
let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s)
81-
.or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref()))
82-
.unwrap_or(match sess.linker_flavor() {
83-
LinkerFlavor::Msvc => {
84-
msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref())
85-
}
86-
LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(),
87-
LinkerFlavor::Em => "emcc".as_ref(),
88-
LinkerFlavor::Gcc => "cc".as_ref(),
89-
LinkerFlavor::Ld => "ld".as_ref(),
90-
LinkerFlavor::Lld(_) => "lld".as_ref(),
91-
});
92-
93-
let mut cmd = cmd(linker_path);
94-
9580
// The compiler's sysroot often has some bundled tools, so add it to the
9681
// PATH for the child.
9782
let mut new_path = sess.host_filesearch(PathKind::All)
@@ -118,7 +103,7 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
118103
}
119104
cmd.env("PATH", env::join_paths(new_path).unwrap());
120105

121-
(linker_path.to_path_buf(), cmd)
106+
(linker.to_path_buf(), cmd)
122107
}
123108

124109
pub fn remove(sess: &Session, path: &Path) {
@@ -608,6 +593,69 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
608593
}
609594
}
610595

596+
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
597+
fn infer_from(
598+
sess: &Session,
599+
linker: Option<PathBuf>,
600+
flavor: Option<LinkerFlavor>,
601+
) -> Option<(PathBuf, LinkerFlavor)> {
602+
match (linker, flavor) {
603+
(Some(linker), Some(flavor)) => Some((linker, flavor)),
604+
// only the linker flavor is known; use the default linker for the selected flavor
605+
(None, Some(flavor)) => Some((PathBuf::from(match flavor {
606+
LinkerFlavor::Em => if cfg!(windows) { "emcc.bat" } else { "emcc" },
607+
LinkerFlavor::Gcc => "cc",
608+
LinkerFlavor::Ld => "ld",
609+
LinkerFlavor::Msvc => "link.exe",
610+
LinkerFlavor::Lld(_) => "lld",
611+
}), flavor)),
612+
(Some(linker), None) => {
613+
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
614+
sess.fatal("couldn't extract file stem from specified linker");
615+
}).to_owned();
616+
617+
let flavor = if stem == "emcc" {
618+
LinkerFlavor::Em
619+
} else if stem == "gcc" || stem.ends_with("-gcc") {
620+
LinkerFlavor::Gcc
621+
} else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
622+
LinkerFlavor::Ld
623+
} else if stem == "link" || stem == "lld-link" {
624+
LinkerFlavor::Msvc
625+
} else if stem == "lld" || stem == "rust-lld" {
626+
LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
627+
} else {
628+
// fall back to the value in the target spec
629+
sess.target.target.linker_flavor
630+
};
631+
632+
Some((linker, flavor))
633+
},
634+
(None, None) => None,
635+
}
636+
}
637+
638+
// linker and linker flavor specified via command line have precedence over what the target
639+
// specification specifies
640+
if let Some(ret) = infer_from(
641+
sess,
642+
sess.opts.cg.linker.clone(),
643+
sess.opts.debugging_opts.linker_flavor,
644+
) {
645+
return ret;
646+
}
647+
648+
if let Some(ret) = infer_from(
649+
sess,
650+
sess.target.target.options.linker.clone().map(PathBuf::from),
651+
Some(sess.target.target.linker_flavor),
652+
) {
653+
return ret;
654+
}
655+
656+
bug!("Not enough information provided to determine how to invoke the linker");
657+
}
658+
611659
// Create a dynamic library or executable
612660
//
613661
// This will invoke the system linker/cc to create the resulting file. This
@@ -618,10 +666,10 @@ fn link_natively(sess: &Session,
618666
codegen_results: &CodegenResults,
619667
tmpdir: &Path) {
620668
info!("preparing {:?} to {:?}", crate_type, out_filename);
621-
let flavor = sess.linker_flavor();
669+
let (linker, flavor) = linker_and_flavor(sess);
622670

623671
// The invocations of cc share some flags across platforms
624-
let (pname, mut cmd) = get_linker(sess);
672+
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
625673

626674
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
627675
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
@@ -662,8 +710,8 @@ fn link_natively(sess: &Session,
662710
}
663711

664712
{
665-
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess);
666-
link_args(&mut *linker, sess, crate_type, tmpdir,
713+
let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor);
714+
link_args(&mut *linker, flavor, sess, crate_type, tmpdir,
667715
out_filename, codegen_results);
668716
cmd = linker.finalize();
669717
}
@@ -735,7 +783,7 @@ fn link_natively(sess: &Session,
735783
// linking executables as pie. Different versions of gcc seem to use
736784
// different quotes in the error message so don't check for them.
737785
if sess.target.target.options.linker_is_gnu &&
738-
sess.linker_flavor() != LinkerFlavor::Ld &&
786+
flavor != LinkerFlavor::Ld &&
739787
(out.contains("unrecognized command line option") ||
740788
out.contains("unknown argument")) &&
741789
out.contains("-no-pie") &&
@@ -984,6 +1032,7 @@ fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &
9841032
}
9851033

9861034
fn link_args(cmd: &mut dyn Linker,
1035+
flavor: LinkerFlavor,
9871036
sess: &Session,
9881037
crate_type: config::CrateType,
9891038
tmpdir: &Path,
@@ -1068,7 +1117,7 @@ fn link_args(cmd: &mut dyn Linker,
10681117
// independent executables by default. We have to pass -no-pie to
10691118
// explicitly turn that off. Not applicable to ld.
10701119
if sess.target.target.options.linker_is_gnu
1071-
&& sess.linker_flavor() != LinkerFlavor::Ld {
1120+
&& flavor != LinkerFlavor::Ld {
10721121
cmd.no_position_independent_executable();
10731122
}
10741123
}

Diff for: src/librustc_codegen_llvm/back/linker.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ impl LinkerInfo {
4444

4545
pub fn to_linker<'a>(&'a self,
4646
cmd: Command,
47-
sess: &'a Session) -> Box<dyn Linker+'a> {
48-
match sess.linker_flavor() {
47+
sess: &'a Session,
48+
flavor: LinkerFlavor) -> Box<dyn Linker+'a> {
49+
match flavor {
4950
LinkerFlavor::Lld(LldFlavor::Link) |
5051
LinkerFlavor::Msvc => {
5152
Box::new(MsvcLinker {

Diff for: src/librustc_codegen_llvm/back/write.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,9 @@ fn start_executing_work(tcx: TyCtxt,
15021502

15031503
let assembler_cmd = if modules_config.no_integrated_as {
15041504
// HACK: currently we use linker (gcc) as our assembler
1505-
let (name, mut cmd) = get_linker(sess);
1505+
let (linker, flavor) = link::linker_and_flavor(sess);
1506+
1507+
let (name, mut cmd) = get_linker(sess, &linker, flavor);
15061508
cmd.args(&sess.target.target.options.asm_args);
15071509
Some(Arc::new(AssemblerCommand {
15081510
name,

Diff for: src/librustc_target/spec/mod.rs

+43
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,29 @@ pub enum LldFlavor {
9494
Link,
9595
}
9696

97+
impl LldFlavor {
98+
fn from_str(s: &str) -> Option<Self> {
99+
Some(match s {
100+
"darwin" => LldFlavor::Ld64,
101+
"gnu" => LldFlavor::Ld,
102+
"link" => LldFlavor::Link,
103+
"wasm" => LldFlavor::Wasm,
104+
_ => return None,
105+
})
106+
}
107+
}
108+
109+
impl ToJson for LldFlavor {
110+
fn to_json(&self) -> Json {
111+
match *self {
112+
LldFlavor::Ld64 => "darwin",
113+
LldFlavor::Ld => "gnu",
114+
LldFlavor::Link => "link",
115+
LldFlavor::Wasm => "wasm",
116+
}.to_json()
117+
}
118+
}
119+
97120
impl ToJson for LinkerFlavor {
98121
fn to_json(&self) -> Json {
99122
self.desc().to_json()
@@ -437,6 +460,9 @@ pub struct TargetOptions {
437460
/// Linker to invoke
438461
pub linker: Option<String>,
439462

463+
/// LLD flavor
464+
pub lld_flavor: LldFlavor,
465+
440466
/// Linker arguments that are passed *before* any user-defined libraries.
441467
pub pre_link_args: LinkArgs, // ... unconditionally
442468
pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt
@@ -654,6 +680,7 @@ impl Default for TargetOptions {
654680
TargetOptions {
655681
is_builtin: false,
656682
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()),
683+
lld_flavor: LldFlavor::Ld,
657684
pre_link_args: LinkArgs::new(),
658685
pre_link_args_crt: LinkArgs::new(),
659686
post_link_args: LinkArgs::new(),
@@ -856,6 +883,20 @@ impl Target {
856883
.map(|s| s.to_string() );
857884
}
858885
} );
886+
($key_name:ident, LldFlavor) => ( {
887+
let name = (stringify!($key_name)).replace("_", "-");
888+
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
889+
if let Some(flavor) = LldFlavor::from_str(&s) {
890+
base.options.$key_name = flavor;
891+
} else {
892+
return Some(Err(format!(
893+
"'{}' is not a valid value for lld-flavor. \
894+
Use 'darwin', 'gnu', 'link' or 'wasm.",
895+
s)))
896+
}
897+
Some(Ok(()))
898+
})).unwrap_or(Ok(()))
899+
} );
859900
($key_name:ident, LinkerFlavor) => ( {
860901
let name = (stringify!($key_name)).replace("_", "-");
861902
obj.find(&name[..]).and_then(|o| o.as_string().map(|s| {
@@ -911,6 +952,7 @@ impl Target {
911952

912953
key!(is_builtin, bool);
913954
key!(linker, optional);
955+
try!(key!(lld_flavor, LldFlavor));
914956
key!(pre_link_args, link_args);
915957
key!(pre_link_args_crt, link_args);
916958
key!(pre_link_objects_exe, list);
@@ -1118,6 +1160,7 @@ impl ToJson for Target {
11181160

11191161
target_option_val!(is_builtin);
11201162
target_option_val!(linker);
1163+
target_option_val!(lld_flavor);
11211164
target_option_val!(link_args - pre_link_args);
11221165
target_option_val!(link_args - pre_link_args_crt);
11231166
target_option_val!(pre_link_objects_exe);

Diff for: src/librustc_target/spec/wasm32_unknown_unknown.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub fn target() -> Result<Target, String> {
5353

5454
// we use the LLD shipped with the Rust toolchain by default
5555
linker: Some("rust-lld".to_owned()),
56+
lld_flavor: LldFlavor::Wasm,
5657

5758
.. Default::default()
5859
};

0 commit comments

Comments
 (0)