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

rustc: Link entire archives of native libraries #16110

Merged
merged 1 commit into from
Aug 4, 2014
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
3 changes: 3 additions & 0 deletions src/liballoc/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ mod imp {
use libc::{c_char, c_int, c_void, size_t};

#[link(name = "jemalloc", kind = "static")]
#[cfg(not(test))]
extern {}

extern {
fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
fn je_rallocx(ptr: *mut c_void, size: size_t,
Expand Down
65 changes: 49 additions & 16 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
use super::archive;
use super::rpath;
use super::rpath::RPathConfig;
use super::svh::Svh;
Expand Down Expand Up @@ -1597,29 +1598,61 @@ fn add_local_native_libraries(cmd: &mut Command, sess: &Session) {
// 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.targ_cfg.os != abi::OsMacos && sess.targ_cfg.os != abi::OsiOS;
let takes_hints = sess.targ_cfg.os != abi::OsMacos &&
sess.targ_cfg.os != abi::OsiOS;

let libs = sess.cstore.get_used_libraries();
let libs = libs.borrow();

let mut staticlibs = libs.iter().filter_map(|&(ref l, kind)| {
if kind == cstore::NativeStatic {Some(l)} else {None}
});
let mut others = libs.iter().filter(|&&(_, kind)| {
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");
}
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.as_slice(),
sess.targ_cfg.os,
search_path.as_slice(),
&sess.diagnostic().handler);
let mut v = b"-Wl,-force_load,".to_vec();
v.push_all(lib.as_vec());
cmd.arg(v.as_slice());
}
}
if takes_hints {
cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic");
}

for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() {
for &(ref l, kind) in others {
match kind {
cstore::NativeUnknown | cstore::NativeStatic => {
if takes_hints {
if kind == cstore::NativeStatic {
cmd.arg("-Wl,-Bstatic");
} else {
cmd.arg("-Wl,-Bdynamic");
}
}
cmd.arg(format!("-l{}", *l));
cstore::NativeUnknown => {
cmd.arg(format!("-l{}", l));
}
cstore::NativeFramework => {
cmd.arg("-framework");
cmd.arg(l.as_slice());
cmd.arg("-framework").arg(l.as_slice());
}
cstore::NativeStatic => unreachable!(),
}
}
if takes_hints {
cmd.arg("-Wl,-Bdynamic");
}
}

// # Rust Crate linking
Expand Down
51 changes: 27 additions & 24 deletions src/librustc_back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
}
}

pub fn find_library(name: &str, os: abi::Os, search_paths: &[Path],
handler: &ErrorHandler) -> Path {
let (osprefix, osext) = match os {
abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
};
// On Windows, static libraries sometimes show up as libfoo.a and other
// times show up as foo.lib
let oslibname = format!("{}{}.{}", osprefix, name, osext);
let unixlibname = format!("lib{}.a", name);

for path in search_paths.iter() {
debug!("looking for {} inside {}", name, path.display());
let test = path.join(oslibname.as_slice());
if test.exists() { return test }
if oslibname != unixlibname {
let test = path.join(unixlibname.as_slice());
if test.exists() { return test }
}
}
handler.fatal(format!("could not find native static library `{}`, \
perhaps an -L flag is missing?",
name).as_slice());
}

impl<'a> Archive<'a> {
fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
let ArchiveConfig { handler, dst, lib_search_paths, os, maybe_ar_prog } = config;
Expand Down Expand Up @@ -153,7 +177,9 @@ impl<'a> ArchiveBuilder<'a> {
/// Adds all of the contents of a native library to this archive. This will
/// search in the relevant locations for a library named `name`.
pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
let location = self.find_library(name);
let location = find_library(name, self.archive.os,
self.archive.lib_search_paths.as_slice(),
self.archive.handler);
self.add_archive(&location, name, [])
}

Expand Down Expand Up @@ -285,28 +311,5 @@ impl<'a> ArchiveBuilder<'a> {
}
Ok(())
}

fn find_library(&self, name: &str) -> Path {
let (osprefix, osext) = match self.archive.os {
abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
};
// On Windows, static libraries sometimes show up as libfoo.a and other
// times show up as foo.lib
let oslibname = format!("{}{}.{}", osprefix, name, osext);
let unixlibname = format!("lib{}.a", name);

for path in self.archive.lib_search_paths.iter() {
debug!("looking for {} inside {}", name, path.display());
let test = path.join(oslibname.as_slice());
if test.exists() { return test }
if oslibname != unixlibname {
let test = path.join(unixlibname.as_slice());
if test.exists() { return test }
}
}
self.archive.handler.fatal(format!("could not find native static library `{}`, \
perhaps an -L flag is missing?",
name).as_slice());
}
}

3 changes: 3 additions & 0 deletions src/librustrt/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub unsafe fn try(f: ||) -> ::core::result::Result<(), Box<Any + Send>> {
}

#[link(name = "rustrt_native", kind = "static")]
#[cfg(not(test))]
extern {}

extern {
// Rust's try-catch
// When f(...) returns normally, the return value is null.
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/rt/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ mod imp {
errnum: libc::c_int);
enum backtrace_state {}
#[link(name = "backtrace", kind = "static")]
#[cfg(not(test))]
extern {}

extern {
fn backtrace_create_state(filename: *const libc::c_char,
threaded: libc::c_int,
Expand Down
1 change: 1 addition & 0 deletions src/libstd/rtdeps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#![experimental]

// All platforms need to link to rustrt
#[cfg(not(test))]
#[link(name = "rust_builtin", kind = "static")]
extern {}

Expand Down
1 change: 0 additions & 1 deletion src/test/run-make/extern-fn-with-union/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ extern crate testcrate;

use std::mem;

#[link(name = "test", kind = "static")]
extern {
fn give_back(tu: testcrate::TestUnion) -> u64;
}
Expand Down
6 changes: 6 additions & 0 deletions src/test/run-make/issue-15460/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-include ../tools.mk

all: $(TMPDIR)/libfoo.a
$(RUSTC) foo.rs -C extra-filename=-383hf8
$(RUSTC) bar.rs
$(call RUN,bar)
14 changes: 14 additions & 0 deletions src/test/run-make/issue-15460/bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2014 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.

extern crate foo;
fn main() {
unsafe { foo::foo() }
}
1 change: 1 addition & 0 deletions src/test/run-make/issue-15460/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void foo() {}
16 changes: 16 additions & 0 deletions src/test/run-make/issue-15460/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2014 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.

#![crate_type = "dylib"]

#[link(name = "foo", kind = "static")]
extern {
pub fn foo();
}
5 changes: 3 additions & 2 deletions src/test/run-make/no-duplicate-libs/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-include ../tools.mk

all: $(call STATICLIB,foo) $(call STATICLIB,bar)
all:
$(RUSTC) foo.rs
$(RUSTC) bar.rs
$(RUSTC) main.rs
$(call RUN,main)

21 changes: 21 additions & 0 deletions src/test/run-make/no-duplicate-libs/bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2014 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.

#![no_std]
#![feature(lang_items)]
#![crate_type = "dylib"]

extern crate libc;

#[no_mangle]
pub extern fn bar() {}

#[lang = "stack_exhausted"] fn stack_exhausted() {}
#[lang = "eh_personality"] fn eh_personality() {}
21 changes: 21 additions & 0 deletions src/test/run-make/no-duplicate-libs/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2014 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.

#![no_std]
#![feature(lang_items)]
#![crate_type = "dylib"]

extern crate libc;

#[no_mangle]
pub extern fn foo() {}

#[lang = "stack_exhausted"] fn stack_exhausted() {}
#[lang = "eh_personality"] fn eh_personality() {}
1 change: 0 additions & 1 deletion src/test/run-pass/foreign-dupe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ mod rustrt1 {
mod rustrt2 {
extern crate libc;

#[link(name = "rust_test_helpers")]
extern {
pub fn rust_get_test_int() -> libc::intptr_t;
}
Expand Down