diff options
author | Alon Zakai <azakai@google.com> | 2020-04-22 12:11:46 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-22 12:11:46 -0700 |
commit | 35a36b15e1bf16b78a6f3e174543681748295e81 (patch) | |
tree | 1a5dd5af79b064b73c9475948f077cdc93f47e49 /src/tools/wasm2c-wrapper.h | |
parent | d8b414d22b032efc87dbceb50abef8bce5ce8266 (diff) | |
download | binaryen-35a36b15e1bf16b78a6f3e174543681748295e81.tar.gz binaryen-35a36b15e1bf16b78a6f3e174543681748295e81.tar.bz2 binaryen-35a36b15e1bf16b78a6f3e174543681748295e81.zip |
[fuzzing] wasm2c integration (#2772)
This adds support for fuzzing with wabt's wasm2c that @binji wrote.
Basically we compile the wasm to C, then compile the C to a native
executable with a custom main() to wrap around it. The executable
should then print exactly the same as that wasm when run in either
the binaryen interpreter or in a JS VM with our wrapper JS for that
wasm. In other words, compiling the wasm to C is another way to
run that wasm.
The main reasons I want this are to fuzz wasm2c itself, and to
have another option for fuzzing emcc. For the latter, we do fuzz
wasm-opt quite a lot, but that doesn't fuzz the non-wasm-opt
parts of emcc. And using wasm2c for that is nice since the
starting point is always a wasm file, which means we
can use tools like wasm-reduce and so forth, which can be
integrated with this fuzzer.
This also:
Refactors the fuzzer harness a little to make it easier to
add more "VMs" to run wasms in.
Do not autoreduce when re-running a testcase, which I hit
while developing this.
Diffstat (limited to 'src/tools/wasm2c-wrapper.h')
-rw-r--r-- | src/tools/wasm2c-wrapper.h | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/tools/wasm2c-wrapper.h b/src/tools/wasm2c-wrapper.h new file mode 100644 index 000000000..684bd52f7 --- /dev/null +++ b/src/tools/wasm2c-wrapper.h @@ -0,0 +1,186 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Emit a C wrapper file that can run the wasm after it is compiled with +// wasm2c, useful for fuzzing. +// + +#include <string> + +#include "wasm.h" + +namespace wasm { + +static std::string generateWasm2CWrapper(Module& wasm) { + // First, emit implementations of the wasm's imports so that the wasm2c code + // can call them. The names use wasm2c's name mangling. + std::string ret = R"( +#include <stdint.h> +#include <stdio.h> + +#include "wasm-rt-impl.h" +#include "wasm.h" + +void _Z_fuzzingZ2DsupportZ_logZ2Di32Z_vi(u32 x) { + printf("[LoggingExternalInterface logging %d]\n", x); +} +void (*Z_fuzzingZ2DsupportZ_logZ2Di32Z_vi)(u32) = _Z_fuzzingZ2DsupportZ_logZ2Di32Z_vi; + +void _Z_fuzzingZ2DsupportZ_logZ2Di64Z_vj(u64 x) { + printf("[LoggingExternalInterface logging %ld]\n", (long)x); +} +void (*Z_fuzzingZ2DsupportZ_logZ2Di64Z_vj)(u64) = _Z_fuzzingZ2DsupportZ_logZ2Di64Z_vj; + +void _Z_fuzzingZ2DsupportZ_logZ2Di64Z_vii(u32 x, u32 y) { + printf("[LoggingExternalInterface logging %d %d]\n", x, y); +} +void (*Z_fuzzingZ2DsupportZ_logZ2Di64Z_vii)(u32, u32) = _Z_fuzzingZ2DsupportZ_logZ2Di64Z_vii; + +void _Z_fuzzingZ2DsupportZ_logZ2Df32Z_vf(f32 x) { + printf("[LoggingExternalInterface logging %.17e]\n", x); +} +void (*Z_fuzzingZ2DsupportZ_logZ2Df32Z_vf)(f32) = _Z_fuzzingZ2DsupportZ_logZ2Df32Z_vf; + +void _Z_fuzzingZ2DsupportZ_logZ2Df64Z_vd(f64 x) { + printf("[LoggingExternalInterface logging %.17le]\n", x); +} +void (*Z_fuzzingZ2DsupportZ_logZ2Df64Z_vd)(f64) = _Z_fuzzingZ2DsupportZ_logZ2Df64Z_vd; + +// Miscellaneous imports + +u32 tempRet0 = 0; + +void _Z_envZ_setTempRet0Z_vi(u32 x) { + tempRet0 = x; +} +void (*Z_envZ_setTempRet0Z_vi)(u32) = _Z_envZ_setTempRet0Z_vi; + +u32 _Z_envZ_getTempRet0Z_iv(void) { + return tempRet0; +} +u32 (*Z_envZ_getTempRet0Z_iv)(void) = _Z_envZ_getTempRet0Z_iv; + +// Main + +int main(int argc, char** argv) { + init(); + +)"; + + // For each export in the wasm, emit code to call it and log its result, + // similar to the other wrappers. + for (auto& exp : wasm.exports) { + if (exp->kind != ExternalKind::Function) { + continue; + } + + // Always call the hang limit initializer before each export. + ret += " (*Z_hangLimitInitializerZ_vv)();\n"; + + auto* func = wasm.getFunction(exp->value); + + // Emit a setjmp so that we can handle traps from the compiled wasm. + ret += + std::string(" puts(\"[fuzz-exec] calling ") + exp->name.str + "\");\n"; + ret += " if (setjmp(g_jmp_buf) == 0) {\n"; + auto result = func->sig.results; + + // Emit the call itself. + ret += " "; + if (result != Type::none) { + ret += std::string("printf(\"[fuzz-exec] note result: ") + exp->name.str + + " => "; + switch (result.getSingle()) { + case Type::i32: + ret += "%d\\n\", "; + break; + case Type::i64: + ret += "%ld\\n\", (long)"; + break; + case Type::f32: + ret += "%.17e\\n\", "; + break; + case Type::f64: + ret += "%.17le\\n\", "; + break; + default: + Fatal() << "unhandled wasm2c wrapper result type: " << result; + } + } + + // Emit the callee's name with wasm2c name mangling. + ret += std::string("(*Z_") + exp->name.str + "Z_"; + auto params = func->sig.params.expand(); + + auto wasm2cSignature = [](Type type) { + switch (type.getSingle()) { + case Type::none: + return 'v'; + case Type::i32: + return 'i'; + case Type::i64: + return 'j'; + case Type::f32: + return 'f'; + case Type::f64: + return 'd'; + default: + Fatal() << "unhandled wasm2c wrapper signature type: " << type; + } + }; + + ret += wasm2cSignature(result); + if (params.empty()) { + ret += wasm2cSignature(Type::none); + } else { + for (auto param : params) { + ret += wasm2cSignature(param); + } + } + ret += ")("; + + // Emit the parameters (all 0s, like the other wrappers). + bool first = true; + for (auto param : params) { + WASM_UNUSED(param); + if (!first) { + ret += ", "; + } + ret += "0"; + first = false; + } + if (result != Type::none) { + ret += ")"; + } + ret += ");\n"; + + // Handle a longjmp which indicates a trap happened. + ret += " } else {\n"; + ret += " puts(\"exception!\");\n"; + ret += " }\n"; + } + + ret += R"( + + return 0; +} +)"; + + return ret; +} + +} // namespace wasm |