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 "src/api.h" 9 #include "src/handles.h" 10 #include "src/wasm/wasm-opcodes.h" 11 #include "src/wasm/wasm-result.h" 12 13 namespace v8 { 14 namespace internal { 15 16 namespace compiler { 17 class CallDescriptor; 18 class WasmCompilationUnit; 19 } 20 21 namespace wasm { 22 const size_t kMaxModuleSize = 1024 * 1024 * 1024; 23 const size_t kMaxFunctionSize = 128 * 1024; 24 const size_t kMaxStringSize = 256; 25 const uint32_t kWasmMagic = 0x6d736100; 26 const uint32_t kWasmVersion = 0x0b; 27 const uint8_t kWasmFunctionTypeForm = 0x40; 28 29 // WebAssembly sections are named as strings in the binary format, but 30 // internally V8 uses an enum to handle them. 31 // 32 // Entries have the form F(enumerator, string). 33 #define FOR_EACH_WASM_SECTION_TYPE(F) \ 34 F(Signatures, 1, "type") \ 35 F(ImportTable, 2, "import") \ 36 F(FunctionSignatures, 3, "function") \ 37 F(FunctionTable, 4, "table") \ 38 F(Memory, 5, "memory") \ 39 F(ExportTable, 6, "export") \ 40 F(StartFunction, 7, "start") \ 41 F(FunctionBodies, 8, "code") \ 42 F(DataSegments, 9, "data") \ 43 F(Names, 10, "name") \ 44 F(FunctionTablePad, 11, "table_pad") \ 45 F(Globals, 0, "global") \ 46 F(End, 0, "end") 47 48 // Contants for the above section types: {LEB128 length, characters...}. 49 #define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y' 50 #define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e' 51 #define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l' 52 #define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a' 53 #define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e' 54 #define WASM_SECTION_END 3, 'e', 'n', 'd' 55 #define WASM_SECTION_START_FUNCTION 5, 's', 't', 'a', 'r', 't' 56 #define WASM_SECTION_IMPORT_TABLE 6, 'i', 'm', 'p', 'o', 'r', 't' 57 #define WASM_SECTION_EXPORT_TABLE 6, 'e', 'x', 'p', 'o', 'r', 't' 58 #define WASM_SECTION_FUNCTION_SIGNATURES \ 59 8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n' 60 #define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e' 61 #define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e' 62 #define WASM_SECTION_FUNCTION_TABLE_PAD \ 63 9, 't', 'a', 'b', 'l', 'e', '_', 'p', 'a', 'd' 64 65 // Constants for the above section headers' size (LEB128 + characters). 66 #define WASM_SECTION_MEMORY_SIZE ((size_t)7) 67 #define WASM_SECTION_SIGNATURES_SIZE ((size_t)5) 68 #define WASM_SECTION_GLOBALS_SIZE ((size_t)7) 69 #define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5) 70 #define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6) 71 #define WASM_SECTION_END_SIZE ((size_t)4) 72 #define WASM_SECTION_START_FUNCTION_SIZE ((size_t)6) 73 #define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)7) 74 #define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)7) 75 #define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9) 76 #define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5) 77 #define WASM_SECTION_NAMES_SIZE ((size_t)5) 78 #define WASM_SECTION_FUNCTION_TABLE_PAD_SIZE ((size_t)10) 79 80 class WasmDebugInfo; 81 82 struct WasmSection { 83 enum class Code : uint32_t { 84 #define F(enumerator, order, string) enumerator, 85 FOR_EACH_WASM_SECTION_TYPE(F) 86 #undef F 87 Max 88 }; 89 static WasmSection::Code begin(); 90 static WasmSection::Code end(); 91 static WasmSection::Code next(WasmSection::Code code); 92 static const char* getName(Code code); 93 static int getOrder(Code code); 94 static size_t getNameLength(Code code); 95 static WasmSection::Code lookup(const byte* string, uint32_t length); 96 }; 97 98 enum WasmFunctionDeclBit { 99 kDeclFunctionName = 0x01, 100 kDeclFunctionExport = 0x08 101 }; 102 103 // Constants for fixed-size elements within a module. 104 static const size_t kDeclMemorySize = 3; 105 static const size_t kDeclDataSegmentSize = 13; 106 107 static const uint32_t kMaxReturnCount = 1; 108 109 // Static representation of a WASM function. 110 struct WasmFunction { 111 FunctionSig* sig; // signature of the function. 112 uint32_t func_index; // index into the function table. 113 uint32_t sig_index; // index into the signature table. 114 uint32_t name_offset; // offset in the module bytes of the name, if any. 115 uint32_t name_length; // length in bytes of the name. 116 uint32_t code_start_offset; // offset in the module bytes of code start. 117 uint32_t code_end_offset; // offset in the module bytes of code end. 118 }; 119 120 // Static representation of an imported WASM function. 121 struct WasmImport { 122 FunctionSig* sig; // signature of the function. 123 uint32_t sig_index; // index into the signature table. 124 uint32_t module_name_offset; // offset in module bytes of the module name. 125 uint32_t module_name_length; // length in bytes of the module name. 126 uint32_t function_name_offset; // offset in module bytes of the import name. 127 uint32_t function_name_length; // length in bytes of the import name. 128 }; 129 130 // Static representation of an exported WASM function. 131 struct WasmExport { 132 uint32_t func_index; // index into the function table. 133 uint32_t name_offset; // offset in module bytes of the name to export. 134 uint32_t name_length; // length in bytes of the exported name. 135 }; 136 137 // Static representation of a wasm global variable. 138 struct WasmGlobal { 139 uint32_t name_offset; // offset in the module bytes of the name, if any. 140 uint32_t name_length; // length in bytes of the global name. 141 MachineType type; // type of the global. 142 uint32_t offset; // offset from beginning of globals area. 143 bool exported; // true if this global is exported. 144 }; 145 146 // Static representation of a wasm data segment. 147 struct WasmDataSegment { 148 uint32_t dest_addr; // destination memory address of the data. 149 uint32_t source_offset; // start offset in the module bytes. 150 uint32_t source_size; // end offset in the module bytes. 151 bool init; // true if loaded upon instantiation. 152 }; 153 154 enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin }; 155 156 // Static representation of a module. 157 struct WasmModule { 158 static const uint32_t kPageSize = 0x10000; // Page size, 64kb. 159 static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb 160 static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb 161 162 const byte* module_start; // starting address for the module bytes. 163 const byte* module_end; // end address for the module bytes. 164 uint32_t min_mem_pages; // minimum size of the memory in 64k pages. 165 uint32_t max_mem_pages; // maximum size of the memory in 64k pages. 166 bool mem_export; // true if the memory is exported. 167 bool mem_external; // true if the memory is external. 168 int start_function_index; // start function, if any. 169 ModuleOrigin origin; // origin of the module 170 171 std::vector<WasmGlobal> globals; // globals in this module. 172 uint32_t globals_size; // size of globals table. 173 uint32_t indirect_table_size; // size of indirect function 174 // table (includes padding). 175 std::vector<FunctionSig*> signatures; // signatures in this module. 176 std::vector<WasmFunction> functions; // functions in this module. 177 std::vector<WasmDataSegment> data_segments; // data segments in this module. 178 std::vector<uint16_t> function_table; // function table. 179 std::vector<WasmImport> import_table; // import table. 180 std::vector<WasmExport> export_table; // export table. 181 // We store the semaphore here to extend its lifetime. In <libc-2.21, which we 182 // use on the try bots, semaphore::Wait() can return while some compilation 183 // tasks are still executing semaphore::Signal(). If the semaphore is cleaned 184 // up right after semaphore::Wait() returns, then this can cause an 185 // invalid-semaphore error in the compilation tasks. 186 // TODO(wasm): Move this semaphore back to CompileInParallel when the try bots 187 // switch to libc-2.21 or higher. 188 base::SmartPointer<base::Semaphore> pending_tasks; 189 190 WasmModule(); 191 192 // Get a string stored in the module bytes representing a name. GetNameWasmModule193 WasmName GetName(uint32_t offset, uint32_t length) const { 194 if (length == 0) return {"<?>", 3}; // no name. 195 CHECK(BoundsCheck(offset, offset + length)); 196 DCHECK_GE(static_cast<int>(length), 0); 197 return {reinterpret_cast<const char*>(module_start + offset), 198 static_cast<int>(length)}; 199 } 200 201 // Get a string stored in the module bytes representing a function name. GetNameWasmModule202 WasmName GetName(WasmFunction* function) const { 203 return GetName(function->name_offset, function->name_length); 204 } 205 206 // Get a string stored in the module bytes representing a name. GetNameOrNullWasmModule207 WasmName GetNameOrNull(uint32_t offset, uint32_t length) const { 208 if (offset == 0 && length == 0) return {NULL, 0}; // no name. 209 CHECK(BoundsCheck(offset, offset + length)); 210 DCHECK_GE(static_cast<int>(length), 0); 211 return {reinterpret_cast<const char*>(module_start + offset), 212 static_cast<int>(length)}; 213 } 214 215 // Get a string stored in the module bytes representing a function name. GetNameOrNullWasmModule216 WasmName GetNameOrNull(const WasmFunction* function) const { 217 return GetNameOrNull(function->name_offset, function->name_length); 218 } 219 220 // Checks the given offset range is contained within the module bytes. BoundsCheckWasmModule221 bool BoundsCheck(uint32_t start, uint32_t end) const { 222 size_t size = module_end - module_start; 223 return start <= size && end <= size; 224 } 225 226 // Creates a new instantiation of the module in the given isolate. 227 MaybeHandle<JSObject> Instantiate(Isolate* isolate, Handle<JSReceiver> ffi, 228 Handle<JSArrayBuffer> memory) const; 229 230 Handle<FixedArray> CompileFunctions(Isolate* isolate) const; 231 FunctionTableSizeWasmModule232 uint32_t FunctionTableSize() const { 233 if (indirect_table_size > 0) { 234 return indirect_table_size; 235 } 236 DCHECK_LE(function_table.size(), UINT32_MAX); 237 return static_cast<uint32_t>(function_table.size()); 238 } 239 }; 240 241 // An instantiated WASM module, including memory, function table, etc. 242 struct WasmModuleInstance { 243 const WasmModule* module; // static representation of the module. 244 // -- Heap allocated -------------------------------------------------------- 245 Handle<JSObject> js_object; // JavaScript module object. 246 Handle<Context> context; // JavaScript native context. 247 Handle<JSArrayBuffer> mem_buffer; // Handle to array buffer of memory. 248 Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals. 249 Handle<FixedArray> function_table; // indirect function table. 250 std::vector<Handle<Code>> function_code; // code objects for each function. 251 std::vector<Handle<Code>> import_code; // code objects for each import. 252 // -- raw memory ------------------------------------------------------------ 253 byte* mem_start; // start of linear memory. 254 uint32_t mem_size; // size of the linear memory. 255 // -- raw globals ----------------------------------------------------------- 256 byte* globals_start; // start of the globals area. 257 WasmModuleInstanceWasmModuleInstance258 explicit WasmModuleInstance(const WasmModule* m) 259 : module(m), 260 function_code(m->functions.size()), 261 import_code(m->import_table.size()), 262 mem_start(nullptr), 263 mem_size(0), 264 globals_start(nullptr) {} 265 }; 266 267 // Interface provided to the decoder/graph builder which contains only 268 // minimal information about the globals, functions, and function tables. 269 struct ModuleEnv { 270 const WasmModule* module; 271 WasmModuleInstance* instance; 272 ModuleOrigin origin; 273 // TODO(mtrofin): remove this once we introduce WASM_DIRECT_CALL 274 // reloc infos. 275 std::vector<Handle<Code>> placeholders; 276 IsValidGlobalModuleEnv277 bool IsValidGlobal(uint32_t index) { 278 return module && index < module->globals.size(); 279 } IsValidFunctionModuleEnv280 bool IsValidFunction(uint32_t index) const { 281 return module && index < module->functions.size(); 282 } IsValidSignatureModuleEnv283 bool IsValidSignature(uint32_t index) { 284 return module && index < module->signatures.size(); 285 } IsValidImportModuleEnv286 bool IsValidImport(uint32_t index) { 287 return module && index < module->import_table.size(); 288 } GetGlobalTypeModuleEnv289 MachineType GetGlobalType(uint32_t index) { 290 DCHECK(IsValidGlobal(index)); 291 return module->globals[index].type; 292 } GetFunctionSignatureModuleEnv293 FunctionSig* GetFunctionSignature(uint32_t index) { 294 DCHECK(IsValidFunction(index)); 295 return module->functions[index].sig; 296 } GetImportSignatureModuleEnv297 FunctionSig* GetImportSignature(uint32_t index) { 298 DCHECK(IsValidImport(index)); 299 return module->import_table[index].sig; 300 } GetSignatureModuleEnv301 FunctionSig* GetSignature(uint32_t index) { 302 DCHECK(IsValidSignature(index)); 303 return module->signatures[index]; 304 } FunctionTableSizeModuleEnv305 uint32_t FunctionTableSize() const { 306 return module->FunctionTableSize(); 307 } 308 asm_jsModuleEnv309 bool asm_js() { return origin == kAsmJsOrigin; } 310 311 Handle<Code> GetCodeOrPlaceholder(uint32_t index) const; 312 Handle<Code> GetImportCode(uint32_t index); 313 314 static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, 315 FunctionSig* sig); 316 static compiler::CallDescriptor* GetI32WasmCallDescriptor( 317 Zone* zone, compiler::CallDescriptor* descriptor); 318 compiler::CallDescriptor* GetCallDescriptor(Zone* zone, uint32_t index); 319 }; 320 321 // A helper for printing out the names of functions. 322 struct WasmFunctionName { 323 const WasmFunction* function_; 324 const WasmModule* module_; WasmFunctionNameWasmFunctionName325 WasmFunctionName(const WasmFunction* function, const ModuleEnv* menv) 326 : function_(function), module_(menv ? menv->module : nullptr) {} 327 }; 328 329 std::ostream& operator<<(std::ostream& os, const WasmModule& module); 330 std::ostream& operator<<(std::ostream& os, const WasmFunction& function); 331 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); 332 333 typedef Result<const WasmModule*> ModuleResult; 334 typedef Result<WasmFunction*> FunctionResult; 335 typedef std::vector<std::pair<int, int>> FunctionOffsets; 336 typedef Result<FunctionOffsets> FunctionOffsetsResult; 337 338 // Extract a function name from the given wasm object. 339 // Returns "<WASM UNNAMED>" if the function is unnamed or the name is not a 340 // valid UTF-8 string. 341 Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm, 342 uint32_t func_index); 343 344 // Extract a function name from the given wasm object. 345 // Returns a null handle if the function is unnamed or the name is not a valid 346 // UTF-8 string. 347 Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm, 348 uint32_t func_index); 349 350 // Return the binary source bytes of a wasm module. 351 SeqOneByteString* GetWasmBytes(JSObject* wasm); 352 353 // Get the debug info associated with the given wasm object. 354 // If no debug info exists yet, it is created automatically. 355 WasmDebugInfo* GetDebugInfo(JSObject* wasm); 356 357 // Check whether the given object is a wasm object. 358 // This checks the number and type of internal fields, so it's not 100 percent 359 // secure. If it turns out that we need more complete checks, we could add a 360 // special marker as internal field, which will definitely never occur anywhere 361 // else. 362 bool IsWasmObject(Object* object); 363 364 namespace testing { 365 366 // Decode, verify, and run the function labeled "main" in the 367 // given encoded module. The module should have no imports. 368 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, 369 const byte* module_end, bool asm_js = false); 370 371 } // namespace testing 372 373 } // namespace wasm 374 } // namespace internal 375 } // namespace v8 376 377 #endif // V8_WASM_MODULE_H_ 378