Skip to content

Commit

Permalink
rustc: Implement #[link(cfg(..))] and crt-static
Browse files Browse the repository at this point in the history
This commit is an implementation of [RFC 1721] which adds a new target feature
to the compiler, `crt-static`, which can be used to select how the C runtime for
a target is linked. Most targets dynamically linke the C runtime by default with
the notable exception of some of the musl targets.

[RFC 1721]: https://github.com/rust-lang/rfcs/blob/master/text/1721-crt-static.md

This commit first adds the new target-feature, `crt-static`. If enabled, then
the `cfg(target_feature = "crt-static")` will be available. Targets like musl
will have this enabled by default. This feature can be controlled through the
standard target-feature interface, `-C target-feature=+crt-static` or
`-C target-feature=-crt-static`.

Next this adds an gated and unstable `#[link(cfg(..))]` feature to enable the
`crt-static` semantics we want with libc. The exact behavior of this attribute
is a little squishy, but it's intended to be a forever-unstable
implementation detail of the liblibc crate.

Specifically the `#[link(cfg(..))]` annotation means that the `#[link]`
directive is only active in a compilation unit if that `cfg` value is satisfied.
For example when compiling an rlib, these directives are just encoded and
ignored for dylibs, and all staticlibs are continued to be put into the rlib as
usual. When placing that rlib into a staticlib, executable, or dylib, however,
the `cfg` is evaluated *as if it were defined in the final artifact* and the
library is decided to be linked or not.

Essentially, what'll happen is:

* On MSVC with `-C target-feature=-crt-static`, the `msvcrt.lib` library will be
  linked to.
* On MSVC with `-C target-feature=+crt-static`, the `libcmt.lib` library will be
  linked to.
* On musl with `-C target-feature=-crt-static`, the object files in liblibc.rlib
  are removed and `-lc` is passed instead.
* On musl with `-C target-feature=+crt-static`, the object files in liblibc.rlib
  are used and `-lc` is not passed.

This commit does **not** include an update to the liblibc module to implement
these changes. I plan to do that just after the 1.14.0 beta release is cut to
ensure we get ample time to test this feature.

cc rust-lang#37406
  • Loading branch information
alexcrichton committed Nov 15, 2016
1 parent c8867f8 commit 957d3e9
Show file tree
Hide file tree
Showing 31 changed files with 547 additions and 92 deletions.
17 changes: 13 additions & 4 deletions src/librustc/middle/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ pub enum NativeLibraryKind {
NativeUnknown, // default way to specify a dynamic library
}

#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct NativeLibrary {
pub kind: NativeLibraryKind,
pub name: String,
pub cfg: Option<P<ast::MetaItem>>,
}

/// The data we save and restore about an inlined item or method. This is not
/// part of the AST that we parse from a file, but it becomes part of the tree
/// that we trans.
Expand Down Expand Up @@ -204,7 +211,7 @@ pub trait CrateStore<'tcx> {
fn crate_hash(&self, cnum: CrateNum) -> Svh;
fn crate_disambiguator(&self, cnum: CrateNum) -> InternedString;
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>;
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>;
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>;
fn is_no_builtins(&self, cnum: CrateNum) -> bool;

Expand All @@ -231,7 +238,7 @@ pub trait CrateStore<'tcx> {
// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
fn crates(&self) -> Vec<CrateNum>;
fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)>;
fn used_libraries(&self) -> Vec<NativeLibrary>;
fn used_link_args(&self) -> Vec<String>;

// utility functions
Expand Down Expand Up @@ -377,7 +384,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
-> InternedString { bug!("crate_disambiguator") }
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
{ bug!("plugin_registrar_fn") }
fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
{ bug!("native_libraries") }
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId> { bug!("reachable_ids") }
fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") }
Expand Down Expand Up @@ -412,7 +419,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
// This is basically a 1-based range of ints, which is a little
// silly - I may fix that.
fn crates(&self) -> Vec<CrateNum> { vec![] }
fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)> { vec![] }
fn used_libraries(&self) -> Vec<NativeLibrary> {
vec![]
}
fn used_link_args(&self) -> Vec<String> { vec![] }

// utility functions
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_back/target/linux_musl_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub fn opts() -> TargetOptions {
// 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
Expand Down Expand Up @@ -67,5 +66,8 @@ pub fn opts() -> TargetOptions {
base.has_rpath = false;
base.position_independent_executables = false;

// These targets statically link libc by default
base.crt_static_default = true;

base
}
6 changes: 6 additions & 0 deletions src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ pub struct TargetOptions {
/// A blacklist of ABIs unsupported by the current target. Note that generic
/// ABIs are considered to be supported on all platforms and cannot be blacklisted.
pub abi_blacklist: Vec<Abi>,

/// Whether or not the CRT is statically linked by default.
pub crt_static_default: bool,
}

impl Default for TargetOptions {
Expand Down Expand Up @@ -425,6 +428,7 @@ impl Default for TargetOptions {
max_atomic_width: None,
panic_strategy: PanicStrategy::Unwind,
abi_blacklist: vec![],
crt_static_default: false,
}
}
}
Expand Down Expand Up @@ -585,6 +589,7 @@ impl Target {
key!(no_integrated_as, bool);
key!(max_atomic_width, Option<u64>);
try!(key!(panic_strategy, PanicStrategy));
key!(crt_static_default, bool);

if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
for name in array.iter().filter_map(|abi| abi.as_string()) {
Expand Down Expand Up @@ -745,6 +750,7 @@ impl ToJson for Target {
target_option_val!(no_integrated_as);
target_option_val!(max_atomic_width);
target_option_val!(panic_strategy);
target_option_val!(crt_static_default);

if default.abi_blacklist != self.options.abi_blacklist {
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
Expand Down
29 changes: 29 additions & 0 deletions src/librustc_driver/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use syntax::{ast, attr};
use llvm::LLVMRustHasFeature;
use rustc::session::Session;
use rustc_trans::back::write::create_target_machine;
use syntax::feature_gate::UnstableFeatures;
use syntax::parse::token::InternedString;
use syntax::parse::token::intern_and_get_ident as intern;
use libc::c_char;
Expand Down Expand Up @@ -47,4 +48,32 @@ pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session) {
cfg.push(attr::mk_name_value_item_str(tf.clone(), intern(&feat[..feat.len() - 1])))
}
}

let mut requested_features = sess.opts.cg.target_feature.split(',');
let unstable_options = sess.opts.debugging_opts.unstable_options;
let is_nightly = UnstableFeatures::from_environment().is_nightly_build();
let found_negative = requested_features.any(|r| r == "-crt-static");
let found_positive = requested_features.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 we switched from the default then that's only allowed on nightly, so
// gate that here.
if (found_positive || found_negative) && (!is_nightly || !unstable_options) {
sess.fatal("specifying the `crt-static` target feature is only allowed \
on the nightly channel with `-Z unstable-options` passed \
as well");
}

if crt_static {
cfg.push(attr::mk_name_value_item_str(tf.clone(), intern("crt-static")));
}
}
49 changes: 35 additions & 14 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use rustc::session::search_paths::PathKind;
use rustc::middle;
use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc::middle::cstore::NativeLibrary;
use rustc::hir::map::Definitions;

use std::cell::{RefCell, Cell};
Expand All @@ -35,6 +36,7 @@ use syntax::ast;
use syntax::abi::Abi;
use syntax::attr;
use syntax::ext::base::SyntaxExtension;
use syntax::feature_gate::{self, GateIssue};
use syntax::parse::token::{InternedString, intern};
use syntax_pos::{Span, DUMMY_SP};
use log;
Expand Down Expand Up @@ -77,9 +79,8 @@ struct ExternCrateInfo {
fn register_native_lib(sess: &Session,
cstore: &CStore,
span: Option<Span>,
name: String,
kind: cstore::NativeLibraryKind) {
if name.is_empty() {
lib: NativeLibrary) {
if lib.name.is_empty() {
match span {
Some(span) => {
struct_span_err!(sess, span, E0454,
Expand All @@ -94,17 +95,21 @@ fn register_native_lib(sess: &Session,
return
}
let is_osx = sess.target.target.options.is_like_osx;
if kind == cstore::NativeFramework && !is_osx {
if lib.kind == cstore::NativeFramework && !is_osx {
let msg = "native frameworks are only available on OSX targets";
match span {
Some(span) => {
span_err!(sess, span, E0455,
"{}", msg)
}
Some(span) => span_err!(sess, span, E0455, "{}", msg),
None => sess.err(msg),
}
}
cstore.add_used_library(name, kind);
if lib.cfg.is_some() && !sess.features.borrow().link_cfg {
feature_gate::emit_feature_err(&sess.parse_sess,
"link_cfg",
span.unwrap(),
GateIssue::Language,
"is feature gated");
}
cstore.add_used_library(lib);
}

// Extra info about a crate loaded for plugins or exported macros.
Expand Down Expand Up @@ -635,9 +640,9 @@ impl<'a> CrateLoader<'a> {

fn register_statically_included_foreign_items(&mut self) {
let libs = self.cstore.get_used_libraries();
for (lib, list) in self.foreign_item_map.iter() {
let is_static = libs.borrow().iter().any(|&(ref name, kind)| {
lib == name && kind == cstore::NativeStatic
for (foreign_lib, list) in self.foreign_item_map.iter() {
let is_static = libs.borrow().iter().any(|lib| {
*foreign_lib == lib.name && lib.kind == cstore::NativeStatic
});
if is_static {
for id in list {
Expand Down Expand Up @@ -898,7 +903,18 @@ impl<'a> CrateLoader<'a> {
InternedString::new("foo")
}
};
register_native_lib(self.sess, self.cstore, Some(m.span), n.to_string(), kind);
let cfg = items.iter().find(|k| {
k.check_name("cfg")
}).and_then(|a| a.meta_item_list());
let cfg = cfg.map(|list| {
list[0].meta_item().unwrap().clone()
});
let lib = NativeLibrary {
name: n.to_string(),
kind: kind,
cfg: cfg,
};
register_native_lib(self.sess, self.cstore, Some(m.span), lib);
}

// Finally, process the #[linked_from = "..."] attribute
Expand All @@ -924,7 +940,12 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> {
}

for &(ref name, kind) in &self.sess.opts.libs {
register_native_lib(self.sess, self.cstore, None, name.clone(), kind);
let lib = NativeLibrary {
name: name.clone(),
kind: kind,
cfg: None,
};
register_native_lib(self.sess, self.cstore, None, lib);
}
self.register_statically_included_foreign_items();
}
Expand Down
12 changes: 6 additions & 6 deletions src/librustc_metadata/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use syntax::{ast, attr};
use syntax::ext::base::SyntaxExtension;
use syntax_pos;

pub use rustc::middle::cstore::{NativeLibraryKind, LinkagePreference};
pub use rustc::middle::cstore::{NativeLibrary, LinkagePreference};
pub use rustc::middle::cstore::{NativeStatic, NativeFramework, NativeUnknown};
pub use rustc::middle::cstore::{CrateSource, LinkMeta};

Expand Down Expand Up @@ -97,7 +97,7 @@ pub struct CStore {
metas: RefCell<FxHashMap<CrateNum, Rc<CrateMetadata>>>,
/// Map from NodeId's of local extern crate statements to crate numbers
extern_mod_crate_map: RefCell<NodeMap<CrateNum>>,
used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
used_libraries: RefCell<Vec<NativeLibrary>>,
used_link_args: RefCell<Vec<String>>,
statically_included_foreign_items: RefCell<NodeSet>,
pub inlined_item_cache: RefCell<DefIdMap<Option<CachedInlinedItem>>>,
Expand Down Expand Up @@ -212,12 +212,12 @@ impl CStore {
libs
}

pub fn add_used_library(&self, lib: String, kind: NativeLibraryKind) {
assert!(!lib.is_empty());
self.used_libraries.borrow_mut().push((lib, kind));
pub fn add_used_library(&self, lib: NativeLibrary) {
assert!(!lib.name.is_empty());
self.used_libraries.borrow_mut().push(lib);
}

pub fn get_used_libraries<'a>(&'a self) -> &'a RefCell<Vec<(String, NativeLibraryKind)>> {
pub fn get_used_libraries(&self) -> &RefCell<Vec<NativeLibrary>> {
&self.used_libraries
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use locator;
use schema;

use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, DepKind, ExternCrate};
use rustc::middle::cstore::{NativeLibraryKind, LinkMeta, LinkagePreference, LoadedMacro};
use rustc::middle::cstore::{NativeLibrary, LinkMeta, LinkagePreference, LoadedMacro};
use rustc::hir::def::{self, Def};
use rustc::middle::lang_items;
use rustc::session::Session;
Expand Down Expand Up @@ -295,7 +295,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
})
}

fn native_libraries(&self, cnum: CrateNum) -> Vec<(NativeLibraryKind, String)>
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
{
self.get_crate_data(cnum).get_native_libraries()
}
Expand Down Expand Up @@ -524,7 +524,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
result
}

fn used_libraries(&self) -> Vec<(String, NativeLibraryKind)>
fn used_libraries(&self) -> Vec<NativeLibrary>
{
self.get_used_libraries().borrow().clone()
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// Decoding metadata from a single crate's metadata

use astencode::decode_inlined_item;
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibraryKind};
use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary};
use index::Index;
use schema::*;

Expand Down Expand Up @@ -980,7 +980,7 @@ impl<'a, 'tcx> CrateMetadata {
}


pub fn get_native_libraries(&self) -> Vec<(NativeLibraryKind, String)> {
pub fn get_native_libraries(&self) -> Vec<NativeLibrary> {
self.root.native_libraries.decode(self).collect()
}

Expand Down
11 changes: 3 additions & 8 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use index::Index;
use schema::*;

use rustc::middle::cstore::{InlinedItemRef, LinkMeta};
use rustc::middle::cstore::{LinkagePreference, NativeLibraryKind};
use rustc::middle::cstore::{LinkagePreference, NativeLibrary};
use rustc::hir::def;
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId};
use rustc::middle::dependency_format::Linkage;
Expand Down Expand Up @@ -1134,14 +1134,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_seq_ref(&tcx.lang_items.missing))
}

fn encode_native_libraries(&mut self) -> LazySeq<(NativeLibraryKind, String)> {
fn encode_native_libraries(&mut self) -> LazySeq<NativeLibrary> {
let used_libraries = self.tcx.sess.cstore.used_libraries();
self.lazy_seq(used_libraries.into_iter().filter_map(|(lib, kind)| {
match kind {
cstore::NativeStatic => None, // these libraries are not propagated
cstore::NativeFramework | cstore::NativeUnknown => Some((kind, lib)),
}
}))
self.lazy_seq(used_libraries)
}

fn encode_codemap(&mut self) -> LazySeq<syntax_pos::FileMap> {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_metadata/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use index;
use rustc::hir;
use rustc::hir::def::{self, CtorKind};
use rustc::hir::def_id::{DefIndex, DefId};
use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibraryKind};
use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary};
use rustc::middle::lang_items;
use rustc::mir;
use rustc::ty::{self, Ty};
Expand Down Expand Up @@ -175,7 +175,7 @@ pub struct CrateRoot {
pub dylib_dependency_formats: LazySeq<Option<LinkagePreference>>,
pub lang_items: LazySeq<(DefIndex, usize)>,
pub lang_items_missing: LazySeq<lang_items::LangItem>,
pub native_libraries: LazySeq<(NativeLibraryKind, String)>,
pub native_libraries: LazySeq<NativeLibrary>,
pub codemap: LazySeq<syntax_pos::FileMap>,
pub impls: LazySeq<TraitImpls>,
pub reachable_ids: LazySeq<DefIndex>,
Expand Down
Loading

0 comments on commit 957d3e9

Please sign in to comment.