summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2019-04-02 14:53:48 -0700
committerGitHub <noreply@github.com>2019-04-02 14:53:48 -0700
commitb192c53c6ef549bb5264bc58a71424ce311a4890 (patch)
tree895f6535ba16bed2c5ec75d365dfe37eda776315 /src/wasm
parent8e8cb825cd28bc1c188f9cf796bd97626bc1bb40 (diff)
downloadbinaryen-b192c53c6ef549bb5264bc58a71424ce311a4890.tar.gz
binaryen-b192c53c6ef549bb5264bc58a71424ce311a4890.tar.bz2
binaryen-b192c53c6ef549bb5264bc58a71424ce311a4890.zip
wasm-emscripten-finalize: Improve shared library support (#1961)
Convert PIC code generated by llvm to work with the current emscripten ABI for dynamic linking: - Convert mutable global imports from GOT.mem and GOT.func into internal globals. - Initialize these globals on started up in g$foo and f$foo imported functions to calculate addresses at runtime. Also: - Add a test case for linking and finalizing a shared library - Allow __stack_pointer global to be non-existent as can be case for a shared library. - Allow __stack_pointer global to be an import, as can be the case for a shared library.
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-emscripten.cpp149
-rw-r--r--src/wasm/wasm.cpp1
2 files changed, 128 insertions, 22 deletions
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index ea24b945d..404d93ca3 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -36,7 +36,9 @@ cashew::IString EM_JS_PREFIX("__em_js__");
static Name STACK_SAVE("stackSave"),
STACK_RESTORE("stackRestore"),
STACK_ALLOC("stackAlloc"),
- STACK_INIT("stack$init");
+ STACK_INIT("stack$init"),
+ POST_INSTANTIATE("__post_instantiate"),
+ ASSIGN_GOT_ENTIRES("__assign_got_enties");
void addExportedFunction(Module& wasm, Function* function) {
wasm.addFunction(function);
@@ -47,15 +49,20 @@ void addExportedFunction(Module& wasm, Function* function) {
}
Global* EmscriptenGlueGenerator::getStackPointerGlobal() {
- // Assumption: The first non-imported global is global is __stack_pointer
+ // Assumption: The stack pointer is either imported as __stack_pointer or
+ // its the first non-imported global.
// TODO(sbc): Find a better way to discover the stack pointer. Perhaps the
// linker could export it by name?
for (auto& g : wasm.globals) {
- if (!g->imported()) {
+ if (g->imported()) {
+ if (g->base == "__stack_pointer") {
+ return g.get();
+ }
+ } else {
return g.get();
}
}
- Fatal() << "stack pointer global not found";
+ return nullptr;
}
Expression* EmscriptenGlueGenerator::generateLoadStackPointer() {
@@ -70,6 +77,8 @@ Expression* EmscriptenGlueGenerator::generateLoadStackPointer() {
);
}
Global* stackPointer = getStackPointerGlobal();
+ if (!stackPointer)
+ Fatal() << "stack pointer global not found";
return builder.makeGetGlobal(stackPointer->name, i32);
}
@@ -85,6 +94,8 @@ Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value
);
}
Global* stackPointer = getStackPointerGlobal();
+ if (!stackPointer)
+ Fatal() << "stack pointer global not found";
return builder.makeSetGlobal(stackPointer->name, value);
}
@@ -143,6 +154,116 @@ void EmscriptenGlueGenerator::generateRuntimeFunctions() {
generateStackRestoreFunction();
}
+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;
+}
+
+Function* EmscriptenGlueGenerator::generateAssignGOTEntriesFunction() {
+ std::vector<Global*> got_entries_func;
+ std::vector<Global*> got_entries_mem;
+ for (auto& g : wasm.globals) {
+ if (!g->imported()) {
+ continue;
+ }
+ if (g->module == "GOT.func") {
+ got_entries_func.push_back(g.get());
+ } else if (g->module == "GOT.mem") {
+ got_entries_mem.push_back(g.get());
+ } else {
+ continue;
+ }
+ // Make this an internal, non-imported, global.
+ g->module.clear();
+ g->init = Builder(wasm).makeConst(Literal(0));
+ }
+
+ if (!got_entries_func.size() && !got_entries_mem.size()) {
+ return nullptr;
+ }
+
+ Function* assign_func =
+ builder.makeFunction(ASSIGN_GOT_ENTIRES, std::vector<NameType>{}, none, {});
+ Block* block = builder.makeBlock();
+ assign_func->body = block;
+
+ for (Global* g : got_entries_mem) {
+ Name getter(std::string("g$") + g->base.c_str());
+ ensureFunctionImport(&wasm, getter, "i");
+ Expression* call = builder.makeCall(getter, {}, i32);
+ SetGlobal* set_global = builder.makeSetGlobal(g->name, call);
+ block->list.push_back(set_global);
+ }
+
+ for (Global* g : got_entries_func) {
+ Name getter(std::string("f$") + g->base.c_str());
+ if (auto* f = wasm.getFunctionOrNull(g->base)) {
+ getter.set((getter.c_str() + std::string("$") + getSig(f)).c_str(), false);
+ }
+ ensureFunctionImport(&wasm, getter, "i");
+ Expression* call = builder.makeCall(getter, {}, i32);
+ SetGlobal* set_global = builder.makeSetGlobal(g->name, call);
+ block->list.push_back(set_global);
+ }
+
+ wasm.addFunction(assign_func);
+ return assign_func;
+}
+
+// For emscripten SIDE_MODULE we generate a single exported function called
+// __post_instantiate which calls two functions:
+//
+// - __assign_got_enties
+// - __wasm_call_ctors
+//
+// The former is function we generate here which calls imported g$XXX functions
+// order to assign values to any globals imported from GOT.func or GOT.mem.
+// These globals hold address of functions and globals respectively.
+//
+// The later is the constructor function generaed by lld which performs any
+// fixups on the memory section and calls static constructors.
+void EmscriptenGlueGenerator::generatePostInstantiateFunction() {
+ Builder builder(wasm);
+ Function* post_instantiate =
+ builder.makeFunction(POST_INSTANTIATE, std::vector<NameType>{}, none, {});
+ wasm.addFunction(post_instantiate);
+
+ if (Function* F = generateAssignGOTEntriesFunction()) {
+ // call __assign_got_enties from post_instantiate
+ Expression* call = builder.makeCall(F->name, {}, none);
+ post_instantiate->body = builder.blockify(call);
+ }
+
+ // The names of standard imports/exports used by lld doesn't quite match that
+ // expected by emscripten.
+ // TODO(sbc): Unify these
+ if (auto* e = wasm.getExportOrNull(WASM_CALL_CTORS)) {
+ Expression* call = builder.makeCall(e->value, {}, none);
+ post_instantiate->body = builder.blockify(post_instantiate->body, call);
+ wasm.removeExport(WASM_CALL_CTORS);
+ }
+
+ auto* ex = new Export();
+ ex->value = post_instantiate->name;
+ ex->name = POST_INSTANTIATE;
+ ex->kind = ExternalKind::Function;
+ wasm.addExport(ex);
+ wasm.updateMaps();
+}
+
Function* EmscriptenGlueGenerator::generateMemoryGrowthFunction() {
Name name(GROW_WASM_MEMORY);
std::vector<NameType> params { { NEW_SIZE, i32 } };
@@ -215,24 +336,6 @@ 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) {}
@@ -262,6 +365,8 @@ private:
void EmscriptenGlueGenerator::replaceStackPointerGlobal() {
Global* stackPointer = getStackPointerGlobal();
+ if (!stackPointer)
+ return;
// Replace all uses of stack pointer global
RemoveStackPointer walker(stackPointer);
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index e135ba750..f7081e49c 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -37,6 +37,7 @@ const char* TargetFeatures = "target_features";
}
Name GROW_WASM_MEMORY("__growWasmMemory"),
+ WASM_CALL_CTORS("__wasm_call_ctors"),
MEMORY_BASE("__memory_base"),
TABLE_BASE("__table_base"),
GET_TEMP_RET0("getTempRet0"),