From 8c0f53d236f664086405870321e5887aaed39f3f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jan 2022 15:04:42 -0800 Subject: [ctor-eval] Add --ignore-external-input option (#4428) This is meant to address one of the main limitations of wasm-ctor-eval in emscripten atm, that libc++ global ctors will read env vars, which means they call an import, which stops us from evalling, emscripten-core/emscripten#15403 (comment) To handle that, this adds an option to ignore external input. When set, we can assume that no env vars will be read, no reading from stdin, no arguments to main(), etc. Perhaps these could each be separate options, but I think keeping it simple for now might be good enough. --- src/tools/wasm-ctor-eval.cpp | 80 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) (limited to 'src/tools/wasm-ctor-eval.cpp') diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index b0a1c0551..009a91c0b 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -142,7 +142,7 @@ std::unique_ptr buildEnvModule(Module& wasm) { // create empty functions with similar signature ModuleUtils::iterImportedFunctions(wasm, [&](Function* func) { - if (func->module == "env") { + if (func->module == env->name) { Builder builder(*env); auto* copied = ModuleUtils::copyFunction(func, *env); copied->module = Name(); @@ -155,7 +155,7 @@ std::unique_ptr buildEnvModule(Module& wasm) { // create tables with similar initial and max values ModuleUtils::iterImportedTables(wasm, [&](Table* table) { - if (table->module == "env") { + if (table->module == env->name) { auto* copied = ModuleUtils::copyTable(table, *env); copied->module = Name(); copied->base = Name(); @@ -165,7 +165,7 @@ std::unique_ptr buildEnvModule(Module& wasm) { }); ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) { - if (global->module == "env") { + if (global->module == env->name) { auto* copied = ModuleUtils::copyGlobal(global, *env); copied->module = Name(); copied->base = Name(); @@ -179,7 +179,7 @@ std::unique_ptr buildEnvModule(Module& wasm) { // create an exported memory with the same initial and max size ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) { - if (memory->module == "env") { + if (memory->module == env->name) { env->memory.name = wasm.memory.name; env->memory.exists = true; env->memory.initial = memory->initial; @@ -194,6 +194,11 @@ std::unique_ptr buildEnvModule(Module& wasm) { return env; } +// Whether to ignore external input to the program as it runs. If set, we will +// assume that stdin is empty, that any env vars we try to read are not set, +// that there are not arguments passed to main, etc. +static bool ignoreExternalInput = false; + struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { Module* wasm; EvallingModuleInstance* instance; @@ -242,10 +247,63 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { } Literals callImport(Function* import, LiteralList& arguments) override { + Name WASI("wasi_snapshot_preview1"); + + if (ignoreExternalInput) { + if (import->module == WASI) { + if (import->base == "environ_sizes_get") { + if (arguments.size() != 2 || arguments[0].type != Type::i32 || + import->getResults() != Type::i32) { + throw FailToEvalException("wasi environ_sizes_get has wrong sig"); + } + + // Write out a count of i32(0) and return __WASI_ERRNO_SUCCESS (0). + store32(arguments[0].geti32(), 0); + return {Literal(int32_t(0))}; + } + + if (import->base == "environ_get") { + if (arguments.size() != 2 || arguments[0].type != Type::i32 || + import->getResults() != Type::i32) { + throw FailToEvalException("wasi environ_get has wrong sig"); + } + + // Just return __WASI_ERRNO_SUCCESS (0). + return {Literal(int32_t(0))}; + } + + if (import->base == "args_sizes_get") { + if (arguments.size() != 2 || arguments[0].type != Type::i32 || + import->getResults() != Type::i32) { + throw FailToEvalException("wasi args_sizes_get has wrong sig"); + } + + // Write out an argc of i32(0) and return a __WASI_ERRNO_SUCCESS (0). + store32(arguments[0].geti32(), 0); + return {Literal(int32_t(0))}; + } + + if (import->base == "args_get") { + if (arguments.size() != 2 || arguments[0].type != Type::i32 || + import->getResults() != Type::i32) { + throw FailToEvalException("wasi args_get has wrong sig"); + } + + // Just return __WASI_ERRNO_SUCCESS (0). + return {Literal(int32_t(0))}; + } + + // Otherwise, we don't recognize this import; continue normally to + // error. + } + } + std::string extra; if (import->module == ENV && import->base == "___cxa_atexit") { extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls " "to atexit are not emitted"; + } else if (import->module == WASI && !ignoreExternalInput) { + extra = "\nrecommendation: consider --ignore-external-input"; } throw FailToEvalException(std::string("call import: ") + import->module.str + "." + import->base.str + @@ -416,14 +474,14 @@ private: }; void evalCtors(Module& wasm, std::vector ctors) { + std::map> linkedInstances; + // build and link the env module auto envModule = buildEnvModule(wasm); CtorEvalExternalInterface envInterface; auto envInstance = std::make_shared(*envModule, &envInterface); - - std::map> linkedInstances; - linkedInstances["env"] = envInstance; + linkedInstances[envModule->name] = envInstance; CtorEvalExternalInterface interface(linkedInstances); try { @@ -525,6 +583,14 @@ int main(int argc, const char* argv[]) { WasmCtorEvalOption, Options::Arguments::One, [&](Options* o, const std::string& argument) { ctorsString = argument; }) + .add("--ignore-external-input", + "-ipi", + "Assumes no env vars are to be read, stdin is empty, etc.", + WasmCtorEvalOption, + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { + ignoreExternalInput = true; + }) .add_positional("INFILE", Options::Arguments::One, [](Options* o, const std::string& argument) { -- cgit v1.2.3