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/wasm-constants.h"
16 #include "src/wasm/wasm-opcodes.h"
17 
18 namespace v8 {
19 
20 namespace internal {
21 
22 class WasmModuleObject;
23 
24 namespace wasm {
25 
26 using WasmName = Vector<const char>;
27 
28 struct AsmJsOffsets;
29 class ErrorThrower;
30 
31 // Reference to a string in the wire bytes.
32 class WireBytesRef {
33  public:
WireBytesRef()34   WireBytesRef() : WireBytesRef(0, 0) {}
WireBytesRef(uint32_t offset,uint32_t length)35   WireBytesRef(uint32_t offset, uint32_t length)
36       : offset_(offset), length_(length) {
37     DCHECK_IMPLIES(offset_ == 0, length_ == 0);
38     DCHECK_LE(offset_, offset_ + length_);  // no uint32_t overflow.
39   }
40 
offset()41   uint32_t offset() const { return offset_; }
length()42   uint32_t length() const { return length_; }
end_offset()43   uint32_t end_offset() const { return offset_ + length_; }
is_empty()44   bool is_empty() const { return length_ == 0; }
is_set()45   bool is_set() const { return offset_ != 0; }
46 
47  private:
48   uint32_t offset_;
49   uint32_t length_;
50 };
51 
52 // Static representation of a wasm function.
53 struct WasmFunction {
54   const FunctionSig* sig;  // signature of the function.
55   uint32_t func_index;     // index into the function table.
56   uint32_t sig_index;      // index into the signature table.
57   WireBytesRef code;       // code of this function.
58   bool imported;
59   bool exported;
60   bool declared;
61 };
62 
63 // Static representation of a wasm global variable.
64 struct WasmGlobal {
65   ValueType type;     // type of the global.
66   bool mutability;    // {true} if mutable.
67   WasmInitExpr init;  // the initialization expression of the global.
68   union {
69     uint32_t index;   // index of imported mutable global.
70     uint32_t offset;  // offset into global memory (if not imported & mutable).
71   };
72   bool imported;  // true if imported.
73   bool exported;  // true if exported.
74 };
75 
76 // Note: An exception signature only uses the params portion of a
77 // function signature.
78 using WasmExceptionSig = FunctionSig;
79 
80 // Static representation of a wasm exception type.
81 struct WasmException {
WasmExceptionWasmException82   explicit WasmException(const WasmExceptionSig* sig) : sig(sig) {}
ToFunctionSigWasmException83   const FunctionSig* ToFunctionSig() const { return sig; }
84 
85   const WasmExceptionSig* sig;  // type signature of the exception.
86 };
87 
88 // Static representation of a wasm data segment.
89 struct WasmDataSegment {
90   // Construct an active segment.
WasmDataSegmentWasmDataSegment91   explicit WasmDataSegment(WasmInitExpr dest_addr)
92       : dest_addr(dest_addr), active(true) {}
93 
94   // Construct a passive segment, which has no dest_addr.
WasmDataSegmentWasmDataSegment95   WasmDataSegment() : active(false) {}
96 
97   WasmInitExpr dest_addr;  // destination memory address of the data.
98   WireBytesRef source;     // start offset in the module bytes.
99   bool active = true;      // true if copied automatically during instantiation.
100 };
101 
102 // Static representation of a wasm indirect call table.
103 struct WasmTable {
104   MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable);
105   ValueType type = kWasmStmt;     // table type.
106   uint32_t initial_size = 0;      // initial table size.
107   uint32_t maximum_size = 0;      // maximum table size.
108   bool has_maximum_size = false;  // true if there is a maximum size.
109   bool imported = false;        // true if imported.
110   bool exported = false;        // true if exported.
111 };
112 
113 // Static representation of wasm element segment (table initializer).
114 struct WasmElemSegment {
115   MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmElemSegment);
116 
117   // Construct an active segment.
WasmElemSegmentWasmElemSegment118   WasmElemSegment(uint32_t table_index, WasmInitExpr offset)
119       : type(kWasmFuncRef),
120         table_index(table_index),
121         offset(offset),
122         status(kStatusActive) {}
123 
124   // Construct a passive or declarative segment, which has no table index or
125   // offset.
WasmElemSegmentWasmElemSegment126   explicit WasmElemSegment(bool declarative)
127       : type(kWasmFuncRef),
128         table_index(0),
129         status(declarative ? kStatusDeclarative : kStatusPassive) {}
130 
131   // Used in the {entries} vector to represent a `ref.null` entry in a passive
132   // segment.
133   V8_EXPORT_PRIVATE static const uint32_t kNullIndex = ~0u;
134 
135   ValueType type;
136   uint32_t table_index;
137   WasmInitExpr offset;
138   std::vector<uint32_t> entries;
139   enum Status {
140     kStatusActive,      // copied automatically during instantiation.
141     kStatusPassive,     // copied explicitly after instantiation.
142     kStatusDeclarative  // purely declarative and never copied.
143   } status;
144 };
145 
146 // Static representation of a wasm import.
147 struct WasmImport {
148   WireBytesRef module_name;   // module name.
149   WireBytesRef field_name;    // import name.
150   ImportExportKindCode kind;  // kind of the import.
151   uint32_t index;             // index into the respective space.
152 };
153 
154 // Static representation of a wasm export.
155 struct WasmExport {
156   WireBytesRef name;          // exported name.
157   ImportExportKindCode kind;  // kind of the export.
158   uint32_t index;             // index into the respective space.
159 };
160 
161 enum class WasmCompilationHintStrategy : uint8_t {
162   kDefault = 0,
163   kLazy = 1,
164   kEager = 2,
165   kLazyBaselineEagerTopTier = 3,
166 };
167 
168 enum class WasmCompilationHintTier : uint8_t {
169   kDefault = 0,
170   kInterpreter = 1,
171   kBaseline = 2,
172   kOptimized = 3,
173 };
174 
175 // Static representation of a wasm compilation hint
176 struct WasmCompilationHint {
177   WasmCompilationHintStrategy strategy;
178   WasmCompilationHintTier baseline_tier;
179   WasmCompilationHintTier top_tier;
180 };
181 
182 enum ModuleOrigin : uint8_t {
183   kWasmOrigin,
184   kAsmJsSloppyOrigin,
185   kAsmJsStrictOrigin
186 };
187 
188 #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)     \
189   ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
190                            : (counters)->prefix##_asm_##suffix())
191 
192 struct ModuleWireBytes;
193 
194 class V8_EXPORT_PRIVATE DecodedFunctionNames {
195  public:
196   WireBytesRef Lookup(const ModuleWireBytes& wire_bytes,
197                       uint32_t function_index,
198                       Vector<const WasmExport> export_table) const;
199   void AddForTesting(int function_index, WireBytesRef name);
200 
201  private:
202   // {function_names_} is populated lazily after decoding, and therefore needs a
203   // mutex to protect concurrent modifications from multiple {WasmModuleObject}.
204   mutable base::Mutex mutex_;
205   mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
206       function_names_;
207 };
208 
209 class V8_EXPORT_PRIVATE DecodedGlobalNames {
210  public:
211   std::pair<WireBytesRef, WireBytesRef> Lookup(
212       uint32_t global_index, Vector<const WasmImport> import_table,
213       Vector<const WasmExport> export_table) const;
214 
215  private:
216   // {global_names_} is populated lazily after decoding, and therefore needs a
217   // mutex to protect concurrent modifications from multiple {WasmModuleObject}.
218   mutable base::Mutex mutex_;
219   mutable std::unique_ptr<
220       std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>>
221       global_names_;
222 };
223 
224 class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
225  public:
226   explicit AsmJsOffsetInformation(Vector<const byte> encoded_offsets);
227 
228   // Destructor defined in wasm-module.cc, where the definition of
229   // {AsmJsOffsets} is available.
230   ~AsmJsOffsetInformation();
231 
232   int GetSourcePosition(int func_index, int byte_offset,
233                         bool is_at_number_conversion);
234 
235   std::pair<int, int> GetFunctionOffsets(int func_index);
236 
237  private:
238   void EnsureDecodedOffsets();
239 
240   // The offset information table is decoded lazily, hence needs to be
241   // protected against concurrent accesses.
242   // Exactly one of the two fields below will be set at a time.
243   mutable base::Mutex mutex_;
244 
245   // Holds the encoded offset table bytes.
246   OwnedVector<const uint8_t> encoded_offsets_;
247 
248   // Holds the decoded offset table.
249   std::unique_ptr<AsmJsOffsets> decoded_offsets_;
250 };
251 
252 // Static representation of a module.
253 struct V8_EXPORT_PRIVATE WasmModule {
254   std::unique_ptr<Zone> signature_zone;
255   uint32_t initial_pages = 0;      // initial size of the memory in 64k pages
256   uint32_t maximum_pages = 0;      // maximum size of the memory in 64k pages
257   bool has_shared_memory = false;  // true if memory is a SharedArrayBuffer
258   bool has_maximum_pages = false;  // true if there is a maximum memory size
259   bool has_memory = false;         // true if the memory was defined or imported
260   bool mem_export = false;         // true if the memory is exported
261   int start_function_index = -1;   // start function, >= 0 if any
262 
263   std::vector<WasmGlobal> globals;
264   // Size of the buffer required for all globals that are not imported and
265   // mutable.
266   uint32_t untagged_globals_buffer_size = 0;
267   uint32_t tagged_globals_buffer_size = 0;
268   uint32_t num_imported_mutable_globals = 0;
269   uint32_t num_imported_functions = 0;
270   uint32_t num_imported_tables = 0;
271   uint32_t num_declared_functions = 0;  // excluding imported
272   uint32_t num_exported_functions = 0;
273   uint32_t num_declared_data_segments = 0;  // From the DataCount section.
274   WireBytesRef code = {0, 0};
275   WireBytesRef name = {0, 0};
276   std::vector<const FunctionSig*> signatures;  // by signature index
277   std::vector<uint32_t> signature_ids;   // by signature index
278   std::vector<WasmFunction> functions;
279   std::vector<WasmDataSegment> data_segments;
280   std::vector<WasmTable> tables;
281   std::vector<WasmImport> import_table;
282   std::vector<WasmExport> export_table;
283   std::vector<WasmException> exceptions;
284   std::vector<WasmElemSegment> elem_segments;
285   std::vector<WasmCompilationHint> compilation_hints;
286   SignatureMap signature_map;  // canonicalizing map for signature indexes.
287 
288   ModuleOrigin origin = kWasmOrigin;  // origin of the module
289   DecodedFunctionNames function_names;
290   DecodedGlobalNames global_names;
291   std::string source_map_url;
292 
293   // Asm.js source position information. Only available for modules compiled
294   // from asm.js.
295   std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;
296 
297   explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
298 
299   DISALLOW_COPY_AND_ASSIGN(WasmModule);
300 };
301 
is_asmjs_module(const WasmModule * module)302 inline bool is_asmjs_module(const WasmModule* module) {
303   return module->origin != kWasmOrigin;
304 }
305 
306 size_t EstimateStoredSize(const WasmModule* module);
307 
308 // Returns the number of possible export wrappers for a given module.
309 V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
310 
311 // Returns the wrapper index for a function in {module} with signature {sig}
312 // and origin defined by {is_import}.
313 int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
314                           bool is_import);
315 
316 // Return the byte offset of the function identified by the given index.
317 // The offset will be relative to the start of the module bytes.
318 // Returns -1 if the function index is invalid.
319 int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);
320 
321 // Returns the function containing the given byte offset.
322 // Returns -1 if the byte offset is not contained in any
323 // function of this module.
324 int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
325 
326 // Returns the function containing the given byte offset.
327 // Will return preceding function if the byte offset is not
328 // contained within a function.
329 int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
330 
331 // Interface to the storage (wire bytes) of a wasm module.
332 // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
333 // on module_bytes, as this storage is only guaranteed to be alive as long as
334 // this struct is alive.
335 struct V8_EXPORT_PRIVATE ModuleWireBytes {
ModuleWireBytesModuleWireBytes336   explicit ModuleWireBytes(Vector<const byte> module_bytes)
337       : module_bytes_(module_bytes) {}
ModuleWireBytesModuleWireBytes338   ModuleWireBytes(const byte* start, const byte* end)
339       : module_bytes_(start, static_cast<int>(end - start)) {
340     DCHECK_GE(kMaxInt, end - start);
341   }
342 
343   // Get a string stored in the module bytes representing a name.
344   WasmName GetNameOrNull(WireBytesRef ref) const;
345 
346   // Get a string stored in the module bytes representing a function name.
347   WasmName GetNameOrNull(const WasmFunction* function,
348                          const WasmModule* module) const;
349 
350   // Checks the given reference is contained within the module bytes.
BoundsCheckModuleWireBytes351   bool BoundsCheck(WireBytesRef ref) const {
352     uint32_t size = static_cast<uint32_t>(module_bytes_.length());
353     return ref.offset() <= size && ref.length() <= size - ref.offset();
354   }
355 
GetFunctionBytesModuleWireBytes356   Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
357     return module_bytes_.SubVector(function->code.offset(),
358                                    function->code.end_offset());
359   }
360 
module_bytesModuleWireBytes361   Vector<const byte> module_bytes() const { return module_bytes_; }
startModuleWireBytes362   const byte* start() const { return module_bytes_.begin(); }
endModuleWireBytes363   const byte* end() const { return module_bytes_.end(); }
lengthModuleWireBytes364   size_t length() const { return module_bytes_.length(); }
365 
366  private:
367   Vector<const byte> module_bytes_;
368 };
369 
370 // A helper for printing out the names of functions.
371 struct WasmFunctionName {
WasmFunctionNameWasmFunctionName372   WasmFunctionName(const WasmFunction* function, WasmName name)
373       : function_(function), name_(name) {}
374 
375   const WasmFunction* function_;
376   const WasmName name_;
377 };
378 
379 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
380 
381 V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
382                                             Handle<Context> context);
383 
384 Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig);
385 Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
386                                   ValueType type);
387 Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
388                                   base::Optional<uint32_t> max_size);
389 Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
390                                  uint32_t min_size,
391                                  base::Optional<uint32_t> max_size);
392 Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
393 Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
394 Handle<JSArray> GetCustomSections(Isolate* isolate,
395                                   Handle<WasmModuleObject> module,
396                                   Handle<String> name, ErrorThrower* thrower);
397 
398 // Get the source position from a given function index and byte offset,
399 // for either asm.js or pure Wasm modules.
400 int GetSourcePosition(const WasmModule*, uint32_t func_index,
401                       uint32_t byte_offset, bool is_at_number_conversion);
402 
403 // Translate function index to the index relative to the first declared (i.e.
404 // non-imported) function.
declared_function_index(const WasmModule * module,int func_index)405 inline int declared_function_index(const WasmModule* module, int func_index) {
406   DCHECK_LE(module->num_imported_functions, func_index);
407   int declared_idx = func_index - module->num_imported_functions;
408   DCHECK_GT(module->num_declared_functions, declared_idx);
409   return declared_idx;
410 }
411 
412 // TruncatedUserString makes it easy to output names up to a certain length, and
413 // output a truncation followed by '...' if they exceed a limit.
414 // Use like this:
415 //   TruncatedUserString<> name (pc, len);
416 //   printf("... %.*s ...", name.length(), name.start())
417 template <int kMaxLen = 50>
418 class TruncatedUserString {
419   static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");
420 
421  public:
422   template <typename T>
TruncatedUserString(Vector<T> name)423   explicit TruncatedUserString(Vector<T> name)
424       : TruncatedUserString(name.begin(), name.length()) {}
425 
TruncatedUserString(const byte * start,size_t len)426   TruncatedUserString(const byte* start, size_t len)
427       : TruncatedUserString(reinterpret_cast<const char*>(start), len) {}
428 
TruncatedUserString(const char * start,size_t len)429   TruncatedUserString(const char* start, size_t len)
430       : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
431     if (len > static_cast<size_t>(kMaxLen)) {
432       memcpy(buffer_, start, kMaxLen - 3);
433       memset(buffer_ + kMaxLen - 3, '.', 3);
434       start_ = buffer_;
435     }
436   }
437 
start()438   const char* start() const { return start_; }
439 
length()440   int length() const { return length_; }
441 
442  private:
443   const char* start_;
444   const int length_;
445   char buffer_[kMaxLen];
446 };
447 
448 // Print the signature into the given {buffer}. If {buffer} is non-empty, it
449 // will be null-terminated, even if the signature is cut off. Returns the number
450 // of characters written, excluding the terminating null-byte.
451 size_t PrintSignature(Vector<char> buffer, const wasm::FunctionSig*);
452 
453 }  // namespace wasm
454 }  // namespace internal
455 }  // namespace v8
456 
457 #endif  // V8_WASM_WASM_MODULE_H_
458