Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamically-linked and/or native musl targets #40113

Merged
merged 14 commits into from
Aug 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@
# build native code.
#android-ndk = "/path/to/ndk"

# Force static or dynamic linkage of the standard library for this target. If
# this target is a host for rustc, this will also affect the linkage of the
# compiler itself. This is useful for building rustc on targets that normally
# only use static libraries. If unset, the target's default linkage is used.
#crt-static = false

# The root location of the MUSL installation directory. The library directory
# will also need to contain libunwind.a for an unwinding implementation. Note
# that this option only makes sense for MUSL targets that produce statically
Expand Down
10 changes: 7 additions & 3 deletions src/bootstrap/bin/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,13 @@ fn main() {
}
}

if target.contains("pc-windows-msvc") {
cmd.arg("-Z").arg("unstable-options");
cmd.arg("-C").arg("target-feature=+crt-static");
if let Ok(s) = env::var("RUSTC_CRT_STATIC") {
if s == "true" {
cmd.arg("-C").arg("target-feature=+crt-static");
}
if s == "false" {
cmd.arg("-C").arg("target-feature=-crt-static");
}
}

// Force all crates compiled by this compiler to (a) be unstable and (b)
Expand Down
4 changes: 4 additions & 0 deletions src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,10 @@ impl<'a> Builder<'a> {
cargo.env("RUSTC_METADATA_SUFFIX", "rustc");
}

if let Some(x) = self.crt_static(target) {
cargo.env("RUSTC_CRT_STATIC", x.to_string());
}

// Enable usage of unstable features
cargo.env("RUSTC_BOOTSTRAP", "1");
self.add_rust_test_threads(&mut cargo);
Expand Down
40 changes: 27 additions & 13 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ impl Step for Std {
target,
});
println!("Uplifting stage1 std ({} -> {})", from.host, target);

// Even if we're not building std this stage, the new sysroot must
// still contain the musl startup objects.
if target.contains("musl") && !target.contains("mips") {
let libdir = builder.sysroot_libdir(compiler, target);
copy_musl_third_party_objects(build, target, &libdir);
}

builder.ensure(StdLink {
compiler: from,
target_compiler: compiler,
Expand All @@ -89,6 +97,11 @@ impl Step for Std {
println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
&compiler.host, target);

if target.contains("musl") && !target.contains("mips") {
let libdir = builder.sysroot_libdir(compiler, target);
copy_musl_third_party_objects(build, target, &libdir);
}

let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
Expand All @@ -105,6 +118,20 @@ impl Step for Std {
}
}

/// Copies the crt(1,i,n).o startup objects
///
/// Since musl supports fully static linking, we can cross link for it even
/// with a glibc-targeting toolchain, given we have the appropriate startup
/// files. As those shipped with glibc won't work, copy the ones provided by
/// musl so we have them on linux-gnu hosts.
fn copy_musl_third_party_objects(build: &Build,
target: Interned<String>,
into: &Path) {
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
}
}

/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
pub fn std_cargo(build: &Build,
Expand Down Expand Up @@ -189,10 +216,6 @@ impl Step for StdLink {
let libdir = builder.sysroot_libdir(target_compiler, target);
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));

if target.contains("musl") && !target.contains("mips") {
copy_musl_third_party_objects(build, target, &libdir);
}

if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
// The sanitizers are only built in stage1 or above, so the dylibs will
// be missing in stage0 and causes panic. See the `std()` function above
Expand All @@ -208,15 +231,6 @@ impl Step for StdLink {
}
}

/// Copies the crt(1,i,n).o startup objects
///
/// Only required for musl targets that statically link to libc
fn copy_musl_third_party_objects(build: &Build, target: Interned<String>, into: &Path) {
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
}
}

fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
for &sanitizer in &["asan", "tsan"] {
let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ pub struct Target {
pub cc: Option<PathBuf>,
pub cxx: Option<PathBuf>,
pub ndk: Option<PathBuf>,
pub crt_static: Option<bool>,
pub musl_root: Option<PathBuf>,
pub qemu_rootfs: Option<PathBuf>,
}
Expand Down Expand Up @@ -275,6 +276,7 @@ struct TomlTarget {
cc: Option<String>,
cxx: Option<String>,
android_ndk: Option<String>,
crt_static: Option<bool>,
musl_root: Option<String>,
qemu_rootfs: Option<String>,
}
Expand Down Expand Up @@ -446,6 +448,7 @@ impl Config {
}
target.cxx = cfg.cxx.clone().map(PathBuf::from);
target.cc = cfg.cc.clone().map(PathBuf::from);
target.crt_static = cfg.crt_static.clone();
target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);

Expand Down
10 changes: 10 additions & 0 deletions src/bootstrap/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,16 @@ impl Build {
base
}

/// Returns if this target should statically link the C runtime, if specified
fn crt_static(&self, target: Interned<String>) -> Option<bool> {
if target.contains("pc-windows-msvc") {
Some(true)
} else {
self.config.target_config.get(&target)
.and_then(|t| t.crt_static)
}
}

/// Returns the "musl root" for this `target`, if defined
fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
self.config.target_config.get(&target)
Expand Down
9 changes: 8 additions & 1 deletion src/bootstrap/sanity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,15 @@ pub fn check(build: &mut Build) {
panic!("the iOS target is only supported on macOS");
}

// Make sure musl-root is valid if specified
// Make sure musl-root is valid
if target.contains("musl") && !target.contains("mips") {
// If this is a native target (host is also musl) and no musl-root is given,
// fall back to the system toolchain in /usr before giving up
if build.musl_root(*target).is_none() && build.config.build == *target {
let target = build.config.target_config.entry(target.clone())
.or_insert(Default::default());
target.musl_root = Some("/usr".into());
}
match build.musl_root(*target) {
Some(root) => {
if fs::metadata(root.join("lib/libc.a")).is_err() {
Expand Down
25 changes: 25 additions & 0 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,31 @@ impl Session {
.unwrap_or(self.opts.debug_assertions)
}

pub fn crt_static(&self) -> bool {
// If the target does not opt in to crt-static support, use its default.
if self.target.target.options.crt_static_respected {
self.crt_static_feature()
} else {
self.target.target.options.crt_static_default
}
}

pub fn crt_static_feature(&self) -> bool {
let requested_features = self.opts.cg.target_feature.split(',');
let found_negative = requested_features.clone().any(|r| r == "-crt-static");
let found_positive = requested_features.clone().any(|r| r == "+crt-static");

// If the target we're compiling for requests a static crt by default,
// then see if the `-crt-static` feature was passed to disable that.
// Otherwise if we don't have a static crt by default then see if the
// `+crt-static` feature was passed.
if self.target.target.options.crt_static_default {
!found_negative
} else {
found_positive
}
}

pub fn must_not_eliminate_frame_pointers(&self) -> bool {
self.opts.debuginfo != DebugInfoLevel::NoDebugInfo ||
!self.target.target.options.eliminate_frame_pointer
Expand Down
9 changes: 2 additions & 7 deletions src/librustc_back/target/linux_musl_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,10 @@ pub fn opts() -> TargetOptions {
base.pre_link_objects_exe.push("crti.o".to_string());
base.post_link_objects.push("crtn.o".to_string());

// MUSL support doesn't currently include dynamic linking, so there's no
// need for dylibs or rpath business. Additionally `-pie` is incompatible
// with `-static`, so we can't pass `-pie`.
base.dynamic_linking = false;
base.has_rpath = false;
base.position_independent_executables = false;

// These targets statically link libc by default
base.crt_static_default = true;
// These targets allow the user to choose between static and dynamic linking.
base.crt_static_respected = true;

base
}
10 changes: 10 additions & 0 deletions src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,12 @@ pub struct TargetOptions {
/// ABIs are considered to be supported on all platforms and cannot be blacklisted.
pub abi_blacklist: Vec<Abi>,

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

/// Whether or not stack probes (__rust_probestack) are enabled
pub stack_probes: bool,
Expand Down Expand Up @@ -478,7 +482,9 @@ impl Default for TargetOptions {
max_atomic_width: None,
panic_strategy: PanicStrategy::Unwind,
abi_blacklist: vec![],
crt_static_allows_dylibs: false,
crt_static_default: false,
crt_static_respected: false,
stack_probes: false,
}
}
Expand Down Expand Up @@ -714,7 +720,9 @@ impl Target {
key!(max_atomic_width, Option<u64>);
key!(min_atomic_width, Option<u64>);
try!(key!(panic_strategy, PanicStrategy));
key!(crt_static_allows_dylibs, bool);
key!(crt_static_default, bool);
key!(crt_static_respected, bool);
key!(stack_probes, bool);

if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
Expand Down Expand Up @@ -902,7 +910,9 @@ impl ToJson for Target {
target_option_val!(min_atomic_width);
target_option_val!(max_atomic_width);
target_option_val!(panic_strategy);
target_option_val!(crt_static_allows_dylibs);
target_option_val!(crt_static_default);
target_option_val!(crt_static_respected);
target_option_val!(stack_probes);

if default.abi_blacklist != self.options.abi_blacklist {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_back/target/windows_msvc_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub fn opts() -> TargetOptions {
is_like_windows: true,
is_like_msvc: true,
pre_link_args: args,
crt_static_allows_dylibs: true,
crt_static_respected: true,

.. Default::default()
}
Expand Down
16 changes: 1 addition & 15 deletions src/librustc_driver/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,7 @@ pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session) {
cfg.insert((tf, Some(feat)));
}

let requested_features = sess.opts.cg.target_feature.split(',');
let found_negative = requested_features.clone().any(|r| r == "-crt-static");
let found_positive = requested_features.clone().any(|r| r == "+crt-static");

// If the target we're compiling for requests a static crt by default,
// then see if the `-crt-static` feature was passed to disable that.
// Otherwise if we don't have a static crt by default then see if the
// `+crt-static` feature was passed.
let crt_static = if sess.target.target.options.crt_static_default {
!found_negative
} else {
found_positive
};

if crt_static {
if sess.crt_static_feature() {
cfg.insert((tf, Some(Symbol::intern("crt-static"))));
}
}
8 changes: 5 additions & 3 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ fn link_args(cmd: &mut Linker,
let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter());

if get_reloc_model(sess) == llvm::RelocMode::PIC
&& !args.any(|x| *x == "-static") {
&& !sess.crt_static() && !args.any(|x| *x == "-static") {
cmd.position_independent_executable();
}
}
Expand Down Expand Up @@ -966,11 +966,13 @@ fn link_args(cmd: &mut Linker,
add_upstream_rust_crates(cmd, sess, crate_type, tmpdir);
add_upstream_native_libraries(cmd, sess, crate_type);

// # Telling the linker what we're doing

// Tell the linker what we're doing.
if crate_type != config::CrateTypeExecutable {
cmd.build_dylib(out_filename);
}
if crate_type == config::CrateTypeExecutable && sess.crt_static() {
cmd.build_static_executable();
}

// FIXME (#2397): At some point we want to rpath our guesses as to
// where extern libraries might live, based on the
Expand Down
10 changes: 10 additions & 0 deletions src/librustc_trans/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub trait Linker {
fn debuginfo(&mut self);
fn no_default_libraries(&mut self);
fn build_dylib(&mut self, out_filename: &Path);
fn build_static_executable(&mut self);
fn args(&mut self, args: &[String]);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
fn subsystem(&mut self, subsystem: &str);
Expand Down Expand Up @@ -179,6 +180,7 @@ impl<'a> Linker for GccLinker<'a> {
fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
fn partial_relro(&mut self) { self.linker_arg("-z,relro"); }
fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); }
fn build_static_executable(&mut self) { self.cmd.arg("-static"); }
fn args(&mut self, args: &[String]) { self.cmd.args(args); }

fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
Expand Down Expand Up @@ -396,6 +398,10 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg(arg);
}

fn build_static_executable(&mut self) {
// noop
}

fn gc_sections(&mut self, _keep_metadata: bool) {
// MSVC's ICF (Identical COMDAT Folding) link optimization is
// slow for Rust and thus we disable it by default when not in
Expand Down Expand Up @@ -683,6 +689,10 @@ impl<'a> Linker for EmLinker<'a> {
bug!("building dynamic library is unsupported on Emscripten")
}

fn build_static_executable(&mut self) {
// noop
}

fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
let symbols = &self.info.exports[&crate_type];

Expand Down
7 changes: 5 additions & 2 deletions src/librustc_trans_utils/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ pub fn invalid_output_for_target(sess: &Session,
match (sess.target.target.options.dynamic_linking,
sess.target.target.options.executables, crate_type) {
(false, _, config::CrateTypeCdylib) |
(false, _, config::CrateTypeProcMacro) |
(false, _, config::CrateTypeDylib) => true,
(false, _, config::CrateTypeDylib) |
(false, _, config::CrateTypeProcMacro) => true,
(true, _, config::CrateTypeCdylib) |
(true, _, config::CrateTypeDylib) => sess.crt_static() &&
!sess.target.target.options.crt_static_allows_dylibs,
(_, false, config::CrateTypeExecutable) => true,
_ => false
}
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn main() {
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=log");
println!("cargo:rustc-link-lib=gcc");
} else if !target.contains("musl") || target.contains("mips") {
} else if !target.contains("musl") {
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=rt");
println!("cargo:rustc-link-lib=pthread");
Expand Down
2 changes: 1 addition & 1 deletion src/libunwind/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {

if target.contains("linux") {
if target.contains("musl") && !target.contains("mips") {
println!("cargo:rustc-link-lib=static=unwind");
// musl is handled in lib.rs
} else if !target.contains("android") {
println!("cargo:rustc-link-lib=gcc_s");
}
Expand Down
Loading