Skip to content

Commit 3b5754e

Browse files
committed
Auto merge of #40018 - japaric:ld, r=alexcrichton
-Z linker-flavor (Please read the commit message first) This PR is an alternative to #36120 (internal lld linker). The main goal of this PR is to make it *possible* to use LLD as a linker to allow out of tree experimentation. Now that LLD is going to be shipped with LLVM 4.0, it should become easier to get a hold of LLD (hopefully, it will be packaged by Linux distros soon). Since LLD is a multiarch linker, it has the potential to make cross compilation easier (less tools need to be installed). Supposedly, LLD is also faster than the gold linker so LLD may improve build times where link times are significant (e.g. 100% incremental compilation reuse). The place where LLD shines is at linking Rust programs that don't depend on system libraries. For example, here's how you would link a bare metal ARM Cortex-M program: ``` $ xargo rustc --target thumbv7m-none-eabi -- -Z linker-flavor=ld -C linker=ld.lld -Z print-link-args "ld.lld" \ "-L" \ "$XARGO_HOME/lib/rustlib/thumbv7m-none-eabi/lib" \ "$PWD/target/thumbv7m-none-eabi/debug/deps/app-de1f86df314ad68c.0.o" \ "-o" \ "$PWD/target/thumbv7m-none-eabi/debug/deps/app-de1f86df314ad68c" \ "--gc-sections" \ "-L" \ "$PWD/target/thumbv7m-none-eabi/debug/deps" \ "-L" \ "$PWD/target/debug/deps" \ "-L" \ "$XARGO_HOME/lib/rustlib/thumbv7m-none-eabi/lib" \ "-Bstatic" \ "-Bdynamic" \ "$XARGO_HOME/lib/rustlib/thumbv7m-none-eabi/lib/libcore-11670d2bd4951fa7.rlib" $ file target/thumbv7m-none-eabi/debug/app app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped, with debug_info ``` This doesn't require installing the `arm-none-eabi-gcc` toolchain. Even cooler (but I'm biased) is that you can link Rust programs that use [`steed`] (`steed` is a `std` re-implementation free of C dependencies for Linux systems) instead of `std` for a bunch of different architectures without having to install a single cross toolchain. [`steed`]: https://github.com/japaric/steed ``` $ xargo rustc --target aarch64-unknown-linux-steed --example hello --release -- -Z print-link-args "ld.lld" \ "-L" \ "$XARGO_HOME/lib/rustlib/aarch64-unknown-linux-steed/lib" \ "$PWD/target/aarch64-unknown-linux-steed/release/examples/hello-80c130ad884c0f8f.0.o" \ "-o" \ "$PWD/target/aarch64-unknown-linux-steed/release/examples/hello-80c130ad884c0f8f" \ "--gc-sections" \ "-L" \ "$PWD/target/aarch64-unknown-linux-steed/release/deps" \ "-L" \ "$PWD/target/release/deps" \ "-L" \ "$XARGO_HOME/lib/rustlib/aarch64-unknown-linux-steed/lib" \ "-Bstatic" \ "-Bdynamic" \ "/tmp/rustc.lAybk9Ltx93Q/libcompiler_builtins-589aede02de78434.rlib" $ file target/aarch64-unknown-linux-steed/release/examples/hello hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped, with debug_info ``` All these targets (architectures) worked with LLD: - [aarch64-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/aarch64-unknown-linux-steed.json) - [arm-unknown-linux-steedeabi](https://github.com/japaric/steed/blob/lld/docker/arm-unknown-linux-steedeabi.json) - [arm-unknown-linux-steedeabihf](https://github.com/japaric/steed/blob/lld/docker/arm-unknown-linux-steedeabihf.json) - [armv7-unknown-linux-steedeabihf](https://github.com/japaric/steed/blob/lld/docker/armv7-unknown-linux-steedeabihf.json) - [i686-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/i686-unknown-linux-steed.json) - [mips-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/mips-unknown-linux-steed.json) - [mipsel-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/mipsel-unknown-linux-steed.json) - [powerpc-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/powerpc-unknown-linux-steed.json) - [powerpc64-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/powerpc64-unknown-linux-steed.json) - [x86_64-unknown-linux-steed](https://github.com/japaric/steed/blob/lld/docker/x86_64-unknown-linux-steed.json) --- The case where lld is unergonomic is linking binaries that depend on system libraries. Like "Hello, world" for `x86_64-unknown-linux-gnu`. Because you have to pass as linker arguments: the path to the startup objects, the path to the dynamic linker and the library search paths. And all those are system specific so they can't be encoded in the target itself. ``` $ cargo \ rustc \ --release \ -- \ -C \ linker=ld.lld \ -Z \ linker-flavor=ld \ -C \ link-args='-dynamic-linker /lib64/ld-linux-x86-64.so.2 -L/usr/lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/6.3.1 /usr/lib/Scrt1.o /usr/lib/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/6.3.1/crtbeginS.o /usr/lib/gcc/x86_64-pc-linux-gnu/6.3.1/crtendS.o /usr/lib/crtn.o' ``` --- Another case where `-Z linker-flavor` may come in handy is directly calling Solaris' linker which is also a multiarch linker (or so I have heard). cc @binarycrusader cc @alexcrichton Heads up: [breaking-change] due to changes in the target specification format.
2 parents 8493dd6 + e192fb3 commit 3b5754e

File tree

92 files changed

+682
-240
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+682
-240
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
- [link_llvm_intrinsics](link-llvm-intrinsics.md)
109109
- [linkage](linkage.md)
110110
- [linked_list_extras](linked-list-extras.md)
111+
- [linker_flavor](linker-flavor.md)
111112
- [log_syntax](log-syntax.md)
112113
- [lookup_host](lookup-host.md)
113114
- [loop_break_value](loop-break-value.md)
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# `linker-flavor`
2+
3+
The tracking issue for this feature is: None
4+
5+
------------------------
6+
7+
Every `rustc` target defaults to some linker. For example, Linux targets default
8+
to gcc. In some cases, you may want to override the default; you can do that
9+
with the unstable CLI argument: `-Z linker-flavor`.
10+
11+
Here how you would use this flag to link a Rust binary for the
12+
`thumbv7m-none-eabi` using LLD instead of GCC.
13+
14+
``` text
15+
$ xargo rustc --target thumbv7m-none-eabi -- \
16+
-C linker=ld.lld \
17+
-Z linker-flavor=ld \
18+
-Z print-link-args | tr ' ' '\n'
19+
"ld.lld"
20+
"-L"
21+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib"
22+
"$PWD/target/thumbv7m-none-eabi/debug/deps/app-512e9dbf385f233c.0.o"
23+
"-o"
24+
"$PWD/target/thumbv7m-none-eabi/debug/deps/app-512e9dbf385f233c"
25+
"--gc-sections"
26+
"-L"
27+
"$PWD/target/thumbv7m-none-eabi/debug/deps"
28+
"-L"
29+
"$PWD/target/debug/deps"
30+
"-L"
31+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib"
32+
"-Bstatic"
33+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib/libcore-e1ccb7dfb1cb9ebb.rlib"
34+
"-Bdynamic"
35+
```
36+
37+
Whereas the default is:
38+
39+
``` text
40+
$ xargo rustc --target thumbv7m-none-eabi -- \
41+
-C link-arg=-nostartfiles \
42+
-Z print-link-args | tr ' ' '\n'
43+
"arm-none-eabi-gcc"
44+
"-L"
45+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib"
46+
"$PWD/target/thumbv7m-none-eabi/debug/deps/app-961e39416baa38d9.0.o"
47+
"-o"
48+
"$PWD/target/thumbv7m-none-eabi/debug/deps/app-961e39416baa38d9"
49+
"-Wl,--gc-sections"
50+
"-nodefaultlibs"
51+
"-L"
52+
"$PWD/target/thumbv7m-none-eabi/debug/deps"
53+
"-L"
54+
"$PWD/target/debug/deps"
55+
"-L"
56+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib"
57+
"-Wl,-Bstatic"
58+
"$SYSROOT/lib/rustlib/thumbv7m-none-eabi/lib/libcore-e1ccb7dfb1cb9ebb.rlib"
59+
"-nostartfiles"
60+
"-Wl,-Bdynamic"
61+
```

src/librustc/session/config.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub use self::DebugInfoLevel::*;
1919
use session::{early_error, early_warn, Session};
2020
use session::search_paths::SearchPaths;
2121

22-
use rustc_back::PanicStrategy;
22+
use rustc_back::{LinkerFlavor, PanicStrategy};
2323
use rustc_back::target::Target;
2424
use lint;
2525
use middle::cstore;
@@ -641,12 +641,14 @@ macro_rules! options {
641641
Some("either `panic` or `abort`");
642642
pub const parse_sanitizer: Option<&'static str> =
643643
Some("one of: `address`, `leak`, `memory` or `thread`");
644+
pub const parse_linker_flavor: Option<&'static str> =
645+
Some(::rustc_back::LinkerFlavor::one_of());
644646
}
645647

646648
#[allow(dead_code)]
647649
mod $mod_set {
648650
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer};
649-
use rustc_back::PanicStrategy;
651+
use rustc_back::{LinkerFlavor, PanicStrategy};
650652

651653
$(
652654
pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
@@ -777,6 +779,14 @@ macro_rules! options {
777779
}
778780
true
779781
}
782+
783+
fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
784+
match v.and_then(LinkerFlavor::from_str) {
785+
Some(lf) => *slote = Some(lf),
786+
_ => return false,
787+
}
788+
true
789+
}
780790
}
781791
) }
782792

@@ -979,6 +989,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
979989
"pass `-install_name @rpath/...` to the macOS linker"),
980990
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
981991
"Use a sanitizer"),
992+
linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
993+
"Linker flavor"),
982994
}
983995

984996
pub fn default_lib_output() -> CrateType {

src/librustc/session/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use syntax::{ast, codemap};
3636
use syntax::feature_gate::AttributeType;
3737
use syntax_pos::{Span, MultiSpan};
3838

39-
use rustc_back::PanicStrategy;
39+
use rustc_back::{LinkerFlavor, PanicStrategy};
4040
use rustc_back::target::Target;
4141
use rustc_data_structures::flock;
4242
use llvm;
@@ -363,6 +363,9 @@ impl Session {
363363
pub fn panic_strategy(&self) -> PanicStrategy {
364364
self.opts.cg.panic.unwrap_or(self.target.target.options.panic_strategy)
365365
}
366+
pub fn linker_flavor(&self) -> LinkerFlavor {
367+
self.opts.debugging_opts.linker_flavor.unwrap_or(self.target.target.linker_flavor)
368+
}
366369
pub fn no_landing_pads(&self) -> bool {
367370
self.opts.debugging_opts.no_landing_pads || self.panic_strategy() == PanicStrategy::Abort
368371
}

src/librustc_back/lib.rs

+42
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ pub mod dynamic_lib;
5252

5353
use serialize::json::{Json, ToJson};
5454

55+
macro_rules! linker_flavor {
56+
($(($variant:ident, $string:expr),)+) => {
57+
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash,
58+
RustcEncodable, RustcDecodable)]
59+
pub enum LinkerFlavor {
60+
$($variant,)+
61+
}
62+
63+
impl LinkerFlavor {
64+
pub const fn one_of() -> &'static str {
65+
concat!("one of: ", $($string, " ",)+)
66+
}
67+
68+
pub fn from_str(s: &str) -> Option<Self> {
69+
Some(match s {
70+
$($string => LinkerFlavor::$variant,)+
71+
_ => return None,
72+
})
73+
}
74+
75+
pub fn desc(&self) -> &str {
76+
match *self {
77+
$(LinkerFlavor::$variant => $string,)+
78+
}
79+
}
80+
}
81+
82+
impl ToJson for LinkerFlavor {
83+
fn to_json(&self) -> Json {
84+
self.desc().to_json()
85+
}
86+
}
87+
}
88+
}
89+
90+
linker_flavor! {
91+
(Em, "em"),
92+
(Gcc, "gcc"),
93+
(Ld, "ld"),
94+
(Msvc, "msvc"),
95+
}
96+
5597
#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
5698
pub enum PanicStrategy {
5799
Unwind,

src/librustc_back/target/aarch64_apple_ios.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213
use super::apple_ios_base::{opts, Arch};
1314

@@ -22,6 +23,7 @@ pub fn target() -> TargetResult {
2223
target_os: "ios".to_string(),
2324
target_env: "".to_string(),
2425
target_vendor: "apple".to_string(),
26+
linker_flavor: LinkerFlavor::Gcc,
2527
options: TargetOptions {
2628
features: "+neon,+fp-armv8,+cyclone".to_string(),
2729
eliminate_frame_pointer: false,

src/librustc_back/target/aarch64_linux_android.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
// See https://developer.android.com/ndk/guides/abis.html#arm64-v8a
@@ -28,6 +29,7 @@ pub fn target() -> TargetResult {
2829
target_os: "android".to_string(),
2930
target_env: "".to_string(),
3031
target_vendor: "unknown".to_string(),
32+
linker_flavor: LinkerFlavor::Gcc,
3133
options: TargetOptions {
3234
abi_blacklist: super::arm_base::abi_blacklist(),
3335
.. base

src/librustc_back/target/aarch64_unknown_freebsd.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -26,6 +27,7 @@ pub fn target() -> TargetResult {
2627
target_os: "freebsd".to_string(),
2728
target_env: "".to_string(),
2829
target_vendor: "unknown".to_string(),
30+
linker_flavor: LinkerFlavor::Gcc,
2931
options: TargetOptions {
3032
abi_blacklist: super::arm_base::abi_blacklist(),
3133
.. base

src/librustc_back/target/aarch64_unknown_fuchsia.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -23,6 +24,7 @@ pub fn target() -> TargetResult {
2324
target_os: "fuchsia".to_string(),
2425
target_env: "".to_string(),
2526
target_vendor: "unknown".to_string(),
27+
linker_flavor: LinkerFlavor::Gcc,
2628
options: TargetOptions {
2729
abi_blacklist: super::arm_base::abi_blacklist(),
2830
.. base

src/librustc_back/target/aarch64_unknown_linux_gnu.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -26,6 +27,7 @@ pub fn target() -> TargetResult {
2627
arch: "aarch64".to_string(),
2728
target_os: "linux".to_string(),
2829
target_vendor: "unknown".to_string(),
30+
linker_flavor: LinkerFlavor::Gcc,
2931
options: TargetOptions {
3032
abi_blacklist: super::arm_base::abi_blacklist(),
3133
.. base

src/librustc_back/target/android_base.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::TargetOptions;
1213

1314
pub fn opts() -> TargetOptions {
1415
let mut base = super::linux_base::opts();
1516
// Many of the symbols defined in compiler-rt are also defined in libgcc.
1617
// Android's linker doesn't like that by default.
17-
base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string());
18+
base.pre_link_args
19+
.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,--allow-multiple-definition".to_string());
1820
base.is_like_android = true;
1921
base.position_independent_executables = true;
2022
base.has_elf_tls = false;

src/librustc_back/target/apple_base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use std::env;
1212

13-
use target::TargetOptions;
13+
use target::{LinkArgs, TargetOptions};
1414

1515
pub fn opts() -> TargetOptions {
1616
// ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
@@ -43,7 +43,7 @@ pub fn opts() -> TargetOptions {
4343
dll_prefix: "lib".to_string(),
4444
dll_suffix: ".dylib".to_string(),
4545
archive_format: "bsd".to_string(),
46-
pre_link_args: Vec::new(),
46+
pre_link_args: LinkArgs::new(),
4747
exe_allocation_crate: super::maybe_jemalloc(),
4848
has_elf_tls: version >= (10, 7),
4949
.. Default::default()

src/librustc_back/target/apple_ios_base.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use std::io;
1213
use std::process::Command;
13-
use target::TargetOptions;
14+
use target::{LinkArgs, TargetOptions};
1415

1516
use self::Arch::*;
1617

@@ -60,7 +61,7 @@ pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
6061
}
6162
}
6263

63-
fn build_pre_link_args(arch: Arch) -> Result<Vec<String>, String> {
64+
fn build_pre_link_args(arch: Arch) -> Result<LinkArgs, String> {
6465
let sdk_name = match arch {
6566
Armv7 | Armv7s | Arm64 => "iphoneos",
6667
I386 | X86_64 => "iphonesimulator"
@@ -70,8 +71,14 @@ fn build_pre_link_args(arch: Arch) -> Result<Vec<String>, String> {
7071

7172
let sdk_root = get_sdk_root(sdk_name)?;
7273

73-
Ok(vec!["-arch".to_string(), arch_name.to_string(),
74-
"-Wl,-syslibroot".to_string(), sdk_root])
74+
let mut args = LinkArgs::new();
75+
args.insert(LinkerFlavor::Gcc,
76+
vec!["-arch".to_string(),
77+
arch_name.to_string(),
78+
"-Wl,-syslibroot".to_string(),
79+
sdk_root]);
80+
81+
Ok(args)
7582
}
7683

7784
fn target_cpu(arch: Arch) -> String {

src/librustc_back/target/arm_linux_androideabi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -24,6 +25,7 @@ pub fn target() -> TargetResult {
2425
target_os: "android".to_string(),
2526
target_env: "".to_string(),
2627
target_vendor: "unknown".to_string(),
28+
linker_flavor: LinkerFlavor::Gcc,
2729
options: TargetOptions {
2830
abi_blacklist: super::arm_base::abi_blacklist(),
2931
.. base

src/librustc_back/target/arm_unknown_linux_gnueabi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -22,6 +23,7 @@ pub fn target() -> TargetResult {
2223
target_os: "linux".to_string(),
2324
target_env: "gnu".to_string(),
2425
target_vendor: "unknown".to_string(),
26+
linker_flavor: LinkerFlavor::Gcc,
2527

2628
options: TargetOptions {
2729
features: "+v6".to_string(),

src/librustc_back/target/arm_unknown_linux_gnueabihf.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use LinkerFlavor;
1112
use target::{Target, TargetOptions, TargetResult};
1213

1314
pub fn target() -> TargetResult {
@@ -22,6 +23,7 @@ pub fn target() -> TargetResult {
2223
target_os: "linux".to_string(),
2324
target_env: "gnu".to_string(),
2425
target_vendor: "unknown".to_string(),
26+
linker_flavor: LinkerFlavor::Gcc,
2527

2628
options: TargetOptions {
2729
features: "+v6,+vfp2".to_string(),

0 commit comments

Comments
 (0)