Skip to content

Commit c7f4f3a

Browse files
committed
wasm: Explicitly export all symbols with LLD
This commit fixes an oddity on the wasm target where LTO can produce working executables but plain old optimizations doesn't. The compiler already knows what set of symbols it would like to export, but LLD only discovers this list transitively through symbol visibilities. LLD may not, however, always find all the symbols that we'd like to export. For example if you depend on an rlib with a `#[no_mangle]` symbol, then if you don't actually use anything from the rlib then the symbol won't appear in the final artifact! It will appear, however, with LTO. This commit attempts to rectify this situation by ensuring that all symbols rustc would otherwise preserve through LTO are also preserved through the linking process with LLD by default.
1 parent de3d640 commit c7f4f3a

File tree

6 files changed

+89
-4
lines changed

6 files changed

+89
-4
lines changed

src/libpanic_abort/lib.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,14 @@ pub unsafe extern fn __rust_start_panic(_payload: usize) -> u32 {
9696
// runtime at all.
9797
pub mod personalities {
9898
#[no_mangle]
99-
#[cfg(not(all(target_os = "windows",
100-
target_env = "gnu",
101-
target_arch = "x86_64")))]
99+
#[cfg(not(any(
100+
target_arch = "wasm32",
101+
all(
102+
target_os = "windows",
103+
target_env = "gnu",
104+
target_arch = "x86_64",
105+
),
106+
)))]
102107
pub extern fn rust_eh_personality() {}
103108

104109
// On x86_64-pc-windows-gnu we use our own personality function that needs

src/librustc_codegen_llvm/back/linker.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ impl LinkerInfo {
8989
Box::new(WasmLd {
9090
cmd,
9191
sess,
92+
info: self
9293
}) as Box<dyn Linker>
9394
}
9495
}
@@ -926,6 +927,7 @@ fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> {
926927
pub struct WasmLd<'a> {
927928
cmd: Command,
928929
sess: &'a Session,
930+
info: &'a LinkerInfo,
929931
}
930932

931933
impl<'a> Linker for WasmLd<'a> {
@@ -1021,7 +1023,10 @@ impl<'a> Linker for WasmLd<'a> {
10211023
fn build_dylib(&mut self, _out_filename: &Path) {
10221024
}
10231025

1024-
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {
1026+
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
1027+
for sym in self.info.exports[&crate_type].iter() {
1028+
self.cmd.arg("--export").arg(&sym);
1029+
}
10251030
}
10261031

10271032
fn subsystem(&mut self, _subsystem: &str) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-include ../../run-make-fulldeps/tools.mk
2+
3+
ifeq ($(TARGET),wasm32-unknown-unknown)
4+
all:
5+
$(RUSTC) bar.rs --target wasm32-unknown-unknown
6+
$(RUSTC) foo.rs --target wasm32-unknown-unknown
7+
$(NODE) verify.js $(TMPDIR)/foo.wasm
8+
$(RUSTC) bar.rs --target wasm32-unknown-unknown -O
9+
$(RUSTC) foo.rs --target wasm32-unknown-unknown -O
10+
$(NODE) verify.js $(TMPDIR)/foo.wasm
11+
$(RUSTC) foo.rs --target wasm32-unknown-unknown -C lto
12+
$(NODE) verify.js $(TMPDIR)/foo.wasm
13+
else
14+
all:
15+
endif
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "rlib"]
12+
13+
#[no_mangle]
14+
pub extern fn foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "cdylib"]
12+
13+
extern crate bar;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
const fs = require('fs');
12+
const process = require('process');
13+
const assert = require('assert');
14+
const buffer = fs.readFileSync(process.argv[2]);
15+
16+
let m = new WebAssembly.Module(buffer);
17+
let list = WebAssembly.Module.exports(m);
18+
console.log('exports', list);
19+
20+
const my_exports = {};
21+
let nexports = 0;
22+
for (const entry of list) {
23+
if (entry.kind !== 'function')
24+
continue;
25+
my_exports[entry.name] = true;
26+
nexports += 1;
27+
}
28+
29+
if (nexports != 1)
30+
throw new Error("should only have one function export");
31+
if (my_exports.foo === undefined)
32+
throw new Error("`foo` wasn't defined");

0 commit comments

Comments
 (0)