summaryrefslogtreecommitdiff
path: root/src/tools/wasm2c-wrapper.h
blob: 684bd52f740174b96759468912fcc8ab6fbd51df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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