From be4249efa4c76151280206c73a2fa6c3b3536748 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sun, 1 May 2016 17:41:28 -0400 Subject: [PATCH 1/3] rustc_back: use a common musl base --- .../target/i686_unknown_linux_musl.rs | 19 +---- src/librustc_back/target/linux_musl_base.rs | 71 +++++++++++++++++++ src/librustc_back/target/mod.rs | 1 + .../target/x86_64_unknown_linux_musl.rs | 56 +-------------- 4 files changed, 74 insertions(+), 73 deletions(-) create mode 100644 src/librustc_back/target/linux_musl_base.rs diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs index cce023b843016..95fc0abece01b 100644 --- a/src/librustc_back/target/i686_unknown_linux_musl.rs +++ b/src/librustc_back/target/i686_unknown_linux_musl.rs @@ -8,31 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// See x86_64_unknown_linux_musl for explanation of arguments - use target::Target; pub fn target() -> Target { - let mut base = super::linux_base::opts(); + let mut base = super::linux_musl_base::opts(); base.cpu = "pentium4".to_string(); base.pre_link_args.push("-m32".to_string()); base.pre_link_args.push("-Wl,-melf_i386".to_string()); - base.pre_link_args.push("-nostdlib".to_string()); - base.pre_link_args.push("-static".to_string()); - base.pre_link_args.push("-Wl,--eh-frame-hdr".to_string()); - - base.pre_link_args.push("-Wl,-(".to_string()); - base.post_link_args.push("-Wl,-)".to_string()); - - base.pre_link_objects_exe.push("crt1.o".to_string()); - base.pre_link_objects_exe.push("crti.o".to_string()); - base.post_link_objects.push("crtn.o".to_string()); - - base.dynamic_linking = false; - base.has_rpath = false; - base.position_independent_executables = false; - Target { llvm_target: "i686-unknown-linux-musl".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_back/target/linux_musl_base.rs b/src/librustc_back/target/linux_musl_base.rs new file mode 100644 index 0000000000000..d55907aeedfbc --- /dev/null +++ b/src/librustc_back/target/linux_musl_base.rs @@ -0,0 +1,71 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::TargetOptions; + +pub fn opts() -> TargetOptions { + let mut base = super::linux_base::opts(); + + // Make sure that the linker/gcc really don't pull in anything, including + // default objects, libs, etc. + base.pre_link_args.push("-nostdlib".to_string()); + base.pre_link_args.push("-static".to_string()); + + // At least when this was tested, the linker would not add the + // `GNU_EH_FRAME` program header to executables generated, which is required + // when unwinding to locate the unwinding information. I'm not sure why this + // argument is *not* necessary for normal builds, but it can't hurt! + base.pre_link_args.push("-Wl,--eh-frame-hdr".to_string()); + + // There's a whole bunch of circular dependencies when dealing with MUSL + // unfortunately. To put this in perspective libc is statically linked to + // liblibc and libunwind is statically linked to libstd: + // + // * libcore depends on `fmod` which is in libc (transitively in liblibc). + // liblibc, however, depends on libcore. + // * compiler-rt has personality symbols that depend on libunwind, but + // libunwind is in libstd which depends on compiler-rt. + // + // Recall that linkers discard libraries and object files as much as + // possible, and with all the static linking and archives flying around with + // MUSL the linker is super aggressively stripping out objects. For example + // the first case has fmod stripped from liblibc (it's in its own object + // file) so it's not there when libcore needs it. In the second example all + // the unused symbols from libunwind are stripped (each is in its own object + // file in libstd) before we end up linking compiler-rt which depends on + // those symbols. + // + // To deal with these circular dependencies we just force the compiler to + // link everything as a group, not stripping anything out until everything + // is processed. The linker will still perform a pass to strip out object + // files but it won't do so until all objects/archives have been processed. + base.pre_link_args.push("-Wl,-(".to_string()); + base.post_link_args.push("-Wl,-)".to_string()); + + // When generating a statically linked executable there's generally some + // small setup needed which is listed in these files. These are provided by + // a musl toolchain and are linked by default by the `musl-gcc` script. Note + // that `gcc` also does this by default, it just uses some different files. + // + // Each target directory for musl has these object files included in it so + // they'll be included from there. + base.pre_link_objects_exe.push("crt1.o".to_string()); + 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; + + base +} diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 3f75201aad2cc..739107afcbd75 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -56,6 +56,7 @@ mod bitrig_base; mod dragonfly_base; mod freebsd_base; mod linux_base; +mod linux_musl_base; mod openbsd_base; mod netbsd_base; mod solaris_base; diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index 622a1fe8baf14..f8f85e5c06609 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -11,64 +11,10 @@ use target::Target; pub fn target() -> Target { - let mut base = super::linux_base::opts(); + let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.push("-m64".to_string()); - // Make sure that the linker/gcc really don't pull in anything, including - // default objects, libs, etc. - base.pre_link_args.push("-nostdlib".to_string()); - base.pre_link_args.push("-static".to_string()); - - // At least when this was tested, the linker would not add the - // `GNU_EH_FRAME` program header to executables generated, which is required - // when unwinding to locate the unwinding information. I'm not sure why this - // argument is *not* necessary for normal builds, but it can't hurt! - base.pre_link_args.push("-Wl,--eh-frame-hdr".to_string()); - - // There's a whole bunch of circular dependencies when dealing with MUSL - // unfortunately. To put this in perspective libc is statically linked to - // liblibc and libunwind is statically linked to libstd: - // - // * libcore depends on `fmod` which is in libc (transitively in liblibc). - // liblibc, however, depends on libcore. - // * compiler-rt has personality symbols that depend on libunwind, but - // libunwind is in libstd which depends on compiler-rt. - // - // Recall that linkers discard libraries and object files as much as - // possible, and with all the static linking and archives flying around with - // MUSL the linker is super aggressively stripping out objects. For example - // the first case has fmod stripped from liblibc (it's in its own object - // file) so it's not there when libcore needs it. In the second example all - // the unused symbols from libunwind are stripped (each is in its own object - // file in libstd) before we end up linking compiler-rt which depends on - // those symbols. - // - // To deal with these circular dependencies we just force the compiler to - // link everything as a group, not stripping anything out until everything - // is processed. The linker will still perform a pass to strip out object - // files but it won't do so until all objects/archives have been processed. - base.pre_link_args.push("-Wl,-(".to_string()); - base.post_link_args.push("-Wl,-)".to_string()); - - // When generating a statically linked executable there's generally some - // small setup needed which is listed in these files. These are provided by - // a musl toolchain and are linked by default by the `musl-gcc` script. Note - // that `gcc` also does this by default, it just uses some different files. - // - // Each target directory for musl has these object files included in it so - // they'll be included from there. - base.pre_link_objects_exe.push("crt1.o".to_string()); - 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; - Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), target_endian: "little".to_string(), From 598498f94ad653320cb698b8bce0fb12fa6c769e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sun, 1 May 2016 17:21:52 -0400 Subject: [PATCH 2/3] rustc_trans: Linker fully wraps command --- src/librustc_trans/back/link.rs | 126 +++++++++++++++-------- src/librustc_trans/back/linker.rs | 161 ++++++++++++++++++++---------- 2 files changed, 190 insertions(+), 97 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 4e77b2bc06940..6eb3686473511 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -135,14 +135,14 @@ pub fn build_link_meta(tcx: &TyCtxt, return r; } -pub fn get_linker(sess: &Session) -> (String, Command) { +pub fn get_linker(sess: &Session) -> (&str, Command) { if let Some(ref linker) = sess.opts.cg.linker { - (linker.clone(), Command::new(linker)) + (linker, Command::new(linker)) } else if sess.target.target.options.is_like_msvc { - ("link.exe".to_string(), msvc::link_exe_cmd(sess)) + ("link.exe", msvc::link_exe_cmd(sess)) } else { - (sess.target.target.options.linker.clone(), - Command::new(&sess.target.target.options.linker)) + let linker = &sess.target.target.options.linker; + (linker, Command::new(linker)) } } @@ -621,6 +621,54 @@ fn link_natively(sess: &Session, dylib: bool, let (pname, mut cmd) = get_linker(sess); cmd.env("PATH", command_path(sess)); + if sess.target.target.options.is_like_msvc { + let mut linker = MsvcLinker { + name: pname, + cmd: cmd, + sess: &sess, + }; + link_natively_helper(&mut linker, + sess, + dylib, + objects, + out_filename, + trans, + outputs, + tmpdir); + } else { + let mut linker = GnuLinker { + name: pname, + cmd: cmd, + sess: &sess, + }; + link_natively_helper(&mut linker, + sess, + dylib, + objects, + out_filename, + trans, + outputs, + tmpdir); + } + + // On OSX, debuggers need this utility to get run to do some munging of + // the symbols + if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo { + match Command::new("dsymutil").arg(out_filename).output() { + Ok(..) => {} + Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), + } + } +} + +fn link_natively_helper(cmd: &mut T, + sess: &Session, + dylib: bool, + objects: &[PathBuf], + out_filename: &Path, + trans: &CrateTranslation, + outputs: &OutputFilenames, + tmpdir: &Path) { let root = sess.target_filesearch(PathKind::Native).get_lib_path(); cmd.args(&sess.target.target.options.pre_link_args); @@ -633,18 +681,18 @@ fn link_natively(sess: &Session, dylib: bool, cmd.arg(root.join(obj)); } - { - let mut linker = if sess.target.target.options.is_like_msvc { - Box::new(MsvcLinker { cmd: &mut cmd, sess: &sess }) as Box - } else { - Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box - }; - link_args(&mut *linker, sess, dylib, tmpdir, - objects, out_filename, trans, outputs); - if !sess.target.target.options.no_compiler_rt { - linker.link_staticlib("compiler-rt"); - } + link_args(cmd, + sess, + dylib, + tmpdir, + objects, + out_filename, + trans, + outputs); + if !sess.target.target.options.no_compiler_rt { + cmd.link_staticlib("compiler-rt"); } + cmd.args(&sess.target.target.options.late_link_args); for obj in &sess.target.target.options.post_link_objects { cmd.arg(root.join(obj)); @@ -677,7 +725,7 @@ fn link_natively(sess: &Session, dylib: bool, let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); sess.struct_err(&format!("linking with `{}` failed: {}", - pname, + cmd, prog.status)) .note(&format!("{:?}", &cmd)) .note(&escape_string(&output[..])) @@ -688,29 +736,19 @@ fn link_natively(sess: &Session, dylib: bool, info!("linker stdout:\n{}", escape_string(&prog.stdout[..])); }, Err(e) => { - sess.fatal(&format!("could not exec the linker `{}`: {}", pname, e)); - } - } - - - // On OSX, debuggers need this utility to get run to do some munging of - // the symbols - if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo { - match Command::new("dsymutil").arg(out_filename).output() { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), + sess.fatal(&format!("could not exec the linker `{}`: {}", cmd, e)); } } } -fn link_args(cmd: &mut Linker, - sess: &Session, - dylib: bool, - tmpdir: &Path, - objects: &[PathBuf], - out_filename: &Path, - trans: &CrateTranslation, - outputs: &OutputFilenames) { +fn link_args(cmd: &mut T, + sess: &Session, + dylib: bool, + tmpdir: &Path, + objects: &[PathBuf], + out_filename: &Path, + trans: &CrateTranslation, + outputs: &OutputFilenames) { // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. @@ -854,7 +892,7 @@ fn link_args(cmd: &mut Linker, // Also note that the native libraries linked here are only the ones located // in the current crate. Upstream crates with native library dependencies // may have their native library pulled in above. -fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) { +fn add_local_native_libraries(cmd: &mut T, sess: &Session) { sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { match k { PathKind::Framework => { cmd.framework_path(path); } @@ -904,8 +942,7 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) { // Rust crates are not considered at all when creating an rlib output. All // dependencies will be linked when producing the final output (instead of // the intermediate rlib version) -fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, - dylib: bool, tmpdir: &Path) { +fn add_upstream_rust_crates(cmd: &mut T, sess: &Session, dylib: bool, tmpdir: &Path) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the // output of that module, adding crates as necessary. @@ -979,8 +1016,11 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, // (aka we're making an executable), we can just pass the rlib blindly to // the linker (fast) because it's fine if it's not actually included as // we're at the end of the dependency chain. - fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path, - dylib: bool, cratepath: &Path) { + fn add_static_crate(cmd: &mut T, + sess: &Session, + tmpdir: &Path, + dylib: bool, + cratepath: &Path) { if !sess.lto() && !dylib { cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); return @@ -1027,7 +1067,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, } // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { + fn add_dynamic_crate(cmd: &mut T, sess: &Session, cratepath: &Path) { // If we're performing LTO, then it should have been previously required // that all upstream rust dependencies were available in an rlib format. assert!(!sess.lto()); @@ -1062,7 +1102,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, // generic function calls a native function, then the generic function must // be instantiated in the target crate, meaning that the native symbol must // also be resolved in the target crate. -fn add_upstream_native_libraries(cmd: &mut Linker, sess: &Session) { +fn add_upstream_native_libraries(cmd: &mut T, sess: &Session) { // Be sure to use a topological sorting of crates because there may be // interdependencies between native libraries. When passing -nodefaultlibs, // for example, almost all native libraries depend on libc, so we have to diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index c02a482f81275..181d27fbcf160 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; +use std::fmt; use std::fs::{self, File}; -use std::io::{self, BufWriter}; use std::io::prelude::*; +use std::io::{self, BufWriter}; use std::path::{Path, PathBuf}; -use std::process::Command; +use std::process::{Command, Output}; use back::archive; use middle::cstore::CrateStore; @@ -31,7 +32,7 @@ use CrateTranslation; /// represents the meaning of each option being passed down. This trait is then /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an /// MSVC linker (e.g. `link.exe`) is being used. -pub trait Linker { +pub trait Linker: fmt::Debug + fmt::Display { fn link_dylib(&mut self, lib: &str); fn link_rust_dylib(&mut self, lib: &str, path: &Path); fn link_framework(&mut self, framework: &str); @@ -49,7 +50,9 @@ pub trait Linker { fn debuginfo(&mut self); fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); - fn args(&mut self, args: &[String]); + fn arg>(&mut self, arg: S) -> &mut Self; + fn args>(&mut self, args: &[S]) -> &mut Self; + fn output(&mut self) -> io::Result; fn hint_static(&mut self); fn hint_dynamic(&mut self); fn whole_archives(&mut self); @@ -59,7 +62,8 @@ pub trait Linker { } pub struct GnuLinker<'a> { - pub cmd: &'a mut Command, + pub name: &'a str, + pub cmd: Command, pub sess: &'a Session, } @@ -69,37 +73,60 @@ impl<'a> GnuLinker<'a> { } } +impl<'a> fmt::Debug for GnuLinker<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.cmd.fmt(f) + } +} + +impl<'a> fmt::Display for GnuLinker<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.name.fmt(f) + } +} + impl<'a> Linker for GnuLinker<'a> { - fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } - fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } - fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } - fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } - fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } - fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } + fn link_dylib(&mut self, lib: &str) { self.arg("-l").arg(lib); } + fn link_staticlib(&mut self, lib: &str) { self.arg("-l").arg(lib); } + fn link_rlib(&mut self, lib: &Path) { self.arg(lib); } + fn include_path(&mut self, path: &Path) { self.arg("-L").arg(path); } + fn framework_path(&mut self, path: &Path) { self.arg("-F").arg(path); } + fn output_filename(&mut self, path: &Path) { self.arg("-o").arg(path); } + fn add_object(&mut self, path: &Path) { self.arg(path); } + fn position_independent_executable(&mut self) { self.arg("-pie"); } + fn arg>(&mut self, arg: S) -> &mut Self { + self.cmd.arg(arg); + self + } + fn args>(&mut self, args: &[S]) -> &mut Self { + for arg in args { + self.arg(arg); + } + self + } + fn output(&mut self) -> io::Result { + self.cmd.output() + } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.cmd.arg("-l").arg(lib); + self.arg("-l").arg(lib); } fn link_framework(&mut self, framework: &str) { - self.cmd.arg("-framework").arg(framework); + self.arg("-framework").arg(framework); } fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { - let target = &self.sess.target.target; - if !target.options.is_like_osx { - self.cmd.arg("-Wl,--whole-archive") - .arg("-l").arg(lib) - .arg("-Wl,--no-whole-archive"); - } else { + if self.sess.target.target.options.is_like_osx { // -force_load is the OSX equivalent of --whole-archive, but it // involves passing the full path to the library to link. let mut v = OsString::from("-Wl,-force_load,"); v.push(&archive::find_library(lib, search_path, &self.sess)); - self.cmd.arg(&v); + self.arg(&v); + } else { + self.arg("-Wl,--whole-archive") + .arg("-l").arg(lib) + .arg("-Wl,--no-whole-archive"); } } @@ -107,10 +134,11 @@ impl<'a> Linker for GnuLinker<'a> { if self.sess.target.target.options.is_like_osx { let mut v = OsString::from("-Wl,-force_load,"); v.push(lib); - self.cmd.arg(&v); + self.arg(&v); } else { - self.cmd.arg("-Wl,--whole-archive").arg(lib) - .arg("-Wl,--no-whole-archive"); + self.arg("-Wl,--whole-archive") + .arg(lib) + .arg("-Wl,--no-whole-archive"); } } @@ -130,10 +158,10 @@ impl<'a> Linker for GnuLinker<'a> { // for partial linking when using multiple codegen units (-r). So we // insert it here. if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-Wl,-dead_strip"); + self.arg("-Wl,-dead_strip"); } else if self.sess.target.target.options.is_like_solaris { - self.cmd.arg("-Wl,-z"); - self.cmd.arg("-Wl,ignore"); + self.arg("-Wl,-z"); + self.arg("-Wl,ignore"); // If we're building a dylib, we don't use --gc-sections because LLVM // has already done the best it can do, and we also don't want to @@ -141,7 +169,7 @@ impl<'a> Linker for GnuLinker<'a> { // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% // reduction. } else if !is_dylib { - self.cmd.arg("-Wl,--gc-sections"); + self.arg("-Wl,--gc-sections"); } } @@ -152,7 +180,7 @@ impl<'a> Linker for GnuLinker<'a> { // need a numeric argument, but other linkers do. if self.sess.opts.optimize == config::OptLevel::Default || self.sess.opts.optimize == config::OptLevel::Aggressive { - self.cmd.arg("-Wl,-O1"); + self.arg("-Wl,-O1"); } } @@ -161,42 +189,42 @@ impl<'a> Linker for GnuLinker<'a> { } fn no_default_libraries(&mut self) { - self.cmd.arg("-nodefaultlibs"); + self.arg("-nodefaultlibs"); } fn build_dylib(&mut self, out_filename: &Path) { // On mac we need to tell the linker to let this library be rpathed if self.sess.target.target.options.is_like_osx { - self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]); + self.args(&["-dynamiclib", "-Wl,-dylib"]); if self.sess.opts.cg.rpath { let mut v = OsString::from("-Wl,-install_name,@rpath/"); v.push(out_filename.file_name().unwrap()); - self.cmd.arg(&v); + self.arg(&v); } } else { - self.cmd.arg("-shared"); + self.arg("-shared"); } } fn whole_archives(&mut self) { if !self.takes_hints() { return } - self.cmd.arg("-Wl,--whole-archive"); + self.arg("-Wl,--whole-archive"); } fn no_whole_archives(&mut self) { if !self.takes_hints() { return } - self.cmd.arg("-Wl,--no-whole-archive"); + self.arg("-Wl,--no-whole-archive"); } fn hint_static(&mut self) { if !self.takes_hints() { return } - self.cmd.arg("-Wl,-Bstatic"); + self.arg("-Wl,-Bstatic"); } fn hint_dynamic(&mut self) { if !self.takes_hints() { return } - self.cmd.arg("-Wl,-Bdynamic"); + self.arg("-Wl,-Bdynamic"); } fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) { @@ -205,26 +233,51 @@ impl<'a> Linker for GnuLinker<'a> { } pub struct MsvcLinker<'a> { - pub cmd: &'a mut Command, + pub name: &'a str, + pub cmd: Command, pub sess: &'a Session, } +impl<'a> fmt::Debug for MsvcLinker<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.cmd.fmt(f) + } +} + +impl<'a> fmt::Display for MsvcLinker<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.name.fmt(f) + } +} + impl<'a> Linker for MsvcLinker<'a> { - fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } + fn link_rlib(&mut self, lib: &Path) { self.arg(lib); } + fn add_object(&mut self, path: &Path) { self.arg(path); } + fn arg>(&mut self, arg: S) -> &mut Self { + self.cmd.arg(arg); + self + } + fn args>(&mut self, args: &[S]) -> &mut Self { + for arg in args { + self.arg(arg); + } + self + } + fn output(&mut self) -> io::Result { + self.cmd.output() + } fn build_dylib(&mut self, out_filename: &Path) { - self.cmd.arg("/DLL"); + self.arg("/DLL"); let mut arg: OsString = "/IMPLIB:".into(); arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); + self.arg(arg); } - fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); } + fn gc_sections(&mut self, _is_dylib: bool) { self.arg("/OPT:REF,ICF"); } fn link_dylib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); + self.arg(&format!("{}.lib", lib)); } fn link_rust_dylib(&mut self, lib: &str, path: &Path) { @@ -234,12 +287,12 @@ impl<'a> Linker for MsvcLinker<'a> { // not present. let name = format!("{}.dll.lib", lib); if fs::metadata(&path.join(&name)).is_ok() { - self.cmd.arg(name); + self.arg(name); } } fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); + self.arg(&format!("{}.lib", lib)); } fn position_independent_executable(&mut self) { @@ -261,13 +314,13 @@ impl<'a> Linker for MsvcLinker<'a> { fn include_path(&mut self, path: &Path) { let mut arg = OsString::from("/LIBPATH:"); arg.push(path); - self.cmd.arg(&arg); + self.arg(&arg); } fn output_filename(&mut self, path: &Path) { let mut arg = OsString::from("/OUT:"); arg.push(path); - self.cmd.arg(&arg); + self.arg(&arg); } fn framework_path(&mut self, _path: &Path) { @@ -292,7 +345,7 @@ impl<'a> Linker for MsvcLinker<'a> { fn debuginfo(&mut self) { // This will cause the Microsoft linker to generate a PDB file // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); + self.arg("/DEBUG"); } fn whole_archives(&mut self) { @@ -367,6 +420,6 @@ impl<'a> Linker for MsvcLinker<'a> { } let mut arg = OsString::from("/DEF:"); arg.push(path); - self.cmd.arg(&arg); + self.arg(&arg); } } From 08cac93661a78f7dc2fad7d0838decef8b3edb21 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sun, 1 May 2016 19:19:31 -0400 Subject: [PATCH 3/3] rustc_trans: implement lld compatibility --- src/librustc_trans/back/link.rs | 13 ++++++++ src/librustc_trans/back/linker.rs | 50 ++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 6eb3686473511..6a20be24f304d 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -621,6 +621,18 @@ fn link_natively(sess: &Session, dylib: bool, let (pname, mut cmd) = get_linker(sess); cmd.env("PATH", command_path(sess)); + let is_lld = pname.ends_with("lld"); + + if is_lld { + if sess.target.target.options.is_like_msvc { + cmd.args(&["-flavor", "link"]); + } else if sess.target.target.options.is_like_osx { + cmd.args(&["-flavor", "darwin"]); + } else { + cmd.args(&["-flavor", "gnu"]); + } + } + if sess.target.target.options.is_like_msvc { let mut linker = MsvcLinker { name: pname, @@ -640,6 +652,7 @@ fn link_natively(sess: &Session, dylib: bool, name: pname, cmd: cmd, sess: &sess, + is_lld: is_lld, }; link_natively_helper(&mut linker, sess, diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 181d27fbcf160..d495ae64745fb 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -65,6 +65,7 @@ pub struct GnuLinker<'a> { pub name: &'a str, pub cmd: Command, pub sess: &'a Session, + pub is_lld: bool, } impl<'a> GnuLinker<'a> { @@ -86,8 +87,24 @@ impl<'a> fmt::Display for GnuLinker<'a> { } impl<'a> Linker for GnuLinker<'a> { - fn link_dylib(&mut self, lib: &str) { self.arg("-l").arg(lib); } - fn link_staticlib(&mut self, lib: &str) { self.arg("-l").arg(lib); } + fn link_dylib(&mut self, lib: &str) { + if self.is_lld && self.sess.target.target.options.is_like_osx { + let mut v = OsString::from("-l"); + v.push(lib); + self.arg(&v); + } else { + self.arg("-l").arg(lib); + } + } + fn link_staticlib(&mut self, lib: &str) { + if self.is_lld && self.sess.target.target.options.is_like_osx { + let mut v = OsString::from("-l"); + v.push(lib); + self.arg(&v); + } else { + self.arg("-l").arg(lib); + } + } fn link_rlib(&mut self, lib: &Path) { self.arg(lib); } fn include_path(&mut self, path: &Path) { self.arg("-L").arg(path); } fn framework_path(&mut self, path: &Path) { self.arg("-F").arg(path); } @@ -95,7 +112,20 @@ impl<'a> Linker for GnuLinker<'a> { fn add_object(&mut self, path: &Path) { self.arg(path); } fn position_independent_executable(&mut self) { self.arg("-pie"); } fn arg>(&mut self, arg: S) -> &mut Self { - self.cmd.arg(arg); + if self.is_lld { + match arg.as_ref().to_str() { + Some(args_str) => { + for arg_str in args_str.trim_left_matches("-Wl,").split(',') { + self.cmd.arg(arg_str); + } + }, + None => { + self.cmd.arg(arg.as_ref()); + }, + } + } else { + self.cmd.arg(arg); + } self } fn args>(&mut self, args: &[S]) -> &mut Self { @@ -109,7 +139,13 @@ impl<'a> Linker for GnuLinker<'a> { } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.arg("-l").arg(lib); + if self.is_lld && self.sess.target.target.options.is_like_osx { + let mut v = OsString::from("-l"); + v.push(lib); + self.arg(&v); + } else { + self.arg("-l").arg(lib); + } } fn link_framework(&mut self, framework: &str) { @@ -124,9 +160,9 @@ impl<'a> Linker for GnuLinker<'a> { v.push(&archive::find_library(lib, search_path, &self.sess)); self.arg(&v); } else { - self.arg("-Wl,--whole-archive") - .arg("-l").arg(lib) - .arg("-Wl,--no-whole-archive"); + self.arg("-Wl,--whole-archive"); + self.link_staticlib(lib); + self.arg("-Wl,--no-whole-archive"); } }