Skip to content

Commit 07d373f

Browse files
committed
rustc: Add a new crate type, cdylib
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 rust-lang#33132
1 parent 9743c66 commit 07d373f

File tree

16 files changed

+272
-91
lines changed

16 files changed

+272
-91
lines changed

src/librustc/middle/dependency_format.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,10 @@ fn calculate_type(sess: &session::Session,
115115
// got long ago), so don't bother with anything.
116116
config::CrateTypeRlib => return Vec::new(),
117117

118-
// Staticlibs must have all static dependencies. If any fail to be
119-
// found, we generate some nice pretty errors.
120-
config::CrateTypeStaticlib => {
118+
// Staticlibs and cdylibs must have all static dependencies. If any fail
119+
// to be found, we generate some nice pretty errors.
120+
config::CrateTypeStaticlib |
121+
config::CrateTypeCdylib => {
121122
match attempt_static(sess) {
122123
Some(v) => return v,
123124
None => {}

src/librustc/middle/reachable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
145145
// Creates a new reachability computation context.
146146
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ReachableContext<'a, 'tcx> {
147147
let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| {
148-
*ty != config::CrateTypeExecutable
148+
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib
149149
});
150150
ReachableContext {
151151
tcx: tcx,

src/librustc/middle/weak_lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) {
7070
let needs_check = sess.crate_types.borrow().iter().any(|kind| {
7171
match *kind {
7272
config::CrateTypeDylib |
73+
config::CrateTypeCdylib |
7374
config::CrateTypeExecutable |
7475
config::CrateTypeStaticlib => true,
7576
config::CrateTypeRlib => false,

src/librustc/session/config.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ pub enum CrateType {
300300
CrateTypeDylib,
301301
CrateTypeRlib,
302302
CrateTypeStaticlib,
303+
CrateTypeCdylib,
303304
}
304305

305306
#[derive(Clone)]
@@ -1326,6 +1327,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
13261327
"rlib" => CrateTypeRlib,
13271328
"staticlib" => CrateTypeStaticlib,
13281329
"dylib" => CrateTypeDylib,
1330+
"cdylib" => CrateTypeCdylib,
13291331
"bin" => CrateTypeExecutable,
13301332
_ => {
13311333
return Err(format!("unknown crate type: `{}`",
@@ -1413,7 +1415,8 @@ impl fmt::Display for CrateType {
14131415
CrateTypeExecutable => "bin".fmt(f),
14141416
CrateTypeDylib => "dylib".fmt(f),
14151417
CrateTypeRlib => "rlib".fmt(f),
1416-
CrateTypeStaticlib => "staticlib".fmt(f)
1418+
CrateTypeStaticlib => "staticlib".fmt(f),
1419+
CrateTypeCdylib => "cdylib".fmt(f),
14171420
}
14181421
}
14191422
}

src/librustc_driver/driver.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
11751175
Some(ref n) if *n == "dylib" => {
11761176
Some(config::CrateTypeDylib)
11771177
}
1178+
Some(ref n) if *n == "cdylib" => {
1179+
Some(config::CrateTypeCdylib)
1180+
}
11781181
Some(ref n) if *n == "lib" => {
11791182
Some(config::default_lib_output())
11801183
}

src/librustc_metadata/creader.rs

+1
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ impl<'a> CrateReader<'a> {
743743
match *ct {
744744
config::CrateTypeExecutable => need_exe_alloc = true,
745745
config::CrateTypeDylib |
746+
config::CrateTypeCdylib |
746747
config::CrateTypeStaticlib => need_lib_alloc = true,
747748
config::CrateTypeRlib => {}
748749
}

src/librustc_trans/back/link.rs

+42-38
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ pub fn invalid_output_for_target(sess: &Session,
228228
crate_type: config::CrateType) -> bool {
229229
match (sess.target.target.options.dynamic_linking,
230230
sess.target.target.options.executables, crate_type) {
231+
(false, _, config::CrateTypeCdylib) |
231232
(false, _, config::CrateTypeDylib) => true,
232233
(_, false, config::CrateTypeExecutable) => true,
233234
_ => false
@@ -250,6 +251,7 @@ pub fn filename_for_input(sess: &Session,
250251
config::CrateTypeRlib => {
251252
outputs.out_directory.join(&format!("lib{}.rlib", libname))
252253
}
254+
config::CrateTypeCdylib |
253255
config::CrateTypeDylib => {
254256
let (prefix, suffix) = (&sess.target.target.options.dll_prefix,
255257
&sess.target.target.options.dll_suffix);
@@ -278,9 +280,10 @@ pub fn each_linked_rlib(sess: &Session,
278280
f: &mut FnMut(ast::CrateNum, &Path)) {
279281
let crates = sess.cstore.used_crates(LinkagePreference::RequireStatic).into_iter();
280282
let fmts = sess.dependency_formats.borrow();
281-
let fmts = fmts.get(&config::CrateTypeExecutable).or_else(|| {
282-
fmts.get(&config::CrateTypeStaticlib)
283-
}).unwrap_or_else(|| {
283+
let fmts = fmts.get(&config::CrateTypeExecutable)
284+
.or_else(|| fmts.get(&config::CrateTypeStaticlib))
285+
.or_else(|| fmts.get(&config::CrateTypeCdylib));
286+
let fmts = fmts.unwrap_or_else(|| {
284287
bug!("could not find formats for rlibs")
285288
});
286289
for (cnum, path) in crates {
@@ -335,13 +338,9 @@ fn link_binary_output(sess: &Session,
335338
config::CrateTypeStaticlib => {
336339
link_staticlib(sess, &objects, &out_filename, tmpdir.path());
337340
}
338-
config::CrateTypeExecutable => {
339-
link_natively(sess, false, &objects, &out_filename, trans, outputs,
340-
tmpdir.path());
341-
}
342-
config::CrateTypeDylib => {
343-
link_natively(sess, true, &objects, &out_filename, trans, outputs,
344-
tmpdir.path());
341+
_ => {
342+
link_natively(sess, crate_type, &objects, &out_filename, trans,
343+
outputs, tmpdir.path());
345344
}
346345
}
347346

@@ -609,13 +608,14 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
609608
//
610609
// This will invoke the system linker/cc to create the resulting file. This
611610
// links to all upstream files as well.
612-
fn link_natively(sess: &Session, dylib: bool,
613-
objects: &[PathBuf], out_filename: &Path,
611+
fn link_natively(sess: &Session,
612+
crate_type: config::CrateType,
613+
objects: &[PathBuf],
614+
out_filename: &Path,
614615
trans: &CrateTranslation,
615616
outputs: &OutputFilenames,
616617
tmpdir: &Path) {
617-
info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects,
618-
out_filename);
618+
info!("preparing {:?} from {:?} to {:?}", crate_type, objects, out_filename);
619619

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

627-
let pre_link_objects = if dylib {
628-
&sess.target.target.options.pre_link_objects_dll
629-
} else {
627+
let pre_link_objects = if crate_type == config::CrateTypeExecutable {
630628
&sess.target.target.options.pre_link_objects_exe
629+
} else {
630+
&sess.target.target.options.pre_link_objects_dll
631631
};
632632
for obj in pre_link_objects {
633633
cmd.arg(root.join(obj));
@@ -639,7 +639,7 @@ fn link_natively(sess: &Session, dylib: bool,
639639
} else {
640640
Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
641641
};
642-
link_args(&mut *linker, sess, dylib, tmpdir,
642+
link_args(&mut *linker, sess, crate_type, tmpdir,
643643
objects, out_filename, trans, outputs);
644644
if !sess.target.target.options.no_compiler_rt {
645645
linker.link_staticlib("compiler-rt");
@@ -705,7 +705,7 @@ fn link_natively(sess: &Session, dylib: bool,
705705

706706
fn link_args(cmd: &mut Linker,
707707
sess: &Session,
708-
dylib: bool,
708+
crate_type: config::CrateType,
709709
tmpdir: &Path,
710710
objects: &[PathBuf],
711711
out_filename: &Path,
@@ -727,26 +727,28 @@ fn link_args(cmd: &mut Linker,
727727

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

734734
// When linking a dynamic library, we put the metadata into a section of the
735735
// executable. This metadata is in a separate object file from the main
736736
// object file, so we link that in here.
737-
if dylib {
737+
if crate_type == config::CrateTypeDylib {
738738
cmd.add_object(&outputs.with_extension("metadata.o"));
739739
}
740740

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

747748
let used_link_args = sess.cstore.used_link_args();
748749

749-
if !dylib && t.options.position_independent_executables {
750+
if crate_type == config::CrateTypeExecutable &&
751+
t.options.position_independent_executables {
750752
let empty_vec = Vec::new();
751753
let empty_str = String::new();
752754
let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec);
@@ -801,12 +803,12 @@ fn link_args(cmd: &mut Linker,
801803
// in this DAG so far because they're only dylibs and dylibs can only depend
802804
// on other dylibs (e.g. other native deps).
803805
add_local_native_libraries(cmd, sess);
804-
add_upstream_rust_crates(cmd, sess, dylib, tmpdir);
806+
add_upstream_rust_crates(cmd, sess, crate_type, tmpdir);
805807
add_upstream_native_libraries(cmd, sess);
806808

807809
// # Telling the linker what we're doing
808810

809-
if dylib {
811+
if crate_type != config::CrateTypeExecutable {
810812
cmd.build_dylib(out_filename);
811813
}
812814

@@ -904,8 +906,10 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) {
904906
// Rust crates are not considered at all when creating an rlib output. All
905907
// dependencies will be linked when producing the final output (instead of
906908
// the intermediate rlib version)
907-
fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
908-
dylib: bool, tmpdir: &Path) {
909+
fn add_upstream_rust_crates(cmd: &mut Linker,
910+
sess: &Session,
911+
crate_type: config::CrateType,
912+
tmpdir: &Path) {
909913
// All of the heavy lifting has previously been accomplished by the
910914
// dependency_format module of the compiler. This is just crawling the
911915
// output of that module, adding crates as necessary.
@@ -915,11 +919,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
915919
// involves just passing the right -l flag.
916920

917921
let formats = sess.dependency_formats.borrow();
918-
let data = if dylib {
919-
formats.get(&config::CrateTypeDylib).unwrap()
920-
} else {
921-
formats.get(&config::CrateTypeExecutable).unwrap()
922-
};
922+
let data = formats.get(&crate_type).unwrap();
923923

924924
// Invoke get_used_crates to ensure that we get a topological sorting of
925925
// crates.
@@ -934,7 +934,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
934934
Linkage::NotLinked |
935935
Linkage::IncludedFromDylib => {}
936936
Linkage::Static => {
937-
add_static_crate(cmd, sess, tmpdir, dylib, &src.rlib.unwrap().0)
937+
add_static_crate(cmd, sess, tmpdir, crate_type,
938+
&src.rlib.unwrap().0)
938939
}
939940
Linkage::Dynamic => {
940941
add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0)
@@ -979,9 +980,12 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
979980
// (aka we're making an executable), we can just pass the rlib blindly to
980981
// the linker (fast) because it's fine if it's not actually included as
981982
// we're at the end of the dependency chain.
982-
fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path,
983-
dylib: bool, cratepath: &Path) {
984-
if !sess.lto() && !dylib {
983+
fn add_static_crate(cmd: &mut Linker,
984+
sess: &Session,
985+
tmpdir: &Path,
986+
crate_type: config::CrateType,
987+
cratepath: &Path) {
988+
if !sess.lto() && crate_type != config::CrateTypeDylib {
985989
cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
986990
return
987991
}
@@ -1017,7 +1021,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
10171021

10181022
if any_objects {
10191023
archive.build();
1020-
if dylib {
1024+
if crate_type == config::CrateTypeDylib {
10211025
cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst));
10221026
} else {
10231027
cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst));

0 commit comments

Comments
 (0)