Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2015 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmModule.h"
20 :
21 : #include "jsnspr.h"
22 :
23 : #include "jit/JitOptions.h"
24 : #include "wasm/WasmCompile.h"
25 : #include "wasm/WasmInstance.h"
26 : #include "wasm/WasmJS.h"
27 : #include "wasm/WasmSerialize.h"
28 :
29 : #include "jsatominlines.h"
30 :
31 : #include "vm/ArrayBufferObject-inl.h"
32 : #include "vm/Debugger-inl.h"
33 :
34 : using namespace js;
35 : using namespace js::jit;
36 : using namespace js::wasm;
37 :
38 : using mozilla::IsNaN;
39 :
40 : #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
41 : // On MIPS, CodeLabels are instruction immediates so InternalLinks only
42 : // patch instruction immediates.
43 : LinkDataTier::InternalLink::InternalLink(Kind kind)
44 : {
45 : MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
46 : }
47 :
48 : bool
49 : LinkDataTier::InternalLink::isRawPointerPatch()
50 : {
51 : return false;
52 : }
53 : #else
54 : // On the rest, CodeLabels are raw pointers so InternalLinks only patch
55 : // raw pointers.
56 0 : LinkDataTier::InternalLink::InternalLink(Kind kind)
57 : {
58 0 : MOZ_ASSERT(kind == CodeLabel || kind == RawPointer);
59 0 : }
60 :
61 : bool
62 0 : LinkDataTier::InternalLink::isRawPointerPatch()
63 : {
64 0 : return true;
65 : }
66 : #endif
67 :
68 : size_t
69 0 : LinkDataTier::SymbolicLinkArray::serializedSize() const
70 : {
71 0 : size_t size = 0;
72 0 : for (const Uint32Vector& offsets : *this)
73 0 : size += SerializedPodVectorSize(offsets);
74 0 : return size;
75 : }
76 :
77 : uint8_t*
78 0 : LinkDataTier::SymbolicLinkArray::serialize(uint8_t* cursor) const
79 : {
80 0 : for (const Uint32Vector& offsets : *this)
81 0 : cursor = SerializePodVector(cursor, offsets);
82 0 : return cursor;
83 : }
84 :
85 : const uint8_t*
86 0 : LinkDataTier::SymbolicLinkArray::deserialize(const uint8_t* cursor)
87 : {
88 0 : for (Uint32Vector& offsets : *this) {
89 0 : cursor = DeserializePodVector(cursor, &offsets);
90 0 : if (!cursor)
91 0 : return nullptr;
92 : }
93 0 : return cursor;
94 : }
95 :
96 : size_t
97 0 : LinkDataTier::SymbolicLinkArray::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
98 : {
99 0 : size_t size = 0;
100 0 : for (const Uint32Vector& offsets : *this)
101 0 : size += offsets.sizeOfExcludingThis(mallocSizeOf);
102 0 : return size;
103 : }
104 :
105 : size_t
106 0 : LinkDataTier::serializedSize() const
107 : {
108 : return sizeof(pod()) +
109 0 : SerializedPodVectorSize(internalLinks) +
110 0 : symbolicLinks.serializedSize();
111 : }
112 :
113 : uint8_t*
114 0 : LinkDataTier::serialize(uint8_t* cursor) const
115 : {
116 0 : MOZ_ASSERT(tier == Tier::Ion);
117 :
118 0 : cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
119 0 : cursor = SerializePodVector(cursor, internalLinks);
120 0 : cursor = symbolicLinks.serialize(cursor);
121 0 : return cursor;
122 : }
123 :
124 : const uint8_t*
125 0 : LinkDataTier::deserialize(const uint8_t* cursor)
126 : {
127 0 : (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
128 0 : (cursor = DeserializePodVector(cursor, &internalLinks)) &&
129 0 : (cursor = symbolicLinks.deserialize(cursor));
130 0 : return cursor;
131 : }
132 :
133 : size_t
134 0 : LinkDataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
135 : {
136 0 : return internalLinks.sizeOfExcludingThis(mallocSizeOf) +
137 0 : symbolicLinks.sizeOfExcludingThis(mallocSizeOf);
138 : }
139 :
140 : Tiers
141 0 : LinkData::tiers() const
142 : {
143 0 : return Tiers(tier_->tier);
144 : }
145 :
146 : const LinkDataTier&
147 0 : LinkData::linkData(Tier tier) const
148 : {
149 0 : switch (tier) {
150 : case Tier::Debug:
151 : case Tier::Baseline:
152 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Baseline);
153 0 : return *tier_;
154 : case Tier::Ion:
155 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Ion);
156 0 : return *tier_;
157 : case Tier::TBD:
158 0 : return *tier_;
159 : default:
160 0 : MOZ_CRASH();
161 : }
162 : }
163 :
164 : LinkDataTier&
165 0 : LinkData::linkData(Tier tier)
166 : {
167 0 : switch (tier) {
168 : case Tier::Debug:
169 : case Tier::Baseline:
170 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Baseline);
171 0 : return *tier_;
172 : case Tier::Ion:
173 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Ion);
174 0 : return *tier_;
175 : case Tier::TBD:
176 0 : return *tier_;
177 : default:
178 0 : MOZ_CRASH();
179 : }
180 : }
181 :
182 : bool
183 0 : LinkData::initTier(Tier tier)
184 : {
185 0 : MOZ_ASSERT(!tier_);
186 0 : tier_ = js::MakeUnique<LinkDataTier>(tier);
187 0 : return tier_ != nullptr;
188 : }
189 :
190 : size_t
191 0 : LinkData::serializedSize() const
192 : {
193 0 : return tier_->serializedSize();
194 : }
195 :
196 : uint8_t*
197 0 : LinkData::serialize(uint8_t* cursor) const
198 : {
199 0 : cursor = tier_->serialize(cursor);
200 0 : return cursor;
201 : }
202 :
203 : const uint8_t*
204 0 : LinkData::deserialize(const uint8_t* cursor)
205 : {
206 0 : (cursor = tier_->deserialize(cursor));
207 0 : return cursor;
208 : }
209 :
210 : size_t
211 0 : LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
212 : {
213 0 : return tier_->sizeOfExcludingThis(mallocSizeOf);
214 : }
215 :
216 : /* virtual */ void
217 0 : Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
218 : {
219 0 : if (maybeBytecodeSize)
220 0 : *maybeBytecodeSize = bytecode_->bytes.length();
221 :
222 : // The compiled debug code must not be saved, set compiled size to 0,
223 : // so Module::assumptionsMatch will return false during assumptions
224 : // deserialization.
225 0 : if (maybeCompiledSize && metadata().debugEnabled)
226 0 : *maybeCompiledSize = 0;
227 :
228 0 : if (maybeCompiledSize && !metadata().debugEnabled) {
229 0 : *maybeCompiledSize = assumptions_.serializedSize() +
230 0 : linkData_.serializedSize() +
231 0 : SerializedVectorSize(imports_) +
232 0 : SerializedVectorSize(exports_) +
233 0 : SerializedPodVectorSize(dataSegments_) +
234 0 : SerializedVectorSize(elemSegments_) +
235 0 : code_->serializedSize();
236 : }
237 0 : }
238 :
239 : /* virtual */ void
240 0 : Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
241 : uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const
242 : {
243 0 : MOZ_ASSERT(!!maybeBytecodeBegin == !!maybeBytecodeSize);
244 0 : MOZ_ASSERT(!!maybeCompiledBegin == !!maybeCompiledSize);
245 :
246 0 : if (maybeBytecodeBegin) {
247 : // Bytecode deserialization is not guarded by Assumptions and thus must not
248 : // change incompatibly between builds. Thus, for simplicity, the format
249 : // of the bytecode file is simply a .wasm file (thus, backwards
250 : // compatibility is ensured by backwards compatibility of the wasm
251 : // binary format).
252 :
253 0 : const Bytes& bytes = bytecode_->bytes;
254 0 : uint8_t* bytecodeEnd = WriteBytes(maybeBytecodeBegin, bytes.begin(), bytes.length());
255 0 : MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
256 : }
257 :
258 0 : MOZ_ASSERT_IF(maybeCompiledBegin && metadata().debugEnabled, maybeCompiledSize == 0);
259 :
260 0 : if (maybeCompiledBegin && !metadata().debugEnabled) {
261 : // Assumption must be serialized at the beginning of the compiled bytes so
262 : // that compiledAssumptionsMatch can detect a build-id mismatch before any
263 : // other decoding occurs.
264 :
265 0 : uint8_t* cursor = maybeCompiledBegin;
266 0 : cursor = assumptions_.serialize(cursor);
267 0 : cursor = linkData_.serialize(cursor);
268 0 : cursor = SerializeVector(cursor, imports_);
269 0 : cursor = SerializeVector(cursor, exports_);
270 0 : cursor = SerializePodVector(cursor, dataSegments_);
271 0 : cursor = SerializeVector(cursor, elemSegments_);
272 0 : cursor = code_->serialize(cursor, linkData_);
273 0 : MOZ_RELEASE_ASSERT(cursor == maybeCompiledBegin + maybeCompiledSize);
274 : }
275 0 : }
276 :
277 : /* static */ bool
278 0 : Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
279 : {
280 0 : Assumptions cached;
281 0 : if (!cached.deserialize(compiledBegin, remain))
282 0 : return false;
283 :
284 0 : return current == cached;
285 : }
286 :
287 : /* static */ SharedModule
288 0 : Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
289 : const uint8_t* compiledBegin, size_t compiledSize,
290 : Metadata* maybeMetadata)
291 : {
292 0 : MutableBytes bytecode = js_new<ShareableBytes>();
293 0 : if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeSize))
294 0 : return nullptr;
295 :
296 0 : memcpy(bytecode->bytes.begin(), bytecodeBegin, bytecodeSize);
297 :
298 0 : Assumptions assumptions;
299 0 : const uint8_t* cursor = assumptions.deserialize(compiledBegin, compiledSize);
300 0 : if (!cursor)
301 0 : return nullptr;
302 :
303 0 : LinkData linkData;
304 0 : if (!linkData.initTier(Tier::Ion))
305 0 : return nullptr;
306 :
307 0 : cursor = linkData.deserialize(cursor);
308 0 : if (!cursor)
309 0 : return nullptr;
310 :
311 0 : ImportVector imports;
312 0 : cursor = DeserializeVector(cursor, &imports);
313 0 : if (!cursor)
314 0 : return nullptr;
315 :
316 0 : ExportVector exports;
317 0 : cursor = DeserializeVector(cursor, &exports);
318 0 : if (!cursor)
319 0 : return nullptr;
320 :
321 0 : DataSegmentVector dataSegments;
322 0 : cursor = DeserializePodVector(cursor, &dataSegments);
323 0 : if (!cursor)
324 0 : return nullptr;
325 :
326 0 : ElemSegmentVector elemSegments;
327 0 : cursor = DeserializeVector(cursor, &elemSegments);
328 0 : if (!cursor)
329 0 : return nullptr;
330 :
331 0 : MutableCode code = js_new<Code>();
332 0 : cursor = code->deserialize(cursor, bytecode, linkData, maybeMetadata);
333 0 : if (!cursor)
334 0 : return nullptr;
335 :
336 0 : MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
337 0 : MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
338 :
339 0 : return js_new<Module>(Move(assumptions),
340 : *code,
341 : nullptr, // Serialized code is never debuggable
342 0 : Move(linkData),
343 0 : Move(imports),
344 0 : Move(exports),
345 0 : Move(dataSegments),
346 0 : Move(elemSegments),
347 0 : *bytecode);
348 : }
349 :
350 : /* virtual */ JSObject*
351 0 : Module::createObject(JSContext* cx)
352 : {
353 0 : if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly))
354 0 : return nullptr;
355 :
356 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
357 0 : return WasmModuleObject::create(cx, *this, proto);
358 : }
359 :
360 : struct MemUnmap
361 : {
362 : uint32_t size;
363 0 : MemUnmap() : size(0) {}
364 0 : explicit MemUnmap(uint32_t size) : size(size) {}
365 0 : void operator()(uint8_t* p) { MOZ_ASSERT(size); PR_MemUnmap(p, size); }
366 : };
367 :
368 : typedef UniquePtr<uint8_t, MemUnmap> UniqueMapping;
369 :
370 : static UniqueMapping
371 0 : MapFile(PRFileDesc* file, PRFileInfo* info)
372 : {
373 0 : if (PR_GetOpenFileInfo(file, info) != PR_SUCCESS)
374 0 : return nullptr;
375 :
376 0 : PRFileMap* map = PR_CreateFileMap(file, info->size, PR_PROT_READONLY);
377 0 : if (!map)
378 0 : return nullptr;
379 :
380 : // PRFileMap objects do not need to be kept alive after the memory has been
381 : // mapped, so unconditionally close the PRFileMap, regardless of whether
382 : // PR_MemMap succeeds.
383 0 : uint8_t* memory = (uint8_t*)PR_MemMap(map, 0, info->size);
384 0 : PR_CloseFileMap(map);
385 0 : return UniqueMapping(memory, MemUnmap(info->size));
386 : }
387 :
388 : bool
389 0 : wasm::CompiledModuleAssumptionsMatch(PRFileDesc* compiled, JS::BuildIdCharVector&& buildId)
390 : {
391 : PRFileInfo info;
392 0 : UniqueMapping mapping = MapFile(compiled, &info);
393 0 : if (!mapping)
394 0 : return false;
395 :
396 0 : Assumptions assumptions(Move(buildId));
397 0 : return Module::assumptionsMatch(assumptions, mapping.get(), info.size);
398 : }
399 :
400 : SharedModule
401 0 : wasm::DeserializeModule(PRFileDesc* bytecodeFile, PRFileDesc* maybeCompiledFile,
402 : JS::BuildIdCharVector&& buildId, UniqueChars filename,
403 : unsigned line, unsigned column)
404 : {
405 : PRFileInfo bytecodeInfo;
406 0 : UniqueMapping bytecodeMapping = MapFile(bytecodeFile, &bytecodeInfo);
407 0 : if (!bytecodeMapping)
408 0 : return nullptr;
409 :
410 0 : if (PRFileDesc* compiledFile = maybeCompiledFile) {
411 : PRFileInfo compiledInfo;
412 0 : UniqueMapping compiledMapping = MapFile(compiledFile, &compiledInfo);
413 0 : if (!compiledMapping)
414 0 : return nullptr;
415 :
416 0 : return Module::deserialize(bytecodeMapping.get(), bytecodeInfo.size,
417 0 : compiledMapping.get(), compiledInfo.size);
418 : }
419 :
420 : // Since the compiled file's assumptions don't match, we must recompile from
421 : // bytecode. The bytecode file format is simply that of a .wasm (see
422 : // Module::serialize).
423 :
424 0 : MutableBytes bytecode = js_new<ShareableBytes>();
425 0 : if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeInfo.size))
426 0 : return nullptr;
427 :
428 0 : memcpy(bytecode->bytes.begin(), bytecodeMapping.get(), bytecodeInfo.size);
429 :
430 0 : ScriptedCaller scriptedCaller;
431 0 : scriptedCaller.filename = Move(filename);
432 0 : scriptedCaller.line = line;
433 0 : scriptedCaller.column = column;
434 :
435 0 : CompileArgs args(Assumptions(Move(buildId)), Move(scriptedCaller));
436 :
437 0 : UniqueChars error;
438 0 : return Compile(*bytecode, Move(args), &error);
439 : }
440 :
441 : /* virtual */ void
442 0 : Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
443 : Metadata::SeenSet* seenMetadata,
444 : ShareableBytes::SeenSet* seenBytes,
445 : Code::SeenSet* seenCode,
446 : size_t* code,
447 : size_t* data) const
448 : {
449 0 : code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
450 0 : *data += mallocSizeOf(this) +
451 0 : assumptions_.sizeOfExcludingThis(mallocSizeOf) +
452 0 : linkData_.sizeOfExcludingThis(mallocSizeOf) +
453 0 : SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
454 0 : SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
455 0 : dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
456 0 : SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
457 0 : bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
458 0 : if (unlinkedCodeForDebugging_)
459 0 : *data += unlinkedCodeForDebugging_->sizeOfExcludingThis(mallocSizeOf);
460 0 : }
461 :
462 :
463 : // Extracting machine code as JS object. The result has the "code" property, as
464 : // a Uint8Array, and the "segments" property as array objects. The objects
465 : // contain offsets in the "code" array and basic information about a code
466 : // segment/function body.
467 : bool
468 0 : Module::extractCode(JSContext* cx, MutableHandleValue vp) const
469 : {
470 0 : RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
471 0 : if (!result)
472 0 : return false;
473 :
474 : // The tier could be a parameter to extractCode. For now, any tier will do.
475 0 : Tier tier = code().anyTier();
476 :
477 0 : const CodeSegment& codeSegment = code_->segment(tier);
478 0 : RootedObject code(cx, JS_NewUint8Array(cx, codeSegment.length()));
479 0 : if (!code)
480 0 : return false;
481 :
482 0 : memcpy(code->as<TypedArrayObject>().viewDataUnshared(), codeSegment.base(), codeSegment.length());
483 :
484 0 : RootedValue value(cx, ObjectValue(*code));
485 0 : if (!JS_DefineProperty(cx, result, "code", value, JSPROP_ENUMERATE))
486 0 : return false;
487 :
488 0 : RootedObject segments(cx, NewDenseEmptyArray(cx));
489 0 : if (!segments)
490 0 : return false;
491 :
492 0 : for (const CodeRange& p : metadata(tier).codeRanges) {
493 0 : RootedObject segment(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
494 0 : if (!segment)
495 0 : return false;
496 :
497 0 : value.setNumber((uint32_t)p.begin());
498 0 : if (!JS_DefineProperty(cx, segment, "begin", value, JSPROP_ENUMERATE))
499 0 : return false;
500 :
501 0 : value.setNumber((uint32_t)p.end());
502 0 : if (!JS_DefineProperty(cx, segment, "end", value, JSPROP_ENUMERATE))
503 0 : return false;
504 :
505 0 : value.setNumber((uint32_t)p.kind());
506 0 : if (!JS_DefineProperty(cx, segment, "kind", value, JSPROP_ENUMERATE))
507 0 : return false;
508 :
509 0 : if (p.isFunction()) {
510 0 : value.setNumber((uint32_t)p.funcIndex());
511 0 : if (!JS_DefineProperty(cx, segment, "funcIndex", value, JSPROP_ENUMERATE))
512 0 : return false;
513 :
514 0 : value.setNumber((uint32_t)p.funcNormalEntry());
515 0 : if (!JS_DefineProperty(cx, segment, "funcBodyBegin", value, JSPROP_ENUMERATE))
516 0 : return false;
517 :
518 0 : value.setNumber((uint32_t)p.end());
519 0 : if (!JS_DefineProperty(cx, segment, "funcBodyEnd", value, JSPROP_ENUMERATE))
520 0 : return false;
521 : }
522 :
523 0 : if (!NewbornArrayPush(cx, segments, ObjectValue(*segment)))
524 0 : return false;
525 : }
526 :
527 0 : value.setObject(*segments);
528 0 : if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE))
529 0 : return false;
530 :
531 0 : vp.setObject(*result);
532 0 : return true;
533 : }
534 :
535 : static uint32_t
536 0 : EvaluateInitExpr(const ValVector& globalImports, InitExpr initExpr)
537 : {
538 0 : switch (initExpr.kind()) {
539 : case InitExpr::Kind::Constant:
540 0 : return initExpr.val().i32();
541 : case InitExpr::Kind::GetGlobal:
542 0 : return globalImports[initExpr.globalIndex()].i32();
543 : }
544 :
545 0 : MOZ_CRASH("bad initializer expression");
546 : }
547 :
548 : bool
549 0 : Module::initSegments(JSContext* cx,
550 : HandleWasmInstanceObject instanceObj,
551 : Handle<FunctionVector> funcImports,
552 : HandleWasmMemoryObject memoryObj,
553 : const ValVector& globalImports) const
554 : {
555 0 : Instance& instance = instanceObj->instance();
556 0 : const SharedTableVector& tables = instance.tables();
557 :
558 : // Perform all error checks up front so that this function does not perform
559 : // partial initialization if an error is reported.
560 :
561 0 : for (const ElemSegment& seg : elemSegments_) {
562 0 : uint32_t numElems = seg.elemCodeRangeIndices.length();
563 :
564 0 : uint32_t tableLength = tables[seg.tableIndex]->length();
565 0 : uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
566 :
567 0 : if (offset > tableLength || tableLength - offset < numElems) {
568 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
569 0 : "elem", "table");
570 0 : return false;
571 : }
572 : }
573 :
574 0 : if (memoryObj) {
575 0 : for (const DataSegment& seg : dataSegments_) {
576 0 : uint32_t memoryLength = memoryObj->buffer().byteLength();
577 0 : uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
578 :
579 0 : if (offset > memoryLength || memoryLength - offset < seg.length) {
580 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
581 0 : "data", "memory");
582 0 : return false;
583 : }
584 : }
585 : } else {
586 0 : MOZ_ASSERT(dataSegments_.empty());
587 : }
588 :
589 : // Now that initialization can't fail partway through, write data/elem
590 : // segments into memories/tables.
591 :
592 0 : for (const ElemSegment& seg : elemSegments_) {
593 0 : Table& table = *tables[seg.tableIndex];
594 0 : uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
595 0 : Tier tier = Tier::TBD;
596 0 : const CodeRangeVector& codeRanges = metadata(tier).codeRanges;
597 0 : uint8_t* codeBase = instance.codeBase(tier);
598 :
599 0 : for (uint32_t i = 0; i < seg.elemCodeRangeIndices.length(); i++) {
600 0 : uint32_t funcIndex = seg.elemFuncIndices[i];
601 0 : if (funcIndex < funcImports.length() && IsExportedWasmFunction(funcImports[funcIndex])) {
602 0 : MOZ_ASSERT(!metadata().isAsmJS());
603 0 : MOZ_ASSERT(!table.isTypedFunction());
604 :
605 0 : HandleFunction f = funcImports[funcIndex];
606 0 : WasmInstanceObject* exportInstanceObj = ExportedFunctionToInstanceObject(f);
607 0 : Tier exportTier = Tier::TBD;
608 0 : const CodeRange& cr = exportInstanceObj->getExportedFunctionCodeRange(f, exportTier);
609 0 : Instance& exportInstance = exportInstanceObj->instance();
610 0 : table.set(offset + i, exportInstance.codeBase(exportTier) + cr.funcTableEntry(), exportInstance);
611 : } else {
612 0 : const CodeRange& cr = codeRanges[seg.elemCodeRangeIndices[i]];
613 0 : uint32_t entryOffset = table.isTypedFunction()
614 0 : ? cr.funcNormalEntry()
615 0 : : cr.funcTableEntry();
616 0 : table.set(offset + i, codeBase + entryOffset, instance);
617 : }
618 : }
619 : }
620 :
621 0 : if (memoryObj) {
622 0 : uint8_t* memoryBase = memoryObj->buffer().dataPointerEither().unwrap(/* memcpy */);
623 :
624 0 : for (const DataSegment& seg : dataSegments_) {
625 0 : MOZ_ASSERT(seg.bytecodeOffset <= bytecode_->length());
626 0 : MOZ_ASSERT(seg.length <= bytecode_->length() - seg.bytecodeOffset);
627 0 : uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
628 0 : memcpy(memoryBase + offset, bytecode_->begin() + seg.bytecodeOffset, seg.length);
629 : }
630 : }
631 :
632 0 : return true;
633 : }
634 :
635 : static const Import&
636 0 : FindImportForFuncImport(const ImportVector& imports, uint32_t funcImportIndex)
637 : {
638 0 : for (const Import& import : imports) {
639 0 : if (import.kind != DefinitionKind::Function)
640 0 : continue;
641 0 : if (funcImportIndex == 0)
642 0 : return import;
643 0 : funcImportIndex--;
644 : }
645 0 : MOZ_CRASH("ran out of imports");
646 : }
647 :
648 : bool
649 0 : Module::instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const
650 : {
651 : #ifdef DEBUG
652 0 : for (auto t : code().tiers())
653 0 : MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
654 : #endif
655 :
656 0 : if (metadata().isAsmJS())
657 0 : return true;
658 :
659 0 : Tier tier = code().anyTier();
660 :
661 0 : for (size_t i = 0; i < metadata(tier).funcImports.length(); i++) {
662 0 : HandleFunction f = funcImports[i];
663 0 : if (!IsExportedFunction(f) || ExportedFunctionToInstance(f).isAsmJS())
664 0 : continue;
665 :
666 0 : uint32_t funcIndex = ExportedFunctionToFuncIndex(f);
667 0 : Instance& instance = ExportedFunctionToInstance(f);
668 0 : const FuncExport& funcExport = instance.metadata(tier).lookupFuncExport(funcIndex);
669 :
670 0 : if (funcExport.sig() != metadata(tier).funcImports[i].sig()) {
671 0 : const Import& import = FindImportForFuncImport(imports_, i);
672 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_SIG,
673 0 : import.module.get(), import.field.get());
674 0 : return false;
675 : }
676 : }
677 :
678 0 : return true;
679 : }
680 :
681 : static bool
682 0 : CheckLimits(JSContext* cx, uint32_t declaredMin, const Maybe<uint32_t>& declaredMax, uint32_t actualLength,
683 : const Maybe<uint32_t>& actualMax, bool isAsmJS, const char* kind)
684 : {
685 0 : if (isAsmJS) {
686 0 : MOZ_ASSERT(actualLength >= declaredMin);
687 0 : MOZ_ASSERT(!declaredMax);
688 0 : MOZ_ASSERT(actualLength == actualMax.value());
689 0 : return true;
690 : }
691 :
692 0 : if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
693 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, kind);
694 0 : return false;
695 : }
696 :
697 0 : if ((actualMax && declaredMax && *actualMax > *declaredMax) || (!actualMax && declaredMax)) {
698 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_MAX, kind);
699 0 : return false;
700 : }
701 :
702 0 : return true;
703 : }
704 :
705 : // asm.js module instantiation supplies its own buffer, but for wasm, create and
706 : // initialize the buffer if one is requested. Either way, the buffer is wrapped
707 : // in a WebAssembly.Memory object which is what the Instance stores.
708 : bool
709 0 : Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const
710 : {
711 0 : if (!metadata().usesMemory()) {
712 0 : MOZ_ASSERT(!memory);
713 0 : MOZ_ASSERT(dataSegments_.empty());
714 0 : return true;
715 : }
716 :
717 0 : uint32_t declaredMin = metadata().minMemoryLength;
718 0 : Maybe<uint32_t> declaredMax = metadata().maxMemoryLength;
719 :
720 0 : if (memory) {
721 0 : ArrayBufferObjectMaybeShared& buffer = memory->buffer();
722 0 : MOZ_ASSERT_IF(metadata().isAsmJS(), buffer.isPreparedForAsmJS());
723 0 : MOZ_ASSERT_IF(!metadata().isAsmJS(), buffer.as<ArrayBufferObject>().isWasm());
724 :
725 0 : if (!CheckLimits(cx, declaredMin, declaredMax, buffer.byteLength(), buffer.wasmMaxSize(),
726 0 : metadata().isAsmJS(), "Memory")) {
727 0 : return false;
728 : }
729 : } else {
730 0 : MOZ_ASSERT(!metadata().isAsmJS());
731 0 : MOZ_ASSERT(metadata().memoryUsage == MemoryUsage::Unshared);
732 :
733 : RootedArrayBufferObjectMaybeShared buffer(cx,
734 0 : ArrayBufferObject::createForWasm(cx, declaredMin, declaredMax));
735 0 : if (!buffer)
736 0 : return false;
737 :
738 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
739 :
740 0 : memory.set(WasmMemoryObject::create(cx, buffer, proto));
741 0 : if (!memory)
742 0 : return false;
743 : }
744 :
745 0 : return true;
746 : }
747 :
748 : bool
749 0 : Module::instantiateTable(JSContext* cx, MutableHandleWasmTableObject tableObj,
750 : SharedTableVector* tables) const
751 : {
752 0 : if (tableObj) {
753 0 : MOZ_ASSERT(!metadata().isAsmJS());
754 :
755 0 : MOZ_ASSERT(metadata().tables.length() == 1);
756 0 : const TableDesc& td = metadata().tables[0];
757 0 : MOZ_ASSERT(td.external);
758 :
759 0 : Table& table = tableObj->table();
760 0 : if (!CheckLimits(cx, td.limits.initial, td.limits.maximum, table.length(), table.maximum(),
761 0 : metadata().isAsmJS(), "Table")) {
762 0 : return false;
763 : }
764 :
765 0 : if (!tables->append(&table)) {
766 0 : ReportOutOfMemory(cx);
767 0 : return false;
768 : }
769 : } else {
770 0 : for (const TableDesc& td : metadata().tables) {
771 0 : SharedTable table;
772 0 : if (td.external) {
773 0 : MOZ_ASSERT(!tableObj);
774 0 : MOZ_ASSERT(td.kind == TableKind::AnyFunction);
775 :
776 0 : tableObj.set(WasmTableObject::create(cx, td.limits));
777 0 : if (!tableObj)
778 0 : return false;
779 :
780 0 : table = &tableObj->table();
781 : } else {
782 0 : table = Table::create(cx, td, /* HandleWasmTableObject = */ nullptr);
783 0 : if (!table)
784 0 : return false;
785 : }
786 :
787 0 : if (!tables->emplaceBack(table)) {
788 0 : ReportOutOfMemory(cx);
789 0 : return false;
790 : }
791 : }
792 : }
793 :
794 0 : return true;
795 : }
796 :
797 : static bool
798 0 : GetFunctionExport(JSContext* cx,
799 : HandleWasmInstanceObject instanceObj,
800 : Handle<FunctionVector> funcImports,
801 : const Export& exp,
802 : MutableHandleValue val)
803 : {
804 0 : if (exp.funcIndex() < funcImports.length() &&
805 0 : IsExportedWasmFunction(funcImports[exp.funcIndex()]))
806 : {
807 0 : val.setObject(*funcImports[exp.funcIndex()]);
808 0 : return true;
809 : }
810 :
811 0 : RootedFunction fun(cx);
812 0 : if (!instanceObj->getExportedFunction(cx, instanceObj, exp.funcIndex(), &fun))
813 0 : return false;
814 :
815 0 : val.setObject(*fun);
816 0 : return true;
817 : }
818 :
819 : static bool
820 0 : GetGlobalExport(JSContext* cx, const GlobalDescVector& globals, uint32_t globalIndex,
821 : const ValVector& globalImports, MutableHandleValue jsval)
822 : {
823 0 : const GlobalDesc& global = globals[globalIndex];
824 :
825 : // Imports are located upfront in the globals array.
826 0 : Val val;
827 0 : switch (global.kind()) {
828 0 : case GlobalKind::Import: val = globalImports[globalIndex]; break;
829 0 : case GlobalKind::Variable: MOZ_CRASH("mutable variables can't be exported");
830 0 : case GlobalKind::Constant: val = global.constantValue(); break;
831 : }
832 :
833 0 : switch (global.type()) {
834 : case ValType::I32: {
835 0 : jsval.set(Int32Value(val.i32()));
836 0 : return true;
837 : }
838 : case ValType::I64: {
839 0 : MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
840 0 : RootedObject obj(cx, CreateI64Object(cx, val.i64()));
841 0 : if (!obj)
842 0 : return false;
843 0 : jsval.set(ObjectValue(*obj));
844 0 : return true;
845 : }
846 : case ValType::F32: {
847 0 : float f = val.f32();
848 0 : if (JitOptions.wasmTestMode && IsNaN(f)) {
849 0 : RootedObject obj(cx, CreateCustomNaNObject(cx, &f));
850 0 : if (!obj)
851 0 : return false;
852 0 : jsval.set(ObjectValue(*obj));
853 0 : return true;
854 : }
855 0 : jsval.set(DoubleValue(double(f)));
856 0 : return true;
857 : }
858 : case ValType::F64: {
859 0 : double d = val.f64();
860 0 : if (JitOptions.wasmTestMode && IsNaN(d)) {
861 0 : RootedObject obj(cx, CreateCustomNaNObject(cx, &d));
862 0 : if (!obj)
863 0 : return false;
864 0 : jsval.set(ObjectValue(*obj));
865 0 : return true;
866 : }
867 0 : jsval.set(DoubleValue(d));
868 0 : return true;
869 : }
870 : default: {
871 0 : break;
872 : }
873 : }
874 0 : MOZ_CRASH("unexpected type when creating global exports");
875 : }
876 :
877 : static bool
878 0 : CreateExportObject(JSContext* cx,
879 : HandleWasmInstanceObject instanceObj,
880 : Handle<FunctionVector> funcImports,
881 : HandleWasmTableObject tableObj,
882 : HandleWasmMemoryObject memoryObj,
883 : const ValVector& globalImports,
884 : const ExportVector& exports)
885 : {
886 0 : const Instance& instance = instanceObj->instance();
887 0 : const Metadata& metadata = instance.metadata();
888 :
889 0 : if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
890 0 : RootedValue val(cx);
891 0 : if (!GetFunctionExport(cx, instanceObj, funcImports, exports[0], &val))
892 0 : return false;
893 0 : instanceObj->initExportsObj(val.toObject());
894 0 : return true;
895 : }
896 :
897 0 : RootedObject exportObj(cx);
898 0 : if (metadata.isAsmJS())
899 0 : exportObj = NewBuiltinClassInstance<PlainObject>(cx);
900 : else
901 0 : exportObj = NewObjectWithGivenProto<PlainObject>(cx, nullptr);
902 0 : if (!exportObj)
903 0 : return false;
904 :
905 0 : for (const Export& exp : exports) {
906 0 : JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
907 0 : if (!atom)
908 0 : return false;
909 :
910 0 : RootedId id(cx, AtomToId(atom));
911 0 : RootedValue val(cx);
912 0 : switch (exp.kind()) {
913 : case DefinitionKind::Function:
914 0 : if (!GetFunctionExport(cx, instanceObj, funcImports, exp, &val))
915 0 : return false;
916 0 : break;
917 : case DefinitionKind::Table:
918 0 : val = ObjectValue(*tableObj);
919 0 : break;
920 : case DefinitionKind::Memory:
921 0 : val = ObjectValue(*memoryObj);
922 0 : break;
923 : case DefinitionKind::Global:
924 0 : if (!GetGlobalExport(cx, metadata.globals, exp.globalIndex(), globalImports, &val))
925 0 : return false;
926 0 : break;
927 : }
928 :
929 0 : if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
930 0 : return false;
931 : }
932 :
933 0 : if (!metadata.isAsmJS()) {
934 0 : if (!JS_FreezeObject(cx, exportObj))
935 0 : return false;
936 : }
937 :
938 0 : instanceObj->initExportsObj(*exportObj);
939 0 : return true;
940 : }
941 :
942 : bool
943 0 : Module::instantiate(JSContext* cx,
944 : Handle<FunctionVector> funcImports,
945 : HandleWasmTableObject tableImport,
946 : HandleWasmMemoryObject memoryImport,
947 : const ValVector& globalImports,
948 : HandleObject instanceProto,
949 : MutableHandleWasmInstanceObject instance) const
950 : {
951 0 : if (!instantiateFunctions(cx, funcImports))
952 0 : return false;
953 :
954 0 : RootedWasmMemoryObject memory(cx, memoryImport);
955 0 : if (!instantiateMemory(cx, &memory))
956 0 : return false;
957 :
958 0 : RootedWasmTableObject table(cx, tableImport);
959 0 : SharedTableVector tables;
960 0 : if (!instantiateTable(cx, &table, &tables))
961 0 : return false;
962 :
963 0 : auto globalSegment = GlobalSegment::create(metadata().globalDataLength);
964 0 : if (!globalSegment) {
965 0 : ReportOutOfMemory(cx);
966 0 : return false;
967 : }
968 :
969 0 : SharedCode code(code_);
970 :
971 0 : if (metadata().debugEnabled) {
972 : // The first time through, use the pre-linked code in the module but
973 : // mark it as busy. Subsequently, instantiate the copy of the code
974 : // bytes that we keep around for debugging instead, because the debugger
975 : // may patch the pre-linked code at any time.
976 0 : if (!codeIsBusy_.compareExchange(false, true)) {
977 : auto codeSegment = CodeSegment::create(Tier::Baseline,
978 0 : *unlinkedCodeForDebugging_,
979 : *bytecode_,
980 : linkData_.linkData(Tier::Baseline),
981 0 : metadata());
982 0 : if (!codeSegment) {
983 0 : ReportOutOfMemory(cx);
984 0 : return false;
985 : }
986 :
987 0 : code = js_new<Code>(Move(codeSegment), metadata());
988 0 : if (!code) {
989 0 : ReportOutOfMemory(cx);
990 0 : return false;
991 : }
992 : }
993 : }
994 :
995 : // To support viewing the source of an instance (Instance::createText), the
996 : // instance must hold onto a ref of the bytecode (keeping it alive). This
997 : // wastes memory for most users, so we try to only save the source when a
998 : // developer actually cares: when the compartment is debuggable (which is
999 : // true when the web console is open), has code compiled with debug flag
1000 : // enabled or a names section is present (since this going to be stripped
1001 : // for non-developer builds).
1002 :
1003 0 : const ShareableBytes* maybeBytecode = nullptr;
1004 0 : if (cx->compartment()->isDebuggee() || metadata().debugEnabled ||
1005 0 : !metadata().funcNames.empty())
1006 : {
1007 0 : maybeBytecode = bytecode_.get();
1008 : }
1009 :
1010 : // The debug object must be present even when debugging is not enabled: It
1011 : // provides the lazily created source text for the program, even if that
1012 : // text is a placeholder message when debugging is not enabled.
1013 :
1014 0 : bool binarySource = cx->compartment()->debuggerObservesBinarySource();
1015 0 : auto debug = cx->make_unique<DebugState>(code, maybeBytecode, binarySource);
1016 0 : if (!debug)
1017 0 : return false;
1018 :
1019 0 : instance.set(WasmInstanceObject::create(cx,
1020 : code,
1021 0 : Move(debug),
1022 0 : Move(globalSegment),
1023 : memory,
1024 0 : Move(tables),
1025 : funcImports,
1026 : globalImports,
1027 0 : instanceProto));
1028 0 : if (!instance)
1029 0 : return false;
1030 :
1031 0 : if (!CreateExportObject(cx, instance, funcImports, table, memory, globalImports, exports_))
1032 0 : return false;
1033 :
1034 : // Register the instance with the JSCompartment so that it can find out
1035 : // about global events like profiling being enabled in the compartment.
1036 : // Registration does not require a fully-initialized instance and must
1037 : // precede initSegments as the final pre-requisite for a live instance.
1038 :
1039 0 : if (!cx->compartment()->wasm.registerInstance(cx, instance))
1040 0 : return false;
1041 :
1042 : // Perform initialization as the final step after the instance is fully
1043 : // constructed since this can make the instance live to content (even if the
1044 : // start function fails).
1045 :
1046 0 : if (!initSegments(cx, instance, funcImports, memory, globalImports))
1047 0 : return false;
1048 :
1049 : // Now that the instance is fully live and initialized, the start function.
1050 : // Note that failure may cause instantiation to throw, but the instance may
1051 : // still be live via edges created by initSegments or the start function.
1052 :
1053 0 : if (metadata().startFuncIndex) {
1054 0 : FixedInvokeArgs<0> args(cx);
1055 0 : if (!instance->instance().callExport(cx, *metadata().startFuncIndex, args))
1056 0 : return false;
1057 : }
1058 :
1059 0 : uint32_t mode = uint32_t(metadata().isAsmJS() ? Telemetry::ASMJS : Telemetry::WASM);
1060 0 : cx->runtime()->addTelemetry(JS_TELEMETRY_AOT_USAGE, mode);
1061 :
1062 0 : return true;
1063 : }
|