Skip to content

Commit

Permalink
rustc: Add a new crate type, cdylib
Browse files Browse the repository at this point in the history
This commit is an implementation of [RFC 1510] which adds a new crate type,
`cdylib`, to the compiler. This new crate type differs from the existing `dylib`
crate type in a few key ways:

* No metadata is present in the final artifact
* Symbol visibility rules are the same as executables, that is only reachable
  `extern` functions are visible symbols
* LTO is allowed
* All libraries are always linked statically

This commit is relatively simple by just plubming the compiler with another
crate type which takes different branches here and there. The only major change
is an implementation of the `Linker::export_symbols` function on Unix which now
actually does something. This helps restrict the public symbols from a cdylib on
Unix.

With this PR a "hello world" `cdylib` is 7.2K while the same `dylib` is 2.4MB,
which is some nice size savings!

[RFC 1510]: rust-lang/rfcs#1510

Closes #33132
  • Loading branch information
alexcrichton committed May 19, 2016
1 parent 9743c66 commit 07d373f
Show file tree
Hide file tree
Showing 16 changed files with 272 additions and 91 deletions.
7 changes: 4 additions & 3 deletions src/librustc/middle/dependency_format.rs
Expand Up @@ -115,9 +115,10 @@ fn calculate_type(sess: &session::Session,
// got long ago), so don't bother with anything.
config::CrateTypeRlib => return Vec::new(),

// Staticlibs must have all static dependencies. If any fail to be
// found, we generate some nice pretty errors.
config::CrateTypeStaticlib => {
// Staticlibs and cdylibs must have all static dependencies. If any fail
// to be found, we generate some nice pretty errors.
config::CrateTypeStaticlib |
config::CrateTypeCdylib => {
match attempt_static(sess) {
Some(v) => return v,
None => {}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/reachable.rs
Expand Up @@ -145,7 +145,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
// Creates a new reachability computation context.
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ReachableContext<'a, 'tcx> {
let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| {
*ty != config::CrateTypeExecutable
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib
});
ReachableContext {
tcx: tcx,
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/weak_lang_items.rs
Expand Up @@ -70,6 +70,7 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) {
let needs_check = sess.crate_types.borrow().iter().any(|kind| {
match *kind {
config::CrateTypeDylib |
config::CrateTypeCdylib |
config::CrateTypeExecutable |
config::CrateTypeStaticlib => true,
config::CrateTypeRlib => false,
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/session/config.rs
Expand Up @@ -300,6 +300,7 @@ pub enum CrateType {
CrateTypeDylib,
CrateTypeRlib,
CrateTypeStaticlib,
CrateTypeCdylib,
}

#[derive(Clone)]
Expand Down Expand Up @@ -1326,6 +1327,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
"rlib" => CrateTypeRlib,
"staticlib" => CrateTypeStaticlib,
"dylib" => CrateTypeDylib,
"cdylib" => CrateTypeCdylib,
"bin" => CrateTypeExecutable,
_ => {
return Err(format!("unknown crate type: `{}`",
Expand Down Expand Up @@ -1413,7 +1415,8 @@ impl fmt::Display for CrateType {
CrateTypeExecutable => "bin".fmt(f),
CrateTypeDylib => "dylib".fmt(f),
CrateTypeRlib => "rlib".fmt(f),
CrateTypeStaticlib => "staticlib".fmt(f)
CrateTypeStaticlib => "staticlib".fmt(f),
CrateTypeCdylib => "cdylib".fmt(f),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_driver/driver.rs
Expand Up @@ -1175,6 +1175,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
Some(ref n) if *n == "dylib" => {
Some(config::CrateTypeDylib)
}
Some(ref n) if *n == "cdylib" => {
Some(config::CrateTypeCdylib)
}
Some(ref n) if *n == "lib" => {
Some(config::default_lib_output())
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_metadata/creader.rs
Expand Up @@ -743,6 +743,7 @@ impl<'a> CrateReader<'a> {
match *ct {
config::CrateTypeExecutable => need_exe_alloc = true,
config::CrateTypeDylib |
config::CrateTypeCdylib |
config::CrateTypeStaticlib => need_lib_alloc = true,
config::CrateTypeRlib => {}
}
Expand Down
80 changes: 42 additions & 38 deletions src/librustc_trans/back/link.rs
Expand Up @@ -228,6 +228,7 @@ pub fn invalid_output_for_target(sess: &Session,
crate_type: config::CrateType) -> bool {
match (sess.target.target.options.dynamic_linking,
sess.target.target.options.executables, crate_type) {
(false, _, config::CrateTypeCdylib) |
(false, _, config::CrateTypeDylib) => true,
(_, false, config::CrateTypeExecutable) => true,
_ => false
Expand All @@ -250,6 +251,7 @@ pub fn filename_for_input(sess: &Session,
config::CrateTypeRlib => {
outputs.out_directory.join(&format!("lib{}.rlib", libname))
}
config::CrateTypeCdylib |
config::CrateTypeDylib => {
let (prefix, suffix) = (&sess.target.target.options.dll_prefix,
&sess.target.target.options.dll_suffix);
Expand Down Expand Up @@ -278,9 +280,10 @@ pub fn each_linked_rlib(sess: &Session,
f: &mut FnMut(ast::CrateNum, &Path)) {
let crates = sess.cstore.used_crates(LinkagePreference::RequireStatic).into_iter();
let fmts = sess.dependency_formats.borrow();
let fmts = fmts.get(&config::CrateTypeExecutable).or_else(|| {
fmts.get(&config::CrateTypeStaticlib)
}).unwrap_or_else(|| {
let fmts = fmts.get(&config::CrateTypeExecutable)
.or_else(|| fmts.get(&config::CrateTypeStaticlib))
.or_else(|| fmts.get(&config::CrateTypeCdylib));
let fmts = fmts.unwrap_or_else(|| {
bug!("could not find formats for rlibs")
});
for (cnum, path) in crates {
Expand Down Expand Up @@ -335,13 +338,9 @@ fn link_binary_output(sess: &Session,
config::CrateTypeStaticlib => {
link_staticlib(sess, &objects, &out_filename, tmpdir.path());
}
config::CrateTypeExecutable => {
link_natively(sess, false, &objects, &out_filename, trans, outputs,
tmpdir.path());
}
config::CrateTypeDylib => {
link_natively(sess, true, &objects, &out_filename, trans, outputs,
tmpdir.path());
_ => {
link_natively(sess, crate_type, &objects, &out_filename, trans,
outputs, tmpdir.path());
}
}

Expand Down Expand Up @@ -609,13 +608,14 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
//
// This will invoke the system linker/cc to create the resulting file. This
// links to all upstream files as well.
fn link_natively(sess: &Session, dylib: bool,
objects: &[PathBuf], out_filename: &Path,
fn link_natively(sess: &Session,
crate_type: config::CrateType,
objects: &[PathBuf],
out_filename: &Path,
trans: &CrateTranslation,
outputs: &OutputFilenames,
tmpdir: &Path) {
info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects,
out_filename);
info!("preparing {:?} from {:?} to {:?}", crate_type, objects, out_filename);

// The invocations of cc share some flags across platforms
let (pname, mut cmd) = get_linker(sess);
Expand All @@ -624,10 +624,10 @@ fn link_natively(sess: &Session, dylib: bool,
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
cmd.args(&sess.target.target.options.pre_link_args);

let pre_link_objects = if dylib {
&sess.target.target.options.pre_link_objects_dll
} else {
let pre_link_objects = if crate_type == config::CrateTypeExecutable {
&sess.target.target.options.pre_link_objects_exe
} else {
&sess.target.target.options.pre_link_objects_dll
};
for obj in pre_link_objects {
cmd.arg(root.join(obj));
Expand All @@ -639,7 +639,7 @@ fn link_natively(sess: &Session, dylib: bool,
} else {
Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
};
link_args(&mut *linker, sess, dylib, tmpdir,
link_args(&mut *linker, sess, crate_type, tmpdir,
objects, out_filename, trans, outputs);
if !sess.target.target.options.no_compiler_rt {
linker.link_staticlib("compiler-rt");
Expand Down Expand Up @@ -705,7 +705,7 @@ fn link_natively(sess: &Session, dylib: bool,

fn link_args(cmd: &mut Linker,
sess: &Session,
dylib: bool,
crate_type: config::CrateType,
tmpdir: &Path,
objects: &[PathBuf],
out_filename: &Path,
Expand All @@ -727,26 +727,28 @@ fn link_args(cmd: &mut Linker,

// If we're building a dynamic library then some platforms need to make sure
// that all symbols are exported correctly from the dynamic library.
if dylib {
cmd.export_symbols(sess, trans, tmpdir);
if crate_type != config::CrateTypeExecutable {
cmd.export_symbols(sess, trans, tmpdir, crate_type);
}

// 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 {
if crate_type == config::CrateTypeDylib {
cmd.add_object(&outputs.with_extension("metadata.o"));
}

// Try to strip as much out of the generated object by removing unused
// sections if possible. See more comments in linker.rs
if !sess.opts.cg.link_dead_code {
cmd.gc_sections(dylib);
let keep_metadata = crate_type == config::CrateTypeDylib;
cmd.gc_sections(keep_metadata);
}

let used_link_args = sess.cstore.used_link_args();

if !dylib && t.options.position_independent_executables {
if crate_type == config::CrateTypeExecutable &&
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);
Expand Down Expand Up @@ -801,12 +803,12 @@ fn link_args(cmd: &mut Linker,
// in this DAG so far because they're only dylibs and dylibs can only depend
// on other dylibs (e.g. other native deps).
add_local_native_libraries(cmd, sess);
add_upstream_rust_crates(cmd, sess, dylib, tmpdir);
add_upstream_rust_crates(cmd, sess, crate_type, tmpdir);
add_upstream_native_libraries(cmd, sess);

// # Telling the linker what we're doing

if dylib {
if crate_type != config::CrateTypeExecutable {
cmd.build_dylib(out_filename);
}

Expand Down Expand Up @@ -904,8 +906,10 @@ 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 Linker,
sess: &Session,
crate_type: config::CrateType,
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.
Expand All @@ -915,11 +919,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
// involves just passing the right -l flag.

let formats = sess.dependency_formats.borrow();
let data = if dylib {
formats.get(&config::CrateTypeDylib).unwrap()
} else {
formats.get(&config::CrateTypeExecutable).unwrap()
};
let data = formats.get(&crate_type).unwrap();

// Invoke get_used_crates to ensure that we get a topological sorting of
// crates.
Expand All @@ -934,7 +934,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
Linkage::NotLinked |
Linkage::IncludedFromDylib => {}
Linkage::Static => {
add_static_crate(cmd, sess, tmpdir, dylib, &src.rlib.unwrap().0)
add_static_crate(cmd, sess, tmpdir, crate_type,
&src.rlib.unwrap().0)
}
Linkage::Dynamic => {
add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0)
Expand Down Expand Up @@ -979,9 +980,12 @@ 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) {
if !sess.lto() && !dylib {
fn add_static_crate(cmd: &mut Linker,
sess: &Session,
tmpdir: &Path,
crate_type: config::CrateType,
cratepath: &Path) {
if !sess.lto() && crate_type != config::CrateTypeDylib {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
return
}
Expand Down Expand Up @@ -1017,7 +1021,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,

if any_objects {
archive.build();
if dylib {
if crate_type == config::CrateTypeDylib {
cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst));
} else {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst));
Expand Down

0 comments on commit 07d373f

Please sign in to comment.