summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2018-11-30 12:20:33 -0800
committerGitHub <noreply@github.com>2018-11-30 12:20:33 -0800
commit3814fd6f2dab5adb5fb81022308818e4dd3b1020 (patch)
tree00b9787fcef8599e6d8c61674c510e3dcf52391f
parent0e068c386ef1588c09e57a4d081be626d83bc31c (diff)
downloadbinaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.tar.gz
binaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.tar.bz2
binaryen-3814fd6f2dab5adb5fb81022308818e4dd3b1020.zip
wasm-emscripten-finalize: Remove stack pointer global from shared libs (#1791)
The wasm backend uses a wasm global (__stack_pointer) for the shadow stack location. In order to make this work with shared libraries the main module would have to export this global and shared libraries would need to import it. This means we'd be relying of mutable globals which are not yet implemented in all browsers. This change allows is to move forward with shared libraries without mutable global support by replacing all stack pointer access in shared libraries with functions calls.
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp21
-rw-r--r--src/wasm-emscripten.h4
-rw-r--r--src/wasm/wasm-emscripten.cpp71
3 files changed, 82 insertions, 14 deletions
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 6814ae032..1a3499757 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -155,11 +155,6 @@ int main(int argc, const char *argv[]) {
dataSize = dataEndConst->value.geti32() - globalBase;
}
- std::vector<Name> initializerFunctions;
- if (wasm.getFunctionOrNull("__wasm_call_ctors")) {
- initializerFunctions.push_back("__wasm_call_ctors");
- }
-
EmscriptenGlueGenerator generator(wasm);
generator.fixInvokeFunctionNames();
@@ -171,10 +166,24 @@ int main(int argc, const char *argv[]) {
passRunner.run();
}
- if (!isSideModule) {
+ std::vector<Name> initializerFunctions;
+
+ if (isSideModule) {
+ generator.replaceStackPointerGlobal();
+ // rename __wasm_call_ctors to __post_instantiate which is what
+ // emscripten expects.
+ // TODO(sbc): Unify these two names
+ if (Export* ex = wasm.getExportOrNull("__wasm_call_ctors")) {
+ ex->name = "__post_instantiate";
+ }
+ } else {
generator.generateRuntimeFunctions();
generator.generateMemoryGrowthFunction();
+ if (wasm.getFunctionOrNull("__wasm_call_ctors")) {
+ initializerFunctions.push_back("__wasm_call_ctors");
+ }
}
+
generator.generateDynCallThunks();
generator.generateJSCallThunks(numReservedFunctionPointers);
std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions, numReservedFunctionPointers);
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index aa42b86b2..c15a4b8af 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -41,6 +41,10 @@ public:
// signature in the indirect function table.
void generateDynCallThunks();
+ // Convert stack pointer access from get_global/set_global to calling save
+ // and restore functions.
+ void replaceStackPointerGlobal();
+
// Create thunks to support emscripten's addFunction functionality. Creates (#
// of reserved function pointers) thunks for each indirectly called function
// signature.
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 14104c1eb..a43fc1fe7 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -25,6 +25,7 @@
#include "wasm-traversal.h"
#include "wasm.h"
#include "ir/function-type-utils.h"
+#include "ir/import-utils.h"
#include "ir/module-utils.h"
namespace wasm {
@@ -32,7 +33,10 @@ namespace wasm {
cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
cashew::IString EM_JS_PREFIX("__em_js__");
-static constexpr const char* dummyFunction = "__wasm_nullptr";
+static Name STACK_SAVE("stackSave"),
+ STACK_RESTORE("stackRestore"),
+ STACK_ALLOC("stackAlloc"),
+ DUMMY_FUNC("__wasm_nullptr");
void addExportedFunction(Module& wasm, Function* function) {
wasm.addFunction(function);
@@ -80,10 +84,9 @@ Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value
}
void EmscriptenGlueGenerator::generateStackSaveFunction() {
- Name name("stackSave");
std::vector<NameType> params { };
Function* function = builder.makeFunction(
- name, std::move(params), i32, {}
+ STACK_SAVE, std::move(params), i32, {}
);
function->body = generateLoadStackPointer();
@@ -92,10 +95,9 @@ void EmscriptenGlueGenerator::generateStackSaveFunction() {
}
void EmscriptenGlueGenerator::generateStackAllocFunction() {
- Name name("stackAlloc");
std::vector<NameType> params { { "0", i32 } };
Function* function = builder.makeFunction(
- name, std::move(params), i32, { { "1", i32 } }
+ STACK_ALLOC, std::move(params), i32, { { "1", i32 } }
);
Expression* loadStack = generateLoadStackPointer();
GetLocal* getSizeArg = builder.makeGetLocal(0, i32);
@@ -118,10 +120,9 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() {
}
void EmscriptenGlueGenerator::generateStackRestoreFunction() {
- Name name("stackRestore");
std::vector<NameType> params { { "0", i32 } };
Function* function = builder.makeFunction(
- name, std::move(params), none, {}
+ STACK_RESTORE, std::move(params), none, {}
);
GetLocal* getArg = builder.makeGetLocal(0, i32);
Expression* store = generateStoreStackPointer(getArg);
@@ -182,7 +183,7 @@ void EmscriptenGlueGenerator::generateDynCallThunks() {
tableSegmentData = wasm.table.segments[0].data;
}
for (const auto& indirectFunc : tableSegmentData) {
- if (indirectFunc == dummyFunction) {
+ if (indirectFunc == DUMMY_FUNC) {
continue;
}
std::string sig = getSig(wasm.getFunction(indirectFunc));
@@ -207,6 +208,60 @@ void EmscriptenGlueGenerator::generateDynCallThunks() {
}
}
+static Function* ensureFunctionImport(Module* module, Name name, std::string sig) {
+ // Then see if its already imported
+ ImportInfo info(*module);
+ if (Function* f = info.getImportedFunction(ENV, name)) {
+ return f;
+ }
+ // Failing that create a new function import.
+ auto import = new Function;
+ import->name = name;
+ import->module = ENV;
+ import->base = name;
+ auto* functionType = ensureFunctionType(sig, module);
+ import->type = functionType->name;
+ FunctionTypeUtils::fillFunction(import, functionType);
+ module->addFunction(import);
+ return import;
+}
+
+struct RemoveStackPointer : public PostWalker<RemoveStackPointer> {
+ RemoveStackPointer(Global* StackPointer) : StackPointer(StackPointer) {}
+
+ void visitGetGlobal(GetGlobal* curr) {
+ if (getModule()->getGlobalOrNull(curr->name) == StackPointer) {
+ ensureFunctionImport(getModule(), STACK_SAVE, "i");
+ if (!builder) builder = make_unique<Builder>(*getModule());
+ replaceCurrent(builder->makeCall(STACK_SAVE, {}, i32));
+ }
+ }
+
+ void visitSetGlobal(SetGlobal* curr) {
+ if (getModule()->getGlobalOrNull(curr->name) == StackPointer) {
+ ensureFunctionImport(getModule(), STACK_RESTORE, "vi");
+ if (!builder) builder = make_unique<Builder>(*getModule());
+ replaceCurrent(builder->makeCall(STACK_RESTORE, {curr->value}, none));
+ }
+ }
+
+private:
+ std::unique_ptr<Builder> builder;
+ Global* StackPointer;
+};
+
+void EmscriptenGlueGenerator::replaceStackPointerGlobal() {
+ Global* stackPointer = getStackPointerGlobal();
+
+ // Replace all uses of stack pointer global
+ RemoveStackPointer walker(stackPointer);
+ walker.walkModule(&wasm);
+
+ // Finally remove the stack pointer global itself. This avoids importing
+ // a mutable global.
+ wasm.removeGlobal(stackPointer->name);
+}
+
struct JSCallWalker : public PostWalker<JSCallWalker> {
Module &wasm;
JSCallWalker(Module &_wasm) : wasm(_wasm) {