Skip to content

Commit 87b8c9e

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 c49d102 commit 87b8c9e

File tree

5 files changed

+162
-3
lines changed

5 files changed

+162
-3
lines changed

src/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc_trans/Cargo.toml

+1
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

+2-1
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,8 @@ fn link_args(cmd: &mut Linker,
823823

824824
// If we're building a dynamic library then some platforms need to make sure
825825
// that all symbols are exported correctly from the dynamic library.
826-
if crate_type != config::CrateTypeExecutable {
826+
if crate_type != config::CrateTypeExecutable ||
827+
sess.target.target.options.is_like_emscripten {
827828
cmd.export_symbols(tmpdir, crate_type);
828829
}
829830

src/librustc_trans/back/linker.rs

+157-2
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,155 @@ 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
577+
// on debug info
578+
self.cmd.arg(match self.sess.opts.debuginfo {
579+
DebugInfoLevel::NoDebugInfo => "-g0",
580+
DebugInfoLevel::LimitedDebugInfo => "-g3",
581+
DebugInfoLevel::FullDebugInfo => "-g4"
582+
});
583+
}
584+
585+
fn no_default_libraries(&mut self) {
586+
self.cmd.arg("-s");
587+
self.cmd.arg("DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]");
588+
}
589+
590+
fn build_dylib(&mut self, _out_filename: &Path) {
591+
bug!("building dynamic library is unsupported on Emscripten")
592+
}
593+
594+
fn whole_archives(&mut self) {
595+
// noop
596+
}
597+
598+
fn no_whole_archives(&mut self) {
599+
// noop
600+
}
601+
602+
fn hint_static(&mut self) {
603+
// noop
604+
}
605+
606+
fn hint_dynamic(&mut self) {
607+
// noop
608+
}
609+
610+
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
611+
let mut arg = OsString::new();
612+
613+
let symbols = &self.info.exports[&crate_type];
614+
615+
debug!("EXPORTED SYMBOLS:");
616+
617+
self.cmd.arg("-s");
618+
arg.push("EXPORTED_FUNCTIONS=");
619+
let mut encoded = String::new();
620+
621+
{
622+
let mut encoder = json::Encoder::new(&mut encoded);
623+
let res = encoder.emit_seq(symbols.len(), |encoder| {
624+
for (i, sym) in symbols.iter().enumerate() {
625+
encoder.emit_seq_elt(i, |encoder| {
626+
encoder.emit_str(&("_".to_string() + sym))
627+
})?;
628+
}
629+
Ok(())
630+
});
631+
if let Err(e) = res {
632+
self.sess.fatal(&format!("failed to encode exported symbols: {}", e));
633+
}
634+
}
635+
debug!("{}", encoded);
636+
arg.push(encoded);
637+
638+
self.cmd.arg(arg);
639+
}
640+
641+
fn subsystem(&mut self, _subsystem: &str) {
642+
// noop
643+
}
644+
}
645+
491646
fn exported_symbols(scx: &SharedCrateContext,
492647
exported_symbols: &ExportedSymbols,
493648
crate_type: CrateType)

src/librustc_trans/lib.rs

+1
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)