summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2019-12-12 19:15:39 -0800
committerGitHub <noreply@github.com>2019-12-12 19:15:39 -0800
commit16c6b44da64630cd6906433cf35edabcea93cffc (patch)
treef36d41e14f17b64bbfd3f7080cb624565fc11c59
parent89d1cf92be0636a219ee6415eead387241963dcf (diff)
downloadbinaryen-16c6b44da64630cd6906433cf35edabcea93cffc.tar.gz
binaryen-16c6b44da64630cd6906433cf35edabcea93cffc.tar.bz2
binaryen-16c6b44da64630cd6906433cf35edabcea93cffc.zip
Support stack overflow checks in standalone mode (#2525)
In normal mode we call a JS import, but we can't import from JS in standalone mode. Instead, just trap in that case with an unreachable. (The error reporting is not as good in this case, but at least it catches all errors and halts, and the emitted wasm is valid for standalone mode.) Helps emscripten-core/emscripten#10019
-rwxr-xr-xscripts/test/lld.py14
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp2
-rw-r--r--src/wasm-emscripten.h3
-rw-r--r--src/wasm/wasm-emscripten.cpp22
-rw-r--r--test/lld/safe_stack_standalone-wasm.wat89
-rw-r--r--test/lld/safe_stack_standalone-wasm.wat.out254
6 files changed, 372 insertions, 12 deletions
diff --git a/scripts/test/lld.py b/scripts/test/lld.py
index f509059de..992a35655 100755
--- a/scripts/test/lld.py
+++ b/scripts/test/lld.py
@@ -19,14 +19,14 @@ from . import support
def args_for_finalize(filename):
+ ret = ['--global-base=568']
if 'safe_stack' in filename:
- return ['--check-stack-overflow', '--global-base=568']
- elif 'shared' in filename:
- return ['--side-module']
- elif 'standalone-wasm' in filename:
- return ['--standalone-wasm', '--global-base=568']
- else:
- return ['--global-base=568']
+ ret += ['--check-stack-overflow']
+ if 'shared' in filename:
+ ret += ['--side-module']
+ if 'standalone-wasm' in filename:
+ ret += ['--standalone-wasm']
+ return ret
def test_wasm_emscripten_finalize():
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index f569b54cb..b45b61b8f 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -207,6 +207,8 @@ int main(int argc, const char* argv[]) {
}
EmscriptenGlueGenerator generator(wasm);
+ generator.setStandalone(standaloneWasm);
+
generator.fixInvokeFunctionNames();
std::vector<Name> initializerFunctions;
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index 8aa911ef9..c7689b4bc 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -31,6 +31,8 @@ public:
: wasm(wasm), builder(wasm), stackPointerOffset(stackPointerOffset),
useStackPointerGlobal(stackPointerOffset == 0) {}
+ void setStandalone(bool standalone_) { standalone = standalone_; }
+
void generateRuntimeFunctions();
Function* generateMemoryGrowthFunction();
Function* generateAssignGOTEntriesFunction();
@@ -69,6 +71,7 @@ private:
Builder builder;
Address stackPointerOffset;
bool useStackPointerGlobal;
+ bool standalone;
// Used by generateDynCallThunk to track all the dynCall functions created
// so far.
std::unordered_set<Signature> sigs;
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index be4e51b9e..f069e558d 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -103,18 +103,25 @@ inline Expression* stackBoundsCheck(Builder& builder,
Expression* value,
Global* stackPointer,
Global* stackLimit,
- Name handler) {
+ Name handlerName) {
// Add a local to store the value of the expression. We need the value twice:
// once to check if it has overflowed, and again to assign to store it.
auto newSP = Builder::addVar(func, stackPointer->type);
+ // If we imported a handler, call it. That can show a nice error in JS.
+ // Otherwise, just trap.
+ Expression* handler;
+ if (handlerName.is()) {
+ handler = builder.makeCall(handlerName, {}, none);
+ } else {
+ handler = builder.makeUnreachable();
+ }
// (if (i32.lt_u (local.tee $newSP (...value...)) (global.get $__stack_limit))
- // (call $handler))
auto check =
builder.makeIf(builder.makeBinary(
BinaryOp::LtUInt32,
builder.makeLocalTee(newSP, value, stackPointer->type),
builder.makeGlobalGet(stackLimit->name, stackLimit->type)),
- builder.makeCall(handler, {}, none));
+ handler);
// (global.set $__stack_pointer (local.get $newSP))
auto newSet = builder.makeGlobalSet(
stackPointer->name, builder.makeLocalGet(newSP, stackPointer->type));
@@ -530,8 +537,7 @@ void EmscriptenGlueGenerator::enforceStackLimit() {
Builder::Mutable);
wasm.addGlobal(stackLimit);
- auto handler = importStackOverflowHandler();
-
+ Name handler = importStackOverflowHandler();
StackLimitEnforcer walker(stackPointer, stackLimit, builder, handler);
PassRunner runner(&wasm);
walker.run(&runner, &wasm);
@@ -549,6 +555,12 @@ void EmscriptenGlueGenerator::generateSetStackLimitFunction() {
}
Name EmscriptenGlueGenerator::importStackOverflowHandler() {
+ // We can call an import to handle stack overflows normally, but not in
+ // standalone mode, where we can't import from JS.
+ if (standalone) {
+ return Name();
+ }
+
ImportInfo info(wasm);
if (auto* existing = info.getImportedFunction(ENV, STACK_OVERFLOW_IMPORT)) {
diff --git a/test/lld/safe_stack_standalone-wasm.wat b/test/lld/safe_stack_standalone-wasm.wat
new file mode 100644
index 000000000..e2d8a79de
--- /dev/null
+++ b/test/lld/safe_stack_standalone-wasm.wat
@@ -0,0 +1,89 @@
+(module
+ (type $0 (func (param i32 i32) (result i32)))
+ (type $1 (func))
+ (type $2 (func (result i32)))
+ (import "env" "printf" (func $printf (param i32 i32) (result i32)))
+ (memory $0 2)
+ (data (i32.const 568) "%d:%d\n\00Result: %d\n\00")
+ (table $0 1 1 funcref)
+ (global $global$0 (mut i32) (i32.const 66128))
+ (global $global$1 i32 (i32.const 66128))
+ (global $global$2 i32 (i32.const 587))
+ (export "memory" (memory $0))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "__data_end" (global $global$2))
+ (export "main" (func $main))
+ (func $__wasm_call_ctors (; 1 ;) (type $1)
+ )
+ (func $foo (; 2 ;) (type $0) (param $0 i32) (param $1 i32) (result i32)
+ (local $2 i32)
+ (global.set $global$0
+ (local.tee $2
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store offset=4
+ (local.get $2)
+ (local.get $1)
+ )
+ (i32.store
+ (local.get $2)
+ (local.get $0)
+ )
+ (drop
+ (call $printf
+ (i32.const 568)
+ (local.get $2)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ (i32.add
+ (local.get $1)
+ (local.get $0)
+ )
+ )
+ (func $__original_main (; 3 ;) (type $2) (result i32)
+ (local $0 i32)
+ (global.set $global$0
+ (local.tee $0
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store
+ (local.get $0)
+ (call $foo
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (drop
+ (call $printf
+ (i32.const 575)
+ (local.get $0)
+ )
+ )
+ (global.set $global$0
+ (i32.add
+ (local.get $0)
+ (i32.const 16)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $main (; 4 ;) (type $0) (param $0 i32) (param $1 i32) (result i32)
+ (call $__original_main)
+ )
+)
+
diff --git a/test/lld/safe_stack_standalone-wasm.wat.out b/test/lld/safe_stack_standalone-wasm.wat.out
new file mode 100644
index 000000000..3632831ac
--- /dev/null
+++ b/test/lld/safe_stack_standalone-wasm.wat.out
@@ -0,0 +1,254 @@
+(module
+ (type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (type $none_=>_i32 (func (result i32)))
+ (type $i32_=>_i32 (func (param i32) (result i32)))
+ (import "env" "printf" (func $printf (param i32 i32) (result i32)))
+ (memory $0 2)
+ (data (i32.const 568) "%d:%d\n\00Result: %d\n\00")
+ (table $0 1 1 funcref)
+ (global $global$0 (mut i32) (i32.const 66128))
+ (global $global$1 i32 (i32.const 66128))
+ (global $global$2 i32 (i32.const 587))
+ (global $__stack_limit (mut i32) (i32.const 0))
+ (export "memory" (memory $0))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "__data_end" (global $global$2))
+ (export "main" (func $main))
+ (export "__set_stack_limit" (func $__set_stack_limit))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (export "_start" (func $_start))
+ (func $__wasm_call_ctors (; 1 ;)
+ (nop)
+ )
+ (func $foo (; 2 ;) (param $0 i32) (param $1 i32) (result i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (block
+ (if
+ (i32.lt_u
+ (local.tee $3
+ (local.tee $2
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $3)
+ )
+ )
+ (i32.store offset=4
+ (local.get $2)
+ (local.get $1)
+ )
+ (i32.store
+ (local.get $2)
+ (local.get $0)
+ )
+ (drop
+ (call $printf
+ (i32.const 568)
+ (local.get $2)
+ )
+ )
+ (block
+ (if
+ (i32.lt_u
+ (local.tee $4
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $4)
+ )
+ )
+ (i32.add
+ (local.get $1)
+ (local.get $0)
+ )
+ )
+ (func $__original_main (; 3 ;) (result i32)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (block
+ (if
+ (i32.lt_u
+ (local.tee $1
+ (local.tee $0
+ (i32.sub
+ (global.get $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $1)
+ )
+ )
+ (i32.store
+ (local.get $0)
+ (call $foo
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (drop
+ (call $printf
+ (i32.const 575)
+ (local.get $0)
+ )
+ )
+ (block
+ (if
+ (i32.lt_u
+ (local.tee $2
+ (i32.add
+ (local.get $0)
+ (i32.const 16)
+ )
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $2)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $main (; 4 ;) (param $0 i32) (param $1 i32) (result i32)
+ (call $__original_main)
+ )
+ (func $__set_stack_limit (; 5 ;) (param $0 i32)
+ (global.set $__stack_limit
+ (local.get $0)
+ )
+ )
+ (func $stackSave (; 6 ;) (result i32)
+ (global.get $global$0)
+ )
+ (func $stackAlloc (; 7 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (local $2 i32)
+ (block
+ (if
+ (i32.lt_u
+ (local.tee $2
+ (local.tee $1
+ (i32.and
+ (i32.sub
+ (global.get $global$0)
+ (local.get $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $2)
+ )
+ )
+ (local.get $1)
+ )
+ (func $stackRestore (; 8 ;) (param $0 i32)
+ (local $1 i32)
+ (if
+ (i32.lt_u
+ (local.tee $1
+ (local.get $0)
+ )
+ (global.get $__stack_limit)
+ )
+ (unreachable)
+ )
+ (global.set $global$0
+ (local.get $1)
+ )
+ )
+ (func $__growWasmMemory (; 9 ;) (param $newSize i32) (result i32)
+ (memory.grow
+ (local.get $newSize)
+ )
+ )
+ (func $_start (; 10 ;)
+ (drop
+ (call $main
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ )
+)
+(;
+--BEGIN METADATA --
+{
+ "staticBump": 19,
+ "tableSize": 1,
+ "initializers": [
+ "__wasm_call_ctors"
+ ],
+ "declares": [
+ "printf"
+ ],
+ "externs": [
+ ],
+ "implementedFunctions": [
+ "___wasm_call_ctors",
+ "_main",
+ "___set_stack_limit",
+ "_stackSave",
+ "_stackAlloc",
+ "_stackRestore",
+ "___growWasmMemory",
+ "__start"
+ ],
+ "exports": [
+ "__wasm_call_ctors",
+ "main",
+ "__set_stack_limit",
+ "stackSave",
+ "stackAlloc",
+ "stackRestore",
+ "__growWasmMemory",
+ "_start"
+ ],
+ "namedGlobals": {
+ "__heap_base" : "66128",
+ "__data_end" : "587"
+ },
+ "invokeFuncs": [
+ ],
+ "features": [
+ ],
+ "mainReadsParams": 0
+}
+-- END METADATA --
+;)