summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/shared-constants.h1
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp25
-rw-r--r--src/wasm-emscripten.h2
-rw-r--r--src/wasm/wasm-emscripten.cpp149
-rw-r--r--src/wasm/wasm.cpp1
5 files changed, 144 insertions, 34 deletions
diff --git a/src/shared-constants.h b/src/shared-constants.h
index ae7d915ef..52124d891 100644
--- a/src/shared-constants.h
+++ b/src/shared-constants.h
@@ -22,6 +22,7 @@
namespace wasm {
extern Name GROW_WASM_MEMORY,
+ WASM_CALL_CTORS,
MEMORY_BASE,
TABLE_BASE,
GET_TEMP_RET0,
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 2aa5de46f..180383a90 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -46,6 +46,7 @@ int main(int argc, const char *argv[]) {
std::string dataSegmentFile;
bool emitBinary = true;
bool debugInfo = false;
+ bool isSideModule = false;
bool legalizeJavaScriptFFI = true;
uint64_t globalBase = INVALID_BASE;
uint64_t initialStackPointer = INVALID_BASE;
@@ -79,7 +80,11 @@ int main(int argc, const char *argv[]) {
[&initialStackPointer](Options*, const std::string&argument ) {
initialStackPointer = std::stoull(argument);
})
-
+ .add("--side-module", "", "Input is an emscripten side module",
+ Options::Arguments::Zero,
+ [&isSideModule](Options *o, const std::string& argument) {
+ isSideModule = true;
+ })
.add("--input-source-map", "-ism", "Consume source map from the specified file",
Options::Arguments::One,
[&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; })
@@ -130,7 +135,6 @@ int main(int argc, const char *argv[]) {
WasmPrinter::printModule(&wasm, std::cerr);
}
- bool isSideModule = false;
for (const UserSection& section : wasm.userSections) {
if (section.name == BinaryConsts::UserSections::Dylink) {
isSideModule = true;
@@ -166,12 +170,6 @@ int main(int argc, const char *argv[]) {
std::vector<Name> initializerFunctions;
- // The names of standard imports/exports used by lld doesn't quite match that
- // expected by emscripten.
- // TODO(sbc): Unify these
- if (Export* ex = wasm.getExportOrNull("__wasm_call_ctors")) {
- ex->name = "__post_instantiate";
- }
if (wasm.table.imported()) {
if (wasm.table.base != "table") wasm.table.base = Name("table");
}
@@ -182,14 +180,17 @@ int main(int argc, const char *argv[]) {
if (isSideModule) {
generator.replaceStackPointerGlobal();
+ generator.generatePostInstantiateFunction();
} else {
generator.generateRuntimeFunctions();
generator.generateMemoryGrowthFunction();
generator.generateStackInitialization(initialStackPointer);
- // emscripten calls this by default for side libraries so we only need
- // to include in as a static ctor for main module case.
- if (wasm.getExportOrNull("__post_instantiate")) {
- initializerFunctions.push_back("__post_instantiate");
+ // For side modules these gets called via __post_instantiate
+ if (Function* F = generator.generateAssignGOTEntriesFunction()) {
+ initializerFunctions.push_back(F->name);
+ }
+ if (auto* e = wasm.getExportOrNull(WASM_CALL_CTORS)) {
+ initializerFunctions.push_back(e->value);
}
}
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index acb2994ad..67ba408f5 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -36,7 +36,9 @@ public:
void generateRuntimeFunctions();
Function* generateMemoryGrowthFunction();
+ Function* generateAssignGOTEntriesFunction();
void generateStackInitialization(Address addr);
+ void generatePostInstantiateFunction();
// Create thunks for use with emscripten Runtime.dynCall. Creates one for each
// signature in the indirect function table.
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"),