#include <sstream>
#include <string>
#include "wasm.h"
namespace wasm {
inline std::string wasm2cMangle(Name name, Signature sig) {
const char escapePrefix = 'Z';
std::string mangled = "Z_";
for (unsigned char c : name.str) {
if ((isalnum(c) && c != escapePrefix) || c == '_') {
mangled += c;
} else {
mangled += escapePrefix;
std::stringstream ss;
ss << std::hex << std::uppercase << unsigned(c);
mangled += ss.str();
}
}
mangled += "Z_";
auto wasm2cSignature = [](Type type) {
TODO_SINGLE_COMPOUND(type);
switch (type.getBasic()) {
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;
}
};
mangled += wasm2cSignature(sig.results);
if (sig.params.isTuple()) {
for (const auto& param : sig.params) {
mangled += wasm2cSignature(param);
}
} else {
mangled += wasm2cSignature(sig.params);
}
return mangled;
}
inline std::string generateWasm2CWrapper(Module& wasm) {
std::string ret = R"(
#include <inttypes.h>
#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 %" PRId64 "]\n", (int64_t)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();
// We go through each export and call it, in turn. Note that we use a loop
// so we can do all this with a single setjmp. A setjmp is needed to handle
// wasm traps, and emitting a single one helps compilation speed into wasm as
// compile times are O(size * num_setjmps).
for (size_t curr = 0;; curr++) {
)";
ret += R"(
// Prepare to call the export, so we can catch traps.
if (WASM_RT_SETJMP(g_jmp_buf) != 0) {
puts("exception!");
} else {
// Call the proper export.
switch(curr) {
)";
size_t functionExportIndex = 0;
for (auto& exp : wasm.exports) {
if (exp->kind != ExternalKind::Function) {
continue;
}
ret += " case " + std::to_string(functionExportIndex++) + ":\n";
auto* func = wasm.getFunction(exp->value);
ret += std::string(" puts(\"[fuzz-exec] calling ") +
exp->name.toString() + "\");\n";
auto result = func->getResults();
ret += " ";
if (result != Type::none) {
ret += std::string("printf(\"[fuzz-exec] note result: ") +
exp->name.toString() + " => ";
TODO_SINGLE_COMPOUND(result);
switch (result.getBasic()) {
case Type::i32:
ret += "%d\\n\", ";
break;
case Type::i64:
ret += "%\" PRId64 \"\\n\", (int64_t)";
break;
case Type::f32:
ret += "%.17e\\n\", ";
break;
case Type::f64:
ret += "%.17le\\n\", ";
break;
default:
Fatal() << "unhandled wasm2c wrapper result type: " << result;
}
}
ret += "(*";
ret += wasm2cMangle(exp->name, func->getSig());
ret += ")(";
bool first = true;
for ([[maybe_unused]] const auto& param : func->getParams()) {
if (!first) {
ret += ", ";
}
ret += "0";
first = false;
}
if (result != Type::none) {
ret += ")";
}
ret += ");\n";
ret += " break;\n";
}
ret += R"( default:
return 0; // All done.
}
}
}
return 0;
}
)";
return ret;
}
}