Skip to content

Commit a3f0ee9

Browse files
committed
Auto merge of #40113 - smaeul:native-musl, r=alexcrichton
Support dynamically-linked and/or native musl targets These changes allow native compilation on musl-based distributions and the use of dynamic libraries on linux-musl targets. This is intended to remove limitations based on past assumptions about musl targets, while maintaining existing behavior by default. A minor related bugfix is included.
2 parents ca89841 + e6cd941 commit a3f0ee9

File tree

20 files changed

+133
-48
lines changed

20 files changed

+133
-48
lines changed

config.toml.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@
292292
# build native code.
293293
#android-ndk = "/path/to/ndk"
294294

295+
# Force static or dynamic linkage of the standard library for this target. If
296+
# this target is a host for rustc, this will also affect the linkage of the
297+
# compiler itself. This is useful for building rustc on targets that normally
298+
# only use static libraries. If unset, the target's default linkage is used.
299+
#crt-static = false
300+
295301
# The root location of the MUSL installation directory. The library directory
296302
# will also need to contain libunwind.a for an unwinding implementation. Note
297303
# that this option only makes sense for MUSL targets that produce statically

src/bootstrap/bin/rustc.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,13 @@ fn main() {
237237
}
238238
}
239239

240-
if target.contains("pc-windows-msvc") {
241-
cmd.arg("-Z").arg("unstable-options");
242-
cmd.arg("-C").arg("target-feature=+crt-static");
240+
if let Ok(s) = env::var("RUSTC_CRT_STATIC") {
241+
if s == "true" {
242+
cmd.arg("-C").arg("target-feature=+crt-static");
243+
}
244+
if s == "false" {
245+
cmd.arg("-C").arg("target-feature=-crt-static");
246+
}
243247
}
244248

245249
// Force all crates compiled by this compiler to (a) be unstable and (b)

src/bootstrap/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ impl<'a> Builder<'a> {
503503
cargo.env("RUSTC_METADATA_SUFFIX", "rustc");
504504
}
505505

506+
if let Some(x) = self.crt_static(target) {
507+
cargo.env("RUSTC_CRT_STATIC", x.to_string());
508+
}
509+
506510
// Enable usage of unstable features
507511
cargo.env("RUSTC_BOOTSTRAP", "1");
508512
self.add_rust_test_threads(&mut cargo);

src/bootstrap/compile.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ impl Step for Std {
7777
target,
7878
});
7979
println!("Uplifting stage1 std ({} -> {})", from.host, target);
80+
81+
// Even if we're not building std this stage, the new sysroot must
82+
// still contain the musl startup objects.
83+
if target.contains("musl") && !target.contains("mips") {
84+
let libdir = builder.sysroot_libdir(compiler, target);
85+
copy_musl_third_party_objects(build, target, &libdir);
86+
}
87+
8088
builder.ensure(StdLink {
8189
compiler: from,
8290
target_compiler: compiler,
@@ -89,6 +97,11 @@ impl Step for Std {
8997
println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
9098
&compiler.host, target);
9199

100+
if target.contains("musl") && !target.contains("mips") {
101+
let libdir = builder.sysroot_libdir(compiler, target);
102+
copy_musl_third_party_objects(build, target, &libdir);
103+
}
104+
92105
let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
93106
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
94107
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
@@ -105,6 +118,20 @@ impl Step for Std {
105118
}
106119
}
107120

121+
/// Copies the crt(1,i,n).o startup objects
122+
///
123+
/// Since musl supports fully static linking, we can cross link for it even
124+
/// with a glibc-targeting toolchain, given we have the appropriate startup
125+
/// files. As those shipped with glibc won't work, copy the ones provided by
126+
/// musl so we have them on linux-gnu hosts.
127+
fn copy_musl_third_party_objects(build: &Build,
128+
target: Interned<String>,
129+
into: &Path) {
130+
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
131+
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
132+
}
133+
}
134+
108135
/// Configure cargo to compile the standard library, adding appropriate env vars
109136
/// and such.
110137
pub fn std_cargo(build: &Build,
@@ -189,10 +216,6 @@ impl Step for StdLink {
189216
let libdir = builder.sysroot_libdir(target_compiler, target);
190217
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
191218

192-
if target.contains("musl") && !target.contains("mips") {
193-
copy_musl_third_party_objects(build, target, &libdir);
194-
}
195-
196219
if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
197220
// The sanitizers are only built in stage1 or above, so the dylibs will
198221
// be missing in stage0 and causes panic. See the `std()` function above
@@ -208,15 +231,6 @@ impl Step for StdLink {
208231
}
209232
}
210233

211-
/// Copies the crt(1,i,n).o startup objects
212-
///
213-
/// Only required for musl targets that statically link to libc
214-
fn copy_musl_third_party_objects(build: &Build, target: Interned<String>, into: &Path) {
215-
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
216-
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
217-
}
218-
}
219-
220234
fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
221235
for &sanitizer in &["asan", "tsan"] {
222236
let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);

src/bootstrap/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ pub struct Target {
143143
pub cc: Option<PathBuf>,
144144
pub cxx: Option<PathBuf>,
145145
pub ndk: Option<PathBuf>,
146+
pub crt_static: Option<bool>,
146147
pub musl_root: Option<PathBuf>,
147148
pub qemu_rootfs: Option<PathBuf>,
148149
}
@@ -275,6 +276,7 @@ struct TomlTarget {
275276
cc: Option<String>,
276277
cxx: Option<String>,
277278
android_ndk: Option<String>,
279+
crt_static: Option<bool>,
278280
musl_root: Option<String>,
279281
qemu_rootfs: Option<String>,
280282
}
@@ -446,6 +448,7 @@ impl Config {
446448
}
447449
target.cxx = cfg.cxx.clone().map(PathBuf::from);
448450
target.cc = cfg.cc.clone().map(PathBuf::from);
451+
target.crt_static = cfg.crt_static.clone();
449452
target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
450453
target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);
451454

src/bootstrap/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,16 @@ impl Build {
656656
base
657657
}
658658

659+
/// Returns if this target should statically link the C runtime, if specified
660+
fn crt_static(&self, target: Interned<String>) -> Option<bool> {
661+
if target.contains("pc-windows-msvc") {
662+
Some(true)
663+
} else {
664+
self.config.target_config.get(&target)
665+
.and_then(|t| t.crt_static)
666+
}
667+
}
668+
659669
/// Returns the "musl root" for this `target`, if defined
660670
fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
661671
self.config.target_config.get(&target)

src/bootstrap/sanity.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,15 @@ pub fn check(build: &mut Build) {
151151
panic!("the iOS target is only supported on macOS");
152152
}
153153

154-
// Make sure musl-root is valid if specified
154+
// Make sure musl-root is valid
155155
if target.contains("musl") && !target.contains("mips") {
156+
// If this is a native target (host is also musl) and no musl-root is given,
157+
// fall back to the system toolchain in /usr before giving up
158+
if build.musl_root(*target).is_none() && build.config.build == *target {
159+
let target = build.config.target_config.entry(target.clone())
160+
.or_insert(Default::default());
161+
target.musl_root = Some("/usr".into());
162+
}
156163
match build.musl_root(*target) {
157164
Some(root) => {
158165
if fs::metadata(root.join("lib/libc.a")).is_err() {

src/librustc/session/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,31 @@ impl Session {
429429
.unwrap_or(self.opts.debug_assertions)
430430
}
431431

432+
pub fn crt_static(&self) -> bool {
433+
// If the target does not opt in to crt-static support, use its default.
434+
if self.target.target.options.crt_static_respected {
435+
self.crt_static_feature()
436+
} else {
437+
self.target.target.options.crt_static_default
438+
}
439+
}
440+
441+
pub fn crt_static_feature(&self) -> bool {
442+
let requested_features = self.opts.cg.target_feature.split(',');
443+
let found_negative = requested_features.clone().any(|r| r == "-crt-static");
444+
let found_positive = requested_features.clone().any(|r| r == "+crt-static");
445+
446+
// If the target we're compiling for requests a static crt by default,
447+
// then see if the `-crt-static` feature was passed to disable that.
448+
// Otherwise if we don't have a static crt by default then see if the
449+
// `+crt-static` feature was passed.
450+
if self.target.target.options.crt_static_default {
451+
!found_negative
452+
} else {
453+
found_positive
454+
}
455+
}
456+
432457
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
433458
self.opts.debuginfo != DebugInfoLevel::NoDebugInfo ||
434459
!self.target.target.options.eliminate_frame_pointer

src/librustc_back/target/linux_musl_base.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,10 @@ pub fn opts() -> TargetOptions {
6060
base.pre_link_objects_exe.push("crti.o".to_string());
6161
base.post_link_objects.push("crtn.o".to_string());
6262

63-
// MUSL support doesn't currently include dynamic linking, so there's no
64-
// need for dylibs or rpath business. Additionally `-pie` is incompatible
65-
// with `-static`, so we can't pass `-pie`.
66-
base.dynamic_linking = false;
67-
base.has_rpath = false;
68-
base.position_independent_executables = false;
69-
7063
// These targets statically link libc by default
7164
base.crt_static_default = true;
65+
// These targets allow the user to choose between static and dynamic linking.
66+
base.crt_static_respected = true;
7267

7368
base
7469
}

src/librustc_back/target/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,12 @@ pub struct TargetOptions {
416416
/// ABIs are considered to be supported on all platforms and cannot be blacklisted.
417417
pub abi_blacklist: Vec<Abi>,
418418

419+
/// Whether or not linking dylibs to a static CRT is allowed.
420+
pub crt_static_allows_dylibs: bool,
419421
/// Whether or not the CRT is statically linked by default.
420422
pub crt_static_default: bool,
423+
/// Whether or not crt-static is respected by the compiler (or is a no-op).
424+
pub crt_static_respected: bool,
421425

422426
/// Whether or not stack probes (__rust_probestack) are enabled
423427
pub stack_probes: bool,
@@ -478,7 +482,9 @@ impl Default for TargetOptions {
478482
max_atomic_width: None,
479483
panic_strategy: PanicStrategy::Unwind,
480484
abi_blacklist: vec![],
485+
crt_static_allows_dylibs: false,
481486
crt_static_default: false,
487+
crt_static_respected: false,
482488
stack_probes: false,
483489
}
484490
}
@@ -714,7 +720,9 @@ impl Target {
714720
key!(max_atomic_width, Option<u64>);
715721
key!(min_atomic_width, Option<u64>);
716722
try!(key!(panic_strategy, PanicStrategy));
723+
key!(crt_static_allows_dylibs, bool);
717724
key!(crt_static_default, bool);
725+
key!(crt_static_respected, bool);
718726
key!(stack_probes, bool);
719727

720728
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
@@ -902,7 +910,9 @@ impl ToJson for Target {
902910
target_option_val!(min_atomic_width);
903911
target_option_val!(max_atomic_width);
904912
target_option_val!(panic_strategy);
913+
target_option_val!(crt_static_allows_dylibs);
905914
target_option_val!(crt_static_default);
915+
target_option_val!(crt_static_respected);
906916
target_option_val!(stack_probes);
907917

908918
if default.abi_blacklist != self.options.abi_blacklist {

0 commit comments

Comments
 (0)