diff options
author | Abbas Mashayekh <martianboy2005@gmail.com> | 2021-04-16 19:38:35 +0430 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-16 08:08:35 -0700 |
commit | f738f5c838476da230bd5a4dc75e56c4b7be9ba3 (patch) | |
tree | 0d5add88387f42ba2ad0e75f1d2c2c2046f2ba4c /src/tools/wasm-ctor-eval.cpp | |
parent | bd2e8661a31aa02f701e31110108a5f5c194afed (diff) | |
download | binaryen-f738f5c838476da230bd5a4dc75e56c4b7be9ba3.tar.gz binaryen-f738f5c838476da230bd5a4dc75e56c4b7be9ba3.tar.bz2 binaryen-f738f5c838476da230bd5a4dc75e56c4b7be9ba3.zip |
Very simple module linking in wasm-shell (#3792)
This is a rewrite of the wasm-shell tool, with the goal of improved
compatibility with the reference interpreter and the spec test suite.
To facilitate that, module instances are provided with a list of linked
instances, and imported objects are looked up in the correct instance.
The new shell can:
- register and link modules using the (register ...) command.
- parse binary modules with the syntax (module binary ...).
- provide the "spectest" module defined in the reference interpreter
- assert instantiation traps with assert_trap
- better check linkability by looking up the linked instances in
- assert_unlinkable
It cannot call external function references that are not direct imports.
That would require bigger changes.
Diffstat (limited to 'src/tools/wasm-ctor-eval.cpp')
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 134 |
1 files changed, 105 insertions, 29 deletions
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 2f7707853..5bede4444 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -127,8 +127,11 @@ static Index STACK_UPPER_LIMIT = STACK_START + STACK_SIZE; class EvallingModuleInstance : public ModuleInstanceBase<EvallingGlobalManager, EvallingModuleInstance> { public: - EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface) - : ModuleInstanceBase(wasm, externalInterface) { + EvallingModuleInstance(Module& wasm, + ExternalInterface* externalInterface, + std::map<Name, std::shared_ptr<EvallingModuleInstance>> + linkedInstances_ = {}) + : ModuleInstanceBase(wasm, externalInterface, linkedInstances_) { // if any global in the module has a non-const constructor, it is using a // global import, which we don't have, and is illegal to use ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { @@ -165,9 +168,82 @@ public: } }; +// Build an artificial `env` module based on a module's imports, so that the +// interpreter can use correct object instances. It initializes usable global +// imports, and fills the rest with fake values since those are dangerous to +// use. we will fail if dangerous globals are used. +std::unique_ptr<Module> buildEnvModule(Module& wasm) { + auto env = std::make_unique<Module>(); + env->name = "env"; + + // create empty functions with similar signature + ModuleUtils::iterImportedFunctions(wasm, [&](Function* func) { + if (func->module == "env") { + Builder builder(*env); + auto* copied = ModuleUtils::copyFunction(func, *env); + copied->module = Name(); + copied->base = Name(); + copied->body = builder.makeUnreachable(); + env->addExport( + builder.makeExport(func->base, copied->name, ExternalKind::Function)); + } + }); + + // create tables with similar initial and max values + ModuleUtils::iterImportedTables(wasm, [&](Table* table) { + if (table->module == "env") { + auto* copied = ModuleUtils::copyTable(table, *env); + copied->module = Name(); + copied->base = Name(); + env->addExport(Builder(*env).makeExport( + table->base, copied->name, ExternalKind::Table)); + } + }); + + ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) { + if (global->module == "env") { + auto* copied = ModuleUtils::copyGlobal(global, *env); + copied->module = Name(); + copied->base = Name(); + + Builder builder(*env); + if (global->base == STACKTOP || global->base == STACK_MAX) { + copied->init = builder.makeConst(STACK_START); + } else { + copied->init = builder.makeConst(Literal::makeZero(global->type)); + } + env->addExport( + builder.makeExport(global->base, copied->name, ExternalKind::Global)); + } + }); + + // create an exported memory with the same initial and max size + ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) { + if (memory->module == "env") { + env->memory.name = wasm.memory.name; + env->memory.exists = true; + env->memory.initial = memory->initial; + env->memory.max = memory->max; + env->memory.shared = memory->shared; + env->memory.indexType = memory->indexType; + env->addExport(Builder(*env).makeExport( + wasm.memory.base, wasm.memory.name, ExternalKind::Memory)); + } + }); + + return env; +} + struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { Module* wasm; EvallingModuleInstance* instance; + std::map<Name, std::shared_ptr<EvallingModuleInstance>> linkedInstances; + + CtorEvalExternalInterface( + std::map<Name, std::shared_ptr<EvallingModuleInstance>> linkedInstances_ = + {}) { + linkedInstances.swap(linkedInstances_); + } void init(Module& wasm_, EvallingModuleInstance& instance_) override { wasm = &wasm_; @@ -175,31 +251,20 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { } void importGlobals(EvallingGlobalManager& globals, Module& wasm_) override { - // fill usable values for stack imports, and globals initialized to them - ImportInfo imports(wasm_); - if (auto* stackTop = imports.getImportedGlobal(ENV, STACKTOP)) { - globals[stackTop->name] = {Literal(int32_t(STACK_START))}; - if (auto* stackTop = - GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) { - globals[stackTop->name] = {Literal(int32_t(STACK_START))}; - } - } - if (auto* stackMax = imports.getImportedGlobal(ENV, STACK_MAX)) { - globals[stackMax->name] = {Literal(int32_t(STACK_START))}; - if (auto* stackMax = - GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) { - globals[stackMax->name] = {Literal(int32_t(STACK_START))}; - } - } - // fill in fake values for everything else, which is dangerous to use - ModuleUtils::iterDefinedGlobals(wasm_, [&](Global* defined) { - if (globals.find(defined->name) == globals.end()) { - globals[defined->name] = Literal::makeZeros(defined->type); - } - }); - ModuleUtils::iterImportedGlobals(wasm_, [&](Global* import) { - if (globals.find(import->name) == globals.end()) { - globals[import->name] = Literal::makeZeros(import->type); + ModuleUtils::iterImportedGlobals(wasm_, [&](Global* global) { + auto it = linkedInstances.find(global->module); + if (it != linkedInstances.end()) { + auto* inst = it->second.get(); + auto* globalExport = inst->wasm.getExportOrNull(global->base); + if (!globalExport) { + throw FailToEvalException(std::string("importGlobals: ") + + global->module.str + "." + + global->base.str); + } + globals[global->name] = inst->globals[globalExport->value]; + } else { + throw FailToEvalException(std::string("importGlobals: ") + + global->module.str + "." + global->base.str); } }); } @@ -368,14 +433,25 @@ private: }; void evalCtors(Module& wasm, std::vector<std::string> ctors) { - CtorEvalExternalInterface interface; + // build and link the env module + auto envModule = buildEnvModule(wasm); + CtorEvalExternalInterface envInterface; + auto envInstance = + std::make_shared<EvallingModuleInstance>(*envModule, &envInterface); + envInstance->setupEnvironment(); + + std::map<Name, std::shared_ptr<EvallingModuleInstance>> linkedInstances; + linkedInstances["env"] = envInstance; + + CtorEvalExternalInterface interface(linkedInstances); try { // flatten memory, so we do not depend on the layout of data segments if (!MemoryUtils::flatten(wasm.memory)) { Fatal() << " ...stopping since could not flatten memory\n"; } + // create an instance for evalling - EvallingModuleInstance instance(wasm, &interface); + EvallingModuleInstance instance(wasm, &interface, linkedInstances); // set up the stack area and other environment details instance.setupEnvironment(); // we should not add new globals from here on; as a result, using |