• 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_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