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