From eed6168a3ba300c7166a8072347505fc6164c8bf Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 3 Feb 2017 14:58:13 +0000 Subject: [PATCH 1/3] 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. --- src/Cargo.lock | 1 + src/librustc_trans/Cargo.toml | 1 + src/librustc_trans/back/link.rs | 3 +- src/librustc_trans/back/linker.rs | 156 +++++++++++++++++++++++++++++- src/librustc_trans/lib.rs | 1 + 5 files changed, 159 insertions(+), 3 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index 09aefd45e94c8..219ed5d6bc281 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -551,6 +551,7 @@ dependencies = [ "rustc_incremental 0.0.0", "rustc_llvm 0.0.0", "rustc_platform_intrinsics 0.0.0", + "serialize 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index fa48a63b6b8f5..b5c67ad998b69 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -22,5 +22,6 @@ rustc_errors = { path = "../librustc_errors" } rustc_incremental = { path = "../librustc_incremental" } rustc_llvm = { path = "../librustc_llvm" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } +serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 9d48dc523d3a7..c5f6792a45f81 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -830,7 +830,8 @@ 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 crate_type != config::CrateTypeExecutable { + if crate_type != config::CrateTypeExecutable || + sess.target.target.options.is_like_emscripten { cmd.export_symbols(tmpdir, crate_type); } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 7f352f1da517d..6e409f4335524 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -23,8 +23,8 @@ use back::symbol_export::{self, ExportedSymbols}; use middle::dependency_format::Linkage; use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; use session::Session; -use session::config::CrateType; -use session::config; +use session::config::{self, CrateType, OptLevel, DebugInfoLevel}; +use serialize::{json, Encoder}; /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. @@ -51,6 +51,12 @@ impl<'a, 'tcx> LinkerInfo { sess: sess, info: self }) as Box + } else if sess.target.target.options.is_like_emscripten { + Box::new(EmLinker { + cmd: cmd, + sess: sess, + info: self + }) as Box } else { Box::new(GnuLinker { cmd: cmd, @@ -488,6 +494,152 @@ impl<'a> Linker for MsvcLinker<'a> { } } +pub struct EmLinker<'a> { + cmd: &'a mut Command, + sess: &'a Session, + info: &'a LinkerInfo +} + +impl<'a> Linker for EmLinker<'a> { + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn link_dylib(&mut self, lib: &str) { + // Emscripten always links statically + self.link_staticlib(lib); + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + // not supported? + self.link_rlib(lib); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.link_dylib(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.add_object(lib); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn framework_path(&mut self, _path: &Path) { + bug!("frameworks are not supported on Emscripten") + } + + fn link_framework(&mut self, _framework: &str) { + bug!("frameworks are not supported on Emscripten") + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + // noop + } + + fn optimize(&mut self) { + // Emscripten performs own optimizations + self.cmd.arg(match self.sess.opts.optimize { + OptLevel::No => "-O0", + OptLevel::Less => "-O1", + OptLevel::Default => "-O2", + OptLevel::Aggressive => "-O3", + OptLevel::Size => "-Os", + OptLevel::SizeMin => "-Oz" + }); + } + + fn debuginfo(&mut self) { + // Preserve names or generate source maps depending on debug info + self.cmd.arg(match self.sess.opts.debuginfo { + DebugInfoLevel::NoDebugInfo => "-g0", + DebugInfoLevel::LimitedDebugInfo => "-g3", + DebugInfoLevel::FullDebugInfo => "-g4" + }); + } + + fn no_default_libraries(&mut self) { + self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); + } + + fn build_dylib(&mut self, _out_filename: &Path) { + bug!("building dynamic library is unsupported on Emscripten") + } + + fn whole_archives(&mut self) { + // noop + } + + fn no_whole_archives(&mut self) { + // noop + } + + fn hint_static(&mut self) { + // noop + } + + fn hint_dynamic(&mut self) { + // noop + } + + fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { + let symbols = &self.info.exports[&crate_type]; + + debug!("EXPORTED SYMBOLS:"); + + self.cmd.arg("-s"); + + let mut arg = OsString::from("EXPORTED_FUNCTIONS="); + let mut encoded = String::new(); + + { + let mut encoder = json::Encoder::new(&mut encoded); + let res = encoder.emit_seq(symbols.len(), |encoder| { + for (i, sym) in symbols.iter().enumerate() { + encoder.emit_seq_elt(i, |encoder| { + encoder.emit_str(&("_".to_string() + sym)) + })?; + } + Ok(()) + }); + if let Err(e) = res { + self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); + } + } + debug!("{}", encoded); + arg.push(encoded); + + self.cmd.arg(arg); + } + + fn subsystem(&mut self, _subsystem: &str) { + // noop + } +} + fn exported_symbols(scx: &SharedCrateContext, exported_symbols: &ExportedSymbols, crate_type: CrateType) diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 21c92cb4a4ad2..1530fcda3d3ea 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -59,6 +59,7 @@ extern crate rustc_bitflags; #[macro_use] extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; +extern crate serialize; pub use rustc::session; pub use rustc::middle; From 84c2a6716058db8d40a549f8349f7473f7b09e82 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Thu, 9 Feb 2017 13:35:31 +0000 Subject: [PATCH 2/3] Reenable exception catching in Emscripten even on optimized targets --- src/librustc_trans/back/link.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index c5f6792a45f81..1da9fcb0e95e4 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -722,9 +722,13 @@ fn link_natively(sess: &Session, cmd.arg(root.join(obj)); } - if sess.target.target.options.is_like_emscripten && - sess.panic_strategy() == PanicStrategy::Abort { - cmd.args(&["-s", "DISABLE_EXCEPTION_CATCHING=1"]); + if sess.target.target.options.is_like_emscripten { + cmd.arg("-s"); + cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + "DISABLE_EXCEPTION_CATCHING=1" + } else { + "DISABLE_EXCEPTION_CATCHING=0" + }); } { From f35b598bbf3bb86f70c731b8ef981beada052a08 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 10 Feb 2017 17:12:18 +0000 Subject: [PATCH 3/3] Disable memory init file until further notice It's support is currently too buggy in both Rust tests and Cargo. --- src/librustc_trans/back/linker.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 6e409f4335524..830d1d0d3a558 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -570,6 +570,8 @@ impl<'a> Linker for EmLinker<'a> { OptLevel::Size => "-Os", OptLevel::SizeMin => "-Oz" }); + // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved + self.cmd.args(&["--memory-init-file", "0"]); } fn debuginfo(&mut self) {