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