Skip to content

Commit

Permalink
Rollup merge of rust-lang#39490 - RReverser:em-linker, r=alexcrichton
Browse files Browse the repository at this point in the history
Add Emscripten-specific linker

Emscripten claims to accept most GNU linker options, but in fact most of `-Wl,...` are useless for it and instead it requires some additional 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 rust-lang#39171.
 - `optimize` - translates Rust's optimization level to Emscripten optimization level (whether passed via `-C opt-level=...` or `-O...`).
Fixes rust-lang#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 rust-lang#36901.
 - `no_default_libraries` - tells Emscripten to exclude `memcpy` and co.

TODO (in future PR): dynamic linking via `SIDE_MODULE` / `MAIN_MODULE` mechanism.
  • Loading branch information
frewsxcv authored Feb 8, 2017
2 parents a261924 + 87b8c9e commit 08f4ea4
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/librustc_trans/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
3 changes: 2 additions & 1 deletion src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,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);
}

Expand Down
159 changes: 157 additions & 2 deletions src/librustc_trans/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -51,6 +51,12 @@ impl<'a, 'tcx> LinkerInfo {
sess: sess,
info: self
}) as Box<Linker>
} else if sess.target.target.options.is_like_emscripten {
Box::new(EmLinker {
cmd: cmd,
sess: sess,
info: self
}) as Box<Linker>
} else {
Box::new(GnuLinker {
cmd: cmd,
Expand Down Expand Up @@ -488,6 +494,155 @@ 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.arg("-s");
self.cmd.arg("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 mut arg = OsString::new();

let symbols = &self.info.exports[&crate_type];

debug!("EXPORTED SYMBOLS:");

self.cmd.arg("-s");
arg.push("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)
Expand Down
1 change: 1 addition & 0 deletions src/librustc_trans/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 08f4ea4

Please sign in to comment.