Skip to content

Commit

Permalink
Emit linker hints for all library kinds.
Browse files Browse the repository at this point in the history
  • Loading branch information
vadimcn committed Mar 30, 2017
1 parent 5c94997 commit 78c3a49
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 90 deletions.
31 changes: 6 additions & 25 deletions src/librustc_trans/back/link.rs
Expand Up @@ -734,9 +734,10 @@ fn link_natively(sess: &Session,
}

{
let mut linker = trans.linker_info.to_linker(&mut cmd, &sess);
let mut linker = trans.linker_info.to_linker(cmd, &sess);
link_args(&mut *linker, sess, crate_type, tmpdir,
objects, out_filename, outputs, trans);
cmd = linker.finalize();
}
cmd.args(&sess.target.target.options.late_link_args);
for obj in &sess.target.target.options.post_link_objects {
Expand Down Expand Up @@ -1021,38 +1022,18 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
}
});

let pair = sess.cstore.used_libraries().into_iter().filter(|l| {
let relevant_libs = sess.cstore.used_libraries().into_iter().filter(|l| {
relevant_lib(sess, l)
}).partition(|lib| {
lib.kind == NativeLibraryKind::NativeStatic
});
let (staticlibs, others): (Vec<_>, Vec<_>) = pair;

// 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 {
// 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.name.as_str(), &search_path);
}

cmd.hint_dynamic();

for lib in others {
for lib in relevant_libs {
match lib.kind {
NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()),
NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()),
NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()),
NativeLibraryKind::NativeStatic => bug!(),
NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(),
&search_path)
}
}
}
Expand Down
127 changes: 62 additions & 65 deletions src/librustc_trans/back/linker.rs
Expand Up @@ -43,7 +43,7 @@ impl<'a, 'tcx> LinkerInfo {
}

pub fn to_linker(&'a self,
cmd: &'a mut Command,
cmd: Command,
sess: &'a Session) -> Box<Linker+'a> {
if sess.target.target.options.is_like_msvc {
Box::new(MsvcLinker {
Expand All @@ -61,7 +61,8 @@ impl<'a, 'tcx> LinkerInfo {
Box::new(GnuLinker {
cmd: cmd,
sess: sess,
info: self
info: self,
hinted_static: false,
}) as Box<Linker>
}
}
Expand Down Expand Up @@ -93,30 +94,49 @@ pub trait Linker {
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);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
fn subsystem(&mut self, subsystem: &str);
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
fn finalize(&mut self) -> Command;
}

pub struct GnuLinker<'a> {
cmd: &'a mut Command,
cmd: Command,
sess: &'a Session,
info: &'a LinkerInfo
info: &'a LinkerInfo,
hinted_static: bool, // Keeps track of the current hinting mode.
}

impl<'a> GnuLinker<'a> {
fn takes_hints(&self) -> bool {
!self.sess.target.target.options.is_like_osx
}

// 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.
fn hint_static(&mut self) {
if !self.takes_hints() { return }
if !self.hinted_static {
self.cmd.arg("-Wl,-Bstatic");
self.hinted_static = true;
}
}

fn hint_dynamic(&mut self) {
if !self.takes_hints() { return }
if self.hinted_static {
self.cmd.arg("-Wl,-Bdynamic");
self.hinted_static = false;
}
}
}

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 link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); }
fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); }
fn link_rlib(&mut self, lib: &Path) { self.hint_static(); 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); }
Expand All @@ -125,14 +145,23 @@ impl<'a> Linker for GnuLinker<'a> {
fn args(&mut self, args: &[String]) { self.cmd.args(args); }

fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
self.hint_dynamic();
self.cmd.arg("-l").arg(lib);
}

fn link_framework(&mut self, framework: &str) {
self.hint_dynamic();
self.cmd.arg("-framework").arg(framework);
}

// 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.
fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
self.hint_static();
let target = &self.sess.target.target;
if !target.options.is_like_osx {
self.cmd.arg("-Wl,--whole-archive")
Expand All @@ -148,6 +177,7 @@ impl<'a> Linker for GnuLinker<'a> {
}

fn link_whole_rlib(&mut self, lib: &Path) {
self.hint_static();
if self.sess.target.target.options.is_like_osx {
let mut v = OsString::from("-Wl,-force_load,");
v.push(lib);
Expand Down Expand Up @@ -228,26 +258,6 @@ impl<'a> Linker for GnuLinker<'a> {
}
}

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");
}

fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) {
// If we're compiling a dylib, then we let symbol visibility in object
// files to take care of whether they're exported or not.
Expand Down Expand Up @@ -311,10 +321,17 @@ impl<'a> Linker for GnuLinker<'a> {
fn subsystem(&mut self, subsystem: &str) {
self.cmd.arg(&format!("-Wl,--subsystem,{}", subsystem));
}

fn finalize(&mut self) -> Command {
self.hint_dynamic(); // Reset to default before returning the composed command line.
let mut cmd = Command::new("");
::std::mem::swap(&mut cmd, &mut self.cmd);
cmd
}
}

pub struct MsvcLinker<'a> {
cmd: &'a mut Command,
cmd: Command,
sess: &'a Session,
info: &'a LinkerInfo
}
Expand Down Expand Up @@ -416,22 +433,6 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg("/DEBUG");
}

fn whole_archives(&mut self) {
// hints not supported?
}
fn no_whole_archives(&mut self) {
// hints not supported?
}

// On windows static libraries are of the form `foo.lib` and dynamic
// libraries are not linked against directly, but rather through their
// import libraries also called `foo.lib`. As a result there's no
// possibility for a native library to appear both dynamically and
// statically in the same folder so we don't have to worry about hints like
// we do on Unix platforms.
fn hint_static(&mut self) {}
fn hint_dynamic(&mut self) {}

// Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
// export symbols from a dynamic library. When building a dynamic library,
// however, we're going to want some symbols exported, so this function
Expand Down Expand Up @@ -492,10 +493,16 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg("/ENTRY:mainCRTStartup");
}
}

fn finalize(&mut self) -> Command {
let mut cmd = Command::new("");
::std::mem::swap(&mut cmd, &mut self.cmd);
cmd
}
}

pub struct EmLinker<'a> {
cmd: &'a mut Command,
cmd: Command,
sess: &'a Session,
info: &'a LinkerInfo
}
Expand Down Expand Up @@ -591,22 +598,6 @@ impl<'a> Linker for EmLinker<'a> {
bug!("building dynamic library is unsupported on Emscripten")
}

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

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

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

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

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

Expand Down Expand Up @@ -640,6 +631,12 @@ impl<'a> Linker for EmLinker<'a> {
fn subsystem(&mut self, _subsystem: &str) {
// noop
}

fn finalize(&mut self) -> Command {
let mut cmd = Command::new("");
::std::mem::swap(&mut cmd, &mut self.cmd);
cmd
}
}

fn exported_symbols(scx: &SharedCrateContext,
Expand Down

0 comments on commit 78c3a49

Please sign in to comment.