• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_WASM_MODULE_H_
6 #define V8_WASM_MODULE_H_
7 
8 #include <memory>
9 
10 #include "src/api.h"
11 #include "src/debug/debug-interface.h"
12 #include "src/globals.h"
13 #include "src/handles.h"
14 #include "src/managed.h"
15 #include "src/parsing/preparse-data.h"
16 
17 #include "src/wasm/signature-map.h"
18 #include "src/wasm/wasm-opcodes.h"
19 
20 namespace v8 {
21 namespace internal {
22 
23 class WasmCompiledModule;
24 class WasmDebugInfo;
25 class WasmModuleObject;
26 class WasmInstanceObject;
27 class WasmMemoryObject;
28 
29 namespace compiler {
30 class CallDescriptor;
31 }
32 
33 namespace wasm {
34 class ErrorThrower;
35 
36 enum WasmExternalKind {
37   kExternalFunction = 0,
38   kExternalTable = 1,
39   kExternalMemory = 2,
40   kExternalGlobal = 3
41 };
42 
43 // Representation of an initializer expression.
44 struct WasmInitExpr {
45   enum WasmInitKind {
46     kNone,
47     kGlobalIndex,
48     kI32Const,
49     kI64Const,
50     kF32Const,
51     kF64Const
52   } kind;
53 
54   union {
55     int32_t i32_const;
56     int64_t i64_const;
57     float f32_const;
58     double f64_const;
59     uint32_t global_index;
60   } val;
61 
WasmInitExprWasmInitExpr62   WasmInitExpr() : kind(kNone) {}
WasmInitExprWasmInitExpr63   explicit WasmInitExpr(int32_t v) : kind(kI32Const) { val.i32_const = v; }
WasmInitExprWasmInitExpr64   explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
WasmInitExprWasmInitExpr65   explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
WasmInitExprWasmInitExpr66   explicit WasmInitExpr(double v) : kind(kF64Const) { val.f64_const = v; }
WasmInitExprWasmInitExpr67   WasmInitExpr(WasmInitKind kind, uint32_t global_index) : kind(kGlobalIndex) {
68     val.global_index = global_index;
69   }
70 };
71 
72 // Static representation of a WASM function.
73 struct WasmFunction {
74   FunctionSig* sig;      // signature of the function.
75   uint32_t func_index;   // index into the function table.
76   uint32_t sig_index;    // index into the signature table.
77   uint32_t name_offset;  // offset in the module bytes of the name, if any.
78   uint32_t name_length;  // length in bytes of the name.
79   uint32_t code_start_offset;    // offset in the module bytes of code start.
80   uint32_t code_end_offset;      // offset in the module bytes of code end.
81   bool imported;
82   bool exported;
83 };
84 
85 // Static representation of a wasm global variable.
86 struct WasmGlobal {
87   ValueType type;        // type of the global.
88   bool mutability;       // {true} if mutable.
89   WasmInitExpr init;     // the initialization expression of the global.
90   uint32_t offset;       // offset into global memory.
91   bool imported;         // true if imported.
92   bool exported;         // true if exported.
93 };
94 
95 // Static representation of a wasm data segment.
96 struct WasmDataSegment {
97   WasmInitExpr dest_addr;  // destination memory address of the data.
98   uint32_t source_offset;  // start offset in the module bytes.
99   uint32_t source_size;    // end offset in the module bytes.
100 };
101 
102 // Static representation of a wasm indirect call table.
103 struct WasmIndirectFunctionTable {
104   uint32_t min_size;            // minimum table size.
105   uint32_t max_size;            // maximum table size.
106   bool has_max;                 // true if there is a maximum size.
107   // TODO(titzer): Move this to WasmInstance. Needed by interpreter only.
108   std::vector<int32_t> values;  // function table, -1 indicating invalid.
109   bool imported;                // true if imported.
110   bool exported;                // true if exported.
111   SignatureMap map;             // canonicalizing map for sig indexes.
112 };
113 
114 // Static representation of how to initialize a table.
115 struct WasmTableInit {
116   uint32_t table_index;
117   WasmInitExpr offset;
118   std::vector<uint32_t> entries;
119 };
120 
121 // Static representation of a WASM import.
122 struct WasmImport {
123   uint32_t module_name_length;  // length in bytes of the module name.
124   uint32_t module_name_offset;  // offset in module bytes of the module name.
125   uint32_t field_name_length;   // length in bytes of the import name.
126   uint32_t field_name_offset;   // offset in module bytes of the import name.
127   WasmExternalKind kind;        // kind of the import.
128   uint32_t index;               // index into the respective space.
129 };
130 
131 // Static representation of a WASM export.
132 struct WasmExport {
133   uint32_t name_length;   // length in bytes of the exported name.
134   uint32_t name_offset;   // offset in module bytes of the name to export.
135   WasmExternalKind kind;  // kind of the export.
136   uint32_t index;         // index into the respective space.
137 };
138 
139 enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsOrigin };
140 struct ModuleWireBytes;
141 
142 // Static representation of a module.
143 struct V8_EXPORT_PRIVATE WasmModule {
144   static const uint32_t kPageSize = 0x10000;    // Page size, 64kb.
145   static const uint32_t kMinMemPages = 1;       // Minimum memory size = 64kb
146 
147   Zone* owned_zone;
148   uint32_t min_mem_pages = 0;  // minimum size of the memory in 64k pages
149   uint32_t max_mem_pages = 0;  // maximum size of the memory in 64k pages
150   bool has_max_mem = false;    // try if a maximum memory size exists
151   bool has_memory = false;     // true if the memory was defined or imported
152   bool mem_export = false;     // true if the memory is exported
153   // TODO(wasm): reconcile start function index being an int with
154   // the fact that we index on uint32_t, so we may technically not be
155   // able to represent some start_function_index -es.
156   int start_function_index = -1;      // start function, if any
157   ModuleOrigin origin = kWasmOrigin;  // origin of the module
158 
159   std::vector<WasmGlobal> globals;             // globals in this module.
160   uint32_t globals_size = 0;                   // size of globals table.
161   uint32_t num_imported_functions = 0;         // number of imported functions.
162   uint32_t num_declared_functions = 0;         // number of declared functions.
163   uint32_t num_exported_functions = 0;         // number of exported functions.
164   std::vector<FunctionSig*> signatures;        // signatures in this module.
165   std::vector<WasmFunction> functions;         // functions in this module.
166   std::vector<WasmDataSegment> data_segments;  // data segments in this module.
167   std::vector<WasmIndirectFunctionTable> function_tables;  // function tables.
168   std::vector<WasmImport> import_table;        // import table.
169   std::vector<WasmExport> export_table;        // export table.
170   std::vector<WasmTableInit> table_inits;      // initializations of tables
171   // We store the semaphore here to extend its lifetime. In <libc-2.21, which we
172   // use on the try bots, semaphore::Wait() can return while some compilation
173   // tasks are still executing semaphore::Signal(). If the semaphore is cleaned
174   // up right after semaphore::Wait() returns, then this can cause an
175   // invalid-semaphore error in the compilation tasks.
176   // TODO(wasm): Move this semaphore back to CompileInParallel when the try bots
177   // switch to libc-2.21 or higher.
178   std::unique_ptr<base::Semaphore> pending_tasks;
179 
WasmModuleWasmModule180   WasmModule() : WasmModule(nullptr) {}
181   WasmModule(Zone* owned_zone);
~WasmModuleWasmModule182   ~WasmModule() {
183     if (owned_zone) delete owned_zone;
184   }
185 };
186 
187 typedef Managed<WasmModule> WasmModuleWrapper;
188 
189 // An instantiated WASM module, including memory, function table, etc.
190 struct WasmInstance {
191   const WasmModule* module;  // static representation of the module.
192   // -- Heap allocated --------------------------------------------------------
193   Handle<Context> context;               // JavaScript native context.
194   std::vector<Handle<FixedArray>> function_tables;  // indirect function tables.
195   std::vector<Handle<FixedArray>>
196       signature_tables;                    // indirect signature tables.
197   std::vector<Handle<Code>> function_code;  // code objects for each function.
198   // -- raw memory ------------------------------------------------------------
199   byte* mem_start = nullptr;  // start of linear memory.
200   uint32_t mem_size = 0;      // size of the linear memory.
201   // -- raw globals -----------------------------------------------------------
202   byte* globals_start = nullptr;  // start of the globals area.
203 
WasmInstanceWasmInstance204   explicit WasmInstance(const WasmModule* m)
205       : module(m),
206         function_tables(m->function_tables.size()),
207         signature_tables(m->function_tables.size()),
208         function_code(m->functions.size()) {}
209 };
210 
211 // Interface to the storage (wire bytes) of a wasm module.
212 // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
213 // on module_bytes, as this storage is only guaranteed to be alive as long as
214 // this struct is alive.
215 struct V8_EXPORT_PRIVATE ModuleWireBytes {
ModuleWireBytesModuleWireBytes216   ModuleWireBytes(Vector<const byte> module_bytes)
217       : module_bytes_(module_bytes) {}
ModuleWireBytesModuleWireBytes218   ModuleWireBytes(const byte* start, const byte* end)
219       : module_bytes_(start, static_cast<int>(end - start)) {
220     DCHECK_GE(kMaxInt, end - start);
221   }
222 
223   // Get a string stored in the module bytes representing a name.
GetNameModuleWireBytes224   WasmName GetName(uint32_t offset, uint32_t length) const {
225     if (length == 0) return {"<?>", 3};  // no name.
226     CHECK(BoundsCheck(offset, length));
227     DCHECK_GE(length, 0);
228     return Vector<const char>::cast(
229         module_bytes_.SubVector(offset, offset + length));
230   }
231 
232   // Get a string stored in the module bytes representing a function name.
GetNameModuleWireBytes233   WasmName GetName(const WasmFunction* function) const {
234     return GetName(function->name_offset, function->name_length);
235   }
236 
237   // Get a string stored in the module bytes representing a name.
GetNameOrNullModuleWireBytes238   WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
239     if (offset == 0 && length == 0) return {NULL, 0};  // no name.
240     CHECK(BoundsCheck(offset, length));
241     DCHECK_GE(length, 0);
242     return Vector<const char>::cast(
243         module_bytes_.SubVector(offset, offset + length));
244   }
245 
246   // Get a string stored in the module bytes representing a function name.
GetNameOrNullModuleWireBytes247   WasmName GetNameOrNull(const WasmFunction* function) const {
248     return GetNameOrNull(function->name_offset, function->name_length);
249   }
250 
251   // Checks the given offset range is contained within the module bytes.
BoundsCheckModuleWireBytes252   bool BoundsCheck(uint32_t offset, uint32_t length) const {
253     uint32_t size = static_cast<uint32_t>(module_bytes_.length());
254     return offset <= size && length <= size - offset;
255   }
256 
GetFunctionBytesModuleWireBytes257   Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
258     return module_bytes_.SubVector(function->code_start_offset,
259                                    function->code_end_offset);
260   }
261 
startModuleWireBytes262   const byte* start() const { return module_bytes_.start(); }
endModuleWireBytes263   const byte* end() const { return module_bytes_.end(); }
lengthModuleWireBytes264   int length() const { return module_bytes_.length(); }
265 
266  private:
267   const Vector<const byte> module_bytes_;
268 };
269 
270 // Interface provided to the decoder/graph builder which contains only
271 // minimal information about the globals, functions, and function tables.
272 struct V8_EXPORT_PRIVATE ModuleEnv {
ModuleEnvModuleEnv273   ModuleEnv(const WasmModule* module, WasmInstance* instance)
274       : module(module), instance(instance) {}
275 
276   const WasmModule* module;
277   WasmInstance* instance;
278 
IsValidGlobalModuleEnv279   bool IsValidGlobal(uint32_t index) const {
280     return module && index < module->globals.size();
281   }
IsValidFunctionModuleEnv282   bool IsValidFunction(uint32_t index) const {
283     return module && index < module->functions.size();
284   }
IsValidSignatureModuleEnv285   bool IsValidSignature(uint32_t index) const {
286     return module && index < module->signatures.size();
287   }
IsValidTableModuleEnv288   bool IsValidTable(uint32_t index) const {
289     return module && index < module->function_tables.size();
290   }
GetGlobalTypeModuleEnv291   ValueType GetGlobalType(uint32_t index) {
292     DCHECK(IsValidGlobal(index));
293     return module->globals[index].type;
294   }
GetFunctionSignatureModuleEnv295   FunctionSig* GetFunctionSignature(uint32_t index) {
296     DCHECK(IsValidFunction(index));
297     return module->functions[index].sig;
298   }
GetSignatureModuleEnv299   FunctionSig* GetSignature(uint32_t index) {
300     DCHECK(IsValidSignature(index));
301     return module->signatures[index];
302   }
GetTableModuleEnv303   const WasmIndirectFunctionTable* GetTable(uint32_t index) const {
304     DCHECK(IsValidTable(index));
305     return &module->function_tables[index];
306   }
307 
asm_jsModuleEnv308   bool asm_js() { return module->origin == kAsmJsOrigin; }
309 
GetFunctionCodeModuleEnv310   Handle<Code> GetFunctionCode(uint32_t index) {
311     DCHECK_NOT_NULL(instance);
312     return instance->function_code[index];
313   }
314 
315   // TODO(titzer): move these into src/compiler/wasm-compiler.cc
316   static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
317                                                          FunctionSig* sig);
318   static compiler::CallDescriptor* GetI32WasmCallDescriptor(
319       Zone* zone, compiler::CallDescriptor* descriptor);
320   static compiler::CallDescriptor* GetI32WasmCallDescriptorForSimd(
321       Zone* zone, compiler::CallDescriptor* descriptor);
322 };
323 
324 // A ModuleEnv together with ModuleWireBytes.
325 struct ModuleBytesEnv {
ModuleBytesEnvModuleBytesEnv326   ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
327                  Vector<const byte> module_bytes)
328       : module_env(module, instance), wire_bytes(module_bytes) {}
ModuleBytesEnvModuleBytesEnv329   ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
330                  const ModuleWireBytes& wire_bytes)
331       : module_env(module, instance), wire_bytes(wire_bytes) {}
332 
333   ModuleEnv module_env;
334   ModuleWireBytes wire_bytes;
335 };
336 
337 // A helper for printing out the names of functions.
338 struct WasmFunctionName {
WasmFunctionNameWasmFunctionName339   WasmFunctionName(const WasmFunction* function, WasmName name)
340       : function_(function), name_(name) {}
341 
342   const WasmFunction* function_;
343   WasmName name_;
344 };
345 
346 std::ostream& operator<<(std::ostream& os, const WasmModule& module);
347 std::ostream& operator<<(std::ostream& os, const WasmFunction& function);
348 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
349 
350 // Get the debug info associated with the given wasm object.
351 // If no debug info exists yet, it is created automatically.
352 Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
353 
354 // Check whether the given object represents a WebAssembly.Instance instance.
355 // This checks the number and type of internal fields, so it's not 100 percent
356 // secure. If it turns out that we need more complete checks, we could add a
357 // special marker as internal field, which will definitely never occur anywhere
358 // else.
359 bool IsWasmInstance(Object* instance);
360 
361 // Get the script of the wasm module. If the origin of the module is asm.js, the
362 // returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise
363 // it's of type TYPE_WASM.
364 Handle<Script> GetScript(Handle<JSObject> instance);
365 
366 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
367     Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
368     ModuleOrigin origin, Handle<Script> asm_js_script,
369     Vector<const byte> asm_offset_table);
370 
371 V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
372                                             Handle<Context> context);
373 
374 V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
375                                              Handle<WasmModuleObject> module);
376 V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
377                                              Handle<WasmModuleObject> module);
378 V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
379     Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
380     ErrorThrower* thrower);
381 
382 // Get the offset of the code of a function within a module.
383 int GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
384                           int func_index);
385 
386 // Assumed to be called with a code object associated to a wasm module instance.
387 // Intended to be called from runtime functions.
388 // Returns nullptr on failing to get owning instance.
389 WasmInstanceObject* GetOwningWasmInstance(Code* code);
390 
391 MaybeHandle<JSArrayBuffer> GetInstanceMemory(
392     Isolate* isolate, Handle<WasmInstanceObject> instance);
393 
394 int32_t GetInstanceMemorySize(Isolate* isolate,
395                               Handle<WasmInstanceObject> instance);
396 
397 int32_t GrowInstanceMemory(Isolate* isolate,
398                            Handle<WasmInstanceObject> instance, uint32_t pages);
399 
400 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
401                                      bool enable_guard_regions);
402 
403 int32_t GrowWebAssemblyMemory(Isolate* isolate,
404                               Handle<WasmMemoryObject> receiver,
405                               uint32_t pages);
406 
407 int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
408                    uint32_t pages);
409 
410 void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
411                           int index, Handle<JSFunction> js_function);
412 
413 void GrowDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
414                         uint32_t old_size, uint32_t count);
415 
416 //============================================================================
417 //== Compilation and instantiation ===========================================
418 //============================================================================
419 V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate, ErrorThrower* thrower,
420                                     const ModuleWireBytes& bytes);
421 
422 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
423     Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
424     Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
425 
426 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
427     Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
428 
429 V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
430     Isolate* isolate, ErrorThrower* thrower,
431     Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
432     MaybeHandle<JSArrayBuffer> memory);
433 
434 V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
435                                     const ModuleWireBytes& bytes);
436 
437 V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
438                                         Handle<JSPromise> promise,
439                                         Handle<WasmModuleObject> module_object,
440                                         MaybeHandle<JSReceiver> imports);
441 
442 V8_EXPORT_PRIVATE void AsyncCompileAndInstantiate(
443     Isolate* isolate, Handle<JSPromise> promise, const ModuleWireBytes& bytes,
444     MaybeHandle<JSReceiver> imports);
445 
446 namespace testing {
447 void ValidateInstancesChain(Isolate* isolate,
448                             Handle<WasmModuleObject> module_obj,
449                             int instance_count);
450 void ValidateModuleState(Isolate* isolate, Handle<WasmModuleObject> module_obj);
451 void ValidateOrphanedInstance(Isolate* isolate,
452                               Handle<WasmInstanceObject> instance);
453 }  // namespace testing
454 }  // namespace wasm
455 }  // namespace internal
456 }  // namespace v8
457 
458 #endif  // V8_WASM_MODULE_H_
459