diff options
author | Sam Clegg <sbc@chromium.org> | 2019-04-02 14:53:48 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-02 14:53:48 -0700 |
commit | b192c53c6ef549bb5264bc58a71424ce311a4890 (patch) | |
tree | 895f6535ba16bed2c5ec75d365dfe37eda776315 /src/wasm | |
parent | 8e8cb825cd28bc1c188f9cf796bd97626bc1bb40 (diff) | |
download | binaryen-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.cpp | 149 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 1 |
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"), |