Skip to content

Commit eed6168

Browse files
committed
Add Emscripten-specific linker
It claims to accept most GNU linker options, but in fact most of them have no effect and instead it requires some special options which are easier to handle in a separate trait. Currently added: - `export_symbols`: works on executables as special Emscripten case since staticlibs/dylibs aren't compiled to JS, while exports are required to be accessible from JS. Fixes #39171. - `optimize` - translates Rust's optimization level to Emscripten optimization level (whether passed via `-C opt-level=...` or `-O...`). Fixes #36899. - `debuginfo` - translates debug info; Emscripten has 5 debug levels while Rust has 3, so chose to translate `-C debuginfo=1` to `-g3` (preserves whitespace, variable and function names for easy debugging). Fixes #36901. - `no_default_libraries` - tells Emscripten to exlude `memcpy` and co.
1 parent bc524d3 commit eed6168

File tree

5 files changed

+159
-3
lines changed

5 files changed

+159
-3
lines changed

src/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc_trans/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ rustc_errors = { path = "../librustc_errors" }
2222
rustc_incremental = { path = "../librustc_incremental" }
2323
rustc_llvm = { path = "../librustc_llvm" }
2424
rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
25+
serialize = { path = "../libserialize" }
2526
syntax = { path = "../libsyntax" }
2627
syntax_pos = { path = "../libsyntax_pos" }

src/librustc_trans/back/link.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,8 @@ fn link_args(cmd: &mut Linker,
830830

831831
// If we're building a dynamic library then some platforms need to make sure
832832
// that all symbols are exported correctly from the dynamic library.
833-
if crate_type != config::CrateTypeExecutable {
833+
if crate_type != config::CrateTypeExecutable ||
834+
sess.target.target.options.is_like_emscripten {
834835
cmd.export_symbols(tmpdir, crate_type);
835836
}
836837

src/librustc_trans/back/linker.rs

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use back::symbol_export::{self, ExportedSymbols};
2323
use middle::dependency_format::Linkage;
2424
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
2525
use session::Session;
26-
use session::config::CrateType;
27-
use session::config;
26+
use session::config::{self, CrateType, OptLevel, DebugInfoLevel};
27+
use serialize::{json, Encoder};
2828

2929
/// For all the linkers we support, and information they might
3030
/// need out of the shared crate context before we get rid of it.
@@ -51,6 +51,12 @@ impl<'a, 'tcx> LinkerInfo {
5151
sess: sess,
5252
info: self
5353
}) as Box<Linker>
54+
} else if sess.target.target.options.is_like_emscripten {
55+
Box::new(EmLinker {
56+
cmd: cmd,
57+
sess: sess,
58+
info: self
59+
}) as Box<Linker>
5460
} else {
5561
Box::new(GnuLinker {
5662
cmd: cmd,
@@ -488,6 +494,152 @@ impl<'a> Linker for MsvcLinker<'a> {
488494
}
489495
}
490496

497+
pub struct EmLinker<'a> {
498+
cmd: &'a mut Command,
499+
sess: &'a Session,
500+
info: &'a LinkerInfo
501+
}
502+
503+
impl<'a> Linker for EmLinker<'a> {
504+
fn include_path(&mut self, path: &Path) {
505+
self.cmd.arg("-L").arg(path);
506+
}
507+
508+
fn link_staticlib(&mut self, lib: &str) {
509+
self.cmd.arg("-l").arg(lib);
510+
}
511+
512+
fn output_filename(&mut self, path: &Path) {
513+
self.cmd.arg("-o").arg(path);
514+
}
515+
516+
fn add_object(&mut self, path: &Path) {
517+
self.cmd.arg(path);
518+
}
519+
520+
fn link_dylib(&mut self, lib: &str) {
521+
// Emscripten always links statically
522+
self.link_staticlib(lib);
523+
}
524+
525+
fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
526+
// not supported?
527+
self.link_staticlib(lib);
528+
}
529+
530+
fn link_whole_rlib(&mut self, lib: &Path) {
531+
// not supported?
532+
self.link_rlib(lib);
533+
}
534+
535+
fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
536+
self.link_dylib(lib);
537+
}
538+
539+
fn link_rlib(&mut self, lib: &Path) {
540+
self.add_object(lib);
541+
}
542+
543+
fn position_independent_executable(&mut self) {
544+
// noop
545+
}
546+
547+
fn args(&mut self, args: &[String]) {
548+
self.cmd.args(args);
549+
}
550+
551+
fn framework_path(&mut self, _path: &Path) {
552+
bug!("frameworks are not supported on Emscripten")
553+
}
554+
555+
fn link_framework(&mut self, _framework: &str) {
556+
bug!("frameworks are not supported on Emscripten")
557+
}
558+
559+
fn gc_sections(&mut self, _keep_metadata: bool) {
560+
// noop
561+
}
562+
563+
fn optimize(&mut self) {
564+
// Emscripten performs own optimizations
565+
self.cmd.arg(match self.sess.opts.optimize {
566+
OptLevel::No => "-O0",
567+
OptLevel::Less => "-O1",
568+
OptLevel::Default => "-O2",
569+
OptLevel::Aggressive => "-O3",
570+
OptLevel::Size => "-Os",
571+
OptLevel::SizeMin => "-Oz"
572+
});
573+
}
574+
575+
fn debuginfo(&mut self) {
576+
// Preserve names or generate source maps depending on debug info
577+
self.cmd.arg(match self.sess.opts.debuginfo {
578+
DebugInfoLevel::NoDebugInfo => "-g0",
579+
DebugInfoLevel::LimitedDebugInfo => "-g3",
580+
DebugInfoLevel::FullDebugInfo => "-g4"
581+
});
582+
}
583+
584+
fn no_default_libraries(&mut self) {
585+
self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]);
586+
}
587+
588+
fn build_dylib(&mut self, _out_filename: &Path) {
589+
bug!("building dynamic library is unsupported on Emscripten")
590+
}
591+
592+
fn whole_archives(&mut self) {
593+
// noop
594+
}
595+
596+
fn no_whole_archives(&mut self) {
597+
// noop
598+
}
599+
600+
fn hint_static(&mut self) {
601+
// noop
602+
}
603+
604+
fn hint_dynamic(&mut self) {
605+
// noop
606+
}
607+
608+
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
609+
let symbols = &self.info.exports[&crate_type];
610+
611+
debug!("EXPORTED SYMBOLS:");
612+
613+
self.cmd.arg("-s");
614+
615+
let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
616+
let mut encoded = String::new();
617+
618+
{
619+
let mut encoder = json::Encoder::new(&mut encoded);
620+
let res = encoder.emit_seq(symbols.len(), |encoder| {
621+
for (i, sym) in symbols.iter().enumerate() {
622+
encoder.emit_seq_elt(i, |encoder| {
623+
encoder.emit_str(&("_".to_string() + sym))
624+
})?;
625+
}
626+
Ok(())
627+
});
628+
if let Err(e) = res {
629+
self.sess.fatal(&format!("failed to encode exported symbols: {}", e));
630+
}
631+
}
632+
debug!("{}", encoded);
633+
arg.push(encoded);
634+
635+
self.cmd.arg(arg);
636+
}
637+
638+
fn subsystem(&mut self, _subsystem: &str) {
639+
// noop
640+
}
641+
}
642+
491643
fn exported_symbols(scx: &SharedCrateContext,
492644
exported_symbols: &ExportedSymbols,
493645
crate_type: CrateType)

src/librustc_trans/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ extern crate rustc_bitflags;
5959
#[macro_use] extern crate syntax;
6060
extern crate syntax_pos;
6161
extern crate rustc_errors as errors;
62+
extern crate serialize;
6263

6364
pub use rustc::session;
6465
pub use rustc::middle;

0 commit comments

Comments
 (0)