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_WASM_MODULE_H_
6 #define V8_WASM_WASM_MODULE_H_
7
8 #include <memory>
9
10 #include "src/base/optional.h"
11 #include "src/common/globals.h"
12 #include "src/handles/handles.h"
13 #include "src/utils/vector.h"
14 #include "src/wasm/signature-map.h"
15 #include "src/wasm/struct-types.h"
16 #include "src/wasm/wasm-constants.h"
17 #include "src/wasm/wasm-opcodes.h"
18
19 namespace v8 {
20
21 namespace internal {
22
23 class WasmModuleObject;
24
25 namespace wasm {
26
27 using WasmName = Vector<const char>;
28
29 struct AsmJsOffsets;
30 class ErrorThrower;
31
32 // Reference to a string in the wire bytes.
33 class WireBytesRef {
34 public:
WireBytesRef()35 WireBytesRef() : WireBytesRef(0, 0) {}
WireBytesRef(uint32_t offset,uint32_t length)36 WireBytesRef(uint32_t offset, uint32_t length)
37 : offset_(offset), length_(length) {
38 DCHECK_IMPLIES(offset_ == 0, length_ == 0);
39 DCHECK_LE(offset_, offset_ + length_); // no uint32_t overflow.
40 }
41
offset()42 uint32_t offset() const { return offset_; }
length()43 uint32_t length() const { return length_; }
end_offset()44 uint32_t end_offset() const { return offset_ + length_; }
is_empty()45 bool is_empty() const { return length_ == 0; }
is_set()46 bool is_set() const { return offset_ != 0; }
47
48 private:
49 uint32_t offset_;
50 uint32_t length_;
51 };
52
53 // Static representation of a wasm function.
54 struct WasmFunction {
55 const FunctionSig* sig; // signature of the function.
56 uint32_t func_index; // index into the function table.
57 uint32_t sig_index; // index into the signature table.
58 WireBytesRef code; // code of this function.
59 bool imported;
60 bool exported;
61 bool declared;
62 };
63
64 // Static representation of a wasm global variable.
65 struct WasmGlobal {
66 ValueType type; // type of the global.
67 bool mutability; // {true} if mutable.
68 WasmInitExpr init; // the initialization expression of the global.
69 union {
70 uint32_t index; // index of imported mutable global.
71 uint32_t offset; // offset into global memory (if not imported & mutable).
72 };
73 bool imported; // true if imported.
74 bool exported; // true if exported.
75 };
76
77 // Note: An exception signature only uses the params portion of a
78 // function signature.
79 using WasmExceptionSig = FunctionSig;
80
81 // Static representation of a wasm exception type.
82 struct WasmException {
WasmExceptionWasmException83 explicit WasmException(const WasmExceptionSig* sig) : sig(sig) {}
ToFunctionSigWasmException84 const FunctionSig* ToFunctionSig() const { return sig; }
85
86 const WasmExceptionSig* sig; // type signature of the exception.
87 };
88
89 // Static representation of a wasm data segment.
90 struct WasmDataSegment {
91 // Construct an active segment.
WasmDataSegmentWasmDataSegment92 explicit WasmDataSegment(WasmInitExpr dest_addr)
93 : dest_addr(std::move(dest_addr)), active(true) {}
94
95 // Construct a passive segment, which has no dest_addr.
WasmDataSegmentWasmDataSegment96 WasmDataSegment() : active(false) {}
97
98 WasmInitExpr dest_addr; // destination memory address of the data.
99 WireBytesRef source; // start offset in the module bytes.
100 bool active = true; // true if copied automatically during instantiation.
101 };
102
103 // Static representation of wasm element segment (table initializer).
104 struct WasmElemSegment {
105 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmElemSegment);
106
107 // Construct an active segment.
WasmElemSegmentWasmElemSegment108 WasmElemSegment(uint32_t table_index, WasmInitExpr offset)
109 : type(kWasmFuncRef),
110 table_index(table_index),
111 offset(std::move(offset)),
112 status(kStatusActive) {}
113
114 // Construct a passive or declarative segment, which has no table index or
115 // offset.
WasmElemSegmentWasmElemSegment116 explicit WasmElemSegment(bool declarative)
117 : type(kWasmFuncRef),
118 table_index(0),
119 status(declarative ? kStatusDeclarative : kStatusPassive) {}
120
121 // Used in the {entries} vector to represent a `ref.null` entry in a passive
122 // segment.
123 V8_EXPORT_PRIVATE static const uint32_t kNullIndex = ~0u;
124
125 ValueType type;
126 uint32_t table_index;
127 WasmInitExpr offset;
128 std::vector<uint32_t> entries;
129 enum Status {
130 kStatusActive, // copied automatically during instantiation.
131 kStatusPassive, // copied explicitly after instantiation.
132 kStatusDeclarative // purely declarative and never copied.
133 } status;
134 };
135
136 // Static representation of a wasm import.
137 struct WasmImport {
138 WireBytesRef module_name; // module name.
139 WireBytesRef field_name; // import name.
140 ImportExportKindCode kind; // kind of the import.
141 uint32_t index; // index into the respective space.
142 };
143
144 // Static representation of a wasm export.
145 struct WasmExport {
146 WireBytesRef name; // exported name.
147 ImportExportKindCode kind; // kind of the export.
148 uint32_t index; // index into the respective space.
149 };
150
151 enum class WasmCompilationHintStrategy : uint8_t {
152 kDefault = 0,
153 kLazy = 1,
154 kEager = 2,
155 kLazyBaselineEagerTopTier = 3,
156 };
157
158 enum class WasmCompilationHintTier : uint8_t {
159 kDefault = 0,
160 kBaseline = 1,
161 kOptimized = 2,
162 };
163
164 // Static representation of a wasm compilation hint
165 struct WasmCompilationHint {
166 WasmCompilationHintStrategy strategy;
167 WasmCompilationHintTier baseline_tier;
168 WasmCompilationHintTier top_tier;
169 };
170
171 enum ModuleOrigin : uint8_t {
172 kWasmOrigin,
173 kAsmJsSloppyOrigin,
174 kAsmJsStrictOrigin
175 };
176
177 #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix) \
178 ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
179 : (counters)->prefix##_asm_##suffix())
180
181 struct ModuleWireBytes;
182
183 class V8_EXPORT_PRIVATE LazilyGeneratedNames {
184 public:
185 WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
186 uint32_t function_index,
187 Vector<const WasmExport> export_table) const;
188
189 // For memory and global.
190 std::pair<WireBytesRef, WireBytesRef> LookupNameFromImportsAndExports(
191 ImportExportKindCode kind, uint32_t index,
192 const Vector<const WasmImport> import_table,
193 const Vector<const WasmExport> export_table) const;
194
195 void AddForTesting(int function_index, WireBytesRef name);
196
197 private:
198 // {function_names_}, {global_names_}, {memory_names_} and {table_names_} are
199 // populated lazily after decoding, and therefore need a mutex to protect
200 // concurrent modifications from multiple {WasmModuleObject}.
201 mutable base::Mutex mutex_;
202 mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
203 function_names_;
204 mutable std::unique_ptr<
205 std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>>
206 global_names_;
207 mutable std::unique_ptr<
208 std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>>
209 memory_names_;
210 mutable std::unique_ptr<
211 std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>>
212 table_names_;
213 };
214
215 class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
216 public:
217 explicit AsmJsOffsetInformation(Vector<const byte> encoded_offsets);
218
219 // Destructor defined in wasm-module.cc, where the definition of
220 // {AsmJsOffsets} is available.
221 ~AsmJsOffsetInformation();
222
223 int GetSourcePosition(int func_index, int byte_offset,
224 bool is_at_number_conversion);
225
226 std::pair<int, int> GetFunctionOffsets(int func_index);
227
228 private:
229 void EnsureDecodedOffsets();
230
231 // The offset information table is decoded lazily, hence needs to be
232 // protected against concurrent accesses.
233 // Exactly one of the two fields below will be set at a time.
234 mutable base::Mutex mutex_;
235
236 // Holds the encoded offset table bytes.
237 OwnedVector<const uint8_t> encoded_offsets_;
238
239 // Holds the decoded offset table.
240 std::unique_ptr<AsmJsOffsets> decoded_offsets_;
241 };
242
243 struct TypeDefinition {
TypeDefinitionTypeDefinition244 explicit TypeDefinition(const FunctionSig* sig) : function_sig(sig) {}
TypeDefinitionTypeDefinition245 explicit TypeDefinition(const StructType* type) : struct_type(type) {}
TypeDefinitionTypeDefinition246 explicit TypeDefinition(const ArrayType* type) : array_type(type) {}
247 union {
248 const FunctionSig* function_sig;
249 const StructType* struct_type;
250 const ArrayType* array_type;
251 };
252 };
253
254 struct V8_EXPORT_PRIVATE WasmDebugSymbols {
255 enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
256 Type type = Type::None;
257 WireBytesRef external_url;
258 };
259
260 struct WasmTable;
261
262 // Static representation of a module.
263 struct V8_EXPORT_PRIVATE WasmModule {
264 std::unique_ptr<Zone> signature_zone;
265 uint32_t initial_pages = 0; // initial size of the memory in 64k pages
266 uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages
267 bool has_shared_memory = false; // true if memory is a SharedArrayBuffer
268 bool has_maximum_pages = false; // true if there is a maximum memory size
269 bool is_memory64 = false; // true if the memory is 64 bit
270 bool has_memory = false; // true if the memory was defined or imported
271 bool mem_export = false; // true if the memory is exported
272 int start_function_index = -1; // start function, >= 0 if any
273
274 std::vector<WasmGlobal> globals;
275 // Size of the buffer required for all globals that are not imported and
276 // mutable.
277 uint32_t untagged_globals_buffer_size = 0;
278 uint32_t tagged_globals_buffer_size = 0;
279 uint32_t num_imported_mutable_globals = 0;
280 uint32_t num_imported_functions = 0;
281 uint32_t num_imported_tables = 0;
282 uint32_t num_declared_functions = 0; // excluding imported
283 uint32_t num_exported_functions = 0;
284 uint32_t num_declared_data_segments = 0; // From the DataCount section.
285 WireBytesRef code = {0, 0};
286 WireBytesRef name = {0, 0};
287 std::vector<TypeDefinition> types; // by type index
288 std::vector<uint8_t> type_kinds; // by type index
289 // Map from each type index to the index of its corresponding canonical type.
290 // Note: right now, only functions are canonicalized, and arrays and structs
291 // map to themselves.
292 std::vector<uint32_t> canonicalized_type_ids;
293
has_typeWasmModule294 bool has_type(uint32_t index) const { return index < types.size(); }
295
add_signatureWasmModule296 void add_signature(const FunctionSig* sig) {
297 types.push_back(TypeDefinition(sig));
298 type_kinds.push_back(kWasmFunctionTypeCode);
299 uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
300 canonicalized_type_ids.push_back(canonical_id);
301 }
has_signatureWasmModule302 bool has_signature(uint32_t index) const {
303 return index < types.size() && type_kinds[index] == kWasmFunctionTypeCode;
304 }
signatureWasmModule305 const FunctionSig* signature(uint32_t index) const {
306 DCHECK(has_signature(index));
307 return types[index].function_sig;
308 }
309
add_struct_typeWasmModule310 void add_struct_type(const StructType* type) {
311 types.push_back(TypeDefinition(type));
312 type_kinds.push_back(kWasmStructTypeCode);
313 // No canonicalization for structs.
314 canonicalized_type_ids.push_back(0);
315 }
has_structWasmModule316 bool has_struct(uint32_t index) const {
317 return index < types.size() && type_kinds[index] == kWasmStructTypeCode;
318 }
struct_typeWasmModule319 const StructType* struct_type(uint32_t index) const {
320 DCHECK(has_struct(index));
321 return types[index].struct_type;
322 }
323
add_array_typeWasmModule324 void add_array_type(const ArrayType* type) {
325 types.push_back(TypeDefinition(type));
326 type_kinds.push_back(kWasmArrayTypeCode);
327 // No canonicalization for arrays.
328 canonicalized_type_ids.push_back(0);
329 }
has_arrayWasmModule330 bool has_array(uint32_t index) const {
331 return index < types.size() && type_kinds[index] == kWasmArrayTypeCode;
332 }
array_typeWasmModule333 const ArrayType* array_type(uint32_t index) const {
334 DCHECK(has_array(index));
335 return types[index].array_type;
336 }
337
338 std::vector<WasmFunction> functions;
339 std::vector<WasmDataSegment> data_segments;
340 std::vector<WasmTable> tables;
341 std::vector<WasmImport> import_table;
342 std::vector<WasmExport> export_table;
343 std::vector<WasmException> exceptions;
344 std::vector<WasmElemSegment> elem_segments;
345 std::vector<WasmCompilationHint> compilation_hints;
346 SignatureMap signature_map; // canonicalizing map for signature indexes.
347
348 ModuleOrigin origin = kWasmOrigin; // origin of the module
349 LazilyGeneratedNames lazily_generated_names;
350 WasmDebugSymbols debug_symbols;
351
352 // Asm.js source position information. Only available for modules compiled
353 // from asm.js.
354 std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;
355
356 explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
357 WasmModule(const WasmModule&) = delete;
358 WasmModule& operator=(const WasmModule&) = delete;
359 };
360
361 // Static representation of a wasm indirect call table.
362 struct WasmTable {
363 MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable);
364
365 // 'module' can be nullptr
366 // TODO(9495): Update this function as more table types are supported, or
367 // remove it completely when all reference types are allowed.
IsValidTableTypeWasmTable368 static bool IsValidTableType(ValueType type, const WasmModule* module) {
369 if (!type.is_nullable()) return false;
370 HeapType heap_type = type.heap_type();
371 return heap_type == HeapType::kFunc || heap_type == HeapType::kExtern ||
372 heap_type == HeapType::kExn ||
373 (module != nullptr && heap_type.is_index() &&
374 module->has_signature(heap_type.ref_index()));
375 }
376
377 ValueType type = kWasmStmt; // table type.
378 uint32_t initial_size = 0; // initial table size.
379 uint32_t maximum_size = 0; // maximum table size.
380 bool has_maximum_size = false; // true if there is a maximum size.
381 bool imported = false; // true if imported.
382 bool exported = false; // true if exported.
383 };
384
is_asmjs_module(const WasmModule * module)385 inline bool is_asmjs_module(const WasmModule* module) {
386 return module->origin != kWasmOrigin;
387 }
388
389 size_t EstimateStoredSize(const WasmModule* module);
390
391 // Returns the number of possible export wrappers for a given module.
392 V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
393
394 // Returns the wrapper index for a function in {module} with signature {sig}
395 // and origin defined by {is_import}.
396 int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
397 bool is_import);
398
399 // Return the byte offset of the function identified by the given index.
400 // The offset will be relative to the start of the module bytes.
401 // Returns -1 if the function index is invalid.
402 int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);
403
404 // Returns the function containing the given byte offset.
405 // Returns -1 if the byte offset is not contained in any
406 // function of this module.
407 int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
408
409 // Returns the function containing the given byte offset.
410 // Will return preceding function if the byte offset is not
411 // contained within a function.
412 int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
413
414 // Interface to the storage (wire bytes) of a wasm module.
415 // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
416 // on module_bytes, as this storage is only guaranteed to be alive as long as
417 // this struct is alive.
418 struct V8_EXPORT_PRIVATE ModuleWireBytes {
ModuleWireBytesModuleWireBytes419 explicit ModuleWireBytes(Vector<const byte> module_bytes)
420 : module_bytes_(module_bytes) {}
ModuleWireBytesModuleWireBytes421 ModuleWireBytes(const byte* start, const byte* end)
422 : module_bytes_(start, static_cast<int>(end - start)) {
423 DCHECK_GE(kMaxInt, end - start);
424 }
425
426 // Get a string stored in the module bytes representing a name.
427 WasmName GetNameOrNull(WireBytesRef ref) const;
428
429 // Get a string stored in the module bytes representing a function name.
430 WasmName GetNameOrNull(const WasmFunction* function,
431 const WasmModule* module) const;
432
433 // Checks the given reference is contained within the module bytes.
BoundsCheckModuleWireBytes434 bool BoundsCheck(WireBytesRef ref) const {
435 uint32_t size = static_cast<uint32_t>(module_bytes_.length());
436 return ref.offset() <= size && ref.length() <= size - ref.offset();
437 }
438
GetFunctionBytesModuleWireBytes439 Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
440 return module_bytes_.SubVector(function->code.offset(),
441 function->code.end_offset());
442 }
443
module_bytesModuleWireBytes444 Vector<const byte> module_bytes() const { return module_bytes_; }
startModuleWireBytes445 const byte* start() const { return module_bytes_.begin(); }
endModuleWireBytes446 const byte* end() const { return module_bytes_.end(); }
lengthModuleWireBytes447 size_t length() const { return module_bytes_.length(); }
448
449 private:
450 Vector<const byte> module_bytes_;
451 };
452
453 // A helper for printing out the names of functions.
454 struct WasmFunctionName {
WasmFunctionNameWasmFunctionName455 WasmFunctionName(const WasmFunction* function, WasmName name)
456 : function_(function), name_(name) {}
457
458 const WasmFunction* function_;
459 const WasmName name_;
460 };
461
462 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
463
464 V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
465 Handle<Context> context);
466
467 Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig);
468 Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
469 ValueType type);
470 Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
471 base::Optional<uint32_t> max_size);
472 Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
473 uint32_t min_size,
474 base::Optional<uint32_t> max_size);
475 Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
476 Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
477 Handle<JSArray> GetCustomSections(Isolate* isolate,
478 Handle<WasmModuleObject> module,
479 Handle<String> name, ErrorThrower* thrower);
480
481 // Get the source position from a given function index and byte offset,
482 // for either asm.js or pure Wasm modules.
483 int GetSourcePosition(const WasmModule*, uint32_t func_index,
484 uint32_t byte_offset, bool is_at_number_conversion);
485
486 // Translate function index to the index relative to the first declared (i.e.
487 // non-imported) function.
declared_function_index(const WasmModule * module,int func_index)488 inline int declared_function_index(const WasmModule* module, int func_index) {
489 DCHECK_LE(module->num_imported_functions, func_index);
490 int declared_idx = func_index - module->num_imported_functions;
491 DCHECK_GT(module->num_declared_functions, declared_idx);
492 return declared_idx;
493 }
494
495 // TruncatedUserString makes it easy to output names up to a certain length, and
496 // output a truncation followed by '...' if they exceed a limit.
497 // Use like this:
498 // TruncatedUserString<> name (pc, len);
499 // printf("... %.*s ...", name.length(), name.start())
500 template <int kMaxLen = 50>
501 class TruncatedUserString {
502 static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");
503
504 public:
505 template <typename T>
TruncatedUserString(Vector<T> name)506 explicit TruncatedUserString(Vector<T> name)
507 : TruncatedUserString(name.begin(), name.length()) {}
508
TruncatedUserString(const byte * start,size_t len)509 TruncatedUserString(const byte* start, size_t len)
510 : TruncatedUserString(reinterpret_cast<const char*>(start), len) {}
511
TruncatedUserString(const char * start,size_t len)512 TruncatedUserString(const char* start, size_t len)
513 : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
514 if (len > static_cast<size_t>(kMaxLen)) {
515 memcpy(buffer_, start, kMaxLen - 3);
516 memset(buffer_ + kMaxLen - 3, '.', 3);
517 start_ = buffer_;
518 }
519 }
520
start()521 const char* start() const { return start_; }
522
length()523 int length() const { return length_; }
524
525 private:
526 const char* start_;
527 const int length_;
528 char buffer_[kMaxLen];
529 };
530
531 // Print the signature into the given {buffer}, using {delimiter} as separator
532 // between parameter types and return types. If {buffer} is non-empty, it will
533 // be null-terminated, even if the signature is cut off. Returns the number of
534 // characters written, excluding the terminating null-byte.
535 size_t PrintSignature(Vector<char> buffer, const wasm::FunctionSig*,
536 char delimiter = ':');
537
538 } // namespace wasm
539 } // namespace internal
540 } // namespace v8
541
542 #endif // V8_WASM_WASM_MODULE_H_
543