Line data Source code
1 : // Copyright 2019 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 : #include "src/wasm/module-instantiate.h"
6 : #include "src/asmjs/asm-js.h"
7 : #include "src/heap/heap-inl.h" // For CodeSpaceMemoryModificationScope.
8 : #include "src/property-descriptor.h"
9 : #include "src/utils.h"
10 : #include "src/wasm/js-to-wasm-wrapper-cache-inl.h"
11 : #include "src/wasm/module-compiler.h"
12 : #include "src/wasm/wasm-import-wrapper-cache-inl.h"
13 : #include "src/wasm/wasm-module.h"
14 : #include "src/wasm/wasm-objects-inl.h"
15 :
16 : #define TRACE(...) \
17 : do { \
18 : if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
19 : } while (false)
20 :
21 : namespace v8 {
22 : namespace internal {
23 : namespace wasm {
24 :
25 : namespace {
26 8334 : byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
27 8334 : return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
28 : }
29 :
30 5398 : uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
31 : const WasmInitExpr& expr) {
32 5398 : switch (expr.kind) {
33 : case WasmInitExpr::kI32Const:
34 4030 : return expr.val.i32_const;
35 : case WasmInitExpr::kGlobalIndex: {
36 : uint32_t offset =
37 4104 : instance->module()->globals[expr.val.global_index].offset;
38 : auto raw_addr =
39 : reinterpret_cast<Address>(
40 4104 : instance->untagged_globals_buffer()->backing_store()) +
41 1368 : offset;
42 : return ReadLittleEndianValue<uint32_t>(raw_addr);
43 : }
44 : default:
45 0 : UNREACHABLE();
46 : }
47 : }
48 :
49 : // Represents the initialized state of a table.
50 : struct TableInstance {
51 : Handle<WasmTableObject> table_object; // WebAssembly.Table instance
52 : Handle<FixedArray> js_functions; // JSFunctions exported
53 : size_t table_size;
54 : };
55 : } // namespace
56 :
57 : // A helper class to simplify instantiating a module from a module object.
58 : // It closes over the {Isolate}, the {ErrorThrower}, etc.
59 271888 : class InstanceBuilder {
60 : public:
61 : InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
62 : Handle<WasmModuleObject> module_object,
63 : MaybeHandle<JSReceiver> ffi,
64 : MaybeHandle<JSArrayBuffer> memory);
65 :
66 : // Build an instance, in all of its glory.
67 : MaybeHandle<WasmInstanceObject> Build();
68 : // Run the start function, if any.
69 : bool ExecuteStartFunction();
70 :
71 : private:
72 : // A pre-evaluated value to use in import binding.
73 : struct SanitizedImport {
74 : Handle<String> module_name;
75 : Handle<String> import_name;
76 : Handle<Object> value;
77 : };
78 :
79 : Isolate* isolate_;
80 : const WasmFeatures enabled_;
81 : const WasmModule* const module_;
82 : ErrorThrower* thrower_;
83 : Handle<WasmModuleObject> module_object_;
84 : MaybeHandle<JSReceiver> ffi_;
85 : MaybeHandle<JSArrayBuffer> memory_;
86 : Handle<JSArrayBuffer> untagged_globals_;
87 : Handle<FixedArray> tagged_globals_;
88 : std::vector<TableInstance> table_instances_;
89 : std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
90 : Handle<WasmExportedFunction> start_function_;
91 : JSToWasmWrapperCache js_to_wasm_cache_;
92 : std::vector<SanitizedImport> sanitized_imports_;
93 :
94 256800 : UseTrapHandler use_trap_handler() const {
95 513600 : return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler
96 513600 : : kNoTrapHandler;
97 : }
98 :
99 : // Helper routines to print out errors with imports.
100 : #define ERROR_THROWER_WITH_MESSAGE(TYPE) \
101 : void Report##TYPE(const char* error, uint32_t index, \
102 : Handle<String> module_name, Handle<String> import_name) { \
103 : thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \
104 : index, module_name->ToCString().get(), \
105 : import_name->ToCString().get(), error); \
106 : } \
107 : \
108 : MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \
109 : Handle<String> module_name) { \
110 : thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \
111 : module_name->ToCString().get(), error); \
112 : return MaybeHandle<Object>(); \
113 : }
114 :
115 10758 : ERROR_THROWER_WITH_MESSAGE(LinkError)
116 1504 : ERROR_THROWER_WITH_MESSAGE(TypeError)
117 :
118 : #undef ERROR_THROWER_WITH_MESSAGE
119 :
120 : // Look up an import value in the {ffi_} object.
121 : MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
122 : Handle<String> import_name);
123 :
124 : // Look up an import value in the {ffi_} object specifically for linking an
125 : // asm.js module. This only performs non-observable lookups, which allows
126 : // falling back to JavaScript proper (and hence re-executing all lookups) if
127 : // module instantiation fails.
128 : MaybeHandle<Object> LookupImportAsm(uint32_t index,
129 : Handle<String> import_name);
130 :
131 : // Load data segments into the memory.
132 : void LoadDataSegments(Handle<WasmInstanceObject> instance);
133 :
134 : void WriteGlobalValue(const WasmGlobal& global, double value);
135 : void WriteGlobalValue(const WasmGlobal& global,
136 : Handle<WasmGlobalObject> value);
137 :
138 : void WriteGlobalAnyRef(const WasmGlobal& global, Handle<Object> value);
139 :
140 : void SanitizeImports();
141 :
142 : // Find the imported memory buffer if there is one. This is used to see if we
143 : // need to recompile with bounds checks before creating the instance.
144 : MaybeHandle<JSArrayBuffer> FindImportedMemoryBuffer() const;
145 :
146 : // Processes a single imported function.
147 : bool ProcessImportedFunction(Handle<WasmInstanceObject> instance,
148 : int import_index, int func_index,
149 : Handle<String> module_name,
150 : Handle<String> import_name,
151 : Handle<Object> value);
152 :
153 : // Process a single imported table.
154 : bool ProcessImportedTable(Handle<WasmInstanceObject> instance,
155 : int import_index, int table_index,
156 : Handle<String> module_name,
157 : Handle<String> import_name, Handle<Object> value);
158 :
159 : // Process a single imported memory.
160 : bool ProcessImportedMemory(Handle<WasmInstanceObject> instance,
161 : int import_index, Handle<String> module_name,
162 : Handle<String> import_name, Handle<Object> value);
163 :
164 : // Process a single imported global.
165 : bool ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
166 : int import_index, int global_index,
167 : Handle<String> module_name,
168 : Handle<String> import_name, Handle<Object> value);
169 :
170 : // Process a single imported WasmGlobalObject.
171 : bool ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,
172 : int import_index,
173 : Handle<String> module_name,
174 : Handle<String> import_name,
175 : const WasmGlobal& global,
176 : Handle<WasmGlobalObject> global_object);
177 :
178 : // Process the imports, including functions, tables, globals, and memory, in
179 : // order, loading them from the {ffi_} object. Returns the number of imported
180 : // functions.
181 : int ProcessImports(Handle<WasmInstanceObject> instance);
182 :
183 : template <typename T>
184 : T* GetRawGlobalPtr(const WasmGlobal& global);
185 :
186 : // Process initialization of globals.
187 : void InitGlobals();
188 :
189 : // Allocate memory for a module instance as a new JSArrayBuffer.
190 : Handle<JSArrayBuffer> AllocateMemory(uint32_t num_pages);
191 :
192 : bool NeedsWrappers() const;
193 :
194 : // Process the exports, creating wrappers for functions, tables, memories,
195 : // and globals.
196 : void ProcessExports(Handle<WasmInstanceObject> instance);
197 :
198 : void InitializeTables(Handle<WasmInstanceObject> instance);
199 :
200 : void LoadTableSegments(Handle<WasmInstanceObject> instance);
201 :
202 : // Creates new exception tags for all exceptions. Note that some tags might
203 : // already exist if they were imported, those tags will be re-used.
204 : void InitializeExceptions(Handle<WasmInstanceObject> instance);
205 : };
206 :
207 135942 : MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
208 : Isolate* isolate, ErrorThrower* thrower,
209 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
210 : MaybeHandle<JSArrayBuffer> memory) {
211 135942 : InstanceBuilder builder(isolate, thrower, module_object, imports, memory);
212 135944 : auto instance = builder.Build();
213 135944 : if (!instance.is_null() && builder.ExecuteStartFunction()) {
214 133136 : return instance;
215 : }
216 : DCHECK(isolate->has_pending_exception() || thrower->error());
217 2808 : return {};
218 : }
219 :
220 135943 : InstanceBuilder::InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
221 : Handle<WasmModuleObject> module_object,
222 : MaybeHandle<JSReceiver> ffi,
223 : MaybeHandle<JSArrayBuffer> memory)
224 : : isolate_(isolate),
225 271888 : enabled_(module_object->native_module()->enabled_features()),
226 271888 : module_(module_object->module()),
227 : thrower_(thrower),
228 : module_object_(module_object),
229 : ffi_(ffi),
230 543775 : memory_(memory) {
231 271884 : sanitized_imports_.reserve(module_->import_table.size());
232 135944 : }
233 :
234 : // Build an instance, in all of its glory.
235 135942 : MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
236 271884 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "InstanceBuilder::Build");
237 : // Check that an imports argument was provided, if the module requires it.
238 : // No point in continuing otherwise.
239 393505 : if (!module_->import_table.empty() && ffi_.is_null()) {
240 : thrower_->TypeError(
241 269147 : "Imports argument must be present and must be an object");
242 93 : return {};
243 : }
244 :
245 135849 : SanitizeImports();
246 271700 : if (thrower_->error()) return {};
247 :
248 : // TODO(6792): No longer needed once WebAssembly code is off heap.
249 274111 : CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
250 : // From here on, we expect the build pipeline to run without exiting to JS.
251 270913 : DisallowJavascriptExecution no_js(isolate_);
252 : // Record build time into correct bucket, then build instance.
253 270914 : TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER(
254 270914 : isolate_->counters(), module_->origin, wasm_instantiate, module_time));
255 :
256 : //--------------------------------------------------------------------------
257 : // Allocate the memory array buffer.
258 : //--------------------------------------------------------------------------
259 : // We allocate the memory buffer before cloning or reusing the compiled module
260 : // so we will know whether we need to recompile with bounds checks.
261 135457 : uint32_t initial_pages = module_->initial_pages;
262 270914 : auto initial_pages_counter = SELECT_WASM_COUNTER(
263 : isolate_->counters(), module_->origin, wasm, min_mem_pages_count);
264 135457 : initial_pages_counter->AddSample(initial_pages);
265 : // Asm.js has memory_ already set at this point, so we don't want to
266 : // overwrite it.
267 135457 : if (memory_.is_null()) {
268 134217 : memory_ = FindImportedMemoryBuffer();
269 : }
270 135457 : if (!memory_.is_null()) {
271 : // Set externally passed ArrayBuffer non detachable.
272 : Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
273 : memory->set_is_detachable(false);
274 :
275 : DCHECK_IMPLIES(use_trap_handler(), module_->origin == kAsmJsOrigin ||
276 : memory->is_wasm_memory() ||
277 : memory->backing_store() == nullptr);
278 130020 : } else if (initial_pages > 0 || use_trap_handler()) {
279 : // We need to unconditionally create a guard region if using trap handlers,
280 : // even when the size is zero to prevent null-dereference issues
281 : // (e.g. https://crbug.com/769637).
282 : // Allocate memory if the initial size is more than 0 pages.
283 129970 : memory_ = AllocateMemory(initial_pages);
284 129970 : if (memory_.is_null()) {
285 : // failed to allocate memory
286 : DCHECK(isolate_->has_pending_exception() || thrower_->error());
287 16 : return {};
288 : }
289 : }
290 :
291 : //--------------------------------------------------------------------------
292 : // Recompile module if using trap handlers but could not get guarded memory
293 : //--------------------------------------------------------------------------
294 135441 : if (module_->origin == kWasmOrigin && use_trap_handler()) {
295 : // Make sure the memory has suitable guard regions.
296 : WasmMemoryTracker* const memory_tracker =
297 261168 : isolate_->wasm_engine()->memory_tracker();
298 :
299 130584 : if (!memory_tracker->HasFullGuardRegions(
300 130584 : memory_.ToHandleChecked()->backing_store())) {
301 38 : if (!FLAG_wasm_trap_handler_fallback) {
302 : thrower_->LinkError(
303 : "Provided memory is lacking guard regions but fallback was "
304 0 : "disabled.");
305 0 : return {};
306 : }
307 :
308 : TRACE("Recompiling module without bounds checks\n");
309 38 : ErrorThrower thrower(isolate_, "recompile");
310 38 : auto native_module = module_object_->native_module();
311 : CompileNativeModuleWithExplicitBoundsChecks(isolate_, &thrower, module_,
312 38 : native_module);
313 38 : if (thrower.error()) {
314 0 : return {};
315 : }
316 38 : DCHECK(!native_module->use_trap_handler());
317 : }
318 : }
319 :
320 : //--------------------------------------------------------------------------
321 : // Create the WebAssembly.Instance object.
322 : //--------------------------------------------------------------------------
323 135441 : NativeModule* native_module = module_object_->native_module();
324 : TRACE("New module instantiation for %p\n", native_module);
325 : Handle<WasmInstanceObject> instance =
326 135441 : WasmInstanceObject::New(isolate_, module_object_);
327 270878 : NativeModuleModificationScope native_modification_scope(native_module);
328 :
329 : //--------------------------------------------------------------------------
330 : // Set up the globals for the new instance.
331 : //--------------------------------------------------------------------------
332 135440 : uint32_t untagged_globals_buffer_size = module_->untagged_globals_buffer_size;
333 135440 : if (untagged_globals_buffer_size > 0) {
334 3199 : void* backing_store = isolate_->array_buffer_allocator()->Allocate(
335 3199 : untagged_globals_buffer_size);
336 3199 : if (backing_store == nullptr) {
337 0 : thrower_->RangeError("Out of memory: wasm globals");
338 0 : return {};
339 : }
340 : untagged_globals_ =
341 3199 : isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
342 : constexpr bool is_external = false;
343 : constexpr bool is_wasm_memory = false;
344 : JSArrayBuffer::Setup(untagged_globals_, isolate_, is_external,
345 : backing_store, untagged_globals_buffer_size,
346 3199 : SharedFlag::kNotShared, is_wasm_memory);
347 3199 : if (untagged_globals_.is_null()) {
348 0 : thrower_->RangeError("Out of memory: wasm globals");
349 0 : return {};
350 : }
351 : instance->set_globals_start(
352 : reinterpret_cast<byte*>(untagged_globals_->backing_store()));
353 3199 : instance->set_untagged_globals_buffer(*untagged_globals_);
354 : }
355 :
356 135440 : uint32_t tagged_globals_buffer_size = module_->tagged_globals_buffer_size;
357 135440 : if (tagged_globals_buffer_size > 0) {
358 : tagged_globals_ = isolate_->factory()->NewFixedArray(
359 104 : static_cast<int>(tagged_globals_buffer_size));
360 104 : instance->set_tagged_globals_buffer(*tagged_globals_);
361 : }
362 :
363 : //--------------------------------------------------------------------------
364 : // Set up the array of references to imported globals' array buffers.
365 : //--------------------------------------------------------------------------
366 135440 : if (module_->num_imported_mutable_globals > 0) {
367 : // TODO(binji): This allocates one slot for each mutable global, which is
368 : // more than required if multiple globals are imported from the same
369 : // module.
370 : Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
371 184 : module_->num_imported_mutable_globals, TENURED);
372 184 : instance->set_imported_mutable_globals_buffers(*buffers_array);
373 : }
374 :
375 : //--------------------------------------------------------------------------
376 : // Set up the exception table used for exception tag checks.
377 : //--------------------------------------------------------------------------
378 270880 : int exceptions_count = static_cast<int>(module_->exceptions.size());
379 135440 : if (exceptions_count > 0) {
380 : Handle<FixedArray> exception_table =
381 376 : isolate_->factory()->NewFixedArray(exceptions_count, TENURED);
382 376 : instance->set_exceptions_table(*exception_table);
383 376 : exception_wrappers_.resize(exceptions_count);
384 : }
385 :
386 : //--------------------------------------------------------------------------
387 : // Set up table storage space.
388 : //--------------------------------------------------------------------------
389 272137 : int table_count = static_cast<int>(module_->tables.size());
390 135440 : Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
391 136697 : for (int i = module_->num_imported_tables; i < table_count; i++) {
392 1257 : const WasmTable& table = module_->tables[i];
393 : Handle<WasmTableObject> table_obj = WasmTableObject::New(
394 1257 : isolate_, table.initial_size, table.maximum_size, nullptr);
395 2514 : tables->set(i, *table_obj);
396 : }
397 135440 : instance->set_tables(*tables);
398 :
399 137386 : table_instances_.resize(table_count);
400 :
401 : //--------------------------------------------------------------------------
402 : // Process the imports for the module.
403 : //--------------------------------------------------------------------------
404 135441 : int num_imported_functions = ProcessImports(instance);
405 135441 : if (num_imported_functions < 0) return {};
406 :
407 : //--------------------------------------------------------------------------
408 : // Process the initialization for the module's globals.
409 : //--------------------------------------------------------------------------
410 133468 : InitGlobals();
411 :
412 : //--------------------------------------------------------------------------
413 : // Initialize the indirect tables.
414 : //--------------------------------------------------------------------------
415 133468 : if (table_count > 0) {
416 2281 : InitializeTables(instance);
417 : }
418 :
419 : //--------------------------------------------------------------------------
420 : // Initialize the exceptions table.
421 : //--------------------------------------------------------------------------
422 133467 : if (exceptions_count > 0) {
423 336 : InitializeExceptions(instance);
424 : }
425 :
426 : //--------------------------------------------------------------------------
427 : // Create the WebAssembly.Memory object.
428 : //--------------------------------------------------------------------------
429 133467 : if (module_->has_memory) {
430 26618 : if (!instance->has_memory_object()) {
431 : // No memory object exists. Create one.
432 : Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
433 : isolate_, memory_,
434 9208 : module_->maximum_pages != 0 ? module_->maximum_pages : -1);
435 9208 : instance->set_memory_object(*memory_object);
436 : }
437 :
438 : // Add the instance object to the list of instances for this memory.
439 39927 : Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_);
440 13309 : WasmMemoryObject::AddInstance(isolate_, memory_object, instance);
441 :
442 13309 : if (!memory_.is_null()) {
443 : // Double-check the {memory} array buffer matches the instance.
444 : Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
445 13297 : CHECK_EQ(instance->memory_size(), memory->byte_length());
446 13297 : CHECK_EQ(instance->memory_start(), memory->backing_store());
447 : }
448 : }
449 :
450 : //--------------------------------------------------------------------------
451 : // Check that indirect function table segments are within bounds.
452 : //--------------------------------------------------------------------------
453 268801 : for (const WasmElemSegment& elem_segment : module_->elem_segments) {
454 1985 : if (!elem_segment.active) continue;
455 : DCHECK(elem_segment.table_index < table_instances_.size());
456 1945 : uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
457 3890 : size_t table_size = table_instances_[elem_segment.table_index].table_size;
458 5835 : if (!IsInBounds(base, elem_segment.entries.size(), table_size)) {
459 120 : thrower_->LinkError("table initializer is out of bounds");
460 120 : return {};
461 : }
462 : }
463 :
464 : //--------------------------------------------------------------------------
465 : // Check that memory segments are within bounds.
466 : //--------------------------------------------------------------------------
467 267572 : for (const WasmDataSegment& seg : module_->data_segments) {
468 1020 : if (!seg.active) continue;
469 964 : uint32_t base = EvalUint32InitExpr(instance, seg.dest_addr);
470 1928 : if (!IsInBounds(base, seg.source.length(), instance->memory_size())) {
471 144 : thrower_->LinkError("data segment is out of bounds");
472 144 : return {};
473 : }
474 : }
475 :
476 : //--------------------------------------------------------------------------
477 : // Set up the exports object for the new instance.
478 : //--------------------------------------------------------------------------
479 133204 : ProcessExports(instance);
480 266408 : if (thrower_->error()) return {};
481 :
482 : //--------------------------------------------------------------------------
483 : // Initialize the indirect function tables.
484 : //--------------------------------------------------------------------------
485 133204 : if (table_count > 0) {
486 2153 : LoadTableSegments(instance);
487 : }
488 :
489 : //--------------------------------------------------------------------------
490 : // Initialize the memory by loading data segments.
491 : //--------------------------------------------------------------------------
492 266408 : if (module_->data_segments.size() > 0) {
493 636 : LoadDataSegments(instance);
494 : }
495 :
496 : //--------------------------------------------------------------------------
497 : // Debugging support.
498 : //--------------------------------------------------------------------------
499 : // Set all breakpoints that were set on the shared module.
500 133204 : WasmModuleObject::SetBreakpointsOnNewInstance(module_object_, instance);
501 :
502 133201 : if (FLAG_wasm_interpret_all && module_->origin == kWasmOrigin) {
503 : Handle<WasmDebugInfo> debug_info =
504 748 : WasmInstanceObject::GetOrCreateDebugInfo(instance);
505 : std::vector<int> func_indexes;
506 4012 : for (int func_index = num_imported_functions,
507 6467 : num_wasm_functions = static_cast<int>(module_->functions.size());
508 1632 : func_index < num_wasm_functions; ++func_index) {
509 884 : func_indexes.push_back(func_index);
510 : }
511 748 : WasmDebugInfo::RedirectToInterpreter(debug_info, VectorOf(func_indexes));
512 : }
513 :
514 : //--------------------------------------------------------------------------
515 : // Create a wrapper for the start function.
516 : //--------------------------------------------------------------------------
517 133201 : if (module_->start_function_index >= 0) {
518 : int start_index = module_->start_function_index;
519 4971 : auto& function = module_->functions[start_index];
520 : Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
521 9942 : isolate_, function.sig, function.imported);
522 : // TODO(clemensh): Don't generate an exported function for the start
523 : // function. Use CWasmEntry instead.
524 : start_function_ = WasmExportedFunction::New(
525 : isolate_, instance, MaybeHandle<String>(), start_index,
526 9942 : static_cast<int>(function.sig->parameter_count()), wrapper_code);
527 : }
528 :
529 : DCHECK(!isolate_->has_pending_exception());
530 : TRACE("Successfully built instance for module %p\n",
531 : module_object_->native_module());
532 269145 : return instance;
533 : }
534 :
535 133204 : bool InstanceBuilder::ExecuteStartFunction() {
536 266408 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
537 : "InstanceBuilder::ExecuteStartFunction");
538 133204 : if (start_function_.is_null()) return true; // No start function.
539 :
540 4971 : HandleScope scope(isolate_);
541 : // Call the JS function.
542 4971 : Handle<Object> undefined = isolate_->factory()->undefined_value();
543 : MaybeHandle<Object> retval =
544 4971 : Execution::Call(isolate_, start_function_, undefined, 0, nullptr);
545 :
546 4971 : if (retval.is_null()) {
547 : DCHECK(isolate_->has_pending_exception());
548 : return false;
549 : }
550 138107 : return true;
551 : }
552 :
553 : // Look up an import value in the {ffi_} object.
554 122812 : MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
555 : Handle<String> module_name,
556 :
557 : Handle<String> import_name) {
558 : // We pre-validated in the js-api layer that the ffi object is present, and
559 : // a JSObject, if the module has imports.
560 : DCHECK(!ffi_.is_null());
561 :
562 : // Look up the module first.
563 : MaybeHandle<Object> result = Object::GetPropertyOrElement(
564 122812 : isolate_, ffi_.ToHandleChecked(), module_name);
565 122813 : if (result.is_null()) {
566 16 : return ReportTypeError("module not found", index, module_name);
567 : }
568 :
569 : Handle<Object> module = result.ToHandleChecked();
570 :
571 : // Look up the value in the module.
572 245594 : if (!module->IsJSReceiver()) {
573 : return ReportTypeError("module is not an object or function", index,
574 360 : module_name);
575 : }
576 :
577 122437 : result = Object::GetPropertyOrElement(isolate_, module, import_name);
578 122437 : if (result.is_null()) {
579 0 : ReportLinkError("import not found", index, module_name, import_name);
580 0 : return MaybeHandle<JSFunction>();
581 : }
582 :
583 122437 : return result;
584 : }
585 :
586 : // Look up an import value in the {ffi_} object specifically for linking an
587 : // asm.js module. This only performs non-observable lookups, which allows
588 : // falling back to JavaScript proper (and hence re-executing all lookups) if
589 : // module instantiation fails.
590 4779 : MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
591 : uint32_t index, Handle<String> import_name) {
592 : // Check that a foreign function interface object was provided.
593 4779 : if (ffi_.is_null()) {
594 0 : return ReportLinkError("missing imports object", index, import_name);
595 : }
596 :
597 : // Perform lookup of the given {import_name} without causing any observable
598 : // side-effect. We only accept accesses that resolve to data properties,
599 : // which is indicated by the asm.js spec in section 7 ("Linking") as well.
600 : Handle<Object> result;
601 : LookupIterator it = LookupIterator::PropertyOrElement(
602 4779 : isolate_, ffi_.ToHandleChecked(), import_name);
603 4779 : switch (it.state()) {
604 : case LookupIterator::ACCESS_CHECK:
605 : case LookupIterator::INTEGER_INDEXED_EXOTIC:
606 : case LookupIterator::INTERCEPTOR:
607 : case LookupIterator::JSPROXY:
608 : case LookupIterator::ACCESSOR:
609 : case LookupIterator::TRANSITION:
610 18 : return ReportLinkError("not a data property", index, import_name);
611 : case LookupIterator::NOT_FOUND:
612 : // Accepting missing properties as undefined does not cause any
613 : // observable difference from JavaScript semantics, we are lenient.
614 72 : result = isolate_->factory()->undefined_value();
615 72 : break;
616 : case LookupIterator::DATA:
617 4689 : result = it.GetDataValue();
618 4689 : break;
619 : }
620 :
621 4761 : return result;
622 : }
623 :
624 : // Load data segments into the memory.
625 636 : void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
626 : Vector<const uint8_t> wire_bytes =
627 1272 : module_object_->native_module()->wire_bytes();
628 2140 : for (const WasmDataSegment& segment : module_->data_segments) {
629 1548 : uint32_t source_size = segment.source.length();
630 : // Segments of size == 0 are just nops.
631 868 : if (source_size == 0) continue;
632 : // Passive segments are not copied during instantiation.
633 736 : if (!segment.active) continue;
634 680 : uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
635 : DCHECK(IsInBounds(dest_offset, source_size, instance->memory_size()));
636 680 : byte* dest = instance->memory_start() + dest_offset;
637 680 : const byte* src = wire_bytes.start() + segment.source.offset();
638 680 : memcpy(dest, src, source_size);
639 : }
640 636 : }
641 :
642 4374 : void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
643 : TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
644 : reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
645 : global.offset, num, ValueTypes::TypeName(global.type));
646 2187 : switch (global.type) {
647 : case kWasmI32:
648 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
649 1759 : static_cast<int32_t>(num));
650 : break;
651 : case kWasmI64:
652 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
653 24 : static_cast<int64_t>(num));
654 : break;
655 : case kWasmF32:
656 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
657 32 : static_cast<float>(num));
658 : break;
659 : case kWasmF64:
660 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
661 : static_cast<double>(num));
662 : break;
663 : default:
664 0 : UNREACHABLE();
665 : }
666 2187 : }
667 :
668 96 : void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
669 : Handle<WasmGlobalObject> value) {
670 : TRACE("init [globals_start=%p + %u] = ",
671 : reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
672 : global.offset);
673 48 : switch (global.type) {
674 : case kWasmI32: {
675 64 : int32_t num = value->GetI32();
676 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
677 : TRACE("%d", num);
678 : break;
679 : }
680 : case kWasmI64: {
681 16 : int64_t num = value->GetI64();
682 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
683 : TRACE("%" PRId64, num);
684 : break;
685 : }
686 : case kWasmF32: {
687 16 : float num = value->GetF32();
688 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
689 : TRACE("%f", num);
690 : break;
691 : }
692 : case kWasmF64: {
693 0 : double num = value->GetF64();
694 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
695 : TRACE("%lf", num);
696 : break;
697 : }
698 : default:
699 0 : UNREACHABLE();
700 : }
701 : TRACE(", type = %s (from WebAssembly.Global)\n",
702 : ValueTypes::TypeName(global.type));
703 48 : }
704 :
705 48 : void InstanceBuilder::WriteGlobalAnyRef(const WasmGlobal& global,
706 : Handle<Object> value) {
707 96 : tagged_globals_->set(global.offset, *value, UPDATE_WRITE_BARRIER);
708 48 : }
709 :
710 135849 : void InstanceBuilder::SanitizeImports() {
711 : Vector<const uint8_t> wire_bytes =
712 271699 : module_object_->native_module()->wire_bytes();
713 789141 : for (size_t index = 0; index < module_->import_table.size(); ++index) {
714 263047 : const WasmImport& import = module_->import_table[index];
715 :
716 : Handle<String> module_name;
717 : MaybeHandle<String> maybe_module_name =
718 : WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
719 127591 : import.module_name);
720 127592 : if (!maybe_module_name.ToHandle(&module_name)) {
721 : thrower_->LinkError("Could not resolve module name for import %zu",
722 127592 : index);
723 0 : return;
724 : }
725 :
726 : Handle<String> import_name;
727 : MaybeHandle<String> maybe_import_name =
728 : WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
729 127592 : import.field_name);
730 127592 : if (!maybe_import_name.ToHandle(&import_name)) {
731 : thrower_->LinkError("Could not resolve import name for import %zu",
732 0 : index);
733 0 : return;
734 : }
735 :
736 : int int_index = static_cast<int>(index);
737 : MaybeHandle<Object> result =
738 127592 : module_->origin == kAsmJsOrigin
739 4779 : ? LookupImportAsm(int_index, import_name)
740 132371 : : LookupImport(int_index, module_name, import_name);
741 255184 : if (thrower_->error()) {
742 394 : thrower_->LinkError("Could not find value for import %zu", index);
743 394 : return;
744 : }
745 : Handle<Object> value = result.ToHandleChecked();
746 254395 : sanitized_imports_.push_back({module_name, import_name, value});
747 : }
748 : }
749 :
750 134217 : MaybeHandle<JSArrayBuffer> InstanceBuilder::FindImportedMemoryBuffer() const {
751 : DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
752 505476 : for (size_t index = 0; index < module_->import_table.size(); index++) {
753 252738 : const WasmImport& import = module_->import_table[index];
754 :
755 123134 : if (import.kind == kExternalMemory) {
756 9226 : const auto& value = sanitized_imports_[index].value;
757 9226 : if (!value->IsWasmMemoryObject()) {
758 416 : return {};
759 : }
760 4197 : auto memory = Handle<WasmMemoryObject>::cast(value);
761 12591 : Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
762 4197 : return buffer;
763 : }
764 : }
765 129604 : return {};
766 : }
767 :
768 117985 : bool InstanceBuilder::ProcessImportedFunction(
769 : Handle<WasmInstanceObject> instance, int import_index, int func_index,
770 : Handle<String> module_name, Handle<String> import_name,
771 : Handle<Object> value) {
772 : // Function imports must be callable.
773 235970 : if (!value->IsCallable()) {
774 : ReportLinkError("function import requires a callable", import_index,
775 400 : module_name, import_name);
776 400 : return false;
777 : }
778 117585 : auto js_receiver = Handle<JSReceiver>::cast(value);
779 235170 : FunctionSig* expected_sig = module_->functions[func_index].sig;
780 : auto kind = compiler::GetWasmImportCallKind(js_receiver, expected_sig,
781 117585 : enabled_.bigint);
782 117585 : switch (kind) {
783 : case compiler::WasmImportCallKind::kLinkError:
784 : ReportLinkError("imported function does not match the expected type",
785 160 : import_index, module_name, import_name);
786 160 : return false;
787 : case compiler::WasmImportCallKind::kWasmToWasm: {
788 : // The imported function is a WASM function from another instance.
789 107128 : auto imported_function = Handle<WasmExportedFunction>::cast(value);
790 : Handle<WasmInstanceObject> imported_instance(
791 321384 : imported_function->instance(), isolate_);
792 : // The import reference is the instance object itself.
793 107128 : Address imported_target = imported_function->GetWasmCallTarget();
794 : ImportedFunctionEntry entry(instance, func_index);
795 107128 : entry.SetWasmToWasm(*imported_instance, imported_target);
796 : break;
797 : }
798 : default: {
799 : // The imported function is a callable.
800 10297 : NativeModule* native_module = instance->module_object()->native_module();
801 10297 : WasmCode* wasm_code = native_module->import_wrapper_cache()->GetOrCompile(
802 20594 : isolate_->wasm_engine(), isolate_->counters(), kind, expected_sig);
803 : ImportedFunctionEntry entry(instance, func_index);
804 10297 : if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
805 : // Wasm to JS wrappers are treated specially in the import table.
806 10073 : entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
807 : } else {
808 : // Wasm math intrinsics are compiled as regular Wasm functions.
809 : DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic &&
810 : kind <= compiler::WasmImportCallKind::kLastMathIntrinsic);
811 224 : entry.SetWasmToWasm(*instance, wasm_code->instruction_start());
812 : }
813 : break;
814 : }
815 : }
816 : return true;
817 : }
818 :
819 1480 : bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
820 : int import_index, int table_index,
821 : Handle<String> module_name,
822 : Handle<String> import_name,
823 : Handle<Object> value) {
824 2960 : if (!value->IsWasmTableObject()) {
825 : ReportLinkError("table import requires a WebAssembly.Table", import_index,
826 344 : module_name, import_name);
827 344 : return false;
828 : }
829 1136 : const WasmTable& table = module_->tables[table_index];
830 2168 : TableInstance& table_instance = table_instances_[table_index];
831 1136 : table_instance.table_object = Handle<WasmTableObject>::cast(value);
832 1136 : instance->set_table_object(*table_instance.table_object);
833 : table_instance.js_functions =
834 3408 : Handle<FixedArray>(table_instance.table_object->elements(), isolate_);
835 :
836 : int imported_table_size = table_instance.js_functions->length();
837 1136 : if (imported_table_size < static_cast<int>(table.initial_size)) {
838 : thrower_->LinkError("table import %d is smaller than initial %d, got %u",
839 40 : import_index, table.initial_size, imported_table_size);
840 40 : return false;
841 : }
842 :
843 1096 : if (table.has_maximum_size) {
844 : int64_t imported_maximum_size =
845 808 : table_instance.table_object->maximum_length()->Number();
846 808 : if (imported_maximum_size < 0) {
847 : thrower_->LinkError("table import %d has no maximum length, expected %d",
848 0 : import_index, table.maximum_size);
849 0 : return false;
850 : }
851 808 : if (imported_maximum_size > table.maximum_size) {
852 : thrower_->LinkError("table import %d has a larger maximum size %" PRIx64
853 : " than the module's declared maximum %u",
854 : import_index, imported_maximum_size,
855 64 : table.maximum_size);
856 64 : return false;
857 : }
858 : }
859 :
860 : // Allocate a new dispatch table.
861 1032 : if (!instance->has_indirect_function_table()) {
862 : WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
863 1032 : instance, imported_table_size);
864 1032 : table_instances_[table_index].table_size = imported_table_size;
865 : }
866 : // Initialize the dispatch table with the (foreign) JS functions
867 : // that are already in the table.
868 2109144 : for (int i = 0; i < imported_table_size; ++i) {
869 2109144 : Handle<Object> val(table_instance.js_functions->get(i), isolate_);
870 : // TODO(mtrofin): this is the same logic as WasmTableObject::Set:
871 : // insert in the local table a wrapper from the other module, and add
872 : // a reference to the owning instance of the other module.
873 6324632 : if (!val->IsJSFunction()) continue;
874 2800 : if (!WasmExportedFunction::IsWasmExportedFunction(*val)) {
875 : thrower_->LinkError("table import %d[%d] is not a wasm function",
876 0 : import_index, i);
877 0 : return false;
878 : }
879 2800 : auto target_func = Handle<WasmExportedFunction>::cast(val);
880 : Handle<WasmInstanceObject> target_instance =
881 8400 : handle(target_func->instance(), isolate_);
882 : // Look up the signature's canonical id. If there is no canonical
883 : // id, then the signature does not appear at all in this module,
884 : // so putting {-1} in the table will cause checks to always fail.
885 2800 : FunctionSig* sig = target_func->sig();
886 : IndirectFunctionTableEntry(instance, i)
887 : .Set(module_->signature_map.Find(*sig), target_instance,
888 5600 : target_func->function_index());
889 : }
890 : return true;
891 : }
892 :
893 4604 : bool InstanceBuilder::ProcessImportedMemory(Handle<WasmInstanceObject> instance,
894 : int import_index,
895 : Handle<String> module_name,
896 : Handle<String> import_name,
897 : Handle<Object> value) {
898 : // Validation should have failed if more than one memory object was
899 : // provided.
900 : DCHECK(!instance->has_memory_object());
901 9210 : if (!value->IsWasmMemoryObject()) {
902 : ReportLinkError("memory import must be a WebAssembly.Memory object",
903 408 : import_index, module_name, import_name);
904 408 : return false;
905 : }
906 4197 : auto memory = Handle<WasmMemoryObject>::cast(value);
907 4197 : instance->set_memory_object(*memory);
908 12591 : Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
909 : // memory_ should have already been assigned in Build().
910 : DCHECK_EQ(*memory_.ToHandleChecked(), *buffer);
911 : uint32_t imported_cur_pages =
912 4197 : static_cast<uint32_t>(buffer->byte_length() / kWasmPageSize);
913 4197 : if (imported_cur_pages < module_->initial_pages) {
914 : thrower_->LinkError("memory import %d is smaller than initial %u, got %u",
915 : import_index, module_->initial_pages,
916 24 : imported_cur_pages);
917 24 : return false;
918 : }
919 : int32_t imported_maximum_pages = memory->maximum_pages();
920 4173 : if (module_->has_maximum_pages) {
921 1222 : if (imported_maximum_pages < 0) {
922 : thrower_->LinkError(
923 : "memory import %d has no maximum limit, expected at most %u",
924 8 : import_index, imported_maximum_pages);
925 8 : return false;
926 : }
927 1214 : if (static_cast<uint32_t>(imported_maximum_pages) >
928 : module_->maximum_pages) {
929 : thrower_->LinkError(
930 : "memory import %d has a larger maximum size %u than the "
931 : "module's declared maximum %u",
932 40 : import_index, imported_maximum_pages, module_->maximum_pages);
933 40 : return false;
934 : }
935 : }
936 8250 : if (module_->has_shared_memory != buffer->is_shared()) {
937 : thrower_->LinkError(
938 : "mismatch in shared state of memory, declared = %d, imported = %d",
939 16 : module_->has_shared_memory, buffer->is_shared());
940 16 : return false;
941 : }
942 :
943 : return true;
944 : }
945 :
946 296 : bool InstanceBuilder::ProcessImportedWasmGlobalObject(
947 : Handle<WasmInstanceObject> instance, int import_index,
948 : Handle<String> module_name, Handle<String> import_name,
949 : const WasmGlobal& global, Handle<WasmGlobalObject> global_object) {
950 296 : if (global_object->type() != global.type) {
951 : ReportLinkError("imported global does not match the expected type",
952 8 : import_index, module_name, import_name);
953 8 : return false;
954 : }
955 288 : if (global_object->is_mutable() != global.mutability) {
956 : ReportLinkError("imported global does not match the expected mutability",
957 32 : import_index, module_name, import_name);
958 32 : return false;
959 : }
960 256 : if (global.mutability) {
961 : DCHECK_LT(global.index, module_->num_imported_mutable_globals);
962 : Handle<Object> buffer;
963 : Address address_or_offset;
964 208 : if (global.type == kWasmAnyRef) {
965 : static_assert(sizeof(global_object->offset()) <= sizeof(Address),
966 : "The offset into the globals buffer does not fit into "
967 : "the imported_mutable_globals array");
968 216 : buffer = handle(global_object->tagged_buffer(), isolate_);
969 : // For anyref globals we use a relative offset, not an absolute address.
970 72 : address_or_offset = static_cast<Address>(global_object->offset());
971 : } else {
972 408 : buffer = handle(global_object->untagged_buffer(), isolate_);
973 : // It is safe in this case to store the raw pointer to the buffer
974 : // since the backing store of the JSArrayBuffer will not be
975 : // relocated.
976 : address_or_offset = reinterpret_cast<Address>(raw_buffer_ptr(
977 272 : Handle<JSArrayBuffer>::cast(buffer), global_object->offset()));
978 : }
979 416 : instance->imported_mutable_globals_buffers()->set(global.index, *buffer);
980 208 : instance->imported_mutable_globals()[global.index] = address_or_offset;
981 : return true;
982 : }
983 :
984 48 : WriteGlobalValue(global, global_object);
985 48 : return true;
986 : }
987 :
988 2920 : bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
989 : int import_index, int global_index,
990 : Handle<String> module_name,
991 : Handle<String> import_name,
992 : Handle<Object> value) {
993 : // Immutable global imports are converted to numbers and written into
994 : // the {untagged_globals_} array buffer.
995 : //
996 : // Mutable global imports instead have their backing array buffers
997 : // referenced by this instance, and store the address of the imported
998 : // global in the {imported_mutable_globals_} array.
999 2920 : const WasmGlobal& global = module_->globals[global_index];
1000 :
1001 : // The mutable-global proposal allows importing i64 values, but only if
1002 : // they are passed as a WebAssembly.Global object.
1003 : //
1004 : // However, the bigint proposal allows importing constant i64 values,
1005 : // as non WebAssembly.Global object.
1006 8760 : if (global.type == kWasmI64 && !enabled_.bigint &&
1007 3016 : !value->IsWasmGlobalObject()) {
1008 : ReportLinkError("global import cannot have type i64", import_index,
1009 32 : module_name, import_name);
1010 32 : return false;
1011 : }
1012 2888 : if (module_->origin == kAsmJsOrigin) {
1013 : // Accepting {JSFunction} on top of just primitive values here is a
1014 : // workaround to support legacy asm.js code with broken binding. Note
1015 : // that using {NaN} (or Smi::kZero) here is what using the observable
1016 : // conversion via {ToPrimitive} would produce as well.
1017 : // TODO(mstarzinger): Still observable if Function.prototype.valueOf
1018 : // or friends are patched, we might need to check for that as well.
1019 2416 : if (value->IsJSFunction()) value = isolate_->factory()->nan_value();
1020 4814 : if (value->IsPrimitive() && !value->IsSymbol()) {
1021 1195 : if (global.type == kWasmI32) {
1022 1710 : value = Object::ToInt32(isolate_, value).ToHandleChecked();
1023 : } else {
1024 680 : value = Object::ToNumber(isolate_, value).ToHandleChecked();
1025 : }
1026 : }
1027 : }
1028 :
1029 5776 : if (value->IsWasmGlobalObject()) {
1030 296 : auto global_object = Handle<WasmGlobalObject>::cast(value);
1031 : return ProcessImportedWasmGlobalObject(instance, import_index, module_name,
1032 296 : import_name, global, global_object);
1033 : }
1034 :
1035 2592 : if (global.mutability) {
1036 : ReportLinkError(
1037 : "imported mutable global must be a WebAssembly.Global object",
1038 16 : import_index, module_name, import_name);
1039 16 : return false;
1040 : }
1041 :
1042 2576 : if (global.type == ValueType::kWasmAnyRef) {
1043 48 : WriteGlobalAnyRef(global, value);
1044 48 : return true;
1045 : }
1046 :
1047 5056 : if (value->IsNumber()) {
1048 2163 : WriteGlobalValue(global, value->Number());
1049 2163 : return true;
1050 : }
1051 :
1052 365 : if (enabled_.bigint && global.type == kWasmI64) {
1053 : Handle<BigInt> bigint;
1054 :
1055 48 : if (!BigInt::FromObject(isolate_, value).ToHandle(&bigint)) {
1056 : return false;
1057 : }
1058 24 : WriteGlobalValue(global, bigint->AsInt64());
1059 24 : return true;
1060 : }
1061 :
1062 : ReportLinkError("global import must be a number or WebAssembly.Global object",
1063 341 : import_index, module_name, import_name);
1064 341 : return false;
1065 : }
1066 :
1067 : // Process the imports, including functions, tables, globals, and memory, in
1068 : // order, loading them from the {ffi_} object. Returns the number of imported
1069 : // functions.
1070 135440 : int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
1071 : int num_imported_functions = 0;
1072 : int num_imported_tables = 0;
1073 :
1074 : DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
1075 397957 : int num_imports = static_cast<int>(module_->import_table.size());
1076 521090 : for (int index = 0; index < num_imports; ++index) {
1077 127077 : const WasmImport& import = module_->import_table[index];
1078 :
1079 254154 : Handle<String> module_name = sanitized_imports_[index].module_name;
1080 127077 : Handle<String> import_name = sanitized_imports_[index].import_name;
1081 127077 : Handle<Object> value = sanitized_imports_[index].value;
1082 :
1083 127077 : switch (import.kind) {
1084 : case kExternalFunction: {
1085 117985 : uint32_t func_index = import.index;
1086 : DCHECK_EQ(num_imported_functions, func_index);
1087 117985 : if (!ProcessImportedFunction(instance, index, func_index, module_name,
1088 117985 : import_name, value)) {
1089 1973 : return -1;
1090 : }
1091 117425 : num_imported_functions++;
1092 117425 : break;
1093 : }
1094 : case kExternalTable: {
1095 1480 : uint32_t table_index = import.index;
1096 : DCHECK_EQ(table_index, num_imported_tables);
1097 1480 : if (!ProcessImportedTable(instance, index, table_index, module_name,
1098 1480 : import_name, value)) {
1099 : return -1;
1100 : }
1101 : num_imported_tables++;
1102 : break;
1103 : }
1104 : case kExternalMemory: {
1105 4605 : if (!ProcessImportedMemory(instance, index, module_name, import_name,
1106 4604 : value)) {
1107 : return -1;
1108 : }
1109 : break;
1110 : }
1111 : case kExternalGlobal: {
1112 2920 : if (!ProcessImportedGlobal(instance, index, import.index, module_name,
1113 2920 : import_name, value)) {
1114 : return -1;
1115 : }
1116 : break;
1117 : }
1118 : case kExternalException: {
1119 176 : if (!value->IsWasmExceptionObject()) {
1120 : ReportLinkError("exception import requires a WebAssembly.Exception",
1121 32 : index, module_name, import_name);
1122 32 : return -1;
1123 : }
1124 : Handle<WasmExceptionObject> imported_exception =
1125 56 : Handle<WasmExceptionObject>::cast(value);
1126 56 : if (!imported_exception->IsSignatureEqual(
1127 168 : module_->exceptions[import.index].sig)) {
1128 : ReportLinkError("imported exception does not match the expected type",
1129 8 : index, module_name, import_name);
1130 8 : return -1;
1131 : }
1132 48 : Object exception_tag = imported_exception->exception_tag();
1133 : DCHECK(instance->exceptions_table()->get(import.index)->IsUndefined());
1134 96 : instance->exceptions_table()->set(import.index, exception_tag);
1135 96 : exception_wrappers_[import.index] = imported_exception;
1136 48 : break;
1137 : }
1138 : default:
1139 0 : UNREACHABLE();
1140 : break;
1141 : }
1142 : }
1143 : return num_imported_functions;
1144 : }
1145 :
1146 : template <typename T>
1147 : T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
1148 16172 : return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
1149 : }
1150 :
1151 : // Process initialization of globals.
1152 133468 : void InstanceBuilder::InitGlobals() {
1153 275446 : for (auto global : module_->globals) {
1154 8510 : if (global.mutability && global.imported) {
1155 : continue;
1156 : }
1157 :
1158 8302 : switch (global.init.kind) {
1159 : case WasmInitExpr::kI32Const:
1160 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
1161 4729 : global.init.val.i32_const);
1162 : break;
1163 : case WasmInitExpr::kI64Const:
1164 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
1165 88 : global.init.val.i64_const);
1166 : break;
1167 : case WasmInitExpr::kF32Const:
1168 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
1169 180 : global.init.val.f32_const);
1170 : break;
1171 : case WasmInitExpr::kF64Const:
1172 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
1173 854 : global.init.val.f64_const);
1174 : break;
1175 : case WasmInitExpr::kAnyRefConst:
1176 : DCHECK(enabled_.anyref);
1177 112 : if (global.imported) break; // We already initialized imported globals.
1178 :
1179 : tagged_globals_->set(global.offset,
1180 112 : ReadOnlyRoots(isolate_).null_value(),
1181 224 : SKIP_WRITE_BARRIER);
1182 112 : break;
1183 : case WasmInitExpr::kGlobalIndex: {
1184 56 : if (global.type == ValueType::kWasmAnyRef) {
1185 : DCHECK(enabled_.anyref);
1186 : int other_offset =
1187 56 : module_->globals[global.init.val.global_index].offset;
1188 :
1189 : tagged_globals_->set(global.offset,
1190 : tagged_globals_->get(other_offset),
1191 0 : SKIP_WRITE_BARRIER);
1192 : }
1193 : // Initialize with another global.
1194 : uint32_t new_offset = global.offset;
1195 : uint32_t old_offset =
1196 112 : module_->globals[global.init.val.global_index].offset;
1197 : TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset);
1198 56 : size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
1199 : ? sizeof(double)
1200 56 : : sizeof(int32_t);
1201 168 : memcpy(raw_buffer_ptr(untagged_globals_, new_offset),
1202 168 : raw_buffer_ptr(untagged_globals_, old_offset), size);
1203 56 : break;
1204 : }
1205 : case WasmInitExpr::kNone:
1206 : // Happens with imported globals.
1207 : break;
1208 : default:
1209 0 : UNREACHABLE();
1210 : break;
1211 : }
1212 : }
1213 133468 : }
1214 :
1215 : // Allocate memory for a module instance as a new JSArrayBuffer.
1216 129970 : Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) {
1217 129970 : if (num_pages > max_mem_pages()) {
1218 16 : thrower_->RangeError("Out of memory: wasm memory too large");
1219 : return Handle<JSArrayBuffer>::null();
1220 : }
1221 129954 : const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
1222 : SharedFlag shared_flag =
1223 129954 : is_shared_memory ? SharedFlag::kShared : SharedFlag::kNotShared;
1224 : Handle<JSArrayBuffer> mem_buffer;
1225 259908 : if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag)
1226 259908 : .ToHandle(&mem_buffer)) {
1227 0 : thrower_->RangeError("Out of memory: wasm memory");
1228 : }
1229 129954 : return mem_buffer;
1230 : }
1231 :
1232 133203 : bool InstanceBuilder::NeedsWrappers() const {
1233 133203 : if (module_->num_exported_functions > 0) return true;
1234 8768 : for (auto& table_instance : table_instances_) {
1235 720 : if (!table_instance.js_functions.is_null()) return true;
1236 : }
1237 7776 : for (auto& table : module_->tables) {
1238 352 : if (table.exported) return true;
1239 : }
1240 : return false;
1241 : }
1242 :
1243 : // Process the exports, creating wrappers for functions, tables, memories,
1244 : // globals, and exceptions.
1245 133203 : void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
1246 : Handle<FixedArray> export_wrappers(module_object_->export_wrappers(),
1247 399610 : isolate_);
1248 133203 : if (NeedsWrappers()) {
1249 : // If an imported WebAssembly function gets exported, the exported function
1250 : // has to be identical to to imported function. Therefore we cache all
1251 : // imported WebAssembly functions in the instance.
1252 507608 : for (int index = 0, end = static_cast<int>(module_->import_table.size());
1253 : index < end; ++index) {
1254 124185 : const WasmImport& import = module_->import_table[index];
1255 124185 : if (import.kind == kExternalFunction) {
1256 234290 : Handle<Object> value = sanitized_imports_[index].value;
1257 117145 : if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
1258 : WasmInstanceObject::SetWasmExportedFunction(
1259 : isolate_, instance, import.index,
1260 107024 : Handle<WasmExportedFunction>::cast(value));
1261 : }
1262 : }
1263 : }
1264 : }
1265 :
1266 : Handle<JSObject> exports_object;
1267 : bool is_asm_js = false;
1268 133204 : switch (module_->origin) {
1269 : case kWasmOrigin: {
1270 : // Create the "exports" object.
1271 128409 : exports_object = isolate_->factory()->NewJSObjectWithNullProto();
1272 128408 : break;
1273 : }
1274 : case kAsmJsOrigin: {
1275 : Handle<JSFunction> object_function = Handle<JSFunction>(
1276 14385 : isolate_->native_context()->object_function(), isolate_);
1277 4795 : exports_object = isolate_->factory()->NewJSObject(object_function);
1278 : is_asm_js = true;
1279 : break;
1280 : }
1281 : default:
1282 0 : UNREACHABLE();
1283 : }
1284 133202 : instance->set_exports_object(*exports_object);
1285 :
1286 : Handle<String> single_function_name =
1287 133204 : isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName);
1288 :
1289 : PropertyDescriptor desc;
1290 : desc.set_writable(is_asm_js);
1291 : desc.set_enumerable(true);
1292 : desc.set_configurable(is_asm_js);
1293 :
1294 : // Process each export in the export table.
1295 : int export_index = 0; // Index into {export_wrappers}.
1296 494700 : for (const WasmExport& exp : module_->export_table) {
1297 : Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1298 228292 : isolate_, module_object_, exp.name)
1299 456584 : .ToHandleChecked();
1300 : Handle<JSObject> export_to;
1301 236482 : if (is_asm_js && exp.kind == kExternalFunction &&
1302 8190 : String::Equals(isolate_, name, single_function_name)) {
1303 : export_to = instance;
1304 : } else {
1305 : export_to = exports_object;
1306 : }
1307 :
1308 228292 : switch (exp.kind) {
1309 : case kExternalFunction: {
1310 : // Wrap and export the code as a JSFunction.
1311 : // TODO(wasm): reduce duplication with LoadElemSegment() further below
1312 225488 : const WasmFunction& function = module_->functions[exp.index];
1313 : MaybeHandle<WasmExportedFunction> wasm_exported_function =
1314 : WasmInstanceObject::GetWasmExportedFunction(isolate_, instance,
1315 225488 : exp.index);
1316 225488 : if (wasm_exported_function.is_null()) {
1317 : // Wrap the exported code as a JSFunction.
1318 : Handle<Code> export_code =
1319 450423 : export_wrappers->GetValueChecked<Code>(isolate_, export_index);
1320 225212 : MaybeHandle<String> func_name;
1321 225212 : if (is_asm_js) {
1322 : // For modules arising from asm.js, honor the names section.
1323 : WireBytesRef func_name_ref = module_->LookupFunctionName(
1324 : ModuleWireBytes(module_object_->native_module()->wire_bytes()),
1325 24510 : function.func_index);
1326 : func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1327 8170 : isolate_, module_object_, func_name_ref)
1328 16340 : .ToHandleChecked();
1329 : }
1330 : wasm_exported_function = WasmExportedFunction::New(
1331 : isolate_, instance, func_name, function.func_index,
1332 225212 : static_cast<int>(function.sig->parameter_count()), export_code);
1333 : WasmInstanceObject::SetWasmExportedFunction(
1334 : isolate_, instance, exp.index,
1335 225211 : wasm_exported_function.ToHandleChecked());
1336 : }
1337 : desc.set_value(wasm_exported_function.ToHandleChecked());
1338 225488 : export_index++;
1339 : break;
1340 : }
1341 : case kExternalTable: {
1342 : // Export a table as a WebAssembly.Table object.
1343 472 : TableInstance& table_instance = table_instances_[exp.index];
1344 472 : const WasmTable& table = module_->tables[exp.index];
1345 472 : if (table_instance.table_object.is_null()) {
1346 : uint32_t maximum = table.has_maximum_size ? table.maximum_size
1347 424 : : FLAG_wasm_max_table_size;
1348 : table_instance.table_object =
1349 : WasmTableObject::New(isolate_, table.initial_size, maximum,
1350 424 : &table_instance.js_functions);
1351 : }
1352 472 : instance->set_table_object(*table_instance.table_object);
1353 : desc.set_value(table_instance.table_object);
1354 472 : break;
1355 : }
1356 : case kExternalMemory: {
1357 : // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject
1358 : // should already be available if the module has memory, since we always
1359 : // create or import it when building an WasmInstanceObject.
1360 : DCHECK(instance->has_memory_object());
1361 : desc.set_value(
1362 4932 : Handle<WasmMemoryObject>(instance->memory_object(), isolate_));
1363 1644 : break;
1364 : }
1365 : case kExternalGlobal: {
1366 576 : const WasmGlobal& global = module_->globals[exp.index];
1367 : Handle<JSArrayBuffer> untagged_buffer;
1368 : Handle<FixedArray> tagged_buffer;
1369 : uint32_t offset;
1370 :
1371 576 : if (global.mutability && global.imported) {
1372 : Handle<FixedArray> buffers_array(
1373 168 : instance->imported_mutable_globals_buffers(), isolate_);
1374 56 : if (global.type == kWasmAnyRef) {
1375 : tagged_buffer = buffers_array->GetValueChecked<FixedArray>(
1376 48 : isolate_, global.index);
1377 : // For anyref globals we store the relative offset in the
1378 : // imported_mutable_globals array instead of an absolute address.
1379 24 : Address addr = instance->imported_mutable_globals()[global.index];
1380 : DCHECK_LE(addr, static_cast<Address>(
1381 : std::numeric_limits<uint32_t>::max()));
1382 24 : offset = static_cast<uint32_t>(addr);
1383 : } else {
1384 : untagged_buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
1385 64 : isolate_, global.index);
1386 : Address global_addr =
1387 32 : instance->imported_mutable_globals()[global.index];
1388 :
1389 : size_t buffer_size = untagged_buffer->byte_length();
1390 : Address backing_store =
1391 32 : reinterpret_cast<Address>(untagged_buffer->backing_store());
1392 32 : CHECK(global_addr >= backing_store &&
1393 : global_addr < backing_store + buffer_size);
1394 32 : offset = static_cast<uint32_t>(global_addr - backing_store);
1395 : }
1396 : } else {
1397 520 : if (global.type == kWasmAnyRef) {
1398 120 : tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_);
1399 : } else {
1400 : untagged_buffer =
1401 1440 : handle(instance->untagged_globals_buffer(), isolate_);
1402 : }
1403 520 : offset = global.offset;
1404 : }
1405 :
1406 : // Since the global's array untagged_buffer is always provided,
1407 : // allocation should never fail.
1408 : Handle<WasmGlobalObject> global_obj =
1409 : WasmGlobalObject::New(isolate_, untagged_buffer, tagged_buffer,
1410 1728 : global.type, offset, global.mutability)
1411 1152 : .ToHandleChecked();
1412 : desc.set_value(global_obj);
1413 : break;
1414 : }
1415 : case kExternalException: {
1416 112 : const WasmException& exception = module_->exceptions[exp.index];
1417 320 : Handle<WasmExceptionObject> wrapper = exception_wrappers_[exp.index];
1418 112 : if (wrapper.is_null()) {
1419 : Handle<HeapObject> exception_tag(
1420 192 : HeapObject::cast(instance->exceptions_table()->get(exp.index)),
1421 288 : isolate_);
1422 : wrapper =
1423 96 : WasmExceptionObject::New(isolate_, exception.sig, exception_tag);
1424 192 : exception_wrappers_[exp.index] = wrapper;
1425 : }
1426 : desc.set_value(wrapper);
1427 : break;
1428 : }
1429 : default:
1430 0 : UNREACHABLE();
1431 : break;
1432 : }
1433 :
1434 : v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
1435 228292 : isolate_, export_to, name, &desc, Just(kThrowOnError));
1436 228290 : if (!status.IsJust()) {
1437 : DisallowHeapAllocation no_gc;
1438 0 : TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>(no_gc));
1439 : thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
1440 0 : trunc_name.start());
1441 133203 : return;
1442 : }
1443 : }
1444 : DCHECK_EQ(export_index, export_wrappers->length());
1445 :
1446 133203 : if (module_->origin == kWasmOrigin) {
1447 : v8::Maybe<bool> success =
1448 128407 : JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
1449 : DCHECK(success.FromMaybe(false));
1450 : USE(success);
1451 : }
1452 : }
1453 :
1454 2281 : void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
1455 4562 : size_t table_count = module_->tables.size();
1456 4562 : for (size_t index = 0; index < table_count; ++index) {
1457 2281 : const WasmTable& table = module_->tables[index];
1458 :
1459 3546 : if (!instance->has_indirect_function_table() &&
1460 1265 : table.type == kWasmAnyFunc) {
1461 : WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1462 1265 : instance, table.initial_size);
1463 2530 : table_instances_[index].table_size = table.initial_size;
1464 : }
1465 : }
1466 2281 : }
1467 :
1468 1993 : bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
1469 : const TableInstance& table_instance,
1470 : JSToWasmWrapperCache* js_to_wasm_cache,
1471 : const WasmElemSegment& elem_segment, uint32_t dst,
1472 : uint32_t src, size_t count) {
1473 3986 : if (!IsInBounds(dst, count, table_instance.table_size)) return false;
1474 15100 : if (!IsInBounds(src, count, elem_segment.entries.size())) return false;
1475 :
1476 1865 : const WasmModule* module = instance->module();
1477 11202 : for (uint32_t i = 0; i < count; ++i) {
1478 18674 : uint32_t func_index = elem_segment.entries[src + i];
1479 9337 : const WasmFunction* function = &module->functions[func_index];
1480 9337 : int entry_index = static_cast<int>(dst + i);
1481 :
1482 : // Update the local dispatch table first.
1483 18674 : uint32_t sig_id = module->signature_ids[function->sig_index];
1484 : IndirectFunctionTableEntry(instance, entry_index)
1485 18674 : .Set(sig_id, instance, func_index);
1486 :
1487 9337 : if (!table_instance.table_object.is_null()) {
1488 : // Update the table object's other dispatch tables.
1489 : MaybeHandle<WasmExportedFunction> wasm_exported_function =
1490 : WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
1491 1832 : func_index);
1492 1832 : if (wasm_exported_function.is_null()) {
1493 : // No JSFunction entry yet exists for this function. Create one.
1494 : // TODO(titzer): We compile JS->wasm wrappers for functions are
1495 : // not exported but are in an exported table. This should be done
1496 : // at module compile time and cached instead.
1497 :
1498 : Handle<Code> wrapper_code =
1499 : js_to_wasm_cache->GetOrCompileJSToWasmWrapper(
1500 2784 : isolate, function->sig, function->imported);
1501 1392 : MaybeHandle<String> func_name;
1502 1392 : if (module->origin == kAsmJsOrigin) {
1503 : // For modules arising from asm.js, honor the names section.
1504 : auto module_object =
1505 0 : Handle<WasmModuleObject>(instance->module_object(), isolate);
1506 : WireBytesRef func_name_ref = module->LookupFunctionName(
1507 : ModuleWireBytes(module_object->native_module()->wire_bytes()),
1508 0 : func_index);
1509 : func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1510 : isolate, module_object, func_name_ref)
1511 0 : .ToHandleChecked();
1512 : }
1513 : wasm_exported_function = WasmExportedFunction::New(
1514 : isolate, instance, func_name, func_index,
1515 2784 : static_cast<int>(function->sig->parameter_count()), wrapper_code);
1516 : WasmInstanceObject::SetWasmExportedFunction(
1517 : isolate, instance, func_index,
1518 1392 : wasm_exported_function.ToHandleChecked());
1519 : }
1520 : table_instance.js_functions->set(
1521 3664 : entry_index, *wasm_exported_function.ToHandleChecked());
1522 : // UpdateDispatchTables() updates all other dispatch tables, since
1523 : // we have not yet added the dispatch table we are currently building.
1524 : WasmTableObject::UpdateDispatchTables(
1525 : isolate, table_instance.table_object, entry_index, function->sig,
1526 1832 : instance, func_index);
1527 : }
1528 : }
1529 : return true;
1530 : }
1531 :
1532 2153 : void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
1533 6155 : for (auto& elem_segment : module_->elem_segments) {
1534 : // Passive segments are not copied during instantiation.
1535 1849 : if (!elem_segment.active) continue;
1536 :
1537 1809 : uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
1538 : uint32_t src = 0;
1539 1809 : size_t count = elem_segment.entries.size();
1540 :
1541 : bool success = LoadElemSegmentImpl(
1542 3962 : isolate_, instance, table_instances_[elem_segment.table_index],
1543 3618 : &js_to_wasm_cache_, elem_segment, dst, src, count);
1544 1809 : CHECK(success);
1545 : }
1546 :
1547 4306 : int table_count = static_cast<int>(module_->tables.size());
1548 4306 : for (int index = 0; index < table_count; ++index) {
1549 2153 : TableInstance& table_instance = table_instances_[index];
1550 :
1551 : // Add the new dispatch table at the end to avoid redundant lookups.
1552 2153 : if (!table_instance.table_object.is_null()) {
1553 : WasmTableObject::AddDispatchTable(isolate_, table_instance.table_object,
1554 1392 : instance, index);
1555 : }
1556 : }
1557 2153 : }
1558 :
1559 336 : void InstanceBuilder::InitializeExceptions(
1560 : Handle<WasmInstanceObject> instance) {
1561 1008 : Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate_);
1562 1520 : for (int index = 0; index < exceptions_table->length(); ++index) {
1563 1320 : if (!exceptions_table->get(index)->IsUndefined(isolate_)) continue;
1564 : Handle<WasmExceptionTag> exception_tag =
1565 376 : WasmExceptionTag::New(isolate_, index);
1566 752 : exceptions_table->set(index, *exception_tag);
1567 : }
1568 336 : }
1569 :
1570 184 : bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
1571 : uint32_t table_index, uint32_t segment_index, uint32_t dst,
1572 : uint32_t src, uint32_t count) {
1573 : JSToWasmWrapperCache js_to_wasm_cache;
1574 :
1575 : Handle<WasmTableObject> table_object;
1576 : Handle<FixedArray> js_functions;
1577 368 : if (instance->has_table_object()) {
1578 368 : table_object = Handle<WasmTableObject>(instance->table_object(), isolate);
1579 368 : js_functions = Handle<FixedArray>(table_object->elements(), isolate);
1580 : }
1581 :
1582 : TableInstance table_instance = {table_object, js_functions,
1583 368 : instance->indirect_function_table_size()};
1584 :
1585 552 : auto& elem_segment = instance->module()->elem_segments[segment_index];
1586 : return LoadElemSegmentImpl(isolate, instance, table_instance,
1587 368 : &js_to_wasm_cache, elem_segment, dst, src, count);
1588 : }
1589 :
1590 : } // namespace wasm
1591 : } // namespace internal
1592 178779 : } // namespace v8
1593 :
1594 : #undef TRACE
|