Skip to content

Commit 0e94541

Browse files
committed
rustc: Allow using clang for wasm32 targets
This commit adds support code for using `clang` directly to link the wasm32-unknown-unknown target. Currently the target is only really configured to link with LLD directly, but this ensures that `clang` can be configured as well. While not immediately useful in the near term it's likely that more wasm32 targets will pop up over time with Clang's new native support for WebAssembly in the 8.0.0 release. Getting support into rustc early should make it easier to experiment with these targets and try out various changes here and there.
1 parent 7a4df3b commit 0e94541

File tree

4 files changed

+174
-107
lines changed

4 files changed

+174
-107
lines changed

src/librustc_codegen_ssa/back/linker.rs

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,16 @@ impl<'a> GccLinker<'a> {
160160
}
161161

162162
fn takes_hints(&self) -> bool {
163-
!self.sess.target.target.options.is_like_osx
163+
// Really this function only returns true if the underlying linker
164+
// configured for a compiler is binutils `ld.bfd` and `ld.gold`. We
165+
// don't really have a foolproof way to detect that, so rule out some
166+
// platforms where currently this is guaranteed to *not* be the case:
167+
//
168+
// * On OSX they have their own linker, not binutils'
169+
// * For WebAssembly the only functional linker is LLD, which doesn't
170+
// support hint flags
171+
!self.sess.target.target.options.is_like_osx &&
172+
self.sess.target.target.arch != "wasm32"
164173
}
165174

166175
// Some platforms take hints about whether a library is static or dynamic.
@@ -375,6 +384,13 @@ impl<'a> Linker for GccLinker<'a> {
375384
return
376385
}
377386

387+
// Symbol visibility takes care of this for the WebAssembly.
388+
// Additionally the only known linker, LLD, doesn't support the script
389+
// arguments just yet
390+
if self.sess.target.target.arch == "wasm32" {
391+
return;
392+
}
393+
378394
let mut arg = OsString::new();
379395
let path = tmpdir.join("list");
380396

@@ -441,13 +457,13 @@ impl<'a> Linker for GccLinker<'a> {
441457
}
442458

443459
fn group_start(&mut self) {
444-
if !self.sess.target.target.options.is_like_osx {
460+
if self.takes_hints() {
445461
self.linker_arg("--start-group");
446462
}
447463
}
448464

449465
fn group_end(&mut self) {
450-
if !self.sess.target.target.options.is_like_osx {
466+
if self.takes_hints() {
451467
self.linker_arg("--end-group");
452468
}
453469
}
@@ -874,59 +890,7 @@ pub struct WasmLd<'a> {
874890
}
875891

876892
impl<'a> WasmLd<'a> {
877-
fn new(mut cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
878-
// There have been reports in the wild (rustwasm/wasm-bindgen#119) of
879-
// using threads causing weird hangs and bugs. Disable it entirely as
880-
// this isn't yet the bottleneck of compilation at all anyway.
881-
cmd.arg("--no-threads");
882-
883-
// By default LLD only gives us one page of stack (64k) which is a
884-
// little small. Default to a larger stack closer to other PC platforms
885-
// (1MB) and users can always inject their own link-args to override this.
886-
cmd.arg("-z").arg("stack-size=1048576");
887-
888-
// By default LLD's memory layout is:
889-
//
890-
// 1. First, a blank page
891-
// 2. Next, all static data
892-
// 3. Finally, the main stack (which grows down)
893-
//
894-
// This has the unfortunate consequence that on stack overflows you
895-
// corrupt static data and can cause some exceedingly weird bugs. To
896-
// help detect this a little sooner we instead request that the stack is
897-
// placed before static data.
898-
//
899-
// This means that we'll generate slightly larger binaries as references
900-
// to static data will take more bytes in the ULEB128 encoding, but
901-
// stack overflow will be guaranteed to trap as it underflows instead of
902-
// corrupting static data.
903-
cmd.arg("--stack-first");
904-
905-
// FIXME we probably shouldn't pass this but instead pass an explicit
906-
// whitelist of symbols we'll allow to be undefined. Unfortunately
907-
// though we can't handle symbols like `log10` that LLVM injects at a
908-
// super late date without actually parsing object files. For now let's
909-
// stick to this and hopefully fix it before stabilization happens.
910-
cmd.arg("--allow-undefined");
911-
912-
// For now we just never have an entry symbol
913-
cmd.arg("--no-entry");
914-
915-
// Rust code should never have warnings, and warnings are often
916-
// indicative of bugs, let's prevent them.
917-
cmd.arg("--fatal-warnings");
918-
919-
// The symbol visibility story is a bit in flux right now with LLD.
920-
// It's... not entirely clear to me what's going on, but this looks to
921-
// make everything work when `export_symbols` isn't otherwise called for
922-
// things like executables.
923-
cmd.arg("--export-dynamic");
924-
925-
// LLD only implements C++-like demangling, which doesn't match our own
926-
// mangling scheme. Tell LLD to not demangle anything and leave it up to
927-
// us to demangle these symbols later.
928-
cmd.arg("--no-demangle");
929-
893+
fn new(cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
930894
WasmLd { cmd, sess, info }
931895
}
932896
}
@@ -1022,6 +986,7 @@ impl<'a> Linker for WasmLd<'a> {
1022986
}
1023987

1024988
fn build_dylib(&mut self, _out_filename: &Path) {
989+
self.cmd.arg("--no-entry");
1025990
}
1026991

1027992
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {

src/librustc_target/spec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ mod l4re_base;
6666
mod fuchsia_base;
6767
mod redox_base;
6868
mod riscv_base;
69+
mod wasm32_base;
6970

7071
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash,
7172
RustcEncodable, RustcDecodable)]
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use std::collections::BTreeMap;
2+
use super::{LldFlavor, TargetOptions, PanicStrategy, LinkerFlavor};
3+
4+
pub fn options() -> TargetOptions {
5+
let mut lld_args = Vec::new();
6+
let mut clang_args = Vec::new();
7+
let mut arg = |arg: &str| {
8+
lld_args.push(arg.to_string());
9+
clang_args.push(format!("-Wl,{}", arg));
10+
};
11+
12+
// There have been reports in the wild (rustwasm/wasm-bindgen#119) of
13+
// using threads causing weird hangs and bugs. Disable it entirely as
14+
// this isn't yet the bottleneck of compilation at all anyway.
15+
//
16+
// FIXME: we should file an upstream issue with LLD about this
17+
arg("--no-threads");
18+
19+
// By default LLD only gives us one page of stack (64k) which is a
20+
// little small. Default to a larger stack closer to other PC platforms
21+
// (1MB) and users can always inject their own link-args to override this.
22+
arg("-z");
23+
arg("stack-size=1048576");
24+
25+
// By default LLD's memory layout is:
26+
//
27+
// 1. First, a blank page
28+
// 2. Next, all static data
29+
// 3. Finally, the main stack (which grows down)
30+
//
31+
// This has the unfortunate consequence that on stack overflows you
32+
// corrupt static data and can cause some exceedingly weird bugs. To
33+
// help detect this a little sooner we instead request that the stack is
34+
// placed before static data.
35+
//
36+
// This means that we'll generate slightly larger binaries as references
37+
// to static data will take more bytes in the ULEB128 encoding, but
38+
// stack overflow will be guaranteed to trap as it underflows instead of
39+
// corrupting static data.
40+
arg("--stack-first");
41+
42+
// FIXME we probably shouldn't pass this but instead pass an explicit
43+
// whitelist of symbols we'll allow to be undefined. We don't currently have
44+
// a mechanism of knowing, however, which symbols are intended to be
45+
// imported from the environment and which are intended to be imported from
46+
// other objects linked elsewhere. This is a coarse approximation but is
47+
// sure to hide some bugs and frustrate someone at some point, so we should
48+
// ideally work towards a world where we can explicitly list symbols that
49+
// are supposed to be imported and have all other symbols generate errors if
50+
// they remain undefined.
51+
arg("--allow-undefined");
52+
53+
// Rust code should never have warnings, and warnings are often
54+
// indicative of bugs, let's prevent them.
55+
arg("--fatal-warnings");
56+
57+
// LLD only implements C++-like demangling, which doesn't match our own
58+
// mangling scheme. Tell LLD to not demangle anything and leave it up to
59+
// us to demangle these symbols later. Currently rustc does not perform
60+
// further demangling, but tools like twiggy and wasm-bindgen are intended
61+
// to do so.
62+
arg("--no-demangle");
63+
64+
// The symbol visibility story is a bit in flux right now with LLD.
65+
// It's... not entirely clear to me what's going on, but this looks to
66+
// make everything work when `export_symbols` isn't otherwise called for
67+
// things like executables.
68+
//
69+
// This is really only here to get things working. If it can be removed and
70+
// basic tests still work, then sounds like it should be removed!
71+
arg("--export-dynamic");
72+
73+
let mut pre_link_args = BTreeMap::new();
74+
pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
75+
pre_link_args.insert(LinkerFlavor::Gcc, clang_args);
76+
77+
TargetOptions {
78+
// we allow dynamic linking, but only cdylibs. Basically we allow a
79+
// final library artifact that exports some symbols (a wasm module) but
80+
// we don't allow intermediate `dylib` crate types
81+
dynamic_linking: true,
82+
only_cdylib: true,
83+
84+
// This means we'll just embed a `start` function in the wasm module
85+
executables: true,
86+
87+
// relatively self-explanatory!
88+
exe_suffix: ".wasm".to_string(),
89+
dll_prefix: String::new(),
90+
dll_suffix: ".wasm".to_string(),
91+
linker_is_gnu: false,
92+
93+
max_atomic_width: Some(64),
94+
95+
// Unwinding doesn't work right now, so the whole target unconditionally
96+
// defaults to panic=abort. Note that this is guaranteed to change in
97+
// the future once unwinding is implemented. Don't rely on this as we're
98+
// basically guaranteed to change it once WebAssembly supports
99+
// exceptions.
100+
panic_strategy: PanicStrategy::Abort,
101+
102+
// Wasm doesn't have atomics yet, so tell LLVM that we're in a single
103+
// threaded model which will legalize atomics to normal operations.
104+
singlethread: true,
105+
106+
// no dynamic linking, no need for default visibility!
107+
default_hidden_visibility: true,
108+
109+
// we use the LLD shipped with the Rust toolchain by default
110+
linker: Some("rust-lld".to_owned()),
111+
lld_flavor: LldFlavor::Wasm,
112+
113+
// No need for indirection here, simd types can always be passed by
114+
// value as the whole module either has simd or not, which is different
115+
// from x86 (for example) where programs can have functions that don't
116+
// enable simd features.
117+
simd_types_indirect: false,
118+
119+
pre_link_args,
120+
121+
.. Default::default()
122+
}
123+
}
Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,48 @@
1-
// The wasm32-unknown-unknown target is currently an experimental version of a
2-
// wasm-based target which does *not* use the Emscripten toolchain. Instead
3-
// this toolchain is based purely on LLVM's own toolchain, using LLVM's native
4-
// WebAssembly backend as well as LLD for a native linker.
5-
//
6-
// There's some trickery below on crate types supported and various defaults
7-
// (aka panic=abort by default), but otherwise this is in general a relatively
8-
// standard target.
9-
10-
use super::{LldFlavor, LinkerFlavor, Target, TargetOptions, PanicStrategy};
1+
//! A "bare wasm" target representing a WebAssembly output that makes zero
2+
//! assumptions about its environment.
3+
//!
4+
//! The `wasm32-unknown-unknown` target is intended to encapsulate use cases
5+
//! that do not rely on any imported functionality. The binaries generated are
6+
//! entirely self-contained by default when using the standard library. Although
7+
//! the standard library is available, most of it returns an error immediately
8+
//! (e.g. trying to create a TCP stream or something like that).
9+
//!
10+
//! This target is more or less managed by the Rust and WebAssembly Working
11+
//! Group nowadays at https://github.com/rustwasm.
12+
13+
use super::{LldFlavor, LinkerFlavor, Target};
14+
use super::wasm32_base;
1115

1216
pub fn target() -> Result<Target, String> {
13-
let opts = TargetOptions {
14-
// we allow dynamic linking, but only cdylibs. Basically we allow a
15-
// final library artifact that exports some symbols (a wasm module) but
16-
// we don't allow intermediate `dylib` crate types
17-
dynamic_linking: true,
18-
only_cdylib: true,
19-
20-
// This means we'll just embed a `start` function in the wasm module
21-
executables: true,
22-
23-
// relatively self-explanatory!
24-
exe_suffix: ".wasm".to_string(),
25-
dll_prefix: String::new(),
26-
dll_suffix: ".wasm".to_string(),
27-
linker_is_gnu: false,
28-
29-
max_atomic_width: Some(64),
30-
31-
// Unwinding doesn't work right now, so the whole target unconditionally
32-
// defaults to panic=abort. Note that this is guaranteed to change in
33-
// the future once unwinding is implemented. Don't rely on this.
34-
panic_strategy: PanicStrategy::Abort,
35-
36-
// Wasm doesn't have atomics yet, so tell LLVM that we're in a single
37-
// threaded model which will legalize atomics to normal operations.
38-
singlethread: true,
17+
let mut options = wasm32_base::options();
18+
let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
3919

40-
// no dynamic linking, no need for default visibility!
41-
default_hidden_visibility: true,
20+
// Make sure clang uses LLD as its linker and is configured appropriately
21+
// otherwise
22+
clang_args.push("--target=wasm32-unknown-unknown".to_string());
4223

43-
// we use the LLD shipped with the Rust toolchain by default
44-
linker: Some("rust-lld".to_owned()),
45-
lld_flavor: LldFlavor::Wasm,
24+
// Disable attempting to link crt1.o since it typically isn't present and
25+
// isn't needed currently.
26+
clang_args.push("-nostdlib".to_string());
4627

47-
// No need for indirection here, simd types can always be passed by
48-
// value as the whole module either has simd or not, which is different
49-
// from x86 (for example) where programs can have functions that don't
50-
// enable simd features.
51-
simd_types_indirect: false,
28+
// For now this target just never has an entry symbol no matter the output
29+
// type, so unconditionally pass this.
30+
clang_args.push("-Wl,--no-entry".to_string());
31+
options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm))
32+
.unwrap()
33+
.push("--no-entry".to_string());
5234

53-
.. Default::default()
54-
};
5535
Ok(Target {
5636
llvm_target: "wasm32-unknown-unknown".to_string(),
5737
target_endian: "little".to_string(),
5838
target_pointer_width: "32".to_string(),
5939
target_c_int_width: "32".to_string(),
60-
// This is basically guaranteed to change in the future, don't rely on
61-
// this. Use `not(target_os = "emscripten")` for now.
6240
target_os: "unknown".to_string(),
6341
target_env: String::new(),
6442
target_vendor: "unknown".to_string(),
6543
data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(),
6644
arch: "wasm32".to_string(),
6745
linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm),
68-
options: opts,
46+
options,
6947
})
7048
}

0 commit comments

Comments
 (0)