summaryrefslogtreecommitdiff
path: root/src/tools/wasm2c-wrapper.h
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2020-04-22 12:11:46 -0700
committerGitHub <noreply@github.com>2020-04-22 12:11:46 -0700
commit35a36b15e1bf16b78a6f3e174543681748295e81 (patch)
tree1a5dd5af79b064b73c9475948f077cdc93f47e49 /src/tools/wasm2c-wrapper.h
parentd8b414d22b032efc87dbceb50abef8bce5ce8266 (diff)
downloadbinaryen-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.h186
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