Skip to content

Commit

Permalink
Add Emscripten-specific linker
Browse files Browse the repository at this point in the history
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 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 exlude `memcpy` and co.
  • Loading branch information
RReverser committed Feb 7, 2017
1 parent c49d102 commit 87b8c9e
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 87b8c9e

Please sign in to comment.