// except according to those terms.
use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
-use super::archive;
-use super::rpath;
+use super::linker::{Linker, GnuLinker};
use super::rpath::RPathConfig;
+use super::rpath;
use super::svh::Svh;
use session::config;
use session::config::NoDebugInfo;
use util::fs::fix_windows_verbatim_for_gcc;
use rustc_back::tempdir::TempDir;
-use std::ffi::OsString;
use std::fs::{self, PathExt};
use std::io::{self, Read, Write};
use std::mem;
cmd.arg(root.join(obj));
}
- link_args(&mut cmd, sess, dylib, tmpdir.path(),
- trans, obj_filename, out_filename);
- if !sess.target.target.options.no_compiler_rt {
- cmd.arg("-lcompiler-rt");
+ {
+ let mut linker = GnuLinker { cmd: &mut cmd, sess: &sess };
+ link_args(&mut linker, sess, dylib, tmpdir.path(),
+ trans, obj_filename, out_filename);
+ if !sess.target.target.options.no_compiler_rt {
+ linker.link_staticlib("compiler-rt");
+ }
}
for obj in &sess.target.target.options.post_link_objects {
cmd.arg(root.join(obj));
}
}
-fn link_args(cmd: &mut Command,
+fn link_args(cmd: &mut Linker,
sess: &Session,
dylib: bool,
tmpdir: &Path,
// target descriptor
let t = &sess.target.target;
- cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(&lib_path));
-
- cmd.arg("-o").arg(out_filename).arg(obj_filename);
+ cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
+ cmd.output_filename(out_filename);
+ cmd.add_object(obj_filename);
// Stack growth requires statically linking a __morestack function. Note
// that this is listed *before* all other libraries. Due to the usage of the
// will include the __morestack symbol 100% of the time, always resolving
// references to it even if the object above didn't use it.
if t.options.morestack {
- if t.options.is_like_osx {
- let morestack = lib_path.join("libmorestack.a");
-
- let mut v = OsString::from("-Wl,-force_load,");
- v.push(&morestack);
- cmd.arg(&v);
- } else {
- cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]);
- }
+ cmd.link_whole_staticlib("morestack", &[lib_path]);
}
// When linking a dynamic library, we put the metadata into a section of the
// executable. This metadata is in a separate object file from the main
// object file, so we link that in here.
if dylib {
- cmd.arg(&obj_filename.with_extension("metadata.o"));
+ cmd.add_object(&obj_filename.with_extension("metadata.o"));
}
- if t.options.is_like_osx {
- // The dead_strip option to the linker specifies that functions and data
- // unreachable by the entry point will be removed. This is quite useful
- // with Rust's compilation model of compiling libraries at a time into
- // one object file. For example, this brings hello world from 1.7MB to
- // 458K.
- //
- // Note that this is done for both executables and dynamic libraries. We
- // won't get much benefit from dylibs because LLVM will have already
- // stripped away as much as it could. This has not been seen to impact
- // link times negatively.
- //
- // -dead_strip can't be part of the pre_link_args because it's also used
- // for partial linking when using multiple codegen units (-r). So we
- // insert it here.
- cmd.arg("-Wl,-dead_strip");
- }
-
- // 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 eliminate the
- // metadata. If we're building an executable, however, --gc-sections drops
- // the size of hello world from 1.8MB to 597K, a 67% reduction.
- if !dylib && !t.options.is_like_osx {
- cmd.arg("-Wl,--gc-sections");
- }
+ // Try to strip as much out of the generated object by removing unused
+ // sections if possible. See more comments in linker.rs
+ cmd.gc_sections(dylib);
let used_link_args = sess.cstore.get_used_link_args().borrow();
- if t.options.position_independent_executables {
+ if !dylib && t.options.position_independent_executables {
let empty_vec = Vec::new();
let empty_str = String::new();
let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec);
let mut args = args.iter().chain(used_link_args.iter());
- if !dylib
- && (t.options.relocation_model == "pic"
- || *sess.opts.cg.relocation_model.as_ref()
- .unwrap_or(&empty_str) == "pic")
+ let relocation_model = sess.opts.cg.relocation_model.as_ref()
+ .unwrap_or(&empty_str);
+ if (t.options.relocation_model == "pic" || *relocation_model == "pic")
&& !args.any(|x| *x == "-static") {
- cmd.arg("-pie");
+ cmd.position_independent_executable();
}
}
- if t.options.linker_is_gnu {
- // GNU-style linkers support optimization with -O. GNU ld doesn't need a
- // numeric argument, but other linkers do.
- if sess.opts.optimize == config::Default ||
- sess.opts.optimize == config::Aggressive {
- cmd.arg("-Wl,-O1");
- }
- }
+ // Pass optimization flags down to the linker.
+ cmd.optimize();
// We want to prevent the compiler from accidentally leaking in any system
// libraries, so we explicitly ask gcc to not link to any libraries by
// default. Note that this does not happen for windows because windows pulls
// in some large number of libraries and I couldn't quite figure out which
// subset we wanted.
- if !t.options.is_like_windows {
- cmd.arg("-nodefaultlibs");
- }
-
- // Mark all dynamic libraries and executables as compatible with ASLR
- // FIXME #17098: ASLR breaks gdb
- if t.options.is_like_windows && sess.opts.debuginfo == NoDebugInfo {
- // cmd.arg("-Wl,--dynamicbase");
- }
+ cmd.no_default_libraries();
// Take careful note of the ordering of the arguments we pass to the linker
// here. Linkers will assume that things on the left depend on things to the
// # Telling the linker what we're doing
if dylib {
- // On mac we need to tell the linker to let this library be rpathed
- if sess.target.target.options.is_like_osx {
- cmd.args(&["-dynamiclib", "-Wl,-dylib"]);
-
- if sess.opts.cg.rpath {
- let mut v = OsString::from("-Wl,-install_name,@rpath/");
- v.push(out_filename.file_name().unwrap());
- cmd.arg(&v);
- }
- } else {
- cmd.arg("-shared");
- }
+ cmd.build_dylib(out_filename);
}
// FIXME (#2397): At some point we want to rpath our guesses as to
// Finally add all the linker arguments provided on the command line along
// with any #[link_args] attributes found inside the crate
- let empty = Vec::new();
- cmd.args(&sess.opts.cg.link_args.as_ref().unwrap_or(&empty));
- cmd.args(&used_link_args[..]);
+ if let Some(ref args) = sess.opts.cg.link_args {
+ cmd.args(args);
+ }
+ cmd.args(&used_link_args);
}
// # Native library linking
// 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 Command, sess: &Session) {
+fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| {
match k {
- PathKind::Framework => { cmd.arg("-F").arg(path); }
- _ => { cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(path)); }
+ PathKind::Framework => { cmd.framework_path(path); }
+ _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); }
}
FileDoesntMatch
});
- // Some platforms take hints about whether a library is static or dynamic.
- // For those that support this, we ensure we pass the option if the library
- // was flagged "static" (most defaults are dynamic) to ensure that if
- // libfoo.a and libfoo.so both exist that the right one is chosen.
- let takes_hints = !sess.target.target.options.is_like_osx;
-
let libs = sess.cstore.get_used_libraries();
let libs = libs.borrow();
kind != cstore::NativeStatic
});
- // Platforms that take hints generally also support the --whole-archive
- // flag. We need to pass this flag when linking static native libraries to
- // ensure the entire library is included.
- //
- // For more details see #15460, but the gist is that the linker will strip
- // away any unused objects in the archive if we don't otherwise explicitly
- // reference them. This can occur for libraries which are just providing
- // bindings, libraries with generic functions, etc.
- if takes_hints {
- cmd.arg("-Wl,--whole-archive").arg("-Wl,-Bstatic");
- }
+ // Some platforms take hints about whether a library is static or dynamic.
+ // For those that support this, we ensure we pass the option if the library
+ // was flagged "static" (most defaults are dynamic) to ensure that if
+ // libfoo.a and libfoo.so both exist that the right one is chosen.
+ cmd.hint_static();
+
let search_path = archive_search_paths(sess);
for l in staticlibs {
- if takes_hints {
- cmd.arg(&format!("-l{}", l));
- } else {
- // -force_load is the OSX equivalent of --whole-archive, but it
- // involves passing the full path to the library to link.
- let lib = archive::find_library(&l[..],
- &sess.target.target.options.staticlib_prefix,
- &sess.target.target.options.staticlib_suffix,
- &search_path[..],
- &sess.diagnostic().handler);
- let mut v = OsString::from("-Wl,-force_load,");
- v.push(&lib);
- cmd.arg(&v);
- }
- }
- if takes_hints {
- cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic");
+ // Here we explicitly ask that the entire archive is included into the
+ // result artifact. For more details see #15460, but the gist is that
+ // the linker will strip away any unused objects in the archive if we
+ // don't otherwise explicitly reference them. This can occur for
+ // libraries which are just providing bindings, libraries with generic
+ // functions, etc.
+ cmd.link_whole_staticlib(l, &search_path);
}
+ cmd.hint_dynamic();
+
for &(ref l, kind) in others {
match kind {
- cstore::NativeUnknown => {
- cmd.arg(&format!("-l{}", l));
- }
- cstore::NativeFramework => {
- cmd.arg("-framework").arg(&l[..]);
- }
+ cstore::NativeUnknown => cmd.link_dylib(l),
+ cstore::NativeFramework => cmd.link_framework(l),
cstore::NativeStatic => unreachable!(),
}
}
// 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 Command, sess: &Session,
+fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
dylib: bool, tmpdir: &Path,
trans: &CrateTranslation) {
// All of the heavy lifting has previously been accomplished by the
}
// Adds the static "rlib" versions of all crates to the command line.
- fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path,
+ fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path,
cratepath: &Path) {
// When performing LTO on an executable output, all of the
// bytecode from the upstream libraries has already been
archive.remove_file(&format!("{}.o", name));
let files = archive.files();
if files.iter().any(|s| s.ends_with(".o")) {
- cmd.arg(&dst);
+ cmd.link_rlib(&dst);
}
});
} else {
- cmd.arg(&fix_windows_verbatim_for_gcc(cratepath));
+ cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
}
}
// Same thing as above, but for dynamic crates instead of static crates.
- fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: &Path) {
+ fn add_dynamic_crate(cmd: &mut Linker, 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());
// Just need to tell the linker about where the library lives and
// what its name is
if let Some(dir) = cratepath.parent() {
- cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(dir));
+ cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
}
let filestem = cratepath.file_stem().unwrap().to_str().unwrap();
- cmd.arg(&format!("-l{}", unlib(&sess.target, filestem)));
+ cmd.link_dylib(&unlib(&sess.target, filestem));
}
}
// 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 Command, sess: &Session) {
+fn add_upstream_native_libraries(cmd: &mut Linker, 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
let libs = csearch::get_native_libraries(&sess.cstore, cnum);
for &(kind, ref lib) in &libs {
match kind {
- cstore::NativeUnknown => {
- cmd.arg(&format!("-l{}", *lib));
- }
- cstore::NativeFramework => {
- cmd.arg("-framework");
- cmd.arg(&lib[..]);
- }
+ cstore::NativeUnknown => cmd.link_dylib(lib),
+ cstore::NativeFramework => cmd.link_framework(lib),
cstore::NativeStatic => {
sess.bug("statics shouldn't be propagated");
}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::ffi::OsString;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use rustc_back::archive;
+use session::Session;
+use session::config;
+
+/// Linker abstraction used by back::link to build up the command to invoke a
+/// linker.
+///
+/// This trait is the total list of requirements needed by `back::link` and
+/// 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 {
+ fn link_dylib(&mut self, lib: &str);
+ fn link_framework(&mut self, framework: &str);
+ fn link_staticlib(&mut self, lib: &str);
+ fn link_rlib(&mut self, lib: &Path);
+ fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
+ fn include_path(&mut self, path: &Path);
+ fn framework_path(&mut self, path: &Path);
+ fn output_filename(&mut self, path: &Path);
+ fn add_object(&mut self, path: &Path);
+ fn gc_sections(&mut self, is_dylib: bool);
+ fn position_independent_executable(&mut self);
+ fn optimize(&mut self);
+ fn no_default_libraries(&mut self);
+ fn build_dylib(&mut self, out_filename: &Path);
+ fn args(&mut self, args: &[String]);
+ fn hint_static(&mut self);
+ fn hint_dynamic(&mut self);
+ fn whole_archives(&mut self);
+ fn no_whole_archives(&mut self);
+}
+
+pub struct GnuLinker<'a> {
+ pub cmd: &'a mut Command,
+ pub sess: &'a Session,
+}
+
+impl<'a> GnuLinker<'a> {
+ fn takes_hints(&self) -> bool {
+ !self.sess.target.target.options.is_like_osx
+ }
+}
+
+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_framework(&mut self, framework: &str) {
+ self.cmd.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 {
+ // -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,
+ &target.options.staticlib_prefix,
+ &target.options.staticlib_suffix,
+ search_path,
+ &self.sess.diagnostic().handler));
+ self.cmd.arg(&v);
+ }
+ }
+
+ fn gc_sections(&mut self, is_dylib: bool) {
+ // The dead_strip option to the linker specifies that functions and data
+ // unreachable by the entry point will be removed. This is quite useful
+ // with Rust's compilation model of compiling libraries at a time into
+ // one object file. For example, this brings hello world from 1.7MB to
+ // 458K.
+ //
+ // Note that this is done for both executables and dynamic libraries. We
+ // won't get much benefit from dylibs because LLVM will have already
+ // stripped away as much as it could. This has not been seen to impact
+ // link times negatively.
+ //
+ // -dead_strip can't be part of the pre_link_args because it's also used
+ // 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");
+
+ // 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
+ // eliminate the metadata. If we're building an executable, however,
+ // --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");
+ }
+ }
+
+ fn optimize(&mut self) {
+ if !self.sess.target.target.options.linker_is_gnu { return }
+
+ // GNU-style linkers support optimization with -O. GNU ld doesn't
+ // need a numeric argument, but other linkers do.
+ if self.sess.opts.optimize == config::Default ||
+ self.sess.opts.optimize == config::Aggressive {
+ self.cmd.arg("-Wl,-O1");
+ }
+ }
+
+ fn no_default_libraries(&mut self) {
+ // Unfortunately right now passing -nodefaultlibs to gcc on windows
+ // doesn't work so hot (in terms of native dependencies). This if
+ // statement should hopefully be removed one day though!
+ if !self.sess.target.target.options.is_like_windows {
+ self.cmd.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"]);
+
+ 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);
+ }
+ } else {
+ self.cmd.arg("-shared");
+ }
+ }
+
+ fn whole_archives(&mut self) {
+ if !self.takes_hints() { return }
+ self.cmd.arg("-Wl,--whole-archive");
+ }
+
+ fn no_whole_archives(&mut self) {
+ if !self.takes_hints() { return }
+ self.cmd.arg("-Wl,--no-whole-archive");
+ }
+
+ fn hint_static(&mut self) {
+ if !self.takes_hints() { return }
+ self.cmd.arg("-Wl,-Bstatic");
+ }
+
+ fn hint_dynamic(&mut self) {
+ if !self.takes_hints() { return }
+ self.cmd.arg("-Wl,-Bdynamic");
+ }
+}